Отладка сложных скриптов — различия между версиями

Материал из WebHMI Wiki
Перейти к: навигация, поиск
(Энергонезависимые регистры)
Строка 1: Строка 1:
 +
<translate>
 
== Отладка сложных скриптов ==  
 
== Отладка сложных скриптов ==  
 
=== Инициализация проекта ===
 
=== Инициализация проекта ===
Строка 104: Строка 105:
  
 
</syntaxhighlight>
 
</syntaxhighlight>
 
+
</translate>
== Формирование суточных, недельных и т.п. отчетов ==
+
 
+
 
+
<p> Для оптимизации производительности устройства данные для отчетов целесообразно регистрировать "в потоке". В WebHMI для этого есть механизм событий, которые после регистрации фактически дают готовые отчеты. </p>
+
::'''Необходимые переменные, скрипты и события -''' <br>
+
*скрипт, выдающий номер дня, месяца, недели, квартала и т.п. из системного времени во внутренний регистр "день недели", "час", "секунда"
+
* скрипт подсчета разницы "текущие показание - предыдущие", и фиксации результата в регистре "счетчик" (скрипт работает также по изменению регистра "минута")
+
* регистр-флаг генерации события, однократного, достаточного для записи текущего посчитанного счетчика и метки времени
+
* скрипт, снимающий флаг, когда есть событие (т.е. данные уже зафиксированы)'
+
* событие для регистрации
+
Для демонстрации можно использовать пример, в котором на каждой секунде "виртуальный" счетчик увеличивается на 2. Другие скрипты реализуют поминутную запись разницы его показаний в отчет. Для добавления других параметров, необходимо по аналогии добавить их внутрь соответствующих скриптов и событий. 
+
Скрипт, генерирующий номера минут:
+
<syntaxhighlight lang = "lua">
+
function main (userId)
+
  -- получить номер минуты
+
  local min = os.date("%M",os.time());
+
  -- записать во внутренний регистр
+
  WriteReg("minute", min); -- Минуты 
+
end
+
</syntaxhighlight>
+
Скрипт, работающий на изменение регистра "minute":
+
 
+
 
+
<syntaxhighlight lang = "lua">
+
 
+
 
+
function main (userId)
+
+
  local current = GetReg("FlowMeter"); -- текущие показания
+
  local prevMin = GetReg("pMinTotal"); -- предыдущее значение
+
  local cnt = current - prevMin; -- посчитать разницу
+
+
  WriteReg("MinuteFlow", cnt); -- записать счетчик во внутр. регистр. он используется в событии
+
  WriteReg("pMinTotal", current); -- записать текущее в предыдущее для след. периода
+
 
+
  -- поднять флаг для отчета
+
  WriteReg("EventFlag", 1); -- Установить флаг для события 
+
  end
+
 
+
 
+
</syntaxhighlight>
+
 
+
 
+
Скрипт, снимающий флаг и соответственно событие после записи:
+
<syntaxhighlight lang = "lua">
+
 
+
 
+
function main (userId)
+
  -- проверяем выполняется ли событие
+
  local event_state = (GetReg("ES1") == 1); -- Событие 1 выполняется  (ES1@Internal register)
+
  -- и сбрасываем флаг
+
  if event_state then
+
      WriteReg("EventFlag", 0); -- Флаг минутного отчета 
+
  end
+
end
+
 
+
 
+
</syntaxhighlight>
+
Настройки события для данного отчета:<br>
+
[[Файл:Rep example2.png | 800px]]<br>
+
[[Файл:Rep example3.png | 800px]]<br>
+
 
+
 
+
Результатом будет являться отчет такого вида:<br>
+
[[Файл:Rep example.png | 800 px]]<br>
+
При поминутной регистрации данные результаты получаться, если время скана будет меньше чем секунда. Тогда "виртуальный" электросчетчик не пропустит своих секунд.
+

Версия 09:19, 23 марта 2018

Отладка сложных скриптов

Инициализация проекта

Энергонезависимые регистры

Следует помнить что регистры типа Dхх и некоторые другие внутренние регистры, кроме DSxx возвращаются в начальное состояние (как правило в 0) после инициализации проекта (что происходит при любом редактировании элементов проекта - скриптов, регистров, соединений и т.д.). Если такие регистры используются как переменные для формирования триггеров событий или условий выполнения других скриптов, это может приводить к нарушению логики выполнения. Следует учитывать эту особенность при выборе регистров как входных и выходных переменных скриптов.

Глобальные переменные

Переменные в скриптах, объявленные до функции main сохраняют свои значения между вызовами скрипта, но при инициализации также принимают начальные значения. Их можно использовать для хранения констант, коэффициентов и др. подобных величин.

Первый скан

Скрипту можно назначить способ запуска на первом скане при инициализации проекта и сделать некую инициализацию переменных из одного места. Иногда может понадобится определить внутри самого скрипта, был ли это первый вызов после инициализации. Можно создать глобальную переменную first_scan:

first_scan = true;
function main (userId)
  if first_scan then 
     first_scan = false;
    --[[
 Действия при инициализации проекта
    --]]             
 end --if 
end -- main


Следует помнить что регистры типа Dхх, CDxx и другие внутренние регистры, кроме DSxx возвращаются в начальное состояние (как правило в 0) после инициализации проекта (что происходит при любом редактировании элементов проекта - скриптов, регистров, соединений и т.д.). Если такие регистры используются как переменные для формирования триггеров событий или условий выполнения других скриптов, это может приводить к нарушению логики выполнения. Следует учитывать эту особенность при выборе регистров как входных и выходных переменных скриптов.

Модульность

Рекомендуется разбивать сложные скрипты на более простые и часто используемые функции, которые можно использовать повторно. Разбивка на более простые части, расположение их в нужном порядке и группировка помогает контролировать логику работы системы и легче наладить систему. Например, если есть задача управления положением задвижки, можно выделить такие части:

блок "захвата" момента включения привода, для инициализации начальной позиции и времени начала движения
блок расчета текущей позиции в движении (который определяет время между вызовами скрипта и вычисляет пройденный путь штока)
блок авт. управления (проверяет рассогласование между уставкой и тек. положением, определяет направление и дает команду, если нет других блокировок)
блок ручного управления
блок блокировок по наезду на конечные выключатели - для снятия текущей команды и установки позиции в 0/100%


Сложные функции перед вложением их другие скрипты можно отладить отдельно в пользовательском скрипте, выделив часть внутренних регистров под входные переменные, часть под выходные. Эти же регистры вынести на дешборд, вместе с кнопкой запуска отлаживаемого скрипта. Тогда меняя входные наборы данных удобно видеть тут же результат выполнения. Также можно использовать отдельную IDE типа Eclipse для Lua или онлайн версию, чтобы удобно и быстро отлаживать маленькие фрагменты, изучать работу новых функций и т.д.

Отличия записи во внутренние регистры от регистров в устройствах

Есть некоторые отличия в работе функций SetReg и WriteReg применительно к внутренним регистрам (Dxx, DSxx). Эти функции непосредственно меняют значения внутренних регистров внутри скана, а не откладывают запись WriteReg на следующий скан. Таким образом, в конце скана внутренний регистр может иметь значение, отличное от того которые было на входе в скан. Тогда, например возможна ситуация, когда:

  • скрипт 1 меняет значение некоего регистра Dn. (выполняется в каждом скане)
  • скрипт 2 работает по изменению этого регистра Dn. (выполняется по изменению регистра)

Если порядок выполнения скриптов будет 1 - 2, то все будет работать, потому что на входе в скан скрипт 2 видел одно значение, и перед своим выполнением другое (которое успел изменить скрипт 1), и отработает "по изменению". Если же порядок выполнения скриптов поменять местами, то скрипт 2 перестанет, работать, так как на входе в текущий скан он будет видеть измененное значение, а новое изменение произойдет после скрипта 2 в скрипте 1.

Отладочная печать

Желательно сразу ставить после ключевых моментов логики в скриптах отладочную печать функциями TRACE, c DEBUG c номером скрипта или названием функции. Тогда эти фрагменты удобно искать и анализировать в коммуникационном логе. Однако в большой системе, когда отладочной печати становится много, становится неудобно искать необходимые данные. Можжно поступить следующим образом - назначить свою функцию отладочной печати, которая будет вызываться только, если отладочная печать в этом скрипте разрешена. Например, можно в регистр "debug_ID" записывать id скрипта в котором нужна отладночная печать, а функция внутри скрипта будет смотреть на этот номер. Например:

thisScriptID = 15;
function main (userId)
                                          MyDebugPrint("Программа симуляции теплосчетчка");
  local VLVpos = GetReg("curPosition"); -- Текущее положение клапана (DS1005@WebHMI 3point control)
--[[
code
--]]
end 


function MyDebugPrint(str)
    local debug_id = GetReg("debug_ID");
   
    if (debug_id == thisScriptID) then 
        ERROR(str);
    end 
end

Внутри функции задавать уровень лога - INFO, ERROR и т.д. а в настройках системы временно отключать неиспользуемые уровни, чтобы сфокусироваться только на нужной отладочной информации. Можно развить вариант до одновременного вывода в нескольких скриптах, если debug_ID сделать строкой вида "25 26 27 ", т.е. перечень скриптов с включенной отладкой. Номера можно быстро выделять используя строковые функции поиска паттернов:


------- debug printing -------


function DEBUG_(str)


local i = 0;
local tmp = "";


while true do 
i,_, tmp = string.find(str, "(%d+)%s+",i+1);


if i == nil  then break end  -- не найдено 
    if (tmp ~= "0") then 
        -- найдено проверить на совпадение 
        if (tmp == tostring(thisScriptID)) then 
             ERROR(str); 
        end 
    end 
end 
    
end -- DEBUG_
------------------------------