Debugging complex scripts

Материал из WebHMI Wiki
Перейти к: навигация, поиск
Эта страница — перевод страницы Отладка сложных скриптов. Перевод выполнен на 100%.

Другие языки:
English • ‎русский

Project initialization

Non - volatile registers

Другие языки:
English • ‎русский

Please keep in mind that registers of Dxx type and some other internal registers, except for DSxx, Sxx return to the initial state (usually 0) after initialization of the project (what happens with any editing of project elements - scripts, registers, connections, etc.). If such registers are used as variables to generate event triggers or conditions for executing other scripts, this can lead to a violation of the execution logic. You should consider this feature when choosing registers as input and output script variables.

Global variables

Variables in scripts declared before the main function store their values ​​between script calls, but also reset when initializing. They can be used to store constants, coefficients, and other similar values.

First scan

Some script can be assigned to run on the first scan when the project is initialized and make some initialization of variables from one place. Sometimes you might need to define inside the script itself whether this was the first call after initialization. You can create a global variable 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
     -[[
Actions for the first scan
    --]]             
 end --if first_scan
-- other actions
end -- main

Remember that registers such as Dxx, CDxx and other internal registers, except for DSxx, return to the initial state (usually 0) after initializing the project (which happens when editing project elements - scripts, registers, connections, etc.). If such registers are used as variables to generate event triggers or conditions for executing other scripts, this can lead to a violation of the execution logic. You should consider this feature when choosing registers as input and output script variables.

Differences between write function for internal registers and external

There are some differences in the operation of the SetReg and WriteReg functions with respect to internal registers (Dxx, DSxx). These functions directly change the values ​​of the internal registers inside the scan, and do not delay WriteReg writing to the next scan start. Thus, at the end of the scan, the internal register can have a value different from the one at the scan input. Then, for example, a situation is possible where:

  • script 1 changes the value of a certain register Dn. (executed in each scan)
  • script 2 works by changing this register Dn. (executed upon register change)

If the order of execution of the scripts is 1 - 2, then everything will work, because at the current scan start script #2 saw one value, and before its execution another (which script #1 has changed before), and will correctly work 'by change.' If the script execution order is interchanged, then script 2 will not work, because at the the current scan start it will see the changed value, and the new change will occur after script #2 execution in script #1.

Debug messages

It is desirable after the key moments of logic and calculations in scripts to immediately put the functions INFO, ERROR, DEBUG, TRACE, and to output script's name, number at the beginning. Then these fragments are conveniently searched and analyzed in the communication log. In the script editor, there is a debugging console, which always prints out the functions INFO, DEBUG, ERROR, TRACE regardless of the system level settings of the log.

However, in a large system with many interrelated scripts, when there is a lot of debugging data, it becomes inconvenient to look for the necessary data, especially if you need to track a specific chain of execution of several scripts that can not be called at specific moments, not in each scan.

In addition, filtering unnecessary information in logs is also useful from the point of view of performance, since operations of permanent recording on a flash card of a large amount of information can reduce system performance.

You can proceed as follows - assign your debugging function, which will be called only if the debug printing of this script is allowed. For example, you can write the ids of the scripts in the 'debug_ID' string in which debug printing is needed at the moment, and the print function inside the script will look at this number and turn it on or off. For example:

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

thisScriptID = 15
function DEBUG_(str)

local i, tmp = 0, ""

while true do 
i,_, tmp = string.find(str, "%s+(%d+)%s+",i+1) -- search for a pattern of number inside spaces

if i == nil  then break end  -- not found
    if (tmp ~= "0") then 
        -- found pattern check equlity
        if (tmp == tostring(thisScriptID)) then 
             ERROR(str) 
        end 
    end 
end 
    
end -- DEBUG_

Also, for convenience, it is possible to group the interesting chains of scripts as string variables and select them in a new script.

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


Modular principle

It is recommended that you split complex scripts into simpler and more frequently used functions that you can reuse. Divideing into simpler parts, arranging them in the right order and grouping helps to control the logic of the system and makes it easier to set up the system. For example, if there is a task to control the position of the valve, you can distinguish such parts:

block that 'captures' the moment the drive is turned on, to initialize the starting position and the start time of movement
block calculating the current position in motion (which determines the time between the calls of the script and calculates the traversed path of the stock)
block of auto control (checks the mismatch between the setpoint and the current position, determines the direction and gives the command, if there are no other locks)
manual control block
limit switch reactoin block - removes current command and sets current position as 0/100%

Complex functions before embedding them into other scripts can be debugged separately in the user script, arranging some internal registers as the input variables, others for the output. These registers can be placed onto debugging dashboard, together with the button for starting the debugged script. Then changing the input data sets it is convenient to see immediately the result of the execution. You can also use a separate Eclipse IDE for Lua or an online version to conveniently and quickly debug small fragments, learn the operation of new functions, and so on.