2. Защищенные виртуальные методы
Перейдем к описанию методов, и начнем с тех, которые реализуются разработчиком DLL, а точнее могут быть им переопределены. Эти методы являются методами обратного вызова, то есть реализуются они разработчиком, а вызваются симулятором, в те моменты, когда это необходимо. Причем вызываются они самим классом Vehicle, поэтому имеют модификатор доступа protected.
- void initialization() - метод, вызываемый при инициализации подвижной единицы. Здесь выполняется начальная инициализация всего оборудования, которое содержит экипаж. Этот метод вызывается после загрузки DLL-модуля и чтения его конфигурационного файла.
- void loadConfig(QString cfg_path) - загрузка параметров из конфигурационного файла подвижной единицы. При вызове, через параметр cfg_path в метод симулятором передается полный путь к конфигурационному файлу. Этот метод используется исключительно для загрузки параметров конфига, введенных разработчиком, то есть тех, которые не содержаться в базовом классе Vehicle. Те переметры, которые определены в классе Vehicle читаются автоматически.
- void preStep(double t) - действия, выполняемые перед шагом интегрирования уравнений движения поезда. Симулятором сюда передается текущее модельное время в секундах, прошетшее с момента запуска игры.
- void step(double t, double dt) - вызывается на каждом шаге интегрирования уравнений движения поезда, из симулятора сюда передается текущее время t и текущий шаг интегрирования dt.
- void postStep(double t) - действия, выполняемые после шага интегрирования уравнений движения поезда.
- void hardwareOutput() - вывод сигналов во внешний пульт управления. Отсюда можно двигать стрелки и зажигать контрольные лампы на пульте домашнего тренажера, подключенного к симулятору.
Подытоживая, можно пояснить, что любое устройство, технический объект, создавая его компьютерную модель, в том числе локомотив или вагон, можно описать системой дифференциальных и алгебраических уравнений. Так вот, методы preStep() и postStep() следует использовать для решения алгебраических уравнений, а метод step() - для решения дифференциальных, описывающих разрабатываемый железнодорожный экипаж.
3. Публичные виртуальные методы
Эти методы вызываются другими классами, работающими с модулями подвижных единиц через интерфейс класса Vehcile, в частности они вызываются при работе класса Train, описывающего поезд. Поэтому они сделаны публичными. И хоть они и виртуальные, но их вобщем-то не обязательно переопределять. Но, для больше гибкости разработки решено было дать возможность разработчику DLL переопределить их.
- state_vector_t getAcceleration(state_vector_t &Y, double t) - расчет обобщенных ускорений подвижной единицы. Ключевой метод, в котором и сидит физика движения железнодорожного экипажа. Здесь, на основании сил и моментов, приложенных к кузову и колесным парам экипажа, расчитывается продольное ускорение кузова и угловые ускорения колесных пар. Этот метод вызывается классом Train при решении уравнений движения поезда.
Если интересна реализация этого метода, то вот она
simulator/vehicle/vehicle.cpp
Код: Выделить всё
state_vector_t Vehicle::getAcceleration(state_vector_t &Y, double t)
{
(void) t;
// Получаем скорость нашей ПЕ из вектора состояния поезда
double v = Y[idx + s];
// Переводим модуль скорости в км/ч
double V = abs(v) * Physics::kmh;
// Расчитываем силу от продольного профиля пути
double sin_beta = inc / 1000.0;
double G = full_mass * Physics::g * sin_beta;
// Расчитываем силу основного сопротивления движению
double w = b0 + (b1 + b2 * V + b3 * V * V) / q0;
double wk = 700.0 * curv;
double W = full_mass * Physics::g * (w + wk) / 1000.0;
// Расчитываем усилия, передаваемые от колесных пар на кузов
double sumEqWheelForce = 0;
for (size_t i = 1; i <= static_cast<size_t>(num_axis); i++)
{
double eqWheelForce = (Q_a[i] - Physics::fricForce(Q_r[i], dir * Y[idx + s + i])) / rk;
sumEqWheelForce += eqWheelForce;
}
// Расчитываем полную силу сопротивления
double Fr = Physics::fricForce(W + Q_r[0], dir * v);
// Считаем продольное ускорение кузова
*a.begin() = dir * (*Q_a.begin() - Fr + R1 - R2 + sumEqWheelForce - G) / ( full_mass + num_axis * J_axis / rk / rk);
// Считаем угловые ускорения колесных пар
auto end = a.end();
for (auto accel_it = a.begin() + 1; accel_it != end; ++accel_it)
*accel_it = *a.begin() / rk;
return a;
}
Когда может потребоваться переопределить этот метод? Ну, например, если возникнет желание создать вагон, движущийся с учетом наличия жидкого груза, например цистерну, на движения которой влияют колебания жидкости в ней. Можно придумать много других ситуаций, вопрос в том, что для большинства задач переопределять этот метод не надо, и переопределяя его вы действете на свой страх и риск.
Другие методы этой группы более прозаичные
- void keyProcess() - обработка нажатий клавиш на клавиатуре. Переопределять нужно, если какое-то оборудование, реализуемое вами не наследуется от класса Device (класс Device сам обрабатывает клавиатурный ввод) и требуется заставить его реагировать на управление с клавиатуры.
- void hardwareProcess() - вывод сигналов на внешний пульт управления. Релизация этого метода тривиальна. Оставлен виртуальным, пока нет полной ястности с API взаимодействия с внешними пультами. Внутри этого метода вызывается защищеннй виртуальный метод hardwareOutput(), так что необходимости в его переопределении нет, возможно, в будущих версиях он перестанет быть виртуальным.
Особняком стоит следующий метод
- void initBrakeDevices(double p0, double pTM, double pFL) - инициализация тормозных приборов. Передаются: p0 - зарядное давление ТМ; pTM - давление в ТМ данной подвижной единицы; pFL - давление в питательной магистрали (ПМ).
Зачем нужен этот метод? Почему его можно и нужно переопределять? Для того чтобы инициализировать тормозные приборы, когда локомотив или вагон в симуляторе стартует с полностью заряженными тормозами. Но об этом я расскажу отдельно.
3. Сигналы
Специальные методы, не имеющие реализации, используемые в Qt для воздействия из нашего класса на другие классы. При вызове сигнала, в другом классе-наследнике QObject сработает соотвествующий метод-слот и выполнит некие действия.
- void logMessage(QString msg) - посылка сообщения в лог-файл. На момент версии 1.0.4 уже устарел, и в ближайшее время будет удален из API, так как уже в версии 1.0.4 работает система логирования Дмитрия Терещенко, работающая через синглтон Journal;
- void soundPlay(QString name) - играть звук с именем name;
- void soundStop(QString name) - выключить звук с именем name;
- void soundSetVolume(QString name, int volume) - задать звуку с именем name уровень громкости volume (в процентах);
- void soundSetPitch(QString name, float pitch) - задать частоту pitch воспроизведения звука name в долях от его нормальной частоты. Можно как понижать, так и повышать частоту проигрывания звука.
Как видно, сигналы используются для воздействия на звуковую подсистему из кода DLL-модуля ПЕ. Под именем звука понимается его символическое имя, прописанное в файле
data/sounds/<имя ПЕ>/sounds.xml. Вот пример данного файла, взятый из электровоза ВЛ60пк
data/sounds/vl60pk-1543/sounds.xml
Код: Выделить всё
<?xml version="1.0" encoding="UTF-8"?>
<Config>
<!-- Озвучка тумблеров с фиксацией -->
<!-- Включение -->
<Sound>
<Name>K_Tumbler_On</Name>
<Path>k-tumbler-on.wav</Path>
<InitVolume>100</InitVolume>
<MaxVolume>100</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>0</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- Выключение -->
<Sound>
<Name>K_Tumbler_Off</Name>
<Path>k-tumbler-off.wav</Path>
<InitVolume>100</InitVolume>
<MaxVolume>100</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>0</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- Озвучка тумблеров с фиксацией -->
<!-- Включение -->
<Sound>
<Name>K_Tumbler_Nofixed_On</Name>
<Path>k-tumbler-nofixed-on.wav</Path>
<InitVolume>100</InitVolume>
<MaxVolume>100</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>0</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- Выключение -->
<Sound>
<Name>K_Tumbler_Nofixed_Off</Name>
<Path>k-tumbler-nofixed-off.wav</Path>
<InitVolume>100</InitVolume>
<MaxVolume>100</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>0</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- Включение ГВ -->
<Sound>
<Name>GV_On</Name>
<Path>gvon.wav</Path>
<InitVolume>100</InitVolume>
<MaxVolume>100</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>0</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- Выключение ГВ -->
<Sound>
<Name>GV_Off</Name>
<Path>gvoff.wav</Path>
<InitVolume>100</InitVolume>
<MaxVolume>100</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>0</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- Подъем токоприемника -->
<Sound>
<Name>Pantograph_Up</Name>
<Path>TPUp.wav</Path>
<InitVolume>100</InitVolume>
<MaxVolume>100</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>0</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- Опускание токоприемника -->
<Sound>
<Name>Pantograph_Down</Name>
<Path>TPDown.wav</Path>
<InitVolume>100</InitVolume>
<MaxVolume>100</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>0</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- Работа тягового трансформатора -->
<Sound>
<Name>Trac_Transformer</Name>
<Path>trans.wav</Path>
<InitVolume>0</InitVolume>
<MaxVolume>10</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>1</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- Работа расщепителя фаз -->
<Sound>
<Name>Phase_Splitter</Name>
<Path>fasan.wav</Path>
<InitVolume>30</InitVolume>
<MaxVolume>30</MaxVolume>
<InitPitch>0.0</InitPitch>
<Loop>1</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- Звуки работы мотор-вертиляторов -->
<!-- МВ1 -->
<Sound>
<Name>Motor_Fan1</Name>
<Path>vent.wav</Path>
<InitVolume>15</InitVolume>
<MaxVolume>15</MaxVolume>
<InitPitch>0.0</InitPitch>
<Loop>1</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- МВ2 -->
<Sound>
<Name>Motor_Fan2</Name>
<Path>vent.wav</Path>
<InitVolume>20</InitVolume>
<MaxVolume>20</MaxVolume>
<InitPitch>0.0</InitPitch>
<Loop>1</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- МВ3 -->
<Sound>
<Name>Motor_Fan3</Name>
<Path>vent.wav</Path>
<InitVolume>40</InitVolume>
<MaxVolume>40</MaxVolume>
<InitPitch>0.0</InitPitch>
<Loop>1</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- МВ4 -->
<Sound>
<Name>Motor_Fan4</Name>
<Path>vent.wav</Path>
<InitVolume>50</InitVolume>
<MaxVolume>50</MaxVolume>
<InitPitch>0.0</InitPitch>
<Loop>1</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- МВ5 -->
<Sound>
<Name>Motor_Fan5</Name>
<Path>vent.wav</Path>
<InitVolume>70</InitVolume>
<MaxVolume>70</MaxVolume>
<InitPitch>0.0</InitPitch>
<Loop>1</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- МВ6 -->
<Sound>
<Name>Motor_Fan6</Name>
<Path>vent.wav</Path>
<InitVolume>80</InitVolume>
<MaxVolume>80</MaxVolume>
<InitPitch>0.0</InitPitch>
<Loop>1</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- Звуки работы мотор-компрессора -->
<Sound>
<Name>Motor_Compressor</Name>
<Path>compr.wav</Path>
<InitVolume>50</InitVolume>
<MaxVolume>50</MaxVolume>
<InitPitch>0.0</InitPitch>
<Loop>1</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- Щелчек рукоятки крана 395 -->
<Sound>
<Name>Kran_395_ruk</Name>
<Path>395-chelk.wav</Path>
<InitVolume>100</InitVolume>
<MaxVolume>100</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>0</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- Щелчек комбинированного крана -->
<Sound>
<Name>Komb_kran</Name>
<Path>komb-kran.wav</Path>
<InitVolume>100</InitVolume>
<MaxVolume>100</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>0</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- Щелчек комбинированного крана -->
<Sound>
<Name>UBT_367_ruk</Name>
<Path>ubt-367-ruk.wav</Path>
<InitVolume>100</InitVolume>
<MaxVolume>100</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>0</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- Серводвигатель ЭКГ -->
<Sound>
<Name>EKG_serv</Name>
<Path>serv.wav</Path>
<InitVolume>30</InitVolume>
<MaxVolume>30</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>0</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- Тяговый двигатель -->
<Sound>
<Name>TED</Name>
<Path>ted_60-62.wav</Path>
<InitVolume>100</InitVolume>
<MaxVolume>100</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>0</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- Скоростемер -->
<Sound>
<Name>Skorostemer</Name>
<Path>skorostemer.wav</Path>
<InitVolume>0</InitVolume>
<MaxVolume>100</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>1</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- Свисток -->
<Sound>
<Name>Svistok</Name>
<Path>VL60-svistok.wav</Path>
<InitVolume>0</InitVolume>
<MaxVolume>100</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>1</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<!-- Свисток -->
<Sound>
<Name>Tifon</Name>
<Path>VL60-tifon.wav</Path>
<InitVolume>0</InitVolume>
<MaxVolume>100</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>1</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<Sound>
<Name>KRM395_vpusk</Name>
<Path>395_vpusk.wav</Path>
<InitVolume>0</InitVolume>
<MaxVolume>100</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>1</Loop>
<PlayOnStart>1</PlayOnStart>
</Sound>
<Sound>
<Name>KRM395_vipusk</Name>
<Path>395_vypusk.wav</Path>
<InitVolume>0</InitVolume>
<MaxVolume>100</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>1</Loop>
<PlayOnStart>1</PlayOnStart>
</Sound>
<Sound>
<Name>KRM395_1</Name>
<Path>395_1.wav</Path>
<InitVolume>0</InitVolume>
<MaxVolume>100</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>1</Loop>
<PlayOnStart>1</PlayOnStart>
</Sound>
<Sound>
<Name>KRM395_2</Name>
<Path>395_2.wav</Path>
<InitVolume>0</InitVolume>
<MaxVolume>25</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>1</Loop>
<PlayOnStart>1</PlayOnStart>
</Sound>
<Sound>
<Name>KRM395_5</Name>
<Path>395_5.wav</Path>
<InitVolume>0</InitVolume>
<MaxVolume>100</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>1</Loop>
<PlayOnStart>1</PlayOnStart>
</Sound>
<Sound>
<Name>EPT_On</Name>
<Path>tumbler.wav</Path>
<InitVolume>100</InitVolume>
<MaxVolume>100</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>0</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
<Sound>
<Name>EPT_Off</Name>
<Path>tumbler.wav</Path>
<InitVolume>100</InitVolume>
<MaxVolume>100</MaxVolume>
<InitPitch>1.0</InitPitch>
<Loop>0</Loop>
<PlayOnStart>0</PlayOnStart>
</Sound>
</Config>
Каждая секция Sound в этом файле описывает отдельный звук. В ней связывается звуковой файл *.wav (параметр Path) с именем звука (параметр Name). Видно, что одному файлу может быть приведено в соотвествие несколько звуков, как например сделано это для мотор-вентиляторов. Звук-то по сути один и тот же, но играться он будет по-разному, в зависимости от того, с каким вентилятором он связан.
4. Приватные методых
Эти методы вызываются самим классом Vehicle, разработчик DLL-модуля не может их увидеть. Тем не менее, для понимания контекста вызова методов, доступных разработчику, полезно знать как называются и что делают данные методы.
- void loadConfiguration(QString cfg_path) - чтение дефолтных параметров конфига ПЕ. Вызывается при нициализации подвижной единицы. В этом методе читаются такие параметры как EmptyMass, PaylodMass, Length, WheelDiameter, NumAxis, WheelInertia, MainResist. При это выполняется инициализация всех зависимых от перечисленных параметров переменных. После этой процедуры вызывается метод loadConfig(), переопределяемый разработчиком модуля для чтения собственных параметров.
- void loadMainResist(QString cfg_path, QString main_resist_cfg) - чтение параметров формулы основного сопротивления движению.
Тут надо пояснить, что параметры формулы основного сопротивления движению, о которых мы говорили выше, читаются из отдельного файла, который хранится в каталоге игры
cfg/main-resist/. Имя этого XML-файла указывается в параметре MainResist конфига ПЕ. Для чего это?
Отлаживали мы как-то на работе тренажер "Сапсана". Не хотел зараза разгонятся выше 150 км/ч, даже на площадке. Выяснилось, что ошибка в формуле основного сопротивления движению. Для каждого вагона того тренажера эти параметры задаются в конфиге вагона. А вагонов десять. А формула для всех вагонов одна. Я проклял всё, подбирая коэффициенты и правя 10 конфигов одновременно. Чтобы такого не повторялось, в RRS я сделал так. Допустим есть у нас пассажирский цельнометалический вагон. Его удельное основное сопротивлению движения одинаково для многих серий вагонов. А в RRS уже сейчас несколько серий таких вагонов, сделаных Романом Бирюковым. Допустим в форулу закралась ошибка. Что, править конфиги всех серий? Или лучше поправить один файл cfg/main-resist/passcar.xml и все дополнения автоматически получат исправленную формулу, так как вместо хранения коэффициентов, их конфиги просто ссылаются на нужную формулу. Наверное лучше второй вариант
5. Публичные методы
Эти методы вызываются движком игры, за них класс Train дергает каждую подвижную единицу, с целью выполнения ею каких-то вычислении или получения данных. Это интерфейсная часть, общая для всех потомков класса Vehicle. Их нельзя переопределить, более того, добавление своих публичных методов в модуле ПЕ не только бесполезно (так как симулятор ничего не знает о них), но и нежелательно. Все методы класса-наследника от Vehicle, создаваемые разработчиком модуля, должны иметь модификатор доступа private.
Итак, публичные методы класса Vehicle
- void init(QString cfg_path) - иницалиация подвижной единицы. В качестве параметра движком игры сюда передается полный путь к конфигу данной подвижной единицы. В этом методе вызывается приватный метод loadConfiguration(), а так же защищенный виртуальный метод initialization().
- void setIndex(size_t idx) - задать подвижной единице её индекс в векторе состояния поезда. Об этом чуть позже рассажу подробнее.
- void setInclination(double inc) - задать уклон профиля пути.
- void setCurvature(double curv) - задать кривизну пути в плане.
- void setDirection(int dir) - задать направление движения.
- void setForwardForce(double R1) - задать усилие в переднем сцепном приборе.
- void setBackwardForce(double R2) - задать усилие в заднем сцепном приборе.
- void setPayloadCoeff(double payload_coeff) - задать коэффициент загрузки.
- void setRailwayCoord(double value) - задать координату ПЕ.
- void setVelocity(double value) - задать скорость ПЕ.
- void setWheelAngle(size_t i, double value) - задать угол поворота i-й колесной пары (нумеруются с нуля!)
- void setWheelOmega(size_t i, double value) - задать угловую скорость i-й колесной пары.
- void setPrevVehcile(Vehicle *vehicle) - задать указатель на подвижную единицу, прицепленную спереди.
- void setNextVehcile(Vehicle *vehicle) - задать указатель на подвижную единицу, прицепленную сзади.
- void setConfigDir(QString config_dir) - задать путь к конфигам данной ПЕ.
- size_t getIndex() - получить индекс ПЕ.
- double getMass() - получить полную массу ПЕ.
- double getLtngth() - получить длину ПЕ по осям автосцепок.
- double getWheelDiameter() - получить диаметр колеса по кругу катания.
- double getRailwayCoord() - получить координату ПЕ.
- double getVelocity() - получить скорость ПЕ.
- double getWheelAngle(size_t i) - получить углол поворота i-й колесной пары
- double getWheelOmega(size_t i) - получить угловую скорость i-й колесной пары
- float getAnalogSignal(int i) - получить аналоговый сигнал с номером i.
- float *getAnalogSignals() - получить указатель на массив анвлоговых сигналов.
- void integrationPreStep(state_vector_t &Y, double t) - выполнить метод preStep() для ПЕ.
- void integrationStep(state_vector_t &Y, double t, double dt) - выполнить метод step() для ПЕ.
- void integrationPostStep(state_vector_t &Y, double t) - выполнить метод postStep() для ПЕ.
- void getBrakepipeBeginPressure() - получить давление в начале тормозной магистрали.
- void getBrakepipeAuxRate() - получить темп дополнительной разрядки ТМ.
- void setBrakepipePressure() - задать давление в тормозной магистрали.
- QString getDebugMsg() - получить отладочную строку.
- void setUks(double value) - задать напряжение контактной сети.
- void setCurrentKind(int value) - задать род тока в контактной сети.
- void setEPTControl(size_t i, double value) - задать состояние линии управления ЭПТ.
- void getEPTCurrent(size_t i) - получить ток в линии управления ЭПТ.
- void getEPTControl(size_t i) - получить состояние линии управления ЭПТ.
Из этого краткого описания следует, что перечисленные методы предназначены для выполнения трех функций: передать в модуль ПЕ требуемые параметры; получить от модуля ПЕ нужные движку параметры; заставить модуль ПЕ выполнить нужные движку действия. Это своего рода рычаги, с помощью котрых движок игры управляет тем кодом, что разработчик дополнения написал. И при этом симулятор совершенно не интересует, что конкретно происходит внутри этой DLL.
6. Публичные слоты
Слоты, это методы класса QObject и его наследников, которые, будучи связанными с сигналами другого класса, будут выполнятся, когда другой класс вызовет соотвествующий сигнал. Это основная парадигма Qt, которой RRS следует, когда ему это выгодно
Итак
- void receiveData(QByteArray data) - прием данных от системы визуализации, а именно состояния клавиатуры. Состояние клавиатуры, путем вызова соответсвующего сигнала рассылается всем ПЕ в поезде и делает это класс Train.
- void getControlSignals(control_signals_t control_signals) - получение состояния органов управления от внешнего пульта. Срабатывание этого слота приводит к обновлению информации о состоянии пульта, подключенного к симулятору.
Ок, мы разобрали класс Vehicle по полочкам. Для чего я это пишу?
Во-первых, это документация. Её черновой вариант. Но документация как и все что делается в рамках данного проекта - открытое публичное достояние, подлежащее критике и обсуждению. И где, как не на форум проекта, её публиковать и обсуждать?
Во-вторых, такое описание, я надеюсь, поможет понять механику игры и облегчить жизнь разработчикам дополнений. Согласитесь, если вы знаете что творится в кишках программы, под которую вы создаете плагины, то процесс поедет и быстрее, и качественне. Доступ к исходному коду открыт, но данная документация должна послужить путеводителем по этим исходникам.
В-третьих, в нашем сообществе появляются энтузиасты, предлагающие свежие идеи, работающие на благо проекта. Это заставляет меня, как руководителя проекта, идти навстречу таким людям, и, лучший способ - это написание подобных текстов по коду проекта.
Думаю, что мысль приживется, приглашаю к обсуждению.