RailUnion.net


http://railunion.net/blog/index/index_u-26112_b-1518_r-809_sid-ffab590295035dca03cc083cbc302eeb.html

Автор:  i2GR [ 22.07.2016, 14:25 ]
Тема блога:  Railworks. Сигнализация Rail Signals v0.6. АЛСН. Прием сообщений

В качестве пояснения.
Исторически с версией 0.5 я не смог заставить локомотив с составом идентифицировать сообщения АЛС поступающих к нему и спереди и сзади. Чтобы он получал сообщения только с одной стороны – головы поезда и при движении назад и чтобы это было достаточно общим решением, зависящим только от того, где у состава "сценарный" перед и должны ли от светофора со стороны активной кабины доходить в лок путевые сообщения. Поэтому в v0.6 формат был изменен. По мере углубления моих знаний о режимах АЛСН и работе КЛУБа (Спасибо, Стас!), может быть, я сам уже подозреваю сообщения v0.6 в избыточности, но в качестве побочного занятного эффекта они дают хоть на 50%, но сделать похожим на правду работу ЭК в КЛУБе. Но это оффтоп.

Приведенный скрипт с функцией обработки сообщений светофоров приведен практически без изменений по сравнению с работающим проектом, и не включает в себя саму индикацию огней, т.к. она связана с ЭПК, и это сделано отдельной функцией. Дополнительно нужно при индикации проверять не находится ли лок с активной кабина в положении толкача и может быть другие условия. Да, есть некоторые проблемы с системой СВЕТОФОРv06 – АЛСН в некоторых ситуациях. Но он в целом применим для оживления АЛСН, естественно с необходимыми модификациями для конкретного проекта.



Если написание комментов ничего не сломало, то можно вставить весь текст в Loco_EngineScript. и сделать в системной OnCustomSignalMessage(ConsistMessage) вызов tALS:GetSignal(ConsistMessage)

Код: Выделить всё
function OnCustomSignalMessage(ConsistMessage)
tALS:GetSignal(ConsistMessage)
end




Скрипт:
Код: Выделить всё
------------------------------------------------------------
-- типо версия модуля АЛСН
BVU_4_K773   = "18.12.15_20.40"
------------------------------------------------------------
-- таблица с ключами, которые описывают работу АЛСН
tALS   =   {
         -- индекс для таблицы tALS.OnCoil (см. далее) задающий частоту приемника в локе по умолчанию
         Receiver      = 2,
         -- индекс для таблицы tALS.OnRail (см. далее) показывающий, какая частота "на рельсах" по умолчанию (false означает, что сигнала с рельс нет)
         Emitter         = false,
         -- ключ для хранения времени последнего получения сигнала с рельс
         LastTime      = 0,
         -- таблица со значениями возможных частот приемника. "ty" частота задается электронной картой
         OnCoil         = {25,    50,   75,      "ty"},
         -- таблица со значениями возможных символов в обозначении светофоров (Identity) задающих частоту АЛС
         -- индексы в таблицах tALS.OnCoil и tALS.OnRail "синхронизированы", т.е. символу "*" светофора соответствует частота 25 (Гц)
         OnRail         = {"*",    "~",   "=",   "+" },
         -- таблица с состояниями огней АЛСН в локе (1 = ВКЛ, 0 = ВЫКЛ)
         Lights         = {["white"]=0,   ["red"]=0, ["kg"]=0, ["yellow"]=0, ["green1"]=0, ["green2"]=0, ["green3"]=0, ["green4"]=0},
         -- ключ для хранения состояния АЛСН (используется другими частями скрипта лока)
         -- false = ВЫКЛ; true  = ВКЛ, но сигнала АЛС нет, 0, 1, 2, для указания текущего активного огня
         State         = false,
         -- ключ для хранения предыдущего показания при смене огней
         Prev         = false,
         -- ключ для хранения информации о пересечении границы БУ
         Border         = false,
         -- ключ для хранении имени светофора
         Name         = "",
         -- ключ для преобразования римских цифр в обозначении светофора в арабские
         Convert         = {"!" , "@" , "#" , "$" , "%" , "^" , "&"},
         -- ключ для хранения расстояния до светофора
         Distance      = "",
         -- ключ для хранения передаваемого светофором ограничения скорости
         Limit         = false
         }
------------------------------------------------------------
frame = 0   -- номер кадра для лога при отладке.
------------------------------------------------------------
-- функция, вызываемая каждый кадр из системной функции OnCustomSignalMessage(ConsistMessage) скрипта локомотива как:
--[[
function OnCustomSignalMessage(ConsistMessage)
tALS:GetSignal(ConsistMessage)
end
]]
function  tALS:GetSignal(ConsistMessage)
frame = frame + 1 -- счетчик кадров
-- запись в лог
--logfile:write("\n"..frame.."   OCSM.1 ".. ConsistMessage .." CounterOCSM="..tostring(CounterOCSM))
--
-- определение первого символа полученного сообщения
MessageFlag = string.sub(ConsistMessage, 1, 1)
-- если символ = латинская F или B то это сообщение АЛС (E для маркера станции при работе КЛУБ с ЭК. Предполагается, что S для САУТ)
if MessageFlag == "F" or MessageFlag == "B" then
   -- поиск служебных символов "*", "~", "=", "+" и присвоение ключу tALS.Emitter номера индекса таблицы tALS.OnRail, соответствующему этому символу
   for i = 1, 4 do
      if string.find (ConsistMessage, tALS.OnRail[i]) then
         tALS.Emitter = i
         -- определение позиции этого служебного символа в переданном сообщении
         signSeparator = string.find(ConsistMessage, tALS.OnRail[i])
      end
   end --logfile:write("\n "..tostring(tALS.Emitter).." "..tostring(tAutostop.State))
   -- УСЛОВИЕ 1. если включен ЭПК (закомментировано, т.е. по факту не проверяется) и "частоты приемника АЛС" и "частоты на рельсе" совпали, то начинается обработка сообщения светофора
   if --[[tAutostop.State and]] tALS.Emitter == tALS.Receiver then --logfile:write("\n tAutostop.State and tALS.Emitter == tALS.Receiver")
      -- счетчик полученных в течение кадра сообщений (каждый кадр может быть принято до двух сообщений от светофоров (при правильной их работе), поэтому надо учесть все сообщения)
      CounterOCSM = CounterOCSM + 1
      -- если сообщение получено спереди состава
      if MessageFlag == "F" then
         -- вспомогательная переменная с именем светофора спереди
         SignalF = string.sub(ConsistMessage, 11)
         -- вспомогательная переменная с сообщением светофора спереди
         ConsistMessageF = ConsistMessage
         -- если ранее АЛСН была выключена или сообщений не было, то за светофор, показания которого будут отображаться на АЛСН принимается светофор спереди.
         if ALSNSignal == "" then ALSNSignal = SignalF end
      -- если сообщение получено сзади состава
      elseif MessageFlag == "B" then
         -- вспомогательные переменные именем светофора сзади и сообщением от него
         SignalB = string.sub(ConsistMessage, 11)
         ConsistMessageB = ConsistMessage
      end
      --
      --logfile:write("\n"..frame.."   OCSM.2 ".. ConsistMessage .." CounterOCSM="..tostring(CounterOCSM)..tALS:Debug())
      --
      -- если получены два сообщения, то идет дальнейшая обработка
      -- если светофоров два с двух сторон, то это разные сообщения, если светофор один, то получено два одинаковых сообщения
      if CounterOCSM == 2 then
         --logfile:write("\n OCSM.4 CounterOCSM = 2")
         -- обнуление счетчика
         CounterOCSM = 0
         -- проверка смены БУ при движении поезда
         if Call("*:GetControlValue", "SpeedometerKPH", 0) > 0.05 then
            -- если сменилось имя светофора спереди,
            -- перед этим сообщение АЛС поступало спереди,
            -- при этом имя светофора сзади не изменилось (или же его нет совсем, тогда SignalB = prevSignalB = "")
            -- то состав вступает на новый БУ и сообщение АЛС = сообщение от "нового светофора" спереди
            -- понятия СПЕРЕДИ и СЗАДИ это понятия относительно вектора движения поезда, т.е. к какому светофору едет поезл, тот и будет светофором спереди, вне зависимости от того хвост ли это или голова поезда в привычном понимании. это особенность работы движка РВ.
            if SignalB == prevSignalB and ALSNSignal == prevSignalF and ALSNSignal~= SignalF and ALSNSignal~= SignalB then ALSNSignal, tALS.Border = SignalF, true
            --
            --logfile:write("\n OCSM.3 BORDER FRONT")
            --
            -- если сменилось имя светофора сзади,
            -- перед этим сообщение АЛС поступало сзади,
            -- при этом имя светофора спереди не изменилось (или же его нет совсем, тогда SignalF = prevSignalF = "")
            -- то сообщение АЛС = сообщение от "нового светофора" спереди
            elseif SignalF == prevSignalF and ALSNSignal == prevSignalB and ALSNSignal~= SignalB and ALSNSignal~= SignalF then ALSNSignal = SignalB
            --
            --logfile:write("\n OCSM.4 BORDER REAR")
            --
            end
         end
         -- если поезд находится между двумя светофорами, посылющими сообщения
         if SignalF ~= SignalB and SignalF ~= "" and SignalB ~= "" then
            --
            --logfile:write("\n OCSM.5 BORDER FRONT")
            --
            -- если сменился вектор движения поезда, то для РВ светофор спереди стал светофором сзади и наоборот
            -- а нужно чтобы лок по прежнему получал сообщение от того же самого светофора
            -- поэтому если он обрабатывал сообщения спереди, то теперь он будет обрабатывать сообщения сзади
            if SignalB == prevSignalF and ALSNSignal == prevSignalF then ALSNSignal = SignalB end
            -- поэтому если он обрабатывал сообщения сзади, то теперь он будет обрабатывать сообщения спереди
            if SignalF == prevSignalB and ALSNSignal == prevSignalB then ALSNSignal = SignalF end
            -- и в качестве сообщения для обработки принимается сообщение от соотвествующего светофора
            if SignalF == ALSNSignal and SignalB ~= ALSNSignal then ConsistMessage = ConsistMessageF end
            if SignalB == ALSNSignal and SignalF ~= ALSNSignal then ConsistMessage = ConsistMessageB end
         end
         --logfile:write("\n"..frame.."   OCSM.4 newCM ".. ConsistMessage ..tALS:Debug())
         -- фиксируется время поступления последенго сообщения на обработку
         tALS.LastTime   = gSimulationTime
         -- из сообщения извлекается показание светофора
         tALS.State      = tonumber(string.sub(ConsistMessage,2,3))
         -- если показание светофора с нижним желтым огнем (движение с отклонением), то на АЛСН горит Ж
         if tALS.State > 10 then tALS.State = 1 end
         --if SignalState ~= 0 then SignalSpeedLimit = tonumber(string.sub(ConsistMessage, 4, 6)) else SignalSpeedLimit = 20 end logfile:write("\n51 ConsistMessage "..tostring(ConsistMessage).." SignalSpeedLimit "..SignalSpeedLimit)
         -- извлечение из сообщения ограничения скорости (на линке или на треке)
         tALS.Limit      = tonumber(string.sub(ConsistMessage, 4, 6))
         -- если показание светофора К, то ограничение 20 км/ч         
         if tALS.State == 0 then tALS.Limit = 20 end
         -- извлечение из сообщения расстояния до светофора
         tALS.Distance   = string.sub(ConsistMessage, 7, 10)
         -- извлевение собственно имени для отображения на КЛУБе
         tALS.Name      = string.sub(ConsistMessage, 11, signSeparator - 1)
         -- конвертация латинских цифр в арабские
         local latin = string.sub(tALS.Name, 1, 1)
         if latin == "!" or latin == "@" or latin == "#" or latin == "$" or latin == "%" or latin == "^" or latin == "&" then tALS.Name = string.sub(tALS.Name, 2) end
         local latin = string.sub(tALS.Name, -1, -1)
         if latin == "!" or latin == "@" or latin == "#" or latin == "$" or latin == "%" or latin == "^" or latin == "&" then
            for k, v in ipairs(tALS.Convert) do
               if latin == v then tALS.Name = string.gsub(tALS.Name, latin, tostring(k)) end
            end
         end
         -- обновление переменных
         prevSignalF, prevSignalB = SignalF, SignalB
      end
   -- если не выполнено УСЛОВИЕ 1 (ЭПК выключен ИЛИ "частоты приемника АЛС" и "частоты на рельсе" не совпали то сброс всех вспомогательных переменных
   else
      CounterOCSM = 0
      SignalF, prevSignalF, ConsistMessageF, SignalB, prevSignalB, ConsistMessageB, ALSNSignal = "", "", "", "", "", "", ""
      -- SignalF     текущий светофор спереди
      -- prevSignalF предыдущий светофор спереди
      -- ConsistMessageF сообщение от текущего светофор спереди
      -- SignalB текущий светофор сзади
      -- prevSignalB предыдущий светофор сзади
      -- ConsistMessageB сообщение от текущего светофор сзади
      -- ALSNSignal светофор ,сообщение которого обрабатывается.
   end
   --logfile:write("\n OnCustomSignalMessage end "..tALS.State)
end
end -- OnCustomSignalMessage




{ BLOG_REPLIES }

Автор:  Света [ 29.07.2016, 12:07 ]

Вот этот момент:
Код: Выделить всё
   -- поиск служебных символов "*", "~", "=", "+" и присвоение ключу tALS.Emitter номера индекса таблицы tALS.OnRail, соответствующему этому символу
   for i = 1, 4 do
      if string.find (ConsistMessage, tALS.OnRail[i]) then
         tALS.Emitter = i
         -- определение позиции этого служебного символа в переданном сообщении
         signSeparator = string.find(ConsistMessage, tALS.OnRail[i])
      end
   end --logfile:write("\n "..tostring(tALS.Emitter).." "..tostring(tAutostop.State))


Если цель - определить только местоположение последнего в строке символа, можно использовать базовую функцию string.len(х). Эта функция работает намного быстрее по сравнению с циклом, а так как данный блок отрабатывается при каждом сообщении от светофора, экономия будет неплохая.

Автор:  i2GR [ 01.08.2016, 11:41 ]

задача не только в этом.
1. определить сам символ. т.е. на какой частоте АЛС работает светофор. Если не на той, на которую настроен "приемник" в локе, в нем будет белый огонь.
2. предполагается, что служебный символ не всегда последний: для чего использовать в светофоре правое поле identity уже есть

Автор:  Света [ 01.08.2016, 20:35 ]

i2GR писал(а):определить сам символ. т.е. на какой частоте АЛС работает светофор. Если не на той, на которую настроен "приемник" в локе, в нем будет белый огонь.

Это не проблема:
Код: Выделить всё
   y = string.sub(x, string.len (x), string.len (x));
Или:
Код: Выделить всё
   local z = string.len (x)
   y = string.sub(x, z, z); 
где x - строка, y - переменная, в которую записывается значение символа, z - временная переменная, упрощающая вычисление.

i2GR писал(а):предполагается, что служебный символ не всегда последний: для чего использовать в светофоре правое поле identity уже есть

Это другое дело. Тогда предлагаю использовать несколько иную схему. А именно - поставить фиксированную метку, после которой всегда находится символ частоты. Зачем?
1. Станет значительно экономнее поиск и определение этого символа частоты. Ведь если он не находится в определенном месте и не "скраю" строки, то единственный способ его найти - последовательный перебор. При этом надо сравнивать каждый символ со всеми вариантами - "*", "~", "=", "+", а это значит, фактически не один перебор, а несколько. В данном случае 4.
2. В качестве символов использованы метки математических действий - это достаточно опасно. Но я понимаю причину - другие символы применять нельзя из-за того, что они могут затеряться. Если применить вариант, который я предлагаю, для символа частоты можно будет взять любую букву или цифру, так как для распознавателя будет играть роль только метка - а это может быть та же тильда.
Недостаток вижу только один - необходимость ввода лишнего символа. Но, после того, как мной уже поставлено и настроено не один десяток светофоров, могу сказать - одним символом больше, одним меньше - какая разница?
Для вычисления можно применить, например, такой цикл:
Код: Выделить всё
   local Length = string.len (data)
   local index, temp = 1, ""
   
   while index < Length + 1 do
   temp = string.sub(data, index, index);
      if temp == "~" then
         temp = string.sub(data, index+1, index+1);
         break
      end
      index = index + 1
   end
где "data" - это строка. По завершении в переменной "temp" будет символ, расположенный за тильдой. Если тильды по какой-либо причине обнаружено в строке не будет, цикл завершится после сравнения последнего символа в строке. В переменной "temp" в таком случае сохранится значение этого последнего символа. Этим можно воспользоваться. Также в переменной "index" будет количество символов строки - тоже может пригодится.

Автор:  i2GR [ 26.08.2016, 11:44 ]

Немножко оптимизации:
Код: Выделить всё
   for i = 1, 4 do
      if string.find (ConsistMessage, tALS.OnRail[i]) then
         tALS.Emitter = i
         -- определение позиции этого служебного символа в переданном сообщении
         signSeparator = string.find(ConsistMessage, tALS.OnRail[i])
      end

т.е. обсуждаемый избыточный перебор по всем частотам легко заменяется однократным поиском установленной "частоты в локе" в сообщении светофора
Код: Выделить всё
if string.find (ConsistMessage, tALS.OnRail[self.Receiver]) then self.Emitter = true end

Далее условие
Код: Выделить всё
if --[[tAutostop.State]] and tALS.Emitter = tALS.Receiver then

надо преобразовать в
Код: Выделить всё
if tAutostop.State and tALS.Emitter then


Кстати, tAutostop - это таблица в скрипте с переменными и функциями, отвечающими за автостопное торможение, в том числе в ней есть функции включения ЭПК. Для того чтобы просто работала АЛСН без привязки к автостопу нужно либо проверку tAutostop.State закомментировать как в примере либо достаточно в теле скрипта (вне системных функций Initialise, Update и пр.) объявить эту таблицу как
Код: Выделить всё
tAutostop = {State = true}


Часовой пояс: UTC + 4 часа

Powered by phpBB © 2002, 2006 phpBB Group
www.phpbb.com

Blogs powered by User Blog Mod © EXreaction
www.lithiumstudios.org