Advertisement
agmike

Lse.Init&Lse.Pneumatics Manual

Jul 27th, 2011
92
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 36.31 KB | None | 0 0
  1. Итак, после некоторых изменений, меня устраивает тот вид, в котором пневматика функционирует сейчас. А значит есть две вещи: в скорости можно будет оформить это более публично, чем в текущий момент и появляется смысл писать хорошее описание, не боясь того, что придется нафиг все переделывать.
  2.  
  3. Итак, далее идут две вещи — описание того, что я изменил по сравнению с предыдущей версией и рассказ о том, как работает и как используется система.
  4.  
  5. В основном, все переделки затронули только внутренности (моделирование ТМ и прочего я не трогал, практически).
  6. То, что не входит в «практически»:
  7. 1. Убраны, удалены, забыты разбиения на какие-то уровни Abstract, Standard, ..., ибо не нужны. Теперь есть единственный LpePneumaticObject и от него все и идет.
  8. 2. Стерт с лица файлов загадочный и неведомый HandleApoMessage, ибо не нужен. Его единственный смысл был в уведомлении о начале/конце моделирования систем данного вагона. Теперь для этого другие методы.
  9. 3. Дефолтный ТЦ теперь обладает только одной нодой. Нафиг и так же удален юз. Он все равно был корявый. Желающие могут реализовать альтернативные классы, все же.
  10. 4. Убрана нафиг централизованная инициализация через соотв. библиотеку, ибо понятно что. Система инициализации теперь распределенная и гибкая. А так же весьма нетривиальная, в связи с чем здесь будет небольшое отступление.
  11.  
  12. Инициализация.
  13.  
  14. Требования к скриптам управления запуском (и дальнейшей работой) определялись не сразу, но в конце-концов стали такими:
  15. 1. Экономия ресурсов. Не хочу тормозов от крутых скриптов ни у себя, ни у других. Из десяти стоящих на карте локов реально полностью обрабатывать нужно лишь один. Или два, если юзер извращается :)
  16. 2. Из предыдущего следует, что локомотив должен отличать, кто с ним работает — игрок или бот. Если бот — то крутые скрипты не нужно запускать вообще, а запустить маленький, для бота. И ждать, когда же будет черед юзера.
  17. 3. Локомотив должен знать, в каком состоянии ему инициализироватья. Стоял он месяц под забором или его только что сдала нам другая бригада?
  18. 4. Игрок не должен потерять состояние локомотива если он перейдет в другой вагон или поезд. И также не должно быть переходов в режимы бота на дефолтных станциях. Т.е. устойчивость.
  19.  
  20. Первый вариант, удовлетворяющий пунктам — тупо свойства в браузере. Но это тупо:
  21. 1. Это сотня лишних кликов мышью.
  22. 2. Это будет разным у каждого лока.
  23. 3. Это нужно каждый раз писать самому.
  24.  
  25. Другая мысль была, и она была воплощена первой — библиотека, которая будет определять поезд игрока и говорить об этом. Скрипты пневматики/локомотива будут спрашивать её и она будет отвечать — этот поезд управляется игроком, или нет. Устойчивость обеспечивается настраиваемой выдержкой, или сменой только по явному указанию, или вообще блокировкой каких-либо переключений.
  26. Но централизация получалась громоздкой. Да и по пунктам №3 и 2 были трудности — для тупых сессий, типа того, что мы видим в ЗДСимуляторе, это можно сделать, а далее начинаются проблемы. И мне не хочется ставить ограничения на том, что в ТРС, собственно, можно сделать на порядок более богато, чем во всех альтернативах — сценарии.
  27.  
  28. Потому был выбран иной, децентрализованный подход. Он позволяет для каждого вагона:
  29. 1. Управлять состоянием его скрипта
  30. 2. Другим системам следить за тем, что происходит с ним.
  31.  
  32. Вагонная/локомотивная часть системы заключена в классе LseInit. Внешний интерфейс построен на сообщениях.
  33. При отправке управляющего сообщения отправителем надо указывать тот объект, к которому необходимо применить данное действие, получателем должны быть все (широковещательная отправка). Сделать это можно с помощью Router.PostMessage(obj.GetId(), Router.MESSAGE_BROADCAST, "Major", "Minor", 0.0);
  34. Информационные сообщения отсылаются как обычно, широковещательно от источника.
  35.  
  36. Для описания состояния скрипта вводятся два понятия — уровень и предсостояние (уж простите за тавтологию :).
  37.  
  38. Есть 4 уровня — halt, wait, user, auto. При запуске лок переходит автоматом переходит в wait, из него может перейти в user или auto после приема сообщения. В halt все остановлено, ничего не исполняется, в wait работает один поток-монитор, user предполагает запуск полновесного скрипта, в auto — для ботов.
  39.  
  40. Во всех уровнях кроме halt раз в минуту рассылается информационное сообщение Lse.Init.Runlevel, Wait|User|Auto, что требуется для компонентов, которые расположены вне скрипта лока, как например, библиотека пневматики.
  41.  
  42. При переходе из одного уровня в другой рассылается сообщение Lse.Init.RunlevelChange, Wait|User|Auto Init|Term, пока просто для наличия такой функциональности.
  43.  
  44. После заверщения рассылается сообщение Lse.Init.RunlevelChanged, Wait|User|Auto|Halt.
  45.  
  46. Управляющие сообщения:
  47. Lse.Init.TrainPhysics, Enabled|Disabled должно отсылаться при использовании Train.EnablePhysics, ибо соответствующей функции-геттера нет. При получении Disabled происходит автоматический переход в уровень wait. И добавлен отсутствующий геттер LseInit.GetPhysicsState().
  48. Данное сообщение стоит особняком — отправителем должен быть указан поезд (внимание, поезд!). Все вагоны со стандартной инициализацией отреагируют на это сообщение. При этом уровень автоматически сбросится на Wait, все переходы заблокируются.
  49.  
  50. Lse.Init.PreState, Cold|Warm|Hot|HotRunning <idleMinutes> — для задания предсостояния, с которым локомотив должен инициализироваться. По моему мнению, должно быть достаточным 4 значения:
  51. Cold — локомотив после ремонта, с завода; степень неготовности максимальная, требуется длительная процедура запуска.
  52. Warm — локомотив после отстоя в депо после сдачи его предыдущей бригадой.
  53. Hot — локомотив во время смены бригад на промежуточной станции.
  54. HotRunning — помимо предыдущего, он еще и запущен, можно ехать сразу же.
  55. Второй параметр — время в минутах, сколько локомотив находился в этом состоянии. Требуется для продолжительных процессов (воздух в ГР, ЗР, напряжение АБ и т.п.).
  56. По умолчанию должен подразумеваться Warm с выдержкой в сутки. Не обязательно поддерживать все, при запросе неподдерживаемого должен выбираться ближайший из поддерживаемых.
  57.  
  58. Lse.Init.SetRunlevel, Halt|Wait|User|Auto — то самое сообщение, которым локомотив переводится на нужный уровень. Можно перейти из любого в любой (кроме halt, из него принципиально никуда).
  59.  
  60. Использовать этот класс просто:
  61. class MyVehicle isclass Vehicle, LseInit { ... };
  62. class MyLocomotive isclass Locomotive, LseInit { ... };
  63.  
  64. Для перегрузки доступны следующие функции: RcUserInit, RcUserTerm, RcAutoInit, RcAutoTerm, RcWaitInit, RcWaitTerm. Они вызываются из потока (значит, в них можно спать). Обязательно необходимо вызывать функцию предка с помощью inherited(). После запуска/остановки всех нужных потоков и выполнения всех действий функция должна вернуться, при этом будет отослано сообщение о завершении перехода.
  65.  
  66. И далее, прилагаются к этой системе помощники для быстрого задания нужных состояний в редакторе — InitVs, они же Initialization Vehicles. Они представляют из себя крошечной длины вагоны, исполняющие скрипты, рассылающие управляющие сообщения. Они сделаны для удобства и как образец применения этих сообщений. Гораздо быстрее прицепить в конец лока игрока такой вагон, который отошлет ему сообщение RunlevelInit, User, а не лезть в свойста/задавать команду/настраивать правило. И, плюс, это сработает и в случае поезда, выпущенного порталом/сгенерированного скриптом.
  67.  
  68. Сейчас есть два образца такой технологии — оключателя физики (InitV Disable Physics) и, как раз таки, поезда игрока (InitV User Train).
  69.  
  70. Их класс — LseInitVehicle.
  71.  
  72. Работа этих штук задается четырьмя методами:
  73.  
  74. public string GetInitVehicleCategory();
  75. void DoPreInit(Train train);
  76. void DoInit(Train train);
  77. void DoInit(Vehicle[] vehs);
  78.  
  79. Основные действия должны заключаться в методе DoInit(Vehicle[]). Он получает массив вагонов, на которые распространяется его действие.
  80.  
  81. Сперва теория.
  82. Для того, чтобы можно было их группировать, по-разному настраивая разные группы вагонов, определены такие правила:
  83. 1. InitV — стрелка. Его перед указывает, в каком направлении он будет проводить свои инициализационные действия.
  84. 2. Подвластные ему вагоны идут по ходу его стрелки, не включая других вагонов, являющихся InitV.
  85. 3. Подвластные ему вагоны заканчиваются там, где стоит другой InitV с категорией, одинаковой с нашей.
  86.  
  87. Теперь практический пример:
  88. № 8 7 6 5 4 3 2 1
  89. InitV InitV InitV InitV
  90. категория: a b c b
  91. > > > [ ] [ ] [ ] > [ ]
  92.  
  93. Стрелочки — InitVs, квадратные скобки — вагоны.
  94. В момент инициализации InitV номер 6 и 8 получат массив с вагонами 1, 3, 4, 5; InitV №7 — 3, 4, 5; InitV №2 — только 1.
  95. И да, рекомендую всегда делать также, как здесь — стрелки InitV должны всегда быть сонаправлены в составе. Иначе возможны глюки, да и это бессмысленно.
  96.  
  97. Далее, DoPreInit(Train) предназначено для действий, которые влияют на дальнейшую инициализацию. Например, отключение физики. Данное действие логично выполнить перед всеми другими, если вдруг они будут. Эта функция гарантированно выполнится первой, а все другие — после небольшой паузы.
  98.  
  99. DoInit(Train) — аналог DoInit(Vehicle[] vehs), но для всего поезда. Для поездов исключение категориями не ведется, потому рекомендую не совмещать две эти функции. Конкретный InitV должен использовать что-то одно.
  100.  
  101. Категории не предназначены для продвинутой фильтрации. Они предназначены для разделения состава на области, где будут действовать два InitV одной категории, но с разными настройками. Например, один будет отправлять сообщения PreState, Warm 360, а другой — PreState, Hot 10.
  102.  
  103. После завершения инициализации InitV перемещается в конец состава и удаляется.
  104.  
  105. Модели пока не в финишном варианте. Будут другие.
  106.  
  107.  
  108. Далее пневматика.
  109.  
  110. Пневматикой и, в частности, расчетами заведует отдельная библиотка. Почему?
  111. 1. Весь код в одном месте.
  112. 2. Вагоны могут быть обработаны отдельно от локомотивов, в том числе и составы без локомотивов вообще.
  113.  
  114. Для экономии ресурсов, полное моделирование (метод Мак-Кормака для расчета одномерного движения газа в ТМ, обновление объектов, обновление задатчика тормозной силы) применяется только к группе «тронутых» вагонов. Ими являются:
  115. 1. Вагоны/локи, находящиеся в уровне User.
  116. 2. Все вагоны поездов, в которых есть п.1
  117.  
  118. Уровень определяется слушанием Lse.Init.Runlevel, User.
  119.  
  120. Далее, вагоны остаются тронутыми три минуты. Если вот уже три минуты ни они, ни кто-то из их поезда сообщение о уровне не отправлял, они помечаются неактивными.
  121.  
  122. Неактивные вагоны рассчитываются еще 30 минут по упрощенной схеме:
  123. 1. Вместо МакКормака раз в секунду рассчитывается среднее давление в ТМ
  124. 2. Вместо ежекадровых апдейтов объектов они обновляются раз в секунду другой функцией.
  125. 3. Задатчик силы работает также (ну не получается по-другому, да и к тому ж, не такой уж он и тяжелый).
  126.  
  127. Такая зверская оптимизация в п.1 и п.2 вызвана соображением о том, что в отстаивающем составе, плюс к тому не связанным с игроком, маловероятны переходные процессы, есть лишь утечки. Даже если переходные процессы имеются, за ними никто не наблюдает, а значит можно пожертвовать точностью ради кадров в секунду.
  128.  
  129. Пункт следующий, архитектура.
  130. Библиотека работает с объектами, причем весьма абстрактными. Все модели тормозного/пневматического оборудования строятся на них. Бывают они такие:
  131.  
  132. LpeNode — объект, описывающий некий резервуар, объем. Применяется для взаимодействия между различными объектами. Например, при работе у ТМ и у ВР какого-то вагона есть общий LpeNode, который представляет собой объем ТМ в данном вагоне.
  133. Из-за высокой дискретности и, из-за стабильности, нельзя использовать малые объемы, рекомендую не строить из них цепочки. Ограничиться 1-2 максимум (ВР — авторежим — ТЦ, например).
  134.  
  135. LpeConnector — описывает межвагонное соединение. Имеет функции, позволяющие проверить наличие соединения с какой-то стороны и, собственно, присоединено ли :)
  136. Соединение между вагонами считается соединенным, если у обоих соотв. соединения существуют и соединены (тавтология, однако).
  137.  
  138. LpePipe — подкласс предыдущего класса. Расширяет его функциональность концевым краном, характерным для пневматических магистралей. Также присутствует свойство поломки, но я не знаю зачем я его сделал :) Сейчас оно никак не используется, только проверяется.
  139.  
  140. Да, для проверки состояния соединения есть функции в классе LpeUtil. Использовать надо их, они учитывают всю задуманную логику соединений.
  141.  
  142. LpePneumaticObject — базовый класс, который является основой для всех пневматических/тормозных устройств (ТЦ, ВР, ТМ, ...) и схем.
  143. Имеет важные методы, а именно:
  144. Vehicle GetOwner() — возвращает вагон, который содержит это устройство.
  145.  
  146. void Init(Vehicle vehicle) — инициализация устройства. Не забывайте про inherited(vehicle);
  147.  
  148. void Update(float dt) — регулярный апдейт активного устройства. Вызывается раз в кадр, но не реже раза в 0.1 секунды (если кадр длится дольше, вызывается несколько раз, т.е. амортизация). Не забывайте про inherited(dt);
  149.  
  150. void UpdateIdle(float dt) — регулярное обновление неактивного устройства. Вызывается раз в секунду, амортизации нет (хотел бы я посмотреть на тех, кто играет с 1 ФПС :)) Не забывайте про, ну понятно :)
  151. Должен содержать код, стабильный при таких интервалах. Я решил схитрить, не выдумывать велосипед, а поиздеваться над физикой:
  152. 1. Весь расчет выносится в отдельную функцию, принимающую float dt, float leakdt, последний — интервал времени для расчета утечек, а первый — для всего остального. И далее:
  153.  
  154. public void Update(float dt)
  155. {
  156. inherited(dt);
  157.  
  158. _update(dt, dt);
  159. }
  160.  
  161. public void UpdateIdle(float dt)
  162. {
  163. inherited(dt);
  164.  
  165. _update(Math.Fmin(0.1, dt), dt);
  166. }
  167.  
  168. Да, все процессы, кроме утечек, идут со скоростью в 10 раз меньше реальной. Но, как я расписал выше, нас волнуют только утечки.
  169.  
  170. LpeBrakeForceSetter — объект, задающий тормозную силу на конкретном вагоне. Принимает два параметра:
  171. BrakeEffort — требуемая тормозная сила.
  172. ApplyEffort — применять ли её (сделано т.к. первый параметр задается схемой, а второй — библиотекой).
  173. Возвращает:
  174. BrakeEffortApplied — реально действующая сейчас тормозная сила.
  175.  
  176. Как работает задание тормозной силы.
  177. В моей реализации, каждый вагон должен иметь специальный спек. Он задает гигантский ЗР и ГР, разъединяет ТМ у соседей, отключает питание ЗР из ТМ.
  178. Тормозная сила задается с помощью разрядки ТМ на нужное время. При этом дефолтный ВР наполняет ТЦ. После достижения нужного уровня давление в ТМ поднимается до давления ЗР. При необходимости снизить выполняется ступень отпуска. И т.д.
  179. Давление в ТМ задается с помощью SetBrakePipeEfficiency. Величина питания ТМ из ГР установлена в половину величины разрядки через функцию. Значит, балансируя где-то в середине мы поддерживаем давление в ТМ на постоянном уровне, отклоняясь изменяем.
  180. Скрипт расчетчика аналитически вычисляет требуемое давление в ТЦ, опираясь на параметры спека. И задает нужное, манипулируя давлением в ТМ.
  181. Так так такое задание неточное и замедленное, то библиотека суммирует BrakeEffort и BrakeEffortApplied по всему поезду, находит разницу и для компенсации корректирует скорость поезда.
  182. Если поезд состоит из одного вагона, то SetBrakePipeEfficiency не имеет действия и вся сила задатется коррекцией скорости.
  183.  
  184. Такие сложности с дефолтным ТЦ сделаны для реакций в поездах. Да, в ТРС они какие-то хреновенькие, как выяснилось, однако:
  185. 1. Раз написано, пусть остается.
  186. 2. Быть может, все изменится к лучшему.
  187. 3. При трогании они работают.
  188.  
  189. LpeScheme — объект, реализующий схему тормозного оборудования вагона/лока.
  190. Содержит обновляемые схемой поля, хранящие соседей, список соединений, задатчик тормозной силы (а зачем без него вообще использовать эту систему? :))
  191. Вместо Init в нем есть два метода: SchemeCreate, где должны создаваться объекты схемы и SchemeInit, где они должны инициализироваться, вместе с самой схемой. Это сделано для разделения места, где создается оборудования с тем, где оно используется.
  192.  
  193. На этом завершаются объекты, с которыми система работает напрямую. Они являются каркасом.
  194. На этом каркасе реализуются уже реальные, а не абстрактные объекты:
  195. LpeBrakePipe — ТМ
  196. LpeBrakeCylinder — ТЦ
  197. LpeAirDistributor — ВР и т.д.
  198.  
  199. И есть большой файл, содержащий дефолтные реализации работы оборудования — lse.std.pneum.gs.
  200.  
  201. Вообще система гибка, гибка в том, что наследуясь от каркасных классов можно создавать различные конфигурации оборудования, не ограничиваясь стандартным набором ТМ/ТЦ/ВР, который встречается везде и всюду. Легко можно реализовать МРТ, дисковые тормоза, скомбинировать задатчик тормозной силы с тяговой для совместного учета ЭДТ, тяги, боксования, юза, торможения. И т.д.
  202.  
  203. Есть пара замечаний:
  204. LpeStdScheme и LpeStdVehicleScheme. Оба класса определяют некую стандартную конфигурацию. Первый определяет ТМ, второй — остальной стандартный набор (ТЦ, ВР). В принципе, это легко можно сделать в своем классе, но это удобно тем, что имеется стандартизация. Стандартизация особенно важна для соединений, чтобы не было двадцать разных коннекторов ТМ у разных вагонов :)
  205.  
  206. Да, так случилось, что эта пневматика оказалась подходящей для поддержки межвагонных соедиений, пневматических и электрических. Самой системой моделируется только пневматические, электрику надо рассчитывать самим, но система предоставляет унифицированный интерфейс коннекторов. В том числе и ХАД-панель, подобную панели по Ф9 в МСТС, где можно будет управлять концевыми кранами, рукавами, соединениями и пр. (сейчас я как раз ею и занимаюсь).
  207.  
  208. Соединения идентифицируются по именам. Одинаковые имена означают совместимость.
  209. Определены 4 стандартных имени для пневматических магистралей:
  210. tm — ТМ
  211. pm — Питательная
  212. im — Импульсная
  213. mvt — Вспомогательного тормоза
  214.  
  215. На именование нет ограничений. Рекомендации — коннекторы эл. цепей/СМЕ должны должны содержать тип ПС (серию). Желательно какой-то признак скрипта, чтобы можно было отследить совместимость/несовместимость. Номера быть не должно. У пневматических — соответственно обозначению в схеме тормозного оборудования.
  216. Желательно, конечно, выносить этот вопрос на обсуждение, чтобы была взаимозаменяемость/унификация.
  217.  
  218. Далее, важны также спеки. К системе прилагается референсный спек. Из него полагается взять настройки flowsize, volume, pressure. В motor необходимо настроить параметр brakeratio. Он задает силу в Ньютонах при максимальном давлении в ТЦ. Вы должны поставить в него, с запасом, максимальную требуемую вам силу.
  219. В разделе lse-pneumatics-298469 контейнера extensions находятся параметры дефолтных устройств. Обычно требуют настройки параметры торможения и ТЦ. Этот контейнер может также быть в конфиге ПСа и конфиге его спека. Не обязательно заполнять в них все, при отсутствии какого-то в конфиге ПС идет поиск в конфиге спека ПС, а если нет и там — в референс-спеке. В нем есть все параметры со значением по умолчанию.
  220. К моменту выпуска системы я планирую подготовить отдельные спеки для пассажирских вагонов, грузовых 4-осн и 8-осн. Пока, как образец, есть только пассажирский.
  221.  
  222. Поддержка ПСом системы может быть двух видов:
  223. 1. Поддержка спеком. Определяется по наличию раздела lse-pneumatics-298469 контейнера extensions. Такому ПСу создается дефолтная вагонная схема, тип вагона (пасс, груз 4, груз 8) будет определяться автоматически. Подходит для вагонов.
  224. 2. Поддержка реализацией LpeVehicle. Этот интерфейс определяет функцию Lpe_GetVehicleScheme(), что позволяет указать системе на произвольный объект класса LpeScheme. Подходит для локомотивов.
  225.  
  226. Если ПС не соотвествует ни п.1, ни п.2, то такой вагон не поддерживает пневматику и ею не обрабаывается вообще.
  227. Такой ПС не должен быть в поездах, рассчитываемых системой. Это вызовет ошибки и глюки в задании тормозной силы.
  228. Впрочем, переделка ПСа по пункту №1, с учетом дефолтных спеков не должна вызывать затруднений. Это одно из преимуществ этой системы, в ранних вариантах подобных реализаций все вагоны должны были иметь специфичный скрипт.
  229.  
  230. Локомотивам полагается пункт №2. Это связано с тем, что все равно требуется связывание с органами управления специальным скриптом.
  231.  
  232. Я также хотел бы, чтобы поддерживающий данную систему ПС (оба пункта) имели в конце своего имени «(P)», в скобках букву Пи английскую. Это помогло бы в редакторе отличать поддерживающих от неподдерживающих.
  233.  
  234. С библиотекой поставляется набор дефолтных объектов, см файл lse.std.pneum.gs. Там есть реализации ТЦ, ТМ, ВР292, дефолтной вагонной и локомотивной схем.
  235.  
  236. ВР292 все же неудачен. Пока нет альтернатив и времени их писать и настраивать, он остается. В дальнейшем, должен быть написан аналог, который проще в 20 раз, в столько же раз легче. Вместе с 483.
  237.  
  238. В локомотивную схему будет добавлена поддержка магистралей ПМ и МВТ.
  239.  
  240. Также еще не сделано:
  241. 1. ХАД. Делается, но не готово.
  242. 2. Предсостояние. Для него будет добавлена следующая функция в класс LpePneumaticObject:
  243. public void InitPreState(int prestate, int idleMinutes);
  244. Также, возможно, будут добавлены (для удобства, но возможно будут полезны):
  245. public void OnActiveSimulationBegin();
  246. public void OnInactiveSimulationBegin();
  247. public void OnSimulationEnd();
  248. которые будут сигнализировать о соотв. переменах в состоянии симуляции.
  249. Это изменение не нарушит совместимость.
  250. 3. 483. Давно просящийся объект, но на настройку ВРов уходит слишком много времени, пока не могу заняться этим. Если кто-то сможет написать и настроить такой класс, реализующий все требуемые методы, был бы крайне благодарен.
  251. 4. ЭПТ. Будет, делается вместе с тапком.
  252.  
  253. Вроде все. Потом постараюсь оформить в более пристойном варианте :)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement