Динамическая библиотека SimulIDE

Раздел посвящен обсуждению вопросов разработки DLL-модулей подвижного состава
Ответить
andreykod
Сообщения: 50
Зарегистрирован: 17 фев 2020, 11:21
Город: Москва
Настоящее имя: Андрей

Динамическая библиотека SimulIDE

Сообщение andreykod » 31 янв 2021, 11:48

С самого начала разработки ЧС4т и адаптации симулятора схем было понятно, что система с QSharedMemory и этими мутексами в сегментах, никак не масштабируема и вызывает кучу всевозможных проблем, вроде SIGSEGV от "осиротевших" сегментов и прочего. SimulIDE - современный qt opensource проект, имеет относительно простую модульную архитектуру и интерфейс на qwidget. Ну вот прямо напрашивается для применения в нашем проекте в виде динамической библиотеки GUI! Не буду расписывать здесь все внутренности симулятора, это можно посмотреть в самом коде. Идея сама такова:

Мы запускаем приложение в отдельном потоке, для взаимодействия используем сигналы и слоты. Запускать можно виджет со схемой, сам симулятор и вручную проводить инициализацию, а можно и само mainwindow. Сигнал-слоты, потокобезопасны, включая функцию connect. В оригинальном коде симулятора действуют статические указатели на определенные компоненты: симулятор можно запустить вызвав

Код: Выделить всё

Simulator::self()->runContinuous();
в любой точке программы (это касается потока симулятора и его GUI), главное при этом убедиться, что объект существует. Таким образом можно выполнить:

Код: Выделить всё

QObject::connect(Circuit::self()->findChild<Led*>("Led-2"), &Led::stateChanged, receiver, &Receiver_class::stateChanged);
вне потока симулятора.
Основная проблема - это QApplication. Потоки то у нас разные, а QApplication две штуки выходит. Вернее QCoreApplication и QApplication в другом потоке. Вместе они существовать не могут, так еще и GUI должен быть в потоке QApplication.
В этом случае думаю, что можно вот так сделать.
Мы парсим (парсер в qt можно создавать до QApplication) аргументы строки и создаем либо std::thread с QApplication и самим симулятором, либо действуем по старой схеме. В первом случае просто вызываем Circuit::self()->loadCirc("pathtocircuit") в коде локомотива. Второй случай для локомотивов без симуляции схем.

Код: Выделить всё

void startsim(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    
    a.exec();
}
Вот вот этот метод мы отправляем в std::thread

И на самом интересном месте мы опять попадаем в singleton ловушку, на этот раз уже в самом симуляторе. Помните?

Код: Выделить всё

Simulator::self()->runContinuous();
Статические указатели

А если мы захотим запустить несколько локомотивов? А может электричку? Необходима новая синглтоновая обертка, которая будет жить в потоке QApplication, создавать и удалять mainwidow, управлять и предоставлять указатели на главные компоненты объектов симулятора для его внутренностей, API для класса vehicle.
Для этого нужно избавиться от статических указателей понатыканных во всем симуляторе схем. Работа получается огромная, но я уже ее начал. К тому же мне помогает еще то, что в симуляторе будут отсутствовать виджеты редактирования и их компоненты.

Остается еще несколько вопросов к ув. maisvendoo т.к. код RRS я еще не так подробно изучил
Выходит мы ломаем концепцию simulator как отдельного процесса. Возможна ли такая модификация? Лично я думаю, что было бы неплохо создавать окна в коде локомотива. Хотя бы для сообщений, какого-нибудь машинного или клавиатуры клуба.
Вроде OSG qt умеет показывать виджеты? Может mainwindow туда отправлять? Или так нельзя?
Возможна ли вообще такая плотная интеграция симулятора схем с RRS? Стоит мне продолжать работу над этим?

P.S.
Уже сейчас по вышеописанной схеме, но пока со статическими указателями симулятор схем запускается и работает в приложении "затычка"

Готов выслушать все предложения!

Github: https://github.com/asafran/SimulIDE-lib
Последний раз редактировалось andreykod 09 фев 2021, 15:30, всего редактировалось 1 раз.

Аватара пользователя
maisvendoo
Модератор
Сообщения: 339
Зарегистрирован: 13 авг 2019, 10:25
Город: Ростов-на-Дону
Настоящее имя: Дмитрий
VK: https://vk.com/maisvendoo
Контактная информация:

Re: Динамическая библиотека SimulIDE

Сообщение maisvendoo » 31 янв 2021, 21:49

Все таки я склоняюсь к межпроцессному взаимодействию на базе сокетов. Так было изначально, надо вернуть это назад, по крайней мере для взаимодействия с графической подсистемой
andreykod писал(а):
31 янв 2021, 11:48
Возможна ли вообще такая плотная интеграция симулятора схем с RRS? Стоит мне продолжать работу над этим?
Такая интеграция необходима, в рамках процесса simulator вполне возможна.
Возврата к деспотии Ситхов не будет!

andreykod
Сообщения: 50
Зарегистрирован: 17 фев 2020, 11:21
Город: Москва
Настоящее имя: Андрей

Re: Динамическая библиотека SimulIDE

Сообщение andreykod » 01 фев 2021, 17:43

maisvendoo писал(а):
31 янв 2021, 21:49
Все таки я склоняюсь к межпроцессному взаимодействию на базе сокетов. Так было изначально, надо вернуть это назад, по крайней мере для взаимодействия с графической подсистемой
Я тоже смотрю в эту сторону, но пока не могу решить что конкретно и куда отправлять. :?
Там не все так просто.
В симуляторе схем есть четыре основных класса:
  • Circuit : public QGraphicsScene
  • Simulator
  • eElement
  • Component : public QObject, public QGraphicsItem
Первый живет в положенном ему CircuitView : public QGraphicsView. Симулятор живет в первом. От двух других наследуются сами компоненты.

Код: Выделить всё

class  Diode : public Component, public eDiode
{
    Q_OBJECT
    Q_PROPERTY( double Threshold  READ threshold WRITE setThreshold DESIGNABLE true USER true )
    Q_PROPERTY( double Zener_Volt READ zenerV    WRITE setZenerV    DESIGNABLE true USER true )
    

    public:

        QRectF boundingRect() const { return QRect( -12, -8, 24, 16 ); }

        Diode( Circuit* parent, QString type, QString id );
        ~Diode();

        static Component* construct( Circuit* parent, QString type, QString id );
        static LibraryItem *libraryItem();

        virtual void paint( QPainter *p, const QStyleOptionGraphicsItem *option, QWidget *widget );
};
Здесь у нас класс Diode наследованный от класса eDiode, который дополнительно описывает специфику полупроводникового диода. Component - прослойка между Circuit, LibraryItem и самим компонентом. Компонентов таких вот довольно много.
Все это образует единую систему, хотя и Simulator работает в отдельном потоке используя QtConcurrent. Если это как-то разъединить, то у нас перестают работать все остальные системы так, как мы бы хотели. Если вынести GUI в отдельный процесс, то QEvent из этого процесса не будет доходить до симулятора, сигналы о нажатии кнопки у переключателя тоже не будут доходить до его класса.

Никто не отменял возможности по окончании работы Sumulator быстро перекидывать состояния светодиодов и приборов в словарь и скидывать все это дело в класс симулятора через сокет. Но тут всплывает QProcess, не знаю как там в 5.15, но в 5.12 никакими методами, настройками QProcess не смог убедить упрямую винду не перенаправлять весь stdout процесса в консоль приложения, при этом блокируя write и read channel. :evil: При мультипроцессе мы убиваем основную идею: предоставить удобный для пользователя API по которому можно будет легко создать и получить указатель на различные объекты симулятора схем, по этому указателю найти и подключить слоты к нужному компоненту, приостановить симулятор, открыть схему и т.д.

А в OSG Qt можно же виджеты показывать? Может ему скормить CircuitView как виджет? Я в OSG не очень разбираюсь. :pardon:

Сейчас я пока чищу код от self указателей. Может чего придумаю еще. ;) Если не придумается, буду организовывать сокеты.

andreykod
Сообщения: 50
Зарегистрирован: 17 фев 2020, 11:21
Город: Москва
Настоящее имя: Андрей

Re: Динамическая библиотека SimulIDE

Сообщение andreykod » 09 фев 2021, 17:19

https://github.com/asafran/SimulIDE-lib
Всё :) теперь симулятор не является одним большим синглтоном. Сделал все предельно просто, может не очень аккуратно. Вообще там теперь много отличий.
В процессе таких перестановок не выжила подсистема симуляции микроконтроллеров, которая была еще одним синглтоном внутри синглтона (нельзя добавить на схему более одного МК). Теперь я уже убрал а не "заглушил" все инструменты для редактирования и работы с МК. При этом я не убирал возможность создавать subcircuit (подсхемы и микросхемы собственной разработки) https://www.simulide.com/p/creating-packages.html

P.s.
Никак мне в голову ничего не приходит.
maisvendoo, вы не применяли в своих проектах Qt Remote Objects? Хотя я думаю, что получится слишком медленно. Тогда уже проще сделать по вышеописанной резервной схеме.

Аватара пользователя
maisvendoo
Модератор
Сообщения: 339
Зарегистрирован: 13 авг 2019, 10:25
Город: Ростов-на-Дону
Настоящее имя: Дмитрий
VK: https://vk.com/maisvendoo
Контактная информация:

Re: Динамическая библиотека SimulIDE

Сообщение maisvendoo » 09 фев 2021, 18:17

Микроконтроллеры можно имитировать и на стороне РРС.
Qt Remote Objects не пробовал, видимо появилось в какой-то из крайних версий Qt5
Возврата к деспотии Ситхов не будет!

andreykod
Сообщения: 50
Зарегистрирован: 17 фев 2020, 11:21
Город: Москва
Настоящее имя: Андрей

Re: Динамическая библиотека SimulIDE

Сообщение andreykod » 13 фев 2021, 13:48

Qt Remote Objects нам никак не подходит по своей "тяжести" (это ж метаобъектная corba), да и где его применять. :?
maisvendoo писал(а):
09 фев 2021, 18:17
Микроконтроллеры можно имитировать и на стороне РРС.
:yes:

Я вижу несколько путей решения этой задачи:

Вариант 1:
Создаем несколько векторов в классе Circuit просто со значениями, делаем двухстороннее зеркало в классе локомотива.
Недостатки:
  • сложно контролировать симулятор схем, приостанавливать, следить за его состоянием (сложно добавлять в API новый функционал для контроля за некоторыми дополнительными классами)
  • необходимость костылей, таких как семафор в методе инициализации (при открытии схемы не самым лучшим образом происходит поиск элементов в библиотеке, схема открывается долго)
  • возможно будут трудности при разработке МВПС или нескольких секций
  • передача неиспользуемой информации
  • спам пакетами при изменении большого участка схемы (не так страшны сами задержки, как их перемножение при использовании буферов)
  • муторно будет с этими сокетами, конечно :oops:
  • зачем я избавлялся от синглтонов? :D
Преимущества:
  • этот вариант не идет против концепции RRS, не так сложно будет реализовать
  • разработчикам будет несложно работать со схемой в таком виде
Вариант 2:
По аргументу создаем поток с QApplication по вышеописанной схеме и работаем в одном приложении.
Недостатки:
  • костыль
  • превращает физический движок в приложение с GUI (по аргументу)
  • возможно даже удобнее разработчикам будет вариант с векторами
Преимущества:
  • контроль за симулятором с помощью системы сигналов и слотов по Qt::QueuedConnection
  • полная свобода действий, можно даже какие-то свои элементы внедрять, наследоваться от дефолтных классов и переопределять что нибудь
  • возможность создать свой QWidget для схемы, да и вообще возможность "залезать" внутрь инициализации и делать вообще свои классы для работы со схемой
  • возможность легко отправлять QEvent в симулятор схем
  • возможность напрямую считывать данные из необходимых классов с использованием мутексов
  • возможность использования GUI при разработке локомотивов
  • не отправляется ничего лишнего, вызов слота происходит через event loop (при изменении схемы мы не пытаемся отправить весь контейнер, а лишь помещаем одно сообщение в очередь)
Вариант 3: Самый правильный
Оба физ. симулятора живут в одном приложении.
Недостатки:
  • У меня нет пока идей как это можно организовать))) :?
Преимущества:
  • получается почти преимущества 1 варианта + преимущества 2 варианта
Такое можно встретить только в вебе.
https://www.qt.io/blog/2017/02/22/qt-qu ... -streaming
Даже так можно:
https://www.qt.io/web-assembly-example-slate
Но оно тоже требует QGuiApplication :lol:
QGraphicsScene в QML нет аналогов. Есть вот это, но https://doc.qt.io/qt-5/qquickpainteditem.html#details если это все по функционалу доводить до QGraphicsScene :shock:
Потом все переводить на QML, втыкать разные костыли для c++ логики :( ненене, это не подходит

Рубить приложение где то в середине - сразу нарушаем сигнал-слоты между gui и внутренностями, не будут работать evenты на перемещение мышкой и т.д.
Про это я писал выше. Все там взаимосвязано. :(

QGraphicsScene не является виджетом, но как мы будем передавать eventы из GraphicsView если будем его передавать в другой процесс? Даже этот вариант не особо подходит.

andreykod
Сообщения: 50
Зарегистрирован: 17 фев 2020, 11:21
Город: Москва
Настоящее имя: Андрей

Re: Динамическая библиотека SimulIDE

Сообщение andreykod » 18 фев 2021, 17:28

Набросал диаграмму в UML.
Это далеко не все классы, диаграмма вообще кривая получилась :pardon:
https://drive.google.com/file/d/1dd2mcJ ... 8TUrF/view
.svg это векторная картинка, рекомендую скачать, затем открыть браузером

Белый квадрат - это виджеты, голубой - физический симулятор (классы самих элементов туда по идее не входят).
Как читать UML можно посмотреть на хабре.

Пишите сюда, если что-то будет непонятно.

Аватара пользователя
Ромыч РЖДУЗ
Модератор
Сообщения: 837
Зарегистрирован: 13 авг 2019, 19:06
Город: Москва, Зеленоград
Настоящее имя: Роман
Контактная информация:

Re: Динамическая библиотека SimulIDE

Сообщение Ромыч РЖДУЗ » 21 фев 2021, 10:06

andreykod
Прошу нас простить, но из-за занятости Дмитрия другими темами в РГУПС и дома, он пока что не может полноценно оценить ваши работы и как-то применить их в RRS. Однако не думайте, что отмалчивается. Будет время, отпишется обязательно. ;)
Ваши наработки, несомненно найдут применение! :)
"Строю маршрут Москва-Адлер, всё готово на 95%! Осталось освоить редактор маршрутов, а так же нужны фото, профиль, схемы, видео, текстуры вокзалов....
Чёрт! Проект закрыт, автор исчез..." :o

andreykod
Сообщения: 50
Зарегистрирован: 17 фев 2020, 11:21
Город: Москва
Настоящее имя: Андрей

Re: Динамическая библиотека SimulIDE

Сообщение andreykod » 23 фев 2021, 14:23

Да, да! Это я понимаю. ;)
Я так тоже пришёл и предложил сразу внести весомые изменения в код rrs. Здесь главное - прийти к общему решению. Все хорошо обдумать. Если сейчас поспешить, то потом будут проблемы.

Пользуясь случаем, задам немного провокационный, возможно уже частый вопрос. Какие преимущества схемы с разными процессами в rrs? Все понятно с симуляторными комплексами, но в неразделяемом приложении?

Аватара пользователя
maisvendoo
Модератор
Сообщения: 339
Зарегистрирован: 13 авг 2019, 10:25
Город: Ростов-на-Дону
Настоящее имя: Дмитрий
VK: https://vk.com/maisvendoo
Контактная информация:

Re: Динамическая библиотека SimulIDE

Сообщение maisvendoo » 23 фев 2021, 15:12

andreykod писал(а):
23 фев 2021, 14:23
Какие преимущества схемы с разными процессами в rrs? Все понятно с симуляторными комплексами, но в неразделяемом приложении?
Преимущество ровно одно - возможность запускать физику и графику на разных машинах. Изначально ведь было взаимодействие между simulator и viewer по TCP/IP, потом, из-за ошибочного предположения что такая схема вызывает задержки делающие игру неиграбельной, я перешел на общую память. Сейчас она, эта общая память, как кость в горле, ломающая изначальную задумку и понудившая нас на тренажере ЧС2т городить костыли

Я предполагаю перейти к обратно на сокеты, такая схема необходимо потому, что этот проект я применяю и в тренажеростроении, где часто система является распределенной - физика и графика считается на разных машинах.

Как я понимаю, сейчас главная проблема внедрения SimulIDE именно в том, что физическая часть и визуальная работают в разных процессах. У меня на ум приходит пока только одно решение - часть, моделирующая схему как отдельный процесс, взаимодействующий с процессом simulator

Пока я копаю в сторону перелопачивания графики и возможности поездной работы. Поэтому могу предложить вариант форкруть текущую ветку RRS и проэкспериментировать с разными техниками внедрения. Тут вопрос в том, что раскрутится на все темы разработок, во всю ширь я не могу при всем желании
Возврата к деспотии Ситхов не будет!

Ответить

Вернуться в «Программирование модулей подвижного состава и оборудования (C++ API)»