Полезные программы/en — различия между версиями

Материал из WebHMI Wiki
Перейти к: навигация, поиск
(Новая страница: «function main (userId) -- if there are no errors, then circulate over time   If there is an error on one of the air conditioners, it is excluded from the…»)
 
(не показано 27 промежуточных версий этого же участника)
Строка 1: Строка 1:
 +
<languages/>
 
== Useful programs ==
 
== Useful programs ==
 
=== Run script on rising or falling edge of the discrete signal ===
 
=== Run script on rising or falling edge of the discrete signal ===
Строка 5: Строка 6:
 
[[Файл:Rising edge.png | 1000 px | left]] <br clear = all >
 
[[Файл:Rising edge.png | 1000 px | left]] <br clear = all >
  
=== Timer with turn-on delya (TON) ===
+
=== Реализация универсального таймера (TON TOFF) ===
TON timer starts counting while input = 1, and after delay time is also set to "1".  
+
Таймер сравнивает свое текущее состояние с состоянием на входе:
 +
* текущее состояние = 0, вход = 1 - таймер включит выход через время задержки onDelay. Таймер TON получится если указать время offDelay = 0.
 +
* текущее состояние = 1, вход = 0 - таймер выключит выход через время задержки offDelay. Таймер TOFF получится если указать время onDelay = 0.  
  
 +
Для работы таймера необходимо завести 2 DS регистра типа Unix time и Bit и присвоить им имена (алиасы) вида "<Имя1>", "<Имя1>_out". В первом будет хранится время след. переключения, во втором - текущее состояние таймера.
  
 
<syntaxhighlight lang="Lua">
 
<syntaxhighlight lang="Lua">
-- globals
 
TIMER_DELAY = 20 -- timer delay 20 seconds
 
tmrStartTime = 0 -- time stamp of timer start time
 
  
function main (userId)
+
function TimerOnOff(bool_input, onDelay, offDelay, tmrAlias)
  -- local vars
+
--                   bool    seconds  seconds  string
local now = os.time() -- current time
+
    local outAlias = tmrAlias.."_out"
local startBit = (GetReg("tmrStartBit") == 1) -- just bit
+
    local curTmrState = (GetReg(outAlias) == 1) ; DEBUG("curTmr  State "..tostring(curTmrState))
 +
                                                  DEBUG("bool_input = "..tostring(bool_input))
 +
                                               
 +
    if (bool_input == curTmrState) then
 +
        WriteReg(tmrAlias, now) ;                DEBUG("timer input match state ")
 +
        return curTmrState  -- as bool
 +
    elseif bool_input then
 +
                                                    DEBUG("countdown for onDelay = "..(onDelay - (now - GetReg(tmrAlias))))
 +
        if now - GetReg(tmrAlias) > onDelay then                                             
 +
                WriteReg(outAlias, 1)
 +
                                                    DEBUG("detected ON state after delay ")
 +
                return true
 +
          end
 +
    else
 +
                                                    DEBUG("countdown for offDelay = "..(offDelay - (now - GetReg(tmrAlias))))
 +
        if (now - GetReg(tmrAlias) > offDelay) then                                             
 +
                WriteReg(outAlias, 0)
 +
                                                    DEBUG("detected OFF state after delay ")
 +
                return false 
 +
        end   
 +
    end  --
 +
    return  curTmrState  -- output rerurned as bool for simple usage in IFs
  
 +
end -- function
 +
</syntaxhighlight>
 +
 +
Пример использования:
 +
 +
Например есть два дискретных входа с датчиками реле низкого и высокого давления. Их состояние читается и переводится в переменную типа bool для упрощения последующих операторов сравнения. Функция также возвращает bool, чтобы ее можно было сразу использовать в операторах сравнения без дополнительных операторов if.
 +
 +
 +
<syntaxhighlight lang="Lua">
 +
                                              DEBUG(" hotWater timer call ")
 +
local DI2 = (GetReg(39) == 1) -- hotwater low pressure 
 +
local DI3 = (GetReg(40) == 1) -- hotwater high pressure 
 +
local hotWater = TimerOnOff((DI2 or DI3), 15, 15, "Tmr2") -- 15 seconds delay for both on and off . 
 +
 +
-- or like this
 +
if TimerOnOff(((GetReg("hotWaterLowPressure") == 1) or (GetReg(hotWaterHighPressure) == 1)), 15, 2, "Tmr2") then
 +
  -- do something
 +
end -- simple timer in one row !
  
-- checking condition ----------
 
if not startBit then
 
    tmrStartTime = now; -- save timer start
 
    WriteReg("TON_out", 0); -- timer output
 
    return 0
 
else
 
    if (now - tmrStartTime) > TIMER_DELAY then
 
      -- actions upon timer trigger
 
            WriteReg("TON_out", 1)
 
    end  -- if 
 
end -- if
 
end -- main
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Строка 287: Строка 315:
 
   if (not run_state) and run_status then  
 
   if (not run_state) and run_status then  
 
       WriteReg("P43StartTime", now); -- unit start time
 
       WriteReg("P43StartTime", now); -- unit start time
 +
  end
  
  
Строка 302: Строка 331:
 
The timekeeping registers and time stamps should be made non-volatile.
 
The timekeeping registers and time stamps should be made non-volatile.
  
=== Time Circulation algorithm времени (together with redundancy function) ===
+
=== Time Circulation algorithm (together with redundancy function) ===
 
This algorithm is used in systems where it is necessary to alternate the operation of mechanisms (pumps, fans, air conditioners) over time, or on the run hour meters.
 
This algorithm is used in systems where it is necessary to alternate the operation of mechanisms (pumps, fans, air conditioners) over time, or on the run hour meters.
 
For example, a set of 2 units is used, which must be alternated in time. If an error occurs on some unit, then the algorithm starts working only on the working (redundancy function).
 
For example, a set of 2 units is used, which must be alternated in time. If an error occurs on some unit, then the algorithm starts working only on the working (redundancy function).
Строка 326: Строка 355:
  
 
if (not acError1) and (not acError2) then  
 
if (not acError1) and (not acError2) then  
     -- работаем по циркуляции
+
     -- work on circulation
 
     if (now >= switchTime) then  
 
     if (now >= switchTime) then  
 
         if (curActiveAC == 1) then  
 
         if (curActiveAC == 1) then  
Строка 348: Строка 377:
  
  
Второй скрипт смотрит какой кондиционер сейчас активен, и выполняет необходимые действия. В скрипте это просто отладочная печать, но здесь могут быть команды управления инфракрасным передатчиком для выдачи нужной команды, запись в журнал сообщений и переключении и т.п.
+
The second script looks at what kind of conditioner is now active, and performs the necessary actions. In a script, this is just debugging, but there may be commands for controlling the infrared transmitter for issuing the desired command, writing to the message log and switching, etc.
  
  
Строка 354: Строка 383:
 
function main (userId)
 
function main (userId)
 
   --[[
 
   --[[
  включает выбранный кондицинер, в зависимости от указателя
+
    turn on selected a/c depending on pointer
 
   --]]
 
   --]]
   local pointer = GetReg("activeAC"); -- Активный кондиционер (DS100@webmi)
+
   local pointer = GetReg("activeAC"); -- active conditioner (DS100@webmi)
  
  
 
if (pointer==0) then  
 
if (pointer==0) then  
       DEBUG("все выключаем");
+
       DEBUG("all off")
       return 0;
+
       return 0
 
   elseif  
 
   elseif  
 
       (pointer==1) then  
 
       (pointer==1) then  
           DEBUG("включаем 1-й кондиционер");
+
           DEBUG("turn on a/c #1");
 
   else  
 
   else  
           DEBUG("включаем 2-й кондиционер");
+
           DEBUG("turn on a/c #2");
 
        
 
        
 
   end -- if  
 
   end -- if  
 
end
 
end
 
</syntaxhighlight>
 
</syntaxhighlight>
Также здесь нужен будет скрипт, который будет выставлять флаги ошибок работы по неким условиям, читаю состояние автоматов защиты, регистры ошибок по интерфейсу и т.п.
+
Also here you need a script that will expose the flags of errors of work on certain conditions, read the status of the protection devices, the error registers on the interface, and so on.
  
=== 3-х точечное управление клапанами, сервоприводами и др. ===
+
=== 3-point control for a valve or servo ===
  
  
3-х точечным называется способ управления позицией клапана, сервопривода, задвижки и т.п., когда для управления приводом используются 3 провода - "общий", "питание - ВВЕРХ", " питание - ВНИЗ". Такие приводы могут быть (а могут и нет) оснащены также концевыми датчиками положения. Иногда при отсутствии датчиков положения и низких требованиях к точности позиционирования может применяться алгоритм, когда привод уезжает вниз или вверх (либо по 1 датчику положения либо подачей одной команды на время превышающее время полного хода клапана), инициализирует координату, а потом отрабатывает заданную позицию.  
+
A 3-point method is used to control the position of the valve, servo, gate valve, etc., when 3 wires are used to control the drive - 'common', 'power UP', 'power - DOWN'. Such drives may or may not be equipped with end position sensors. Sometimes, in the absence of position sensors and low requirements for positioning accuracy, an algorithm can be used when the drive leaves down or up (either one position sensor or one command for a time longer than the  valve's full travel time), initializes the coordinate, and then go to the specified position.
<p>Для определения промежуточных положений используется расчетное значение, определяемое из характеристики исполнительного механизма "время полного хода", которое можно определить и экспериментальным путем.  
+
<p> To determine intermediate positions, a calculated value is used, determined from the characteristics of the 'full path time', which can also be determined experimentally.
Как упоминалось выше, относительно сложные и разветвленные алгоритмы в WebHMI лучше разбить на функционально законченные и простые программы, взаимодействие и синхронизацию между которыми можно делать используя внутренние регистры.</p>
+
As mentioned above, relatively complex and branched algorithms in WebHMI are better to divide into functionally complete and simple programs, the interaction and synchronization between them can be done using internal registers. </p>
<br>
+
The <br>
Ниже приведен вариант 3-х точечного управления для привода с 2-мя концевыми датчиками.
+
Below is a variant of 3-point control for a valve with 2 end sensors.
Программа разбита на 6 частей:
+
The program is divided into 6 parts:
  
 
[[Файл:3-point control.png|800px|left]]
 
[[Файл:3-point control.png|800px|left]]
 
<br clear = all>
 
<br clear = all>
::'''v3 OpenClose Valve Manual''' - скрипт управляет приводом в ручном режиме. Запускается по изменению номера нажатой кнопки с дешборда.
+
::'''v3 OpenClose Valve Manual''' - manual control program,
::'''v4 Auto Valve Control''' - основной скрипт управления в авт. режиме. Он автоматически выполняет первую инициализацию, и при несовпадении заданной координаты от текущей включает привод в нужном направлении, по достижении позиции останавливает. Также при совпадении заданной позиции как крайней (0,100), скрипт продолжает держать команду, пока не произойдет наезд на концевой выключатель, таким образом выполняя периодическую синхронизацию расчетной позиции с реальной.
+
::'''v4 Auto Valve Control''' - main program. do homing on very first call, and if set in current position are no equal move into the desired direction  when set positions are limits, then keeps on current command till the limit switch is reached
::'''v5 LatchStart_Time''' - скрипт "захвата" начальной позиции движения, выполняется по изменению флага inMotionFlag, который устанавливают предыдущие программы v3 или v4.
+
::'''v5 LatchStart_Time''' - catches start time
::'''v6 CalcNewPosition''' - выполняется 1 раз в сек. (по изменению системного времени), скрипт работает, пока есть флаг inMotionFlag и пересчитывает текущее время, пройденное с начала движения в текущую позицию (которая в свою очередь используется программой v4).
+
::'''v6 CalcNewPosition''' - calculate path done like time since start time while valve in motion  (this path is checked in v4).
::'''v7 Limit sw reaction''' - по достижении конечных выключателей снимает команды, а также снимает запрос от кнопок ручного управления.
+
::'''v7 Limit sw reaction'''  
::'''v8 Drop Cur Cmd on Manual mode''' - при переходе в ручной режим выключает текущую команду.
+
::'''v8 Drop Cur Cmd on Manual mode''' - when manual mode selected, clears current cmds
  
  
В начале программ обозначен порядок требуемый порядок выполнения (v3..v8), поскольку необходимый порядок выполнения программ может измениться нежелательным образом, например при сортировке программ и неточном "перетаскивании" программ в списке. Таким образом префикс напоминает о нужном порядке, также он может отражать функциональную принадлежность скрмпта - "v" Valves, "t- temperature control и т.д. чтобы удобнее ориентироваться в больших списках и ссылаться на него.
+
At the beginning of the programs, the order of the required execution order (v3..v8) is indicated, since the necessary order of program execution can change undesirable, for example, when sorting programs and inaccurate 'dragging' programs in the list. Thus, the prefix reminds you of the desired order, it can also reflect the functionality of the script - 'v' Valves, 't' - temperature control, etc. It is more convenient to navigate in large lists and refer to it.
<p>Исходные тексты скриптов:</p>
+
<p>Script are given below: </ p>
 +
 
 
'''v3'''
 
'''v3'''
 
<syntaxhighlight lang = lua>
 
<syntaxhighlight lang = lua>
------- РУЧНОЕ УПРАВЛЕНИЕ С КНОПОК НА ЭКРАНЕ OpenClose Valve Manual -------------------
+
------- Manual control from dashboard buttons  -------------------
 
function main (userId)
 
function main (userId)
 
    
 
    
   local button_value = GetReg("valveMan_code"); -- номер от кнопок нажатия ОТКР / ЗАКР.
+
   local button_value = GetReg("valveMan_code")-- pressed button code
   local manual_mode = (GetReg("auto_mode") == 0) ; -- АВТ. РЕЖИМ ВКЛЮЧЕН (ds1176@WH Global)
+
   local manual_mode = (GetReg("auto_mode") == 0) -- auto mode ON (ds1176@WH Global)
  
  
Строка 408: Строка 438:
  
 
if (button_value == 10) then  
 
if (button_value == 10) then  
         WriteReg("openCmd", 1); -- Команда "Открытие" (DS1007@WH Valve control)
+
         WriteReg("openCmd", 1); -- opening command (DS1007@WH Valve control)
         WriteReg("closeCmd", 0);
+
         WriteReg("closeCmd", 0)
 
     elseif (button_value == 5) then  
 
     elseif (button_value == 5) then  
           WriteReg("closeCmd", 1); -- Команда "Закрытие" (DS1008@WH Valve control)
+
           WriteReg("closeCmd", 1); -- closing command (DS1008@WH Valve control)
           WriteReg("openCmd", 0);
+
           WriteReg("openCmd", 0)
 
     else
 
     else
         -- на недопустимое значение снимаем команды
+
         -- invalid code - all off
 
                                     DEBUG("Read button value as "..tostring(button_value));
 
                                     DEBUG("Read button value as "..tostring(button_value));
         WriteReg("openCmd", 0);
+
         WriteReg("openCmd", 0)
         WriteReg("closeCmd", 0);
+
         WriteReg("closeCmd", 0)
         WriteReg("inMotion_flag",0); --
+
         WriteReg("inMotion_flag",0)
 
     end --if  
 
     end --if  
 
      
 
      
     -- Фиксация начальной позиции, времени начала движения для скрпита обсчета текущей позиции
+
     -- catching start position, and time
 
     if (button_value == 10) or (button_value == 5) then  
 
     if (button_value == 10) or (button_value == 5) then  
         WriteReg("startPosition", GetReg("curPosition")); -- Начальная позиция движения  (DS1020@WebHMI 3point control)
+
         WriteReg("startPosition", GetReg("curPosition")); -- start position and time (DS1020@WebHMI 3point control)
         WriteReg("startPosTime", os.time()); -- Метка времени начала движения  (DS1012@WebHMI 3point control)
+
         WriteReg("startPosTime", os.time()) -- start time time stamp (DS1012@WebHMI 3point control)
         WriteReg("inMotion_flag",1);
+
         WriteReg("inMotion_flag",1)
 
     end
 
     end
 
end -- manual_mode
 
end -- manual_mode
Строка 436: Строка 466:
 
'''v4'''
 
'''v4'''
 
<syntaxhighlight lang = lua>
 
<syntaxhighlight lang = lua>
-- АВТ. УПРАВЛЕНИЕ ДВИЖЕНИЕМ КЛАПАНА Auto Valve Control ----  
+
-- auto valve control ----  
 
function main (userId)
 
function main (userId)
 
      
 
      
   local now = os.time(); -- текущее время
+
   local now = os.time(); -- cur time
   local timeStmp = GetReg("endPosTime"); -- Метка времени окончания движения  (D1001@WebHMI Kitothemr)
+
   local timeStmp = GetReg("endPosTime")  
   local auto_mode = (GetReg("auto_mode")==1);
+
   local auto_mode = (GetReg("auto_mode")==1)
 
    
 
    
 
if auto_mode then
 
if auto_mode then
   -- проверка на самое первое включение, когда метка времения = 0
+
   -- check if very first run
 
   if (timeStmp == 0) and (not home_mode) then  
 
   if (timeStmp == 0) and (not home_mode) then  
       WriteReg("homingBit",1);
+
       WriteReg("homingBit",1)
                                 DEBUG("timeStmp = 0, homing needed! ");
+
                                 DEBUG("timeStmp = 0, homing needed! ")
 
   end  
 
   end  
 
    
 
    
   local home_mode = (GetReg("homingBit") == 1); -- Бит репозиционирования  (D1006@WebHMI Kitothemr)
+
   local home_mode = (GetReg("homingBit") == 1)
   local full_close = GetReg("LLsw"); -- Датчик полного закрытия (D1010@WebHMI Kitothemr)
+
   local full_close = GetReg("LLsw")
  
  
 
if home_mode then  
 
if home_mode then  
       -- едем вниз до упора чтобы определить позицию
+
       -- go down till low limit sw
               DEBUG("giving home_mode close cmd ! ");
+
               DEBUG("giving home_mode close cmd ! ")
       WriteReg("closeCmd", 1); -- Команда "Закрытие" (D1008@WebHMI Kitothemr)
+
       WriteReg("closeCmd", 1)
 
        
 
        
 
           if (full_close == 1) then  
 
           if (full_close == 1) then  
                   DEBUG("full close ! ");
+
                   DEBUG("full close ! ")
 
                  
 
                  
                 WriteReg("closeCmd",0);
+
                 WriteReg("closeCmd",0)
                 WriteReg("endPosTime", now); -- чтобы избежать повторного home_mode
+
                 WriteReg("endPosTime", now) to avoid homing again
                 WriteReg("homingBit",0);
+
                 WriteReg("homingBit",0)
                 WriteReg("curPosition", 0); -- Текущее положение клапана (D1005@WebHMI Kitothemr)
+
                 WriteReg("curPosition", 0) -- current valve position
 
           end
 
           end
       return 0; -- выходим пока не закончим выход в 0
+
       return 0 --  
 
   end -- home mode  
 
   end -- home mode  
 
    
 
    
   -- ОСНОВНАЯ ЧАСТЬ ----  
+
   --MAIN PART ----  
                           DEBUG("-------- auto valve control ");
+
                           DEBUG("-------- auto valve control ")
 
    
 
    
   local openingStatus = (GetReg("openCmd") == 1); -- Команда "Открытие" (D1007@WebHMI Kitothemr)
+
   local openingStatus = (GetReg("openCmd") == 1)
   local closingStatus = (GetReg("closeCmd") == 1); -- Команда "Закрытие" (D1008@WebHMI Kitothemr)
+
   local closingStatus = (GetReg("closeCmd") == 1)
   local inMotionFlag = openingStatus or closingStatus;
+
   local inMotionFlag = openingStatus or closingStatus
 
    
 
    
   local target_dir= 0; -- знак движения
+
   local target_dir= 0
   local posSV = GetReg("posSetpoint"); -- Уставка положения клапана (D1000@WebHMI Kitothemr)
+
   local posSV = GetReg("posSetpoint"); -- position setpoint
   local posPV = GetReg("curPosition"); -- текущее положение
+
   local posPV = GetReg("curPosition"); -- current position
 
    
 
    
    -- определить начальные время и позицию
 
 
   local startTime = GetReg("startPosTime");
 
   local startTime = GetReg("startPosTime");
 
   local startPos = GetReg("startPosition");
 
   local startPos = GetReg("startPosition");
   local pathdone = 0; -- обсчета пройденного пути
+
   local pathdone = 0;  
 
    
 
    
   local OpenSw = GetReg("HLsw"); -- Датчик полного открытия (D1009@WebHMI Kitothemr)
+
   local OpenSw = GetReg("HLsw"); -- full open limit sw
   local CloseSw = GetReg("LLsw"); -- Датчик полного закр.  (D1009@WebHMI Kitothemr)
+
   local CloseSw = GetReg("LLsw"); -- full close limit sw
 
    
 
    
                             DEBUG("posPV  "..tostring(posPV).." pos SV"..tostring(posSV));
+
                             DEBUG("posPV  "..tostring(posPV).." pos SV"..tostring(posSV))
       -- Уже едем
+
       -- Already moving
 
       if inMotionFlag then  
 
       if inMotionFlag then  
                            DEBUG("Мы уже в движении ");
+
         -- full closed
         -- доехали вниз ?
+
 
           if (closingStatus) and (posPV <= posSV) then  
 
           if (closingStatus) and (posPV <= posSV) then  
               -- если задан 0 как позицию, то едем до нижнего конечника, чтобы синхронизировать позицию с реальным положением
+
               -- if 0 is setpoint then move till the physical limit sw
               if (posPV == 0) and (not CloseSw) then  
+
               if (posSV == 0) and (not CloseSw) then  
                   WriteReg("closeCmd", 1);
+
                   WriteReg("closeCmd", 1)
                   return 0; -- выходим, команду сбросит скрипт Limit Sw Reaction
+
                   return 0 -- exiting, cmd will be off in v7 script
 
               end  
 
               end  
                   WriteReg("closeCmd", 0);
+
                   WriteReg("closeCmd", 0)
                   WriteReg("inMotion_flag",0);
+
                   WriteReg("inMotion_flag",0)
 
             end  
 
             end  
         -- доехали вверх?
+
         -- full open ?
 
           if (openingStatus) and (posPV >= posSV) then  
 
           if (openingStatus) and (posPV >= posSV) then  
              -- если задан верхний предел, то едем до конечника чтобы синхронизировать позицию с реальным положением
+
            --- if 100 is setpoint then move till the physical limit sw
 
                   if (posPV == 100) and (not OpenSw) then  
 
                   if (posPV == 100) and (not OpenSw) then  
                       WriteReg("openCmd", 1);
+
                       WriteReg("openCmd", 1)
                       return 0; -- выходим, команду сбросит скрипт Limit Sw Reaction
+
                       return 0 --exiting the command will be off in v7 script
 
                   end  
 
                   end  
               WriteReg("openCmd", 0);
+
               WriteReg("openCmd", 0)
               WriteReg("inMotion_flag",0);
+
               WriteReg("inMotion_flag",0)
 
           end  
 
           end  
       -- позиция не доехали ?  
+
       -- stopped but not in position ?
 
       else  
 
       else  
 
           if (posSV ~= posPV) then  
 
           if (posSV ~= posPV) then  
               -- уставка поменялась ?
+
               target_dir = (posSV - posPV)/math.abs(posSV - posPV); -- определить знак
            target_dir = (posSV - posPV)/math.abs(posSV - posPV); -- определить знак
+
 
                                             DEBUG_("target_dir =  "..tostring(target_dir));
 
                                             DEBUG_("target_dir =  "..tostring(target_dir));
 
               if (target_dir > 0) then  
 
               if (target_dir > 0) then  
 
                   WriteReg("openCmd" , 1);
 
                   WriteReg("openCmd" , 1);
                                             DEBUG_("will open...");
+
                                             DEBUG_("will open...")
 
               else  
 
               else  
 
                   WriteReg("closeCmd" , 1);
 
                   WriteReg("closeCmd" , 1);
                                             DEBUG_("will close...");
+
                                             DEBUG_("will close...")
 
               end
 
               end
             WriteReg("inMotion_flag", 1); -- уст. флаг для скриптов работающих по началу движения                                   
+
             WriteReg("inMotion_flag", 1) -- sets in motion flag
 
           end
 
           end
 
   end -- auto mode motion control  
 
   end -- auto mode motion control  
Строка 540: Строка 567:
 
'''v5'''
 
'''v5'''
 
<syntaxhighlight lang = lua>
 
<syntaxhighlight lang = lua>
-- Захват позиции LatchStart_Time ---
+
-- LatchStart_Time ---
 
function main (userId)
 
function main (userId)
 
      
 
      
 
                 DEBUG("Entered  latch start time and position");
 
                 DEBUG("Entered  latch start time and position");
 
   -- Add your code here
 
   -- Add your code here
   local flag = (GetReg("inMotion_flag") == 1) ; -- Флаг "в движении" (DS1016@WebHMI 3point control)
+
   local flag = (GetReg("inMotion_flag") == 1) ; -- in motion  (DS1016@WebHMI 3point control)
                 DEBUG("motion flag = "..tostring(flag));
+
                 DEBUG("motion flag = "..tostring(flag))
 
+
 
+
 
   if flag then  
 
   if flag then  
                   DEBUG("now flag = 1");
+
                   DEBUG("now flag = 1")
       WriteReg("startPosition", GetReg("curPosition")); -- Начальная позиция движения  (DS1020@WebHMI 3point control)
+
       WriteReg("startPosition", GetReg("curPosition")) -- start position (DS1020@WebHMI 3point control)
       WriteReg("startPosTime", os.time()); -- Метка времени начала движения  (DS1012@WebHMI 3point control)
+
       WriteReg("startPosTime", os.time()) -- start time stamp (DS1012@WebHMI 3point control)
               
+
end  
  end  
+
  
  
Строка 561: Строка 585:
 
'''v6'''
 
'''v6'''
 
<syntaxhighlight lang=lua>
 
<syntaxhighlight lang=lua>
--- Расчет новой позиции CalcNewPosition
+
--- CalcNewPosition
-- константы
+
-- constants
FULLPATH = 127 ; -- время полного открытия в сек.
+
FULLPATH = 127 -- full close -> full open time
K = 100 / FULLPATH ; -- коэф. пересчета % открытия в временной интервал
+
K = 100 / FULLPATH ; -- travel time to position coef.
  
  
Строка 570: Строка 594:
 
      
 
      
 
                             DEBUG("Entered #5 script, calc. cur position");
 
                             DEBUG("Entered #5 script, calc. cur position");
   local now = os.time(); -- текущее время
+
   local now = os.time()
   local open_sts = (GetReg("openCmd") == 1); -- Команда "Открытие" (D1007@WebHMI Kitothemr)
+
   local open_sts = (GetReg("openCmd") == 1)
   local close_sts = (GetReg("closeCmd") == 1); -- Команда "Закрытие" (D1008@WebHMI Kitothemr)
+
   local close_sts = (GetReg("closeCmd") == 1)
   local inMotionFlag = (open_sts or close_sts) ; -- Команда "Закрытие" (D1008@WebHMI Kitothemr)
+
   local inMotionFlag = (open_sts or close_sts)  
                             DEBUG("in motion flag = "..tostring(inMotionFlag));
+
                             DEBUG("in motion flag = "..tostring(inMotionFlag))
 
    
 
    
   local cur_dir = 0; -- знак направления движения
+
   local cur_dir = 0 -- current direction
   local posPV = GetReg("curPosition"); -- текущая позиция
+
   local posPV = GetReg("curPosition") -- current position
  
  
-- определить начальные время и позицию
+
--  
   local startTime = GetReg("startPosTime");
+
   local startTime = GetReg("startPosTime")
   local startPos = GetReg("startPosition");
+
   local startPos = GetReg("startPosition")
   local pathdone = 0; -- пройденный путь
+
   local pathdone = 0  
 
    
 
    
   -- Уже едем
+
   -- moving now
 
   if inMotionFlag then  
 
   if inMotionFlag then  
       -- определяем направление движения
+
       -- detect direction
 
       if open_sts then  
 
       if open_sts then  
             cur_dir = 1;
+
             cur_dir = 1
 
       else  
 
       else  
             cur_dir = -1;
+
             cur_dir = -1
 
       end  
 
       end  
   pathdone = GetPathDone(startTime, now); -- вычисляем пройеднный путь
+
   pathdone = GetPathDone(startTime, now)
   varPos = startPos + cur_dir*pathdone; -- теперь текущую координату
+
   varPos = startPos + cur_dir*pathdone
 
        
 
        
       -- проверка выхода текущей позиции за допустимые границы
+
       -- check for valid range
 
       if (varPos < 0) or (varPos > 100) then  
 
       if (varPos < 0) or (varPos > 100) then  
                             DEBUG("new varPos calculaed outside limits, cur value "..tostring(varPos));
+
                             DEBUG("new varPos calculaed outside limits, cur value "..tostring(varPos))
 
         if (varPos < 0) then varPos = 0 end  
 
         if (varPos < 0) then varPos = 0 end  
 
         if varPos > 100 then varPos = 100 end  
 
         if varPos > 100 then varPos = 100 end  
 
       end  
 
       end  
     
+
                        DEBUG("startPos , pathdone = , new varPos  "..tostring(startPos).." "..tostring(pathdone).." "..tostring(varPos))
                      DEBUG("startPos , pathdone = , new varPos  "..tostring(startPos).." "..tostring(pathdone).." "..tostring(varPos));
+
       WriteReg("curPosition", varPos); -- writing new position
       WriteReg("curPosition", varPos); -- пишем текущую позицию
+
 
   end -- auto mode motion control  
 
   end -- auto mode motion control  
 
end -- main  
 
end -- main  
  
  
------------- Функция вычисления остатка движения ---------------   
+
 
 
function GetPathDone(startTime, curTime)   
 
function GetPathDone(startTime, curTime)   
    -- смотрим на тек. время и высчитываем %
+
 
 
     local curPos = (curTime - startTime) * K;
 
     local curPos = (curTime - startTime) * K;
     local remainder = curPos - math.floor(curPos);
+
     local remainder = curPos - math.floor(curPos)
 
     if (remainder >= 0.5) then  
 
     if (remainder >= 0.5) then  
         -- округл. вниз
+
         -- rounding
         curPos = math.floor(curPos) + 1;
+
         curPos = math.floor(curPos) + 1
 
     else  
 
     else  
         curPos = math.floor(curPos) ;
+
         curPos = math.floor(curPos)  
 
     end
 
     end
     return curPos;
+
     return curPos
 
end  
 
end  
 
</syntaxhighlight>
 
</syntaxhighlight>
Строка 629: Строка 652:
 
      
 
      
 
   local now = os.time();
 
   local now = os.time();
   local open_sts = (GetReg("openCmd") == 1); -- Команда "Открытие" (D1007@WebHMI Kitothemr)
+
   local open_sts = (GetReg("openCmd") == 1)
   local close_sts = (GetReg("closeCmd") == 1); -- Команда "Закрытие" (D1008@WebHMI Kitothemr)
+
   local close_sts = (GetReg("closeCmd") == 1)
 
    
 
    
   local full_close = (GetReg("LLsw") == 1); -- Датчик полного закрытия (D1010@WebHMI Kitothemr)
+
   local full_close = (GetReg("LLsw") == 1)
   local full_open = (GetReg("HLsw") == 1); -- Датчик полного закрытия (D1010@WebHMI Kitothemr)
+
   local full_open = (GetReg("HLsw") == 1)
 
    
 
    
 
   if (full_open or full_close) then  
 
   if (full_open or full_close) then  
    --  проверить наезд на конечники
+
 
 
           if full_open then
 
           if full_open then
              -- посчитать новую позицию
+
 
 
               WriteReg("curPosition", 100);
 
               WriteReg("curPosition", 100);
 
               WriteReg("openCmd", 0);
 
               WriteReg("openCmd", 0);
               WriteReg("valveMan_code", 0); -- для ручнго режима
+
               WriteReg("valveMan_code", 0); -- for manual mode
 
           end  
 
           end  
 
            
 
            
 
           if full_close then  
 
           if full_close then  
               WriteReg("curPosition", 0);
+
               WriteReg("curPosition", 0)
               WriteReg("closeCmd", 0);
+
               WriteReg("closeCmd", 0)
               WriteReg("valveMan_code", 0); -- для ручного режима
+
               WriteReg("valveMan_code", 0)  
 
           end  
 
           end  
 
            
 
            
       WriteReg("inMotion_flag", 0);
+
       WriteReg("inMotion_flag", 0)
       WriteReg("endPosTime", now); -- Метка времени окончания движения  (DS1001@WH Valve control)
+
       WriteReg("endPosTime", now)
 
      
 
      
 
   end  
 
   end  
 
    
 
    
   -- индикация соcтояния привода
+
   -- show current valve status
 
   if (not open_sts) and (not close_sts) then  
 
   if (not open_sts) and (not close_sts) then  
       WriteReg("valveStatus", 0); -- Стоим
+
       WriteReg("valveStatus", 0) -- stopped
 
   elseif open_sts then  
 
   elseif open_sts then  
       WriteReg("valveStatus", 1); -- Открытие
+
       WriteReg("valveStatus", 1); -- opening
 
   else  
 
   else  
       WriteReg("valveStatus", 2); -- Закрытие
+
       WriteReg("valveStatus", 2); -- closing
 
   end  
 
   end  
 
    
 
    
Строка 672: Строка 695:
 
------ v8 Drop Cur Cmd on Manual mode ----  
 
------ v8 Drop Cur Cmd on Manual mode ----  
 
function main (userId)  
 
function main (userId)  
     local auto_on = (GetReg("auto_mode")==1); -- работаем по изменению регистра режима
+
     local auto_on = (GetReg("auto_mode")==1) -- work on register change
 
      
 
      
 
     if not auto_on then  
 
     if not auto_on then  
                                     DEBUG_("Dropped cmds in manua mode ! ");
+
                                     DEBUG_("Dropped cmds in manua mode ! ")
         WriteReg("openCmd", 0);
+
         WriteReg("openCmd", 0)
         WriteReg("closeCmd", 0);
+
         WriteReg("closeCmd", 0)
         WriteReg("inMotion_flag",0);
+
         WriteReg("inMotion_flag",0)
 
     end  
 
     end  
    prev_mode = auto_on;
 
 
end -- main   
 
end -- main   
  
 
</syntaxhighlight>
 
</syntaxhighlight>

Текущая версия на 10:35, 5 октября 2018

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

Useful programs

Run script on rising or falling edge of the discrete signal

The script needs to be called on register's change. Inside the script, you need to check the current state of this register and perform corresponding actions either on the front (current state = 1) or on the falling edge (= 0). Example of the script:

Rising edge.png

Реализация универсального таймера (TON TOFF)

Таймер сравнивает свое текущее состояние с состоянием на входе:

  • текущее состояние = 0, вход = 1 - таймер включит выход через время задержки onDelay. Таймер TON получится если указать время offDelay = 0.
  • текущее состояние = 1, вход = 0 - таймер выключит выход через время задержки offDelay. Таймер TOFF получится если указать время onDelay = 0.

Для работы таймера необходимо завести 2 DS регистра типа Unix time и Bit и присвоить им имена (алиасы) вида "<Имя1>", "<Имя1>_out". В первом будет хранится время след. переключения, во втором - текущее состояние таймера.

function TimerOnOff(bool_input, onDelay, offDelay, tmrAlias)
--                    bool     seconds   seconds   string 
    local outAlias = tmrAlias.."_out"
    local curTmrState = (GetReg(outAlias) == 1) ; DEBUG("curTmr  State "..tostring(curTmrState))
                                                  DEBUG("bool_input = "..tostring(bool_input))
                                                
    if (bool_input == curTmrState) then 
        WriteReg(tmrAlias, now) ;                 DEBUG("timer input match state ") 
        return curTmrState  -- as bool 
    elseif bool_input then 
                                                    DEBUG("countdown for onDelay = "..(onDelay - (now - GetReg(tmrAlias))))
         if now - GetReg(tmrAlias) > onDelay then                                              
                WriteReg(outAlias, 1)
                                                    DEBUG("detected ON state after delay ")
                return true 
          end 
    else 
                                                    DEBUG("countdown for offDelay = "..(offDelay - (now - GetReg(tmrAlias))))
        if (now - GetReg(tmrAlias) > offDelay) then                                              
                WriteReg(outAlias, 0)
                                                    DEBUG("detected OFF state after delay ")
                return false  
        end     
     end  -- 
    return  curTmrState  -- output rerurned as bool for simple usage in IFs

end -- function

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

Например есть два дискретных входа с датчиками реле низкого и высокого давления. Их состояние читается и переводится в переменную типа bool для упрощения последующих операторов сравнения. Функция также возвращает bool, чтобы ее можно было сразу использовать в операторах сравнения без дополнительных операторов if.


                                               DEBUG(" hotWater timer call ")
local DI2 = (GetReg(39) == 1) -- hotwater low pressure   
local DI3 = (GetReg(40) == 1) -- hotwater high pressure   
local hotWater = TimerOnOff((DI2 or DI3), 15, 15, "Tmr2") -- 15 seconds delay for both on and off .   

-- or like this 
if TimerOnOff(((GetReg("hotWaterLowPressure") == 1) or (GetReg(hotWaterHighPressure) == 1)), 15, 2, "Tmr2") then 
   -- do something 
end -- simple timer in one row !

Signalling (sound, relay, sms, viber, telegram) about connection errors

It is possible to analyze the scan time by the script and if it exceeds the acceptable limit, signal it in different ways. Below is an example of processing a large scan time with signaling to the message buffer and Viber.


cntdownFlag = false; -- timer countdown flag 
timeStmp = 0;  --  time stamp 
msgSent = false; -- message sent flag


function main (userId)
  -- read inputs
  local scan = GetReg(34); -- scan time 
  local c0 = GetReg(42); -- failed connection number 
  local SCANLIMIT = GetReg(886); -- scan time limit 
  local SCANDELAY = GetReg(887); -- error reaction time 
  -- 
  if (scan == nil)  or (c0 == nil) or (SCANLIMIT == nil) or (SCANDELAY == nil) then
   ERROR("scan / c0  was read as nil");
   return 0;
  end 
  -- read time 
  local now = os.time()
  -- message pattern 
  local msg1 = "Scan is long "..tostring(scan).." ".."ms, error in connection "..tostring(c0);
--
if (scan > SCANLIMIT) then  -- scan above limit
     if not cntdownFlag then 
         cntdownFlag = true
         timeStmp = now
     else 
         if (now - timeStmp) > SCANDELAY then 
             if not msgSent then 
                     AddAlertMessage(msg1)
                     SendViberMessage(398044391, msg1) -- Женя
                     msgSent = true
             end 
         end 
     end 
else -- scan in normal 
    if (cntdownFlag == true) and msgSent  then 
       AddInfoMessage("Скан вернулся к норме ")
       SendViberMessage(398044391, "Скан вернулся к норме ")
       msgSent = false
    end 
    cntdownFlag = false
 
end 

end -- main


In WebHMI there is a buzzer for the sound signal, and 2 output relays, which can be controlled for signaling (send a signal to the signal devices or the PLC about the problem).

Moving average

The moving average is useful for smoothing the values ​​of parameters that have noises, pulsations. Algorithm of the moving average:
at the beginning of the filter on the sample, N values ​​are counted by the arithmetic mean, after reaching the end of the sample, one element is discarded (by dividing the sum by the length of the queue), a new one is added instead of it, and the amount is again divided by the length of the queue.

-- globals
mav_len = 20;   -- queue length 
queue_fill = 0; -- queue fill index 
av_sum = 0;     -- accumulator moving average 


function main (userId)


local in_value, tmp_var, out_value  = GetReg(26), 0, 0; -- read input 
   
if (queue_fill < mav_len) then -- queue not filled
    av_sum = av_sum + in_value; -- accumulating sum 
    queue_fill = queue_fill +1; -- and index 
else                            -- now filled queue
    tmp_var = av_sum / mav_len; -- store one element 
    av_sum = av_sum - tmp_var + in_value; -- subtract and add 
end
-- 
if (queue_fill == mav_len ) then
      out_value = av_sum / mav_len; -- get moving average
    else
      out_value = av_sum / queue_fill; -- arithmetic mean 
end
WriteReg("Tout_mav", out_value) -- 

end

PID - control

An example of implementing a PID controller in WebHMI:


G_LIMIT = 100 -- output limit 
--
function main (userId)
  -- local vars
  local now = os.time()
  local nexTime = GetReg("nextPidTime")
  local CYCLE_TIME = GetReg("pidCycleTime")
  
  -- pid settings
  local Kp = GetReg("Kp") -- Proportional part (DS1400@WH Valve control)
  local Ti = GetReg("Ki") -- Integral time constant (DS1404@WH Valve control)
  local Td = GetReg("Kd") -- Diff. constant  (DS1408@WH Valve control)
  local Err, dErr, iSum_Limit = 0, 0, 0 
  local Int_sum = GetReg("pidIntegral") -- integral accumulator 
  local intPart = 0 -- integral part 
  local G = GetReg("pidOut") -- pid output 
  
  -- process 
  local PV = GetReg(1436) -- power  (PWR0@Scylar 8 INT)
  local Sp = GetReg("targetPowerSp") 
  
                                    DEBUG_("seconds left for PID cycle = "..tostring(nexTime - now))
    -- condition of work             
  local auto = (GetReg("auto_mode") == 1) -- auto mode is on (ds1176@WH Global)
  local heatDemand = (GetReg("heatDemand") == 1)
  
if auto then 
  
  if heatDemand then 
                                    -- PID - loop 
          if (now >= nexTime) then 
                                                          DEBUG_("PID compute cycle")
                WriteReg("nextPidTime", now + CYCLE_TIME)
                Err =  Sp - PV -- get error 
                                                    DEBUG_("sp pv Err = "..Sp.." "..PV.." "..Err)
                dErr = Err - GetReg("pidPrevError") -- get error tendency 
                                                    DEBUG_("dErr = "..dErr)
                    -- calc. integral limit
                iSum_Limit = (G_LIMIT * Ti / Kp) / 5
                                                            DEBUG_("iSum_Limit = "..iSum_Limit)
                
                            -- PID loop 
                    --check integral part 
                                                            DEBUG_("prev Int_sum = "..Int_sum)
                                                            if (intPart <= iSum_Limit) and (intPart >= 0.0) then
                    Int_sum = Int_sum + Err -- accumulating integral of error
                                                   DEBUG_("added error to Int_sum ")
                                                            
                    elseif Int_sum < 0 then
                        Int_sum = 0
                    else
                        Int_sum = iSum_Limit -- strict integral part 
                    end
                    if (Ti == 0) then 
                        intPart = 0 
                    else 
                        intPart = (1/Ti)*Int_sum
                    end 
                    
                    
                                                            DEBUG_("new Int_part = "..intPart)
                G = Kp * (Err + intPart + Td*dErr)
                                                            DEBUG_("Calculated G as "..G)
                G = Round(G)
                                                            DEBUG_("Rounded G as "..G)
                    -- check output for limits 
                if G < 0 then
                    G = 0  
                end
                if G > G_LIMIT then
                    G = G_LIMIT
                end
                
                WriteReg("pidPrevError", Err) -- remember previous error 
                WriteReg("dErr", dErr) -- 
                WriteReg("pidIntegral", intPart) -- remember integral 
                WriteReg("pidOut", G) 
                WriteReg("posSPinput", G) -- output for valve position
        end -- time stamp 
  else 
                DEBUG_("no heatDemand") -- 
      G = 0 
  end -- heatDemand 
                -- DEBUG_("PID_out = "..G) 
                WriteReg("pidOut", G) 
                WriteReg("posSPinput", G)
end -- if auto 

end -- main 

-- rounding  
function Round(var)
    
    local integer, fraction = math.modf(var)
    
        if fraction >= 0.5 then 
            integer = integer + 1
        end 
    
    return math.floor(integer)
end 

------ debug printing ------
thisScriptID = 45

function DEBUG_(str)
                --ERROR("entered DEBUG_ in"..thisScriptID.." script");
local i = 0;
local tmp = "";
local id = tostring(thisScriptID);
local debug_id = GetReg("debug_IDs");

local capture_mask = "%s+(%d+)%s+"

while true do 
i,_, tmp = string.find(debug_id,capture_mask,i+1)

    if (i == nil)  then 
        break -- not found
    end  
    -- найдено
    if (tmp ~= "0") then 
        -- found , check equality 
        if (tmp == tostring(thisScriptID)) then 
             DEBUG(str)
             return 0
        end 
    end 
end 

end -- DEBUG_
------------------------------

This algorithm is typical for use in PLCs. Because the regulator is run at regular intervals, i.e. diff. and int. the components are always computed on the same time scale, so it is not necessary to divide and multiply them by time to obtain the derivative and integral, we can select the time constants Ti, Td. In this algorithm, Ti is an inverse quantity (the larger its value, the smaller the contribution of the integral error)

Running hours meter

The running hour meter is convenient for automatically generating a message about the need for routine work for the equipment unit, changing the lead pump in the pumping group to equalize the operating time, and so on.


An example of the implementation of the run hour meter in WebHMI (the program runs in each scan):

-- globals 
run_state = false; -- to remember current state
function main (userId)
  -- locals
  local check_mask = tonumber("0000100000000000",2); -- bit mask to check rotation bit in frequency inverter FC 51 Danfoss
  local run_status = (bit.band(GetReg(109),check_mask) ~= 0); -- check result as a bool var 
  local now = os.time(); -- current system time 
  local time_diff = 0; -- time difference between current time and last call time


-- catching edge of the unit state 
  if (not run_state) and run_status then 
      WriteReg("P43StartTime", now); -- unit start time
  end


-- count time 
 if run_state then 
     time_diff = (now - GetReg("P43StartTime")); -- calc. time diff. 
     WriteReg("P43RunTime", GetReg("P43RunTime")+time_diff); -- increase meter 
     WriteReg("P43StartTime", now); -- overwrite start point 
 end 
 run_state = run_status
end


The timekeeping registers and time stamps should be made non-volatile.

Time Circulation algorithm (together with redundancy function)

This algorithm is used in systems where it is necessary to alternate the operation of mechanisms (pumps, fans, air conditioners) over time, or on the run hour meters. For example, a set of 2 units is used, which must be alternated in time. If an error occurs on some unit, then the algorithm starts working only on the working (redundancy function). An example of setting the required registers is given below:

Circ algorithm regs.png

For simplicity and clarity, it is better to split the scripts into functional modules that can be quickly analyzed and placed in the right order in the program list. The first script looks at the errors and if they do not exist, the air conditioners alternate in time.

CIRCULATION_TIME = 30; -- for tests circulation time is short 


function main (userId)
  --[[
  if there are no errors, then circulate over time 
  If there is an error on one of the air conditioners, it is excluded from the rotation
  if there are errors on both, then we stand 
  --]]
local acError1, acError2 = (GetReg("acError1") == 1), (GetReg("acError2") ==1) ; -- errro on a/c #1 (DS101@webmi)
local switchTime = GetReg("switchTime"); -- next switch over time (DS103@webmi)
local now = os.time()
local curActiveAC = GetReg("activeAC"); -- active a/c (DS100@webmi)


if (not acError1) and (not acError2) then 
    -- work on circulation
    if (now >= switchTime) then 
        if (curActiveAC == 1) then 
            WriteReg("activeAC", 2);
        else 
            WriteReg("activeAC", 1);
        end
        WriteReg("switchTime", now + CIRCULATION_TIME);
    end 
elseif acError1 and (not acError2) then 
        WriteReg("activeAC", 2);
elseif acError2 and (not acError1) then 
        WriteReg("activeAC", 1);
else
        WriteReg("activeAC", 0);
end -- if no errors 


end -- main


The second script looks at what kind of conditioner is now active, and performs the necessary actions. In a script, this is just debugging, but there may be commands for controlling the infrared transmitter for issuing the desired command, writing to the message log and switching, etc.


function main (userId)
  --[[
     turn on selected a/c depending on pointer
  --]]
  local pointer = GetReg("activeAC"); -- active conditioner (DS100@webmi)


if (pointer==0) then 
      DEBUG("all off")
      return 0
  elseif 
      (pointer==1) then 
          DEBUG("turn on a/c #1");
  else 
          DEBUG("turn on a/c #2");
      
  end -- if 
end

Also here you need a script that will expose the flags of errors of work on certain conditions, read the status of the protection devices, the error registers on the interface, and so on.

3-point control for a valve or servo

A 3-point method is used to control the position of the valve, servo, gate valve, etc., when 3 wires are used to control the drive - 'common', 'power UP', 'power - DOWN'. Such drives may or may not be equipped with end position sensors. Sometimes, in the absence of position sensors and low requirements for positioning accuracy, an algorithm can be used when the drive leaves down or up (either one position sensor or one command for a time longer than the valve's full travel time), initializes the coordinate, and then go to the specified position.

To determine intermediate positions, a calculated value is used, determined from the characteristics of the 'full path time', which can also be determined experimentally. As mentioned above, relatively complex and branched algorithms in WebHMI are better to divide into functionally complete and simple programs, the interaction and synchronization between them can be done using internal registers.

The
Below is a variant of 3-point control for a valve with 2 end sensors. The program is divided into 6 parts:

3-point control.png


v3 OpenClose Valve Manual - manual control program,
v4 Auto Valve Control - main program. do homing on very first call, and if set in current position are no equal move into the desired direction when set positions are limits, then keeps on current command till the limit switch is reached
v5 LatchStart_Time - catches start time
v6 CalcNewPosition - calculate path done like time since start time while valve in motion (this path is checked in v4).
v7 Limit sw reaction
v8 Drop Cur Cmd on Manual mode - when manual mode selected, clears current cmds


At the beginning of the programs, the order of the required execution order (v3..v8) is indicated, since the necessary order of program execution can change undesirable, for example, when sorting programs and inaccurate 'dragging' programs in the list. Thus, the prefix reminds you of the desired order, it can also reflect the functionality of the script - 'v' Valves, 't' - temperature control, etc. It is more convenient to navigate in large lists and refer to it.

Script are given below: </ p> v3

------- Manual control from dashboard buttons  -------------------
function main (userId)
  
  local button_value = GetReg("valveMan_code")-- pressed button code 
  local manual_mode = (GetReg("auto_mode") == 0) -- auto mode ON (ds1176@WH Global)


if manual_mode then


if (button_value == 10) then 
         WriteReg("openCmd", 1); -- opening command (DS1007@WH Valve control)
         WriteReg("closeCmd", 0)
    elseif (button_value == 5) then 
          WriteReg("closeCmd", 1); -- closing command (DS1008@WH Valve control)
          WriteReg("openCmd", 0)
    else
        -- invalid code - all off 
                                    DEBUG("Read button value as "..tostring(button_value));
        WriteReg("openCmd", 0)
        WriteReg("closeCmd", 0)
        WriteReg("inMotion_flag",0)
    end --if 
    
    -- catching start position, and time 
    if (button_value == 10) or (button_value == 5) then 
        WriteReg("startPosition", GetReg("curPosition")); -- start position and time (DS1020@WebHMI 3point control)
        WriteReg("startPosTime", os.time()) -- start time time stamp (DS1012@WebHMI 3point control)
        WriteReg("inMotion_flag",1)
    end
end -- manual_mode


end -- main -----------------------


v4

-- auto valve control ---- 
function main (userId)
    
  local now = os.time(); -- cur time
  local timeStmp = GetReg("endPosTime") 
  local auto_mode = (GetReg("auto_mode")==1)
  
if auto_mode then
  -- check if very first run 
  if (timeStmp == 0) and (not home_mode) then 
      WriteReg("homingBit",1)
                                DEBUG("timeStmp = 0, homing needed! ")
  end 
  
  local home_mode = (GetReg("homingBit") == 1)
  local full_close = GetReg("LLsw")


if home_mode then 
      -- go down till low limit sw 
               DEBUG("giving home_mode close cmd ! ")
      WriteReg("closeCmd", 1)
      
          if (full_close == 1) then 
                   DEBUG("full close ! ")
                
                WriteReg("closeCmd",0)
                WriteReg("endPosTime", now) to avoid homing again
                WriteReg("homingBit",0)
                WriteReg("curPosition", 0) -- current valve position
          end
      return 0  -- 
  end -- home mode 
  
  --MAIN PART ---- 
                           DEBUG("-------- auto valve control ")
  
  local openingStatus = (GetReg("openCmd") == 1)
  local closingStatus = (GetReg("closeCmd") == 1)
  local inMotionFlag = openingStatus or closingStatus
  
  local target_dir= 0
  local posSV = GetReg("posSetpoint"); -- position setpoint 
  local posPV = GetReg("curPosition"); -- current position 
  
  local startTime = GetReg("startPosTime");
  local startPos = GetReg("startPosition");
  local pathdone = 0; 
  
  local OpenSw = GetReg("HLsw"); -- full open limit sw 
  local CloseSw = GetReg("LLsw"); -- full close limit sw
  
                            DEBUG("posPV  "..tostring(posPV).." pos SV"..tostring(posSV))
      -- Already moving
      if inMotionFlag then 
        -- full closed 
          if (closingStatus) and (posPV <= posSV) then 
              -- if 0 is setpoint then move till the physical limit sw 
              if (posSV == 0) and (not CloseSw) then 
                   WriteReg("closeCmd", 1)
                   return 0 -- exiting, cmd will be off in v7 script
              end 
                  WriteReg("closeCmd", 0)
                  WriteReg("inMotion_flag",0)
            end 
        -- full open ?
          if (openingStatus) and (posPV >= posSV) then 
             --- if 100 is setpoint then move till the physical limit sw 
                  if (posPV == 100) and (not OpenSw) then 
                       WriteReg("openCmd", 1)
                       return 0 --exiting the command will be off in v7 script 
                  end 
              WriteReg("openCmd", 0)
              WriteReg("inMotion_flag",0)
          end 
      -- stopped but not in position ?
       else 
           if (posSV ~= posPV) then 
               target_dir = (posSV - posPV)/math.abs(posSV - posPV); -- определить знак
                                            DEBUG_("target_dir =  "..tostring(target_dir));
               if (target_dir > 0) then 
                   WriteReg("openCmd" , 1);
                                            DEBUG_("will open...")
               else 
                   WriteReg("closeCmd" , 1);
                                            DEBUG_("will close...")
               end
            WriteReg("inMotion_flag", 1) -- sets in motion flag 
           end
  end -- auto mode motion control 


end -- auto_mode
end -- main


v5

-- LatchStart_Time ---
function main (userId)
    
                 DEBUG("Entered  latch start time and position");
  -- Add your code here
  local flag = (GetReg("inMotion_flag") == 1) ; -- in motion  (DS1016@WebHMI 3point control)
                 DEBUG("motion flag = "..tostring(flag))
  if flag then 
                  DEBUG("now flag = 1")
      WriteReg("startPosition", GetReg("curPosition")) -- start position (DS1020@WebHMI 3point control)
      WriteReg("startPosTime", os.time()) -- start time stamp (DS1012@WebHMI 3point control)
end 


end

v6

--- CalcNewPosition
-- constants
FULLPATH = 127  -- full close -> full open time 
K = 100 / FULLPATH ; -- travel time to position coef.


function main (userId)
    
                            DEBUG("Entered #5 script, calc. cur position");
  local now = os.time()
  local open_sts = (GetReg("openCmd") == 1)
  local close_sts = (GetReg("closeCmd") == 1)
  local inMotionFlag = (open_sts or close_sts) 
                            DEBUG("in motion flag = "..tostring(inMotionFlag))
  
  local cur_dir = 0 -- current direction
  local posPV = GetReg("curPosition") -- current position 


-- 
  local startTime = GetReg("startPosTime")
  local startPos = GetReg("startPosition")
  local pathdone = 0 
  
  -- moving now
  if inMotionFlag then 
      -- detect direction
      if open_sts then 
            cur_dir = 1
      else 
            cur_dir = -1
      end 
  pathdone = GetPathDone(startTime, now)  
  varPos = startPos + cur_dir*pathdone
      
      -- check for valid range 
      if (varPos < 0) or (varPos > 100) then 
                            DEBUG("new varPos calculaed outside limits, cur value "..tostring(varPos))
         if (varPos < 0) then varPos = 0 end 
         if varPos > 100 then varPos = 100 end 
      end 
                        DEBUG("startPos , pathdone = , new varPos  "..tostring(startPos).." "..tostring(pathdone).." "..tostring(varPos))
      WriteReg("curPosition", varPos); -- writing new position 
   end -- auto mode motion control 
end -- main 



function GetPathDone(startTime, curTime)  
  
    local curPos = (curTime - startTime) * K;
    local remainder = curPos - math.floor(curPos)
    if (remainder >= 0.5) then 
        -- rounding
        curPos = math.floor(curPos) + 1
    else 
        curPos = math.floor(curPos) 
    end
    return curPos
end

v7

-- Limit sw reaction------------
function main (userId) 
    
  local now = os.time();
  local open_sts = (GetReg("openCmd") == 1)
  local close_sts = (GetReg("closeCmd") == 1)
  
  local full_close = (GetReg("LLsw") == 1)
  local full_open = (GetReg("HLsw") == 1)
  
  if (full_open or full_close) then 
   
          if full_open then

              WriteReg("curPosition", 100);
              WriteReg("openCmd", 0);
              WriteReg("valveMan_code", 0); -- for manual mode
          end 
          
          if full_close then 
              WriteReg("curPosition", 0)
              WriteReg("closeCmd", 0)
              WriteReg("valveMan_code", 0) 
          end 
          
      WriteReg("inMotion_flag", 0)
      WriteReg("endPosTime", now)
    
  end 
  
  -- show current valve status
   if (not open_sts) and (not close_sts) then 
      WriteReg("valveStatus", 0) -- stopped
  elseif open_sts then 
      WriteReg("valveStatus", 1); -- opening
  else 
      WriteReg("valveStatus", 2); -- closing
  end 
  
end -- main --------------------------------


v8

------ v8 Drop Cur Cmd on Manual mode ---- 
function main (userId) 
    local auto_on = (GetReg("auto_mode")==1) -- work on register change
    
    if not auto_on then 
                                    DEBUG_("Dropped cmds in manua mode ! ")
        WriteReg("openCmd", 0)
        WriteReg("closeCmd", 0)
        WriteReg("inMotion_flag",0)
    end 
end -- main