Информация: Уважаемые посетители! В течение нескольких месяцев на форуме существовала проблема с регистрацией новых пользователей, о которой администрации стало известно недавно. Если вы ранее пытались зарегистрироваться на форуме, но не получили на ваш e-mail письмо с ссылкой для подтверждения регистрации, просим вас зарегистрироваться повторно. Приносим извинения за доставленные неудобства. Если вы все еще испытываете проблемы с регистрацией на форуме, обратитесь за помощью на e-mail: mr.angelo@railroadsim.net

LuaScript - для RS

Другие вопросы и проблемы разработки дополнений

Re: LuaScript - для RS

Сообщение Света » 11.07.2016, 14:13

Skif
Всё ясно. Мне почему-то показалось, что речь о списке параметров локомотива, а он вызывается по F5. Моя ошибка.
Я никогда не пользуюсь сокращенным HUD-ом, поэтому возникла путаница.
Сейчас я пишу об этом элементе:
Изображение
Приведенный фрагмент конфига реализует поведение ручки тяги, продемонстрированное в ролике. Скрипт в этом не участвует. Он нужен, чтобы потом восстановить связь. Разлинковку реверсора и тормоза я сейчас не делаю, так как занимаюсь другими делами. Предполагаю, что используя конфигурацию тяги, можно аналогично разлинковать и их. Но на практике это пока не проверено, не дошли руки.
Аватара пользователя
Света
 
Сообщения: 154
Зарегистрирован: 18.06.2016, 19:38
Благодарил (а): 77 раз.
Поблагодарили: 174 раз.
Играю в: RailWorks
Роль: Разработчик

Re: LuaScript - для RS

Сообщение Света » 16.08.2016, 17:05

Всем доброго дня.

Представляю вниманию разработчиков функцию Timer, предназначенную для вставки в скрипт.

Назначение
Функция предназначена упростить контроль временных интервалов в процессе выполнения кода. Каждый, кто писал скрипт, знает, что для того, чтобы сделать задержку, нужно создать переменную, поместить в неё значение задержки, потом периодически вычитать из этой переменной какое-то число, отслеживая, когда значение задержки станет равным или меньше нуля.
Дополнительное неудобство - если до окончания отсчета эту переменную заново перезапустить, отсчет начнется сначала. Чтобы этого избежать, приходится выдумывать новые защитные переменные.
Когда такой "таймер" использован, он продолжает опрашиваться ЦП, хотя в этом нет необходимости.
Каждый такой таймер (а их в скрипте локомотива предостаточно) - это дополнительный множитель всех неудобств, описанных выше.
Мне это надоело. Предлагаемая функция предназначена несколько упростить эту процедуру.

Адаптация в скрипт
Функция вставляется в конец скрипта. Никакие правки не нужны.

Для инициализации рабочей таблицы, вписываем её в функцию Initialise():
Код: Выделить всё
TabTimerData = {}

Для обеспечения счёта периодически делаем вызов из функции Update ( time ):
Код: Выделить всё
 Timer (time)

Использование/вызов
Для того, чтобы запустить таймер, надо отправить в функцию имя таймера и значение задержки в секундах:
Код: Выделить всё
Timer ("timer_1", 15)

Обратите внимание: пока таймер отсчитывает выдержку, его рестарт такой командой невозможен. Это сделано умышленно. Если нужно перезапустить отсчет сначала, применяется другая команда:
Код: Выделить всё
Timer ("timer_1", 15, "restart")

Контролировать окончание отсчета можно 2 способами. Первый способ - это контроль фактического окончания отсчета. Вариант использования:
Код: Выделить всё
   if Timer ( "timer_1" ) then
      -- блок операторов, если время вышло
   else
      -- блок операторов, если отсчет не завершен
   end

Обратите внимание - если опрашивается несуществующий канал таймера, функция также возвратит "false", как и при незавершенном отсчете.
Второй способ - контроль времени, которое осталось до окончания отсчета. Вариант использования:
Код: Выделить всё
   Pause = Timer ("timer_1", "ctrl")

В переменную Pause будет загружено оставшееся время (сек) в числовом формате. Если отсчет закончился, будет загружена строка "timeroff". В случае опроса несуществующего канала будет возвращено значение "nil".

Если необходимости в каком-либо канале таймера нет, его можно разрушить:
Код: Выделить всё
   Timer ("timer_1", "clear")

Такая команда уничтожит канал "timer_1". Это немного сэкономит время, затрачиваемое на обработку функции.
Автоматического удаления каналов с завершенным отсчетом нет.

Количество каналов ограничено объемом оперативной памяти.
Разрешающая способность (сек, грубо) - 1/fps.

Сам код
Код: Выделить всё
function Timer (data_1, data_2, data_3)
   if type(data_1) == "number" then
      for k,v in pairs(TabTimerData) do
         if type(v) ~= "string" then
            v = v - data_1
            if v <= 0 then
               TabTimerData[k] = "timeroff"
            else
               TabTimerData[k] = v
            end
         end
      end
      return
   end

   if type(data_1) == "string" then
      if not data_2 then
         if TabTimerData[data_1] == "timeroff" then
            return true
         else
            return false
         end
      elseif data_2 == "ctrl" then
         return TabTimerData[data_1]
      elseif data_2 == "clear" then
         TabTimerData[data_1] = nil
      elseif not TabTimerData[data_1] or TabTimerData[data_1] == "timeroff" or data_3 == "restart" then
         TabTimerData[data_1] = data_2
      end
   end
end

Приветствуются пожелания по усовершенствованию. Также, несмотря на успешное тестирование, приветствуется любая информация по практическому использованию функции.
Аватара пользователя
Света
 
Сообщения: 154
Зарегистрирован: 18.06.2016, 19:38
Благодарил (а): 77 раз.
Поблагодарили: 174 раз.
Играю в: RailWorks
Роль: Разработчик

Re: LuaScript - для RS

Сообщение Skif » 16.08.2016, 18:19

У меня есть такая, которая в качестве аргумента принимает имя переменной/контрола, и выполняет задержку исходя из помещенных в настроечный массив констант. Т.е. работает не в виде "Сделайте мне задержку на N тактов" или "Сделайте мне задержку на N секунд" , а "Сделайте мне задержку на прокачку масла при пуске дизеля". Мне так удобнее - все настройки временных задержек переходных процессов у локомотива снесены в один массив в заголовке скрипта.


Хорошая кстати идея - выкладывать фрагменты кода.


Могу выложить функции, позволяющие плавно выводить контролы на требуемые значения с задаваемой скоростью изменения. Есть также функция, позволяющая добавлять к значению синусоидальный шум заданной амплитуды и частоты - превосходно используется для имитации подрагивания стрелок приборов от работы дизеля. Есть, правда, некоторый косяк - вторая требует периодической очистки рабочих переменных, иначе начинает глючить. Причина не выяснена. Но с костылем работает как нужно.

Добавлено спустя 6 минут 27 секунд:
На правах П.С. : функции воистину универсальны - от плавного движения стрелок приборов с задаваемой "резиновостью" до реализации того перемещения по кабине, о котором я писал в блоге.

Также можно использовать для реализации у локомотива "МСТС-ного" способа управления по принципу "Одно нажатие кнопки - одна позиция рукоятки". Рекомендуется в некоторых случаях в качестве
дефолтного метода управления контролами в TS. Руки пока не доходят написать унифицированный хендлер для управления локомотивом, займусь им, когда высвободится немного времени от тренажера. Но контроллер в моей машине уже работает близко к задуманной идее. Краны машиниста ждут своей очереди - не будут, как на ТЭМ2, проскакивать позиции (а у меня и сейчас не проскакивают, но будет еще круче).
Skif
 
Сообщения: 2664
Зарегистрирован: 01.10.2009, 17:42
Благодарил (а): 247 раз.
Поблагодарили: 733 раз.
Блог: Просмотр блога (2)
Имя: Дмитрий

Re: LuaScript - для RS

Сообщение sfateev » 17.08.2016, 17:02

Skif писал(а):...Могу выложить функции, позволяющие плавно выводить контролы на требуемые значения с задаваемой скоростью изменения. Есть также функция, позволяющая добавлять к значению синусоидальный шум заданной амплитуды и частоты - превосходно используется для имитации подрагивания стрелок приборов от работы дизеля. Есть, правда, некоторый косяк - вторая требует периодической очистки рабочих переменных, иначе начинает глючить. Причина не выяснена. Но с костылем работает как нужно..

Skif, выложи, пожалуйста. Честно говоря, я думал, что дрожание стрелок задаётся анимацией. :book:
Аватара пользователя
sfateev
 
Сообщения: 820
Зарегистрирован: 29.05.2006, 10:07
Откуда: Москва
Благодарил (а): 464 раз.
Поблагодарили: 64 раз.
Имя: Святослав

Re: LuaScript - для RS

Сообщение Skif » 17.08.2016, 18:03

Можно и анимацией - но анимацией то ты все равно управляешь посредством скрипта!

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

Как вариант - если не хочется загромождать локомотив лишними контролами - сделать то же самое переменной. С точки зрения скрипта, разница между контролом и переменной только в одном - контролам можно переопределять значение через InputMapper, когда пользователь орудует клавиатурой или мышью. Если переменную определить вне функций Initialize и Update, она будет глобальной и может использоваться для хранения значений между тактами (вызовами функции Update движком). Т.е. тот же контрол, но в который нельзя влезть ручками.

Почему удобнее линковать с анимированными деталями контролы, а не переменные?

1. На этапе отладки можно назначить в Input Mapper управляющие клавиши и подвигать стрелку прибора руками (если это не системный контрол вроде BrakeCylinderPressureBAR - их двигать нельзя).

...и посему крайне желательно ВСЕ приборы в кабине привязывать к СОБСТВЕННЫМ контролам, а системные к ним линковать в скрипте - непосредственно или через дополнительную логику. У меня так реализован редукционный клапан питательной магистрали, проверка плотности тормозной магистрали, сверхзарядка ПМ и УР (только визуальная, ни на что не влияет в тормозах. Для простоты.). Тогда на этапе отладки можно с клавиатуры двигать любые стрелки приборов (для сверки значений с тем, что видно на шкале), зажигать лампочки, имитировать рост температуры охлаждающей жидкости и т.п.

2. Потому, что из контролов удобно создавать логические цепочки if-elseif-else , AND, OR и производные от них, собирая схемы локомотивов (точнее - их поведенческие алгоритмические модели). И их значения видно в отладочном окне ControlStateDialog.

3. Потому, что их значения используются как триггеры для звуковой модели локомотива или вагона. Переменную посадить на триггер нельзя. А контрол - пожалуйста. И если у вас вентиль привода жалюзи должен сделать "ПШШШШ", когда створки повернутся на половину хода, то это можно сделать одним контролом. Не создавая дополнительных переменных (кроме тех, что выше по логической цепочке - есть ли питание вентиля от цепей управления, есть ли воздух в магистрали управления, не держит ли створки термостат и т.п. сахарок)


Рассмотрим еще такое понятие, как "виртуальный контрол". Это контрол, не имеющий интерфейса ввода и вывода. Т.е. к нему ни прикреплен Input Mapper, ни прилинкован анимированный узел модели для индикации состояния. Однако! Такой контрол может управляться скриптом - и следовательно, использоваться для управления звуковыми событиями или процессами в схеме локомотива.

Например, на два виртуальных контрола у моего ТУ7А посажена смена звуков дизеля в зависимости от позиции. Так как звуки у меня записаны очень по хитрому, чтобы проигрывать без склеек переходы между позициями и циклы. Также на контролы посажены стрелки индикаторов температуры и давления - а к ним скриптом подключаются РАЗНЫЕ переменные, рассчитанные в схеме. Температура левого/правого моноблока, до и после дизеля, температура масла/воды. Все, как у реального тепловоза.





Сегодня вечером постараюсь выложить функции.

Добавлено спустя 3 часа 8 минут 51 секунду:
Итак, обещанные функции.

UpdateControl (time, name, index, target, speed) и UpdateControlWithTremor (time, name, index, target, speed, gain, freq) - для КОНТРОЛОВ

UpdateVariable (time, name, target,speed) - для ПЕРЕМЕННЫХ . Варианта с дребезгом нет - так как не используется для вывода данных на приборы. А для расчетов в системе. Тремор
добавлять следует только к тому, что этот тремор физически испытывает - в данном случае, к стрелкам приборов.

Использование: применяется для перевода значений контролов и переменных в заданные с заданным темпом единиц/секунду (именно в СЕКУНДУ, а не В ТАКТ, чем так грешат дефолтные скрипты - скорость процессов в которых ой как зависит от ФПС сцены!). Поэтому можно использовать в том числе для расчета переходных процессов в тормозах, если они у вас не дефолтные.

----------------------------------------------------------

UpdateControl (time, name, index, target, speed)

time - системная переменная времени, имеет значение прошедшего времени с прошлого такта скрипта. НЕ ИЗМЕНЯТЬ. При использовании в теле функции подставить имя переменной времени из родительского цикла. Обычно это inteval.
name - имя контрола, который обновляется данным вызовом функции.
index - индекс контрола, который обновляется данным вызовом функции. Добавлено для совместимости формата данных с системными функциями TS вроде GetControlValue.
target - значение, которое нужно присвоить контролу. Направление движения определяется автоматически. Если значение выходит за диапазон контрола, будет достигнуто соответствующее предельное значение.
speed - скорость, с которой нужно обновить значение. Каждую секунду (игровую), к текущему значению контрола будет добавляться либо вычитаться значение speed, промежуточные значения (с частотой тактирования скрипта - читай, ФПС) рассчитываются в теле функции автоматически. Потому движение будет плавным.

Код функции. Вставить в Simulation скрипт, вне всех методов (т.е. в самом начале либо самом конце файла, вне операторных скобок)

Код: Выделить всё
function UpdateControl (time, name, index, target, speed)         

-- функция апдейта контрола на новое значение с задаваемой скоростью единиц/сек.
-- name - имя контрола
-- index - индекс контрола
-- target - целевое значение
-- speed - скорость перевода

n_value = Call ("*:GetControlValue", name, index);
n_step = time*speed;


if ( n_step > math.abs(n_value - target) ) then   -- устранение "дребезга" вокруг конечного значения.
   n_step = math.abs(n_value - target);
end

if (speed > 0) then

if (n_value > target) then
   Call("*:SetControlValue", name, index, n_value - n_step);
end
if (n_value < target) then
   Call("*:SetControlValue", name, index, n_value + n_step);
end

if (n_value == target) then
   Call("*:SetControlValue", name, index, n_value);
   return 1;
end

else return -1;

end
end


Пример использования в теле скрипта:

Код: Выделить всё
if (AirSpeed >= ApplicationRate) then
      UpdateControl (inteval, "TrainBrakeControl", 0, 0.42, 0.1*AirSpeed);
   end


Комментарий: в данном блоке кода производится сравнение скорости воздушного потока со темпом разрядки УР темпом служебного торможения. Если проверка возвращает True, значение СИСТЕМНОГО контрола (прошу это отметить) TrainBrakeControl с индексом 0 (а обычно 1,2 и так далее у локомотива и не бывает) приводится к значению 0.42 темпом (0.1 х AirSpeed) единиц/сек.

Во, заодно раскрыл секрет, как у меня тормоза разряжают фиксированным темпом вне зависимости от ФПС игры - кто-то интересовался, я помню.


-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Продолжим. Вторая функция.

UpdateControlWithTremor (time, name, index, target, speed, gain, freq)

time - системная переменная времени, имеет значение прошедшего времени с прошлого такта скрипта. НЕ ИЗМЕНЯТЬ. При использовании в теле функции подставить имя переменной времени из родительского цикла. Обычно это inteval.
name - имя контрола, который обновляется данным вызовом функции.
index - индекс контрола, который обновляется данным вызовом функции. Добавлено для совместимости формата данных с системными функциями TS вроде GetControlValue.
target - значение, которое нужно присвоить контролу. Направление движения определяется автоматически. Если значение выходит за диапазон контрола, будет достигнуто соответствующее предельное значение.
speed - скорость, с которой нужно обновить значение. Каждую секунду (игровую), к текущему значению контрола будет добавляться либо вычитаться значение speed, промежуточные значения (с частотой тактирования скрипта - читай, ФПС) рассчитываются в теле функции автоматически. Потому движение будет плавным.

gain - амплитуда синусоидальных колебаний, накладываемых на мгновенное значение на данном такте. Можно добавить генератор случайных чисел, но будьте осторожны - чтобы не зашкалил прибор. Это АБСОЛЮТНЫЕ значения, т.е. в тех же единицах, что и target.
freq - частота синусоидальных колебаний, накладываемых на мгновенное значение на данном такте. Герц, то есть колебаний в секунду. Обычно кратно RPM или аналогичному значению. У спидометра - частоте пульсаций в датчике скорости.

Код функции. Вставить в Simulation скрипт, вне всех методов (т.е. в самом начале либо самом конце файла, вне операторных скобок)


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

TremorCounter = {      -- счетчики для тремора стрелок приборов

   ["EngineRPM"]       = 0,
   ["GMPPress F"]      = 0,
   ["WaterTemp F"]      = 0,
   ["OilPress F"]      = 0,
   ["GMPPress R"]      = 0,
   ["WaterTemp R"]      = 0,
   ["OilPress R"]      = 0,
   ["Speedometer F"]    = 0,
   ["Speedometer R"]    = 0
}

-- В этом блоке ПЕРЕЧИСЛИТЬ ОБЯЗАТЕЛЬНО имена всех контролов в указанном формате, которые будут обновляться функцией UpdateControlWithTremor . Это счетчики колебаний синусоиды.
-- нужны для предотвращения сбоя фазы колебаний и защиты от переполнения переменной. Если кто-то найдет способ этот блок не использовать (см. код функции), это будет здорово.


function UpdateControlWithTremor (time, name, index, target, speed, gain, freq)

-- функция апдейта контрола на новое среднее значение с задаваемой скоростью единиц/сек и колебаниями.
-- name - имя контрола
-- index - индекс контрола
-- target - целевое среднее значение
-- speed - скорость перевода
-- gain - амплитуда синусоидальных колебаний
-- freq - частота синусоидальных колебаний

n_value = Call ("*:GetControlValue", name, index);
n_step = time*speed;
n_inc = 1.0;
if (TremorCounter [name] > 10) then -- защита от переполнения переменной-счетчика.
   TremorCounter [name] = 0;
end

TremorCounter [name] = TremorCounter [name] + n_inc*time;
tremor = gain* math.sin (freq * TremorCounter [name]);

if ( n_step > math.abs(n_value - target) ) then   -- устранение "дребезга" вокруг конечного значения.
   n_step = math.abs(n_value - target);
end

if (speed > 0) then

if (n_value > target) then
   Call("*:SetControlValue", name, index, n_value - n_step + tremor);
end
if (n_value < target) then
   Call("*:SetControlValue", name, index, n_value + n_step + tremor);
end

if (n_value == target) then

   Call("*:SetControlValue", name, index, n_value + tremor);
   return 1;
end

else return -1;
end
end


Пример использования в теле скрипта:

Код: Выделить всё
if (PKM_Front == 0.25) then

   UpdateControl (inteval, "WaterTemp F", 0, WaterTemp, 200);

   UpdateControlWithTremor (inteval, "OilPress F", 0, OilPress, 20, 0.03, EngineRPM*0.05);
   UpdateControlWithTremor (inteval, "GMPPress F", 0, OilPressGMP, 20, 0.02, EngineRPM*0.05);

-- этот код НЕПОЛНЫЙ, здесь длинная цепочка if-elseif-else-end . Не используйте его "как есть"
end


Расшифровка: Если передний переключатель индикаторов установлен в положение "1" , обновляем показания датчика температуры воды переднего пульта (эта функция расписана выше и вам уже знакома).

Также обновляем :

- Индикатор давления масла переднего пульта на контроле "OilPress F", индексом 0, до значения переменной OilPress, темпом 20 Атм/сек. На результат накладывать синусоидальные колебания
амплитудой в 0.03 Атм , с частотой в 0.05 от частоты вращения коленчатого вала дизеля (стрелка упругая, она не будет колебаться на 1-ой гармонике в 500-1500 колебаний в секунду)

Аналогично обновляем индикатор давления масла в гидропередаче, но до другого значения, вибрация чуть слабее (у прибора меньше диапазон, и чем меньше диапазон, тем меньше надо делать амплитуду), но с той же частотой.





Последняя и достаточно простая функция - так как переменные тоже иногда нужно обновлять постоянным темпом (зависящим от системных часов, а не ФПС), по аналогии с функцией для
контролов написана и функция для переменных. Позволяет строить линии задержки (ставите проверку на значение переменной - и увеличиваете ее медленно функцией), рассчитывать
переходные процессы, а можно использовать в сочетании с SetControlValue аналогично функции UpdateControl - результат будет аналогичный.

UpdateVariable (time, name, target,speed)

time - системная переменная времени, имеет значение прошедшего времени с прошлого такта скрипта. НЕ ИЗМЕНЯТЬ. При использовании в теле функции подставить имя переменной времени из родительского цикла. Обычно это inteval.
name - имя переменной, которая обновляется данным вызовом функции.

target - значение, которое нужно присвоить переменной. Направление движения определяется автоматически. Внимание! У переменных нет диапазонов, кроме предельных значений типа - переменная может переполниться, если вы не
контролируете процесс обновления значения условиями!!! Зазеваетесь - получите температуру масла в -65535 градусов.

speed - скорость, с которой нужно обновить значение. Каждую секунду (игровую), к текущему значению будет добавляться либо вычитаться значение speed, промежуточные значения (с частотой тактирования скрипта - читай, ФПС) рассчитываются в теле функции автоматически.

Тело функции:

Код: Выделить всё
function UpdateVariable (time, name, target,speed)

-- функция апдейта переменной на новое значение с задаваемой скоростью единиц/сек.
-- name - имя контрола
-- target - целевое значение
-- speed - скорость перевода

--Эта функция возвращает значение!

n_step = time*speed;
n_value = 0;

if ( n_step > math.abs(name - target) ) then   -- устранение "дребезга" около конечного значения.
   n_step = math.abs(name - target);
end

if (speed >0) then

if ( name > target) then
   n_value = name - n_step;
end
if ( name < target) then
   n_value = name + n_step;
end
if ( name == target) then
   n_value = name;
end

else return -1;
end
return n_value;
end



Эта функция возвращает значение, в отличие от предыдущих! И ее можно использовать в Call-aх для мгновенной передачи рассчитанных значений в контролы. Пример использования:

Код: Выделить всё
elseif (GetPower < 0.5) then   -- дизель остановлен
   
   -- остывание жидкостей до минимальной температуры сезона
   
   WaterTemp    = UpdateVariable (inteval, WaterTemp, MinTemp, 0.010);
   OilTemp    = UpdateVariable (inteval, OilTemp, MinTemp, 0.012);
   OilTempGMP    = UpdateVariable (inteval, OilTempGMP, MinTemp, 0.012);


В общем, все понятно из комментариев - переменным, используемым в скрипте для рассчета температуры воды, масла и масла гидропередачи, назначаются новые значения, равные температуре
жидкостей у "холодного" тепловоза. Проще говоря - они остывают до уличной температуры, заданным темпом. Вода - помедленнее, масло, как более теплоемкое - быстрее. 0.010 и 0.012 градуса в секунду. Как можно было заметить выше, именно эти значения передаются (тоже с плавным ходом) на приборы в кабине. Т.е. водичка остывает медленно - стрелка прибора тоже реагирует не мгновенно, а со своей скоростью. Если приборы отключить, они тоже упадут на ноль не мгновенно, а мягко и лампово. Как и положено настоящим.



Надеюсь, я сделал вашу жизнь веселее. Пользуйтесь, на здоровье. :diablo:

Добавлено спустя 49 минут 5 секунд:
П.С.

Обратите внимание - функции UpdateControl и UpdateControlWithTremor не возвращают рабочих значений, а обновляют значения контролов, принятых в аргументах. Т.е. в любимых олдфагами терминах называются ПРОЦЕДУРАМИ. Ладно, на самом деле - возвращают (-1) при неправильном вводе параметров. Но это обычно нигде не используется. Примерная аналогия в ООП - методы для записи значений в классе или его конструктор.

Функция же UpdateVariable должна использоваться только как часть математического или логического выражения и возвращает ОБНОВЛЕННОЕ ЗНАЧЕНИЕ той самой переменной, которую вы обновляете, т.е является "истинной" ФУНКЦИЕЙ. Забудете его забрать - так оно у вас и останется необновленным. Почему я сделал так, а не написал эту функцию по полной аналогии? А ХЗ - работает, и работает надежно. А переписывать скрипт мне не хочется. Возможно, в последующих локомотивах полностью перепишу по аналогии с функцией для контролов - чтобы выполнялась как процедура, а не вызывалась в рассчетах. :beby:

Также ВАЖНО: значения параметра Speed , передаваемого в тело функции, должно быть БОЛЬШЕ НУЛЯ. Это вам не консольный код и не приложение, которое может ругнуться "ой, мне прислали какую-то херню". Функция просто не выполнится (код -1), значение останется прежним.
Skif
 
Сообщения: 2664
Зарегистрирован: 01.10.2009, 17:42
Благодарил (а): 247 раз.
Поблагодарили: 733 раз.
Блог: Просмотр блога (2)
Имя: Дмитрий

Re: LuaScript - для RS

Сообщение sfateev » 17.08.2016, 22:48

Skif, cпасибище огромное за целый мануал! :up:
Света, спасибо и за твой бесценный труд! :)
Надеюсь воспользоваться этими уроками.
Было бы здорово, если бы кто-нибудь в общих чертах объяснил принцип создания скрипта для АЛСН.
Последний раз редактировалось sfateev 18.08.2016, 03:07, всего редактировалось 1 раз.
Аватара пользователя
sfateev
 
Сообщения: 820
Зарегистрирован: 29.05.2006, 10:07
Откуда: Москва
Благодарил (а): 464 раз.
Поблагодарили: 64 раз.
Имя: Святослав

Re: LuaScript - для RS

Сообщение i2GR » 18.08.2016, 01:00

в блогах как бэ не принцип, а совсем скрипт дешифратора есть
Аватара пользователя
i2GR
 
Сообщения: 425
Зарегистрирован: 04.09.2008, 16:59
Благодарил (а): 174 раз.
Поблагодарили: 257 раз.
Блог: Просмотр блога (4)
Имя: Игорь

Re: LuaScript - для RS

Сообщение sfateev » 18.08.2016, 03:06

i2GR, спасибо! "Слона то я и не приметил...(с)))))"
Аватара пользователя
sfateev
 
Сообщения: 820
Зарегистрирован: 29.05.2006, 10:07
Откуда: Москва
Благодарил (а): 464 раз.
Поблагодарили: 64 раз.
Имя: Святослав

Re: LuaScript - для RS

Сообщение Света » 08.09.2016, 22:16

i2GR писал(а):в блогах как бэ не принцип, а совсем скрипт дешифратора есть

К сожалению, это не будет работать на маршруте.
Замысел хорош, исполнение - хоть и не в моем стиле, но тоже похвально. А алгоритм нерабочий. ИМХО.
Вот почему.
-- счетчик полученных в течение кадра сообщений (каждый кадр может быть принято до двух сообщений от светофоров (при правильной их работе), поэтому надо учесть все сообщения)
CounterOCSM = CounterOCSM + 1

В нормальной обстановке на маршруте с более чем одной единицей ПС сообщений за кадр может быть от 1 до 4 (вернее, так будет после выпила сообщений протокола версии 0.5, а до того их от 2 до 8). Привожу очень укороченный фрагмент лога для подтверждения (повторяющиеся фрагменты вырезаны мной):
Код: Выделить всё
00:00:00 Запуск.
[...]
00:00:14   Новое сообщение - F0311302826~
00:00:14   Новое сообщение - 03113002826~
00:00:14   Новый кадр
00:00:14   Новое сообщение - B000000044Y<C~
00:00:14   Новое сообщение - 0000000044Y<C~
00:00:14   Новое сообщение - F0311302826~
00:00:14   Новое сообщение - 03113002826~
00:00:14   Новый кадр
[...]
00:00:23   Новый кадр
00:00:23   Включение green. Выполнение
00:00:23   Новое сообщение - B000000044Y<C~
00:00:23   Новое сообщение - 0000000044Y<C~
00:00:23   Новое сообщение - F0311302826~
00:00:23   Новое сообщение - 03113002826~
00:00:23   Новый кадр
[...]
00:00:35   Новый кадр
00:00:35   Новое сообщение - B0211302663~
00:00:35   Новое сообщение - 02113002663~
00:00:35   Новое сообщение - B000000066Y<C~
00:00:35   Новое сообщение - 0000000066Y<C~
00:00:35   Новое сообщение - F0311302606~
00:00:35   Новое сообщение - 03113002606~
00:00:35   Новый кадр
[...]
00:01:04   Новый кадр
00:01:04   Новое сообщение - B000000571Y<C~
00:01:04   Новое сообщение - 0000000571Y<C~
00:01:04   Новое сообщение - B0011300013~
00:01:04   Новое сообщение - 00113000013~
00:01:04   Новое сообщение - F0111301002~
00:01:04   Новое сообщение - 01113001002~
00:01:04   Новое сообщение - F000000869XFK~
00:01:04   Новое сообщение - 0000000869XFK~
00:01:04   Новый кадр

Этот лог записан при достаточно распространенной ситуации - после отцепки вагона на станции мой локомотив двигался на другую станцию, где путь приема был занят другим локомотивом. Всего 3 единицы ПС.
Светофоры при этом работали совершенно правильно.
Как можно видеть, между событиями "Новый кадр" поступает от 2 до 8 сообщений.

Исходя из этого, я утверждаю, что первый прогнозированный сбой случится на 20 строке тела функции. Будут засчитаны оригинальное сообщение и его клон для протокола 0.5. Ладно, допустим, отсылка сообщений по протоколу 0.5 упразднена. Значит, сбой случится на 35 секунде, когда будут пойманы сообщения B0211302663~ и B000000066Y<C~. Направление одно, а информация разная.
Другой спорный момент - строка 45. Сравнение скорости локомотива с константой 0,05. Но ведь чувствительность светофоров, при которой они определяют направление движения - 0,15. Разве нет вероятности получить ложный индекс направления в этом окне?

Поэтому, перед тем, как разрабатывать логику обработки сообщений, надо сделать фильтр, который будет отсеивать ложные сообщения. Например, сообщения, которые передаются идущему впереди составу и не блокируются им. Сообщения, которые "утверждают", что они передаются со светофора впереди, тогда как они идут с другой стороны. Сообщения с другими протоколами.
Также надо разработать концепцию определения отсеивания сообщений, отталкиваясь от конструктивных особенностей локомотива. Если в ТЭМ2 один светофорчик, работающий на катушки с двух сторон, то в ТЭ10м сзади катушек, НЯЗ, нет! Поэтому АЛСН априори не может показать цветной код от светофора сзади, даже если он открыт. Но если взять 2ТЭ10м, то показания на светофорчиках в каждой кабине будут разные (я так думаю). Если так, значит надо отмечать коды сообщений для каждой кабины персонально, но в блоке УКБМ-ЭПК обрабатывать только актуальные для активной кабины. Здесь ещё одна трудность - если тепловоз заторможен, то при смене кабины надо менять и индекс принимаемых кодов. При скорости 0,15 светофоры сменят индексы. Опять логику задействовать...

Добавлено спустя 21 минуту 29 секунд:
Skif, спасибо за примеры.
Один момент только насторожил. Вы, наверное, очень долго писали/пишете программы на ассемблере? Удивила вот эта конструкция из взаимоисключающих условий:
Код: Выделить всё
if (n_value > target) then
   Call("*:SetControlValue", name, index, n_value - n_step + tremor);
end
if (n_value < target) then
   Call("*:SetControlValue", name, index, n_value + n_step + tremor);
end

if (n_value == target) then
   Call("*:SetControlValue", name, index, n_value + tremor);
   return 1;
end
Очень напомнило :cofe: :
Код: Выделить всё
   CPWRN   20, 19, $0203      ; Если это цифры 2-3,
   RJEQ   CALL_AT            ;идем на вызов АТ
   cpi   R19,   $03         ; Если 1-я цифра "3",
   RJEQ   BUU               ;идем на контроллер удаленного доступа
   cpi   R19,   $0A         ; Если 1-я цифра "0",
   RJEQ   BVC               ;идем на контроллер блокировки входящих вызовов
   cpi   R19,   $05         ; Если 1-я цифра не "5",
   RJNE   ERROR_NUMBER      ; Переход на НЕВЕРНЫЙ НОМЕР


Зачем три раза проверять условие, если первое же его выполнение исключает выполнение в остальных ветках? Почему не if-elseif-else-end? Это ассемблерная привычка или может есть какой-то нюанс, о котором я не знаю?
Аватара пользователя
Света
 
Сообщения: 154
Зарегистрирован: 18.06.2016, 19:38
Благодарил (а): 77 раз.
Поблагодарили: 174 раз.
Играю в: RailWorks
Роль: Разработчик

Re: LuaScript - для RS

Сообщение i2GR » 09.09.2016, 02:24

ИМХО не работающим является не алгоритм, а конкретная реализация. Некоторое время этот скрипт работал идеально.
Счетчик должен был изменяться только при сообщениях на F... или B...
Что там реально все портит я не знаю. Давно проблемой АЛСН не занимался, но особенностью было то, что понятия "спереди" и "сзади" условны. Спереди - это, по крайней мере, был, тот светофор, расстояние до которого уменьшается.
0.05 это костыль. в идеале я бы хотел регистрировать отличие от нуля.
Надо думать, короче.
Аватара пользователя
i2GR
 
Сообщения: 425
Зарегистрирован: 04.09.2008, 16:59
Благодарил (а): 174 раз.
Поблагодарили: 257 раз.
Блог: Просмотр блога (4)
Имя: Игорь

Re: LuaScript - для RS

Сообщение Skif » 09.09.2016, 10:16

Skif, спасибо за примеры.
Один момент только насторожил. Вы, наверное, очень долго писали/пишете программы на ассемблере? Удивила вот эта конструкция из взаимоисключающих условий:

Код: Выделить всё
if (n_value > target) then
Call("*:SetControlValue", name, index, n_value - n_step + tremor);
end
if (n_value < target) then
Call("*:SetControlValue", name, index, n_value + n_step + tremor);
end

if (n_value == target) then
Call("*:SetControlValue", name, index, n_value + tremor);
return 1;
end


Это не ассемблер. Это SystemC - самый лучший подход при написании "тактируемых" скриптов (запускаемых с частотой кадра - что и имеет место в RW). Так удобнее в отладке и быстрее в работе на системах, где критична частота - три отдельных if работают быстрее, чем if-elseif-else . Ну, и самый универсальный ответ - А МНЕ ТАК УДОБНЕЕ. :olen:
Skif
 
Сообщения: 2664
Зарегистрирован: 01.10.2009, 17:42
Благодарил (а): 247 раз.
Поблагодарили: 733 раз.
Блог: Просмотр блога (2)
Имя: Дмитрий

Re: LuaScript - для RS

Сообщение Света » 09.09.2016, 11:45

i2GR
i2GR писал(а):...особенностью было то, что понятия "спереди" и "сзади" условны. Спереди - это, по крайней мере, был, тот светофор, расстояние до которого уменьшается.
0.05 это костыль. в идеале я бы хотел регистрировать отличие от нуля.
Надо думать, короче.
Я Вам подкину идею.
Главная проблема, ИМХО, заключается в том, что неизвестно направление принятых сообщений. Думаю, Вы со мной в этом согласитесь - ведь именно этот недостаток функции OnCustomSignalMessage побудил Вас лепить костыль с индексами направления. Замысел неплохой, только его единственный серьёзный недостаток - присваивание индексов, привязанное к ПС. Вот главная слабость системы. Ведь индекс изменяется достаточно непредсказуемо - легкие рывки локомотива на границе чувствительности светофора создадут такую смесь сообщений, что любой дешифратор перегрузится.
Поэтому я предлагаю несколько иную концепцию присвоения "направленности" сообщений.
Предлагаю систему, при которой "направленность" будет задаваться без привязки к локомотивам. А по взаимному расположению светофоров относительно друг друга.
Для этого надо выбрать ключевой светофор (или создать маркер на путь), который в начале сценария отправит запрос с сообщением, например, "F" вперед и "B" - назад. Каждый светофор, получив этот запрос, устанавливает внутренний триггер направления. Если "F" пришло сзади или "B" спереди, триггер устанавливается в "F", иначе - в "B". Также, получив этот запрос, светофор пересылает его дальше.
В результате такой операции все светофоры на маршруте окажутся ориентированы относительно друг друга. Те, что "смотрят" в одну сторону, будут иметь один индекс, а те, что "смотрят" им навстречу, - получат индекс-антипод. Останется только присоединить индекс триггера к сообщению локомотива. То есть, дальше уже система отработана.
Локомотивный дешифратор в таком случае получится максимально простым - достаточно один раз синхронизировать направление локомотива и индекс, чтобы четко различать нужные индексы.
Единственное слабое место, которое я сейчас вижу - это кольца и замкнутые съезды. Но такие моменты достаточно легко решаются. Достаточно ввести приоритет присваивания триггера направления по индексу линка.
Аватара пользователя
Света
 
Сообщения: 154
Зарегистрирован: 18.06.2016, 19:38
Благодарил (а): 77 раз.
Поблагодарили: 174 раз.
Играю в: RailWorks
Роль: Разработчик

Re: LuaScript - для RS

Сообщение Skif » 09.09.2016, 14:12

Я за маркеры - именно такая система используется в ТРС. И именно с этой целью. Эти же маркеры раньше использовали для задания направления для движения ботов.

Как вариант - использовать в качестве маркера сам светофор, договорившись,что со стороны линз - "зад", а со стороны лесенки - "перед" для передачи сообщений. Т.е. вперед - это перед локомотивом, а назад - это навстречу локомотиву.
Skif
 
Сообщения: 2664
Зарегистрирован: 01.10.2009, 17:42
Благодарил (а): 247 раз.
Поблагодарили: 733 раз.
Блог: Просмотр блога (2)
Имя: Дмитрий

Re: LuaScript - для RS

Сообщение TRam_ » 09.09.2016, 20:31

Если в ТЭМ2 один светофорчик, работающий на катушки с двух сторон, то в ТЭ10м сзади катушек, НЯЗ, нет! Поэтому АЛСН априори не может показать цветной код от светофора сзади, даже если он открыт.
Пассажирским Ушкам иногда ставят катушки и сзади. Переключатели "с передней на заднюю" у них, как и у большинства двухкабинных локов находятся в машинном (а не в кабинах).
По поводу ТЭМа - при надвиге на горку коды на некоторых станциях тоже подаются, но не спереди а сзади. А чтоб коды принимались и спереди и сзади - это только в метро бывает.
в z7 всё можно, а что нельзя - можно в sU
Аватара пользователя
TRam_
 
Сообщения: 1902
Зарегистрирован: 30.11.2007, 20:14
Благодарил (а): 2 раз.
Поблагодарили: 61 раз.
Играю в: Auran Trainz
Роль: Разработчик
Имя: Владимир

Re: LuaScript - для RS

Сообщение Skif » 10.09.2016, 12:03

Нет такого в метро - не вноси путаницы. Это я тебе как метрополитенщик заявляю. В хвостовой кабине комплект АРС отключается, иначе машина встанет колом из-за приема кода ЛН (ложное направление - оно же используется для контроля стороны открытия дверей в системе МАРС) и замыкания цепей АРС друг на друга через переключатели РЦ АРС. Кроме того, цепи АРС хвостового вагона заведены в контроллер на реверсивную рукоятку, и при извлеченной рукоятке разомкнуты.

Осаживание задним ходом, если оно делается из головной кабины (вагонами вперед), делается с отключением АРС, а из хвостовой кабины - еще и под код ЛН, так как парковые пути обычно не кодируются со стороны неправильного пути.

В системе АРС-Д хвостовой комплект может использоваться в исключительных случаях в качестве резервного при неисправности и отключении головного (тумблер АРС-Р на пульте), в системах МАРС и БАРС в каждой голове стоят два комплекта - основной и дублирующий АРС-Р.
Skif
 
Сообщения: 2664
Зарегистрирован: 01.10.2009, 17:42
Благодарил (а): 247 раз.
Поблагодарили: 733 раз.
Блог: Просмотр блога (2)
Имя: Дмитрий

Пред.След.

Вернуться в [RW] Другие вопросы

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1