Отладка сложных скриптов — различия между версиями
(→Отладочная печать) |
(Отметить эту версию для перевода) |
||
(не показано 8 промежуточных версий этого же участника) | |||
Строка 1: | Строка 1: | ||
+ | <languages/> | ||
<translate> | <translate> | ||
− | + | ||
− | === Инициализация проекта === | + | === Инициализация проекта === <!--T:1--> |
==== Энергонезависимые регистры ==== | ==== Энергонезависимые регистры ==== | ||
Следует помнить что регистры типа Dхх и некоторые другие внутренние регистры, кроме DSxx возвращаются в начальное состояние (как правило в 0) после инициализации проекта (что происходит при любом редактировании элементов проекта - скриптов, регистров, соединений и т.д.). Если такие регистры используются как переменные для формирования триггеров событий или условий выполнения других скриптов, это может приводить к нарушению логики выполнения. Следует учитывать эту особенность при выборе регистров как входных и выходных переменных скриптов. | Следует помнить что регистры типа Dхх и некоторые другие внутренние регистры, кроме DSxx возвращаются в начальное состояние (как правило в 0) после инициализации проекта (что происходит при любом редактировании элементов проекта - скриптов, регистров, соединений и т.д.). Если такие регистры используются как переменные для формирования триггеров событий или условий выполнения других скриптов, это может приводить к нарушению логики выполнения. Следует учитывать эту особенность при выборе регистров как входных и выходных переменных скриптов. | ||
− | ==== Глобальные переменные ==== | + | ==== Глобальные переменные ==== <!--T:2--> |
Переменные в скриптах, объявленные до функции main сохраняют свои значения между вызовами скрипта, но при инициализации также принимают начальные значения. Их можно использовать для хранения констант, коэффициентов и др. подобных величин. | Переменные в скриптах, объявленные до функции main сохраняют свои значения между вызовами скрипта, но при инициализации также принимают начальные значения. Их можно использовать для хранения констант, коэффициентов и др. подобных величин. | ||
− | ==== Первый скан ==== | + | ==== Первый скан ==== <!--T:3--> |
Скрипту можно назначить способ запуска на первом скане при инициализации проекта и сделать некую инициализацию переменных из одного места. | Скрипту можно назначить способ запуска на первом скане при инициализации проекта и сделать некую инициализацию переменных из одного места. | ||
Иногда может понадобится определить внутри самого скрипта, был ли это первый вызов после инициализации. Можно создать глобальную переменную first_scan: | Иногда может понадобится определить внутри самого скрипта, был ли это первый вызов после инициализации. Можно создать глобальную переменную first_scan: | ||
<syntaxhighlight lang = lua> | <syntaxhighlight lang = lua> | ||
− | first_scan = true | + | first_scan = true -- global var remember value between scans |
function main (userId) | function main (userId) | ||
+ | local var = 0 -- local var will be initialized in each script call | ||
if first_scan then | if first_scan then | ||
first_scan = false; | first_scan = false; | ||
Строка 21: | Строка 23: | ||
end --if | end --if | ||
end -- main | end -- main | ||
− | |||
− | |||
</syntaxhighlight> | </syntaxhighlight> | ||
− | + | <!--T:4--> | |
Следует помнить что регистры типа Dхх, CDxx и другие внутренние регистры, кроме DSxx возвращаются в начальное состояние (как правило в 0) после инициализации проекта (что происходит при любом редактировании элементов проекта - скриптов, регистров, соединений и т.д.). Если такие регистры используются как переменные для формирования триггеров событий или условий выполнения других скриптов, это может приводить к нарушению логики выполнения. Следует учитывать эту особенность при выборе регистров как входных и выходных переменных скриптов. | Следует помнить что регистры типа Dхх, CDxx и другие внутренние регистры, кроме DSxx возвращаются в начальное состояние (как правило в 0) после инициализации проекта (что происходит при любом редактировании элементов проекта - скриптов, регистров, соединений и т.д.). Если такие регистры используются как переменные для формирования триггеров событий или условий выполнения других скриптов, это может приводить к нарушению логики выполнения. Следует учитывать эту особенность при выборе регистров как входных и выходных переменных скриптов. | ||
− | + | === Отличия записи во внутренние регистры от регистров в устройствах === <!--T:5--> | |
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | + | ||
− | === Отличия записи во внутренние регистры от регистров в устройствах === | + | |
Есть некоторые отличия в работе функций SetReg и WriteReg применительно к внутренним регистрам (Dxx, DSxx). Эти функции непосредственно меняют значения внутренних регистров внутри скана, а не откладывают запись WriteReg на следующий скан. Таким образом, в конце скана внутренний регистр может иметь значение, отличное от того которые было на входе в скан. Тогда, например возможна ситуация, когда: | Есть некоторые отличия в работе функций SetReg и WriteReg применительно к внутренним регистрам (Dxx, DSxx). Эти функции непосредственно меняют значения внутренних регистров внутри скана, а не откладывают запись WriteReg на следующий скан. Таким образом, в конце скана внутренний регистр может иметь значение, отличное от того которые было на входе в скан. Тогда, например возможна ситуация, когда: | ||
*скрипт 1 меняет значение некоего регистра Dn. (выполняется в каждом скане) | *скрипт 1 меняет значение некоего регистра Dn. (выполняется в каждом скане) | ||
Строка 46: | Строка 34: | ||
Если порядок выполнения скриптов будет 1 - 2, то все будет работать, потому что на входе в скан скрипт 2 видел одно значение, и перед своим выполнением другое (которое успел изменить скрипт 1), и отработает "по изменению". Если же порядок выполнения скриптов поменять местами, то скрипт 2 перестанет, работать, так как на входе в текущий скан он будет видеть измененное значение, а новое изменение произойдет после скрипта 2 в скрипте 1. | Если порядок выполнения скриптов будет 1 - 2, то все будет работать, потому что на входе в скан скрипт 2 видел одно значение, и перед своим выполнением другое (которое успел изменить скрипт 1), и отработает "по изменению". Если же порядок выполнения скриптов поменять местами, то скрипт 2 перестанет, работать, так как на входе в текущий скан он будет видеть измененное значение, а новое изменение произойдет после скрипта 2 в скрипте 1. | ||
− | === Отладочная печать === | + | === Отладочная печать === <!--T:6--> |
Желательно после ключевых моментов логики и расчетов в скриптах сразу ставить отладочную печать функциями INFO, ERROR, DEBUG, TRACE, в начале скрипта выводить его название, номер. Тогда эти фрагменты удобно искать и анализировать в коммуникационном логе. В редакторе скрипта есть отладочная консоль, в которой всегда выводится печать из функций INFO, DEBUG, ERROR, TRACE вне зависимости от системных настроек уровня лога. | Желательно после ключевых моментов логики и расчетов в скриптах сразу ставить отладочную печать функциями INFO, ERROR, DEBUG, TRACE, в начале скрипта выводить его название, номер. Тогда эти фрагменты удобно искать и анализировать в коммуникационном логе. В редакторе скрипта есть отладочная консоль, в которой всегда выводится печать из функций INFO, DEBUG, ERROR, TRACE вне зависимости от системных настроек уровня лога. | ||
Однако в большой системе с множеством взаимосвязанных скриптов, когда отладочной печати становится много, становится неудобно искать необходимые данные, особенно если нужно отследить конкретную цепочку выполнения нескольких скриптов, которые могут вызываться не в каждом скане, а по определенным условиям. | Однако в большой системе с множеством взаимосвязанных скриптов, когда отладочной печати становится много, становится неудобно искать необходимые данные, особенно если нужно отследить конкретную цепочку выполнения нескольких скриптов, которые могут вызываться не в каждом скане, а по определенным условиям. | ||
Строка 52: | Строка 40: | ||
Можно поступить следующим образом - назначить свою функцию отладочной печати, которая будет вызываться только, если отладочная печать этого скрипта разрешена. Например, можно в строковый регистр "debug_ID" записывать id скриптов в которых нужна отладочная печать в данный момент, а функция печати внутри скрипта будет смотреть на этот номер и включаться или отключаться. Например: | Можно поступить следующим образом - назначить свою функцию отладочной печати, которая будет вызываться только, если отладочная печать этого скрипта разрешена. Например, можно в строковый регистр "debug_ID" записывать id скриптов в которых нужна отладочная печать в данный момент, а функция печати внутри скрипта будет смотреть на этот номер и включаться или отключаться. Например: | ||
+ | <!--T:7--> | ||
<syntaxhighlight lang = lua> | <syntaxhighlight lang = lua> | ||
+ | <!--T:8--> | ||
------- debug printing ------- | ------- debug printing ------- | ||
+ | <!--T:9--> | ||
thisScriptID = 15 | thisScriptID = 15 | ||
function DEBUG_(str) | function DEBUG_(str) | ||
+ | <!--T:10--> | ||
local i, tmp = 0, "" | local i, tmp = 0, "" | ||
+ | <!--T:11--> | ||
while true do | while true do | ||
i,_, tmp = string.find(str, "%s+(%d+)%s+",i+1) -- искать шаблон группа цифр внутри пробелов | i,_, tmp = string.find(str, "%s+(%d+)%s+",i+1) -- искать шаблон группа цифр внутри пробелов | ||
+ | <!--T:12--> | ||
if i == nil then break end -- не найдено | if i == nil then break end -- не найдено | ||
if (tmp ~= "0") then | if (tmp ~= "0") then | ||
Строка 76: | Строка 70: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | <!--T:13--> | ||
Также можно для удобства интересующие цепочки скриптов сгруппировать как строковые переменные и выбирать их в новом скрипте. | Также можно для удобства интересующие цепочки скриптов сгруппировать как строковые переменные и выбирать их в новом скрипте. | ||
<syntaxhighlight lang = lua> | <syntaxhighlight lang = lua> | ||
+ | <!--T:14--> | ||
local valve_control = " 2 30 7 9 5 8 10 18 " -- script chain one | local valve_control = " 2 30 7 9 5 8 10 18 " -- script chain one | ||
local heater_control = " 20 21 23 29 43 48" -- script chain two | local heater_control = " 20 21 23 29 43 48" -- script chain two | ||
Строка 92: | Строка 88: | ||
end | end | ||
+ | <!--T:15--> | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | |||
+ | |||
+ | === Модульность === <!--T:16--> | ||
+ | Рекомендуется разбивать сложные скрипты на более простые и часто используемые функции, которые можно использовать повторно. Разбивка на более простые части, расположение их в нужном порядке и группировка помогает контролировать логику работы системы и легче наладить систему. Например, если есть задача управления положением задвижки, можно выделить такие части: | ||
+ | :блок "захвата" момента включения привода, для инициализации начальной позиции и времени начала движения | ||
+ | :блок расчета текущей позиции в движении (который определяет время между вызовами скрипта и вычисляет пройденный путь штока) | ||
+ | :блок авт. управления (проверяет рассогласование между уставкой и тек. положением, определяет направление и дает команду, если нет других блокировок) | ||
+ | :блок ручного управления | ||
+ | :блок блокировок по наезду на конечные выключатели - для снятия текущей команды и установки позиции в 0/100% | ||
+ | Сложные функции перед вложением их другие скрипты можно отладить отдельно в пользовательском скрипте, выделив часть внутренних регистров под входные переменные, часть под выходные. Эти же регистры вынести на дешборд, вместе с кнопкой запуска отлаживаемого скрипта. Тогда меняя входные наборы данных удобно видеть тут же результат выполнения. Также можно использовать отдельную IDE типа Eclipse для Lua или онлайн версию, чтобы удобно и быстро отлаживать маленькие фрагменты, изучать работу новых функций и т.д. | ||
</translate> | </translate> |
Текущая версия на 10:23, 23 марта 2018
Содержание
Инициализация проекта
Энергонезависимые регистры
Следует помнить что регистры типа Dхх и некоторые другие внутренние регистры, кроме DSxx возвращаются в начальное состояние (как правило в 0) после инициализации проекта (что происходит при любом редактировании элементов проекта - скриптов, регистров, соединений и т.д.). Если такие регистры используются как переменные для формирования триггеров событий или условий выполнения других скриптов, это может приводить к нарушению логики выполнения. Следует учитывать эту особенность при выборе регистров как входных и выходных переменных скриптов.
Глобальные переменные
Переменные в скриптах, объявленные до функции main сохраняют свои значения между вызовами скрипта, но при инициализации также принимают начальные значения. Их можно использовать для хранения констант, коэффициентов и др. подобных величин.
Первый скан
Скрипту можно назначить способ запуска на первом скане при инициализации проекта и сделать некую инициализацию переменных из одного места. Иногда может понадобится определить внутри самого скрипта, был ли это первый вызов после инициализации. Можно создать глобальную переменную first_scan:
first_scan = true -- global var remember value between scans
function main (userId)
local var = 0 -- local var will be initialized in each script call
if first_scan then
first_scan = false;
--[[
Действия при инициализации проекта
--]]
end --if
end -- main
Следует помнить что регистры типа Dхх, CDxx и другие внутренние регистры, кроме DSxx возвращаются в начальное состояние (как правило в 0) после инициализации проекта (что происходит при любом редактировании элементов проекта - скриптов, регистров, соединений и т.д.). Если такие регистры используются как переменные для формирования триггеров событий или условий выполнения других скриптов, это может приводить к нарушению логики выполнения. Следует учитывать эту особенность при выборе регистров как входных и выходных переменных скриптов.
Отличия записи во внутренние регистры от регистров в устройствах
Есть некоторые отличия в работе функций SetReg и WriteReg применительно к внутренним регистрам (Dxx, DSxx). Эти функции непосредственно меняют значения внутренних регистров внутри скана, а не откладывают запись WriteReg на следующий скан. Таким образом, в конце скана внутренний регистр может иметь значение, отличное от того которые было на входе в скан. Тогда, например возможна ситуация, когда:
- скрипт 1 меняет значение некоего регистра Dn. (выполняется в каждом скане)
- скрипт 2 работает по изменению этого регистра Dn. (выполняется по изменению регистра)
Если порядок выполнения скриптов будет 1 - 2, то все будет работать, потому что на входе в скан скрипт 2 видел одно значение, и перед своим выполнением другое (которое успел изменить скрипт 1), и отработает "по изменению". Если же порядок выполнения скриптов поменять местами, то скрипт 2 перестанет, работать, так как на входе в текущий скан он будет видеть измененное значение, а новое изменение произойдет после скрипта 2 в скрипте 1.
Отладочная печать
Желательно после ключевых моментов логики и расчетов в скриптах сразу ставить отладочную печать функциями INFO, ERROR, DEBUG, TRACE, в начале скрипта выводить его название, номер. Тогда эти фрагменты удобно искать и анализировать в коммуникационном логе. В редакторе скрипта есть отладочная консоль, в которой всегда выводится печать из функций INFO, DEBUG, ERROR, TRACE вне зависимости от системных настроек уровня лога. Однако в большой системе с множеством взаимосвязанных скриптов, когда отладочной печати становится много, становится неудобно искать необходимые данные, особенно если нужно отследить конкретную цепочку выполнения нескольких скриптов, которые могут вызываться не в каждом скане, а по определенным условиям. Кроме того, фильтрация ненужной информации в логах также полезна с точки зрения производительности, так как операции постоянной записи на флеш-карту большого объема информации могут снижать производительность системы. Можно поступить следующим образом - назначить свою функцию отладочной печати, которая будет вызываться только, если отладочная печать этого скрипта разрешена. Например, можно в строковый регистр "debug_ID" записывать id скриптов в которых нужна отладочная печать в данный момент, а функция печати внутри скрипта будет смотреть на этот номер и включаться или отключаться. Например:
------- debug printing -------
thisScriptID = 15
function DEBUG_(str)
local i, tmp = 0, ""
while true do
i,_, tmp = string.find(str, "%s+(%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_
Также можно для удобства интересующие цепочки скриптов сгруппировать как строковые переменные и выбирать их в новом скрипте.
local valve_control = " 2 30 7 9 5 8 10 18 " -- script chain one
local heater_control = " 20 21 23 29 43 48" -- script chain two
-- local uLimits_control = "14 25 12 13 24 38 121 "
local uLimits_control = " 47 46 "
local pid_control = " 45 "
local test_debug_print = " 32 5 "
local none = " ";
function main (userId)
WriteReg("debug_IDs", uLimits_control); -- debug_IDs (S0@WH system)
-- DEBUG_("test new debug_ in 32 script")
end
Модульность
Рекомендуется разбивать сложные скрипты на более простые и часто используемые функции, которые можно использовать повторно. Разбивка на более простые части, расположение их в нужном порядке и группировка помогает контролировать логику работы системы и легче наладить систему. Например, если есть задача управления положением задвижки, можно выделить такие части:
- блок "захвата" момента включения привода, для инициализации начальной позиции и времени начала движения
- блок расчета текущей позиции в движении (который определяет время между вызовами скрипта и вычисляет пройденный путь штока)
- блок авт. управления (проверяет рассогласование между уставкой и тек. положением, определяет направление и дает команду, если нет других блокировок)
- блок ручного управления
- блок блокировок по наезду на конечные выключатели - для снятия текущей команды и установки позиции в 0/100%
Сложные функции перед вложением их другие скрипты можно отладить отдельно в пользовательском скрипте, выделив часть внутренних регистров под входные переменные, часть под выходные. Эти же регистры вынести на дешборд, вместе с кнопкой запуска отлаживаемого скрипта. Тогда меняя входные наборы данных удобно видеть тут же результат выполнения. Также можно использовать отдельную IDE типа Eclipse для Lua или онлайн версию, чтобы удобно и быстро отлаживать маленькие фрагменты, изучать работу новых функций и т.д.