Битовые операции/en — различия между версиями

Материал из WebHMI Wiki
Перейти к: навигация, поиск
(Новая страница: «local result_num = 0.0; -- to store the result»)
 
(не показано 30 промежуточных версий этого же участника)
Строка 1: Строка 1:
 +
<languages/>
 
=== Bit operations ===  
 
=== Bit operations ===  
  
Строка 105: Строка 106:
  
 
local sign,exp,mantissa = 0,0,0;
 
local sign,exp,mantissa = 0,0,0;
local fraction_table = {}; -- табл. для дробной части
+
local fraction_table = {}; -- table for fraction part
  
-- определить знак
+
-- get sign
 
if result_tab[1] == "1" then  
 
if result_tab[1] == "1" then  
 
     sign = -1;
 
     sign = -1;
Строка 113: Строка 114:
 
     sign = 1;
 
     sign = 1;
 
end  
 
end  
-- экспоненту
+
-- get exponent
 
exp = getNumberFromTab(result_tab,2,11);
 
exp = getNumberFromTab(result_tab,2,11);
                 DEBUG("exp = "..tostring(exp)); -- отл. печать
+
                 DEBUG("exp = "..tostring(exp)); -- debug printing
-- мантиссу
+
-- mantissa
 
for i=13,64 do  
 
for i=13,64 do  
 
     table.insert(fraction_table,result_tab[i]);
 
     table.insert(fraction_table,result_tab[i]);
 
end  
 
end  
-- посчитать мантиссу по-разрядно !!!
+
-- get mantissa bit - by - bit
 
for j=1,52 do  
 
for j=1,52 do  
 
     if fraction_table[j]=="1" then  
 
     if fraction_table[j]=="1" then  
     mantissa = mantissa +(2^(-1*j));
+
     mantissa = mantissa +(2^(-1*j))
 
     end  
 
     end  
 
end  
 
end  
  
 
mantissa = mantissa +1;
 
mantissa = mantissa +1;
                 DEBUG("m = "..tostring(mantissa)); -- отл. печать
+
                 DEBUG("m = "..tostring(mantissa)); -- debug printing
result_num = sign*(2^(exp - 1023))*mantissa;
+
result_num = sign*(2^(exp - 1023))*mantissa
  
-- Обработка исключений
+
-- exception handling
 
if exp == 0 then -- subnormals
 
if exp == 0 then -- subnormals
 
   result_num = sign*(2^(-1022))*(mantissa-1);
 
   result_num = sign*(2^(-1022))*(mantissa-1);
Строка 139: Строка 140:
 
   result_num = NaN;
 
   result_num = NaN;
 
end  
 
end  
-- Вывод результата в регистр типа float  
+
-- output the result into a single float register
 
WriteReg(10, result_num);
 
WriteReg(10, result_num);
  
Строка 146: Строка 147:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==== Формирование общего регистра аварий по разным условиям ====  
+
==== Grouping alerts into a single alert register ====  
  
В WebHMI есть механизм аварий. Т.е. по состоянию бита в регистре можно автоматически генерировать записи в журнале о моменте возникновения, снятия аварии, квитировании ее оператором.  
+
WebHMI has a alerts mechanism. I.e by the state of the bit in the alert register, you can automatically generate entries in the log about the time of occurrence, removal of the accident, acknowledgment by the operator.  
  
Однако в некоторых случаях использовать данный механизм непосредственно, как есть, может не совсем быть удобно. Например - аварийным считается определенный бит или их комбинация, разные коды ошибок (и их надо отличать друг от друга), значение больше или меньше порога и т.д. Если регистр имеет уже настроенные состояния для аварийной ситуации, используемое для отображения, то его нужно просто продублировать, выставив бит в общем аварийном регистре. Также может потребоваться введение задержки на срабатывание аварии, так как значение может иметь "дребезг" который надо фильтровать, прежде чем генерировать аварию в журнале.  
+
However, in some cases, using this mechanism directly, as it is, may not be entirely convenient. For example, a certain bit or a combination thereof, different error codes (and they must be distinguished from each other), a value greater than or less than a threshold, etc. are considered emergency. If the register has already configured states for the emergency used for display, then it must be simply duplicated by setting the bit in the common emergency register. It may also be necessary to introduce an alarm delay, since the value may have a 'bounce' which must be filtered before generating an accident in the log.
Кроме этого удобнее, когда все аварии описаны в одном месте, а не разбросаны по сотням регистров. Т.е. рациональнее использовать один или несколько несколько 32-битных регистров для описания всех возможных аварий, и скрипт в котором эти аварии будут записываться в этот регистр по разным условиям. Ниже приведен пример такого скрипта:
+
In addition, it is more convenient when all accidents are described in one place, rather than scattered over hundreds of registers. I.e. it is more rational to use one or more several 32-bit registers to describe all possible accidents, and a script in which these accidents will be written to this register under different conditions. Below is an example of such a script:
  
Допустим, есть некая функциональная единица, например приточная вентиляционная установка, в которой есть некий общий признак аварии, есть несколько регистров с кодами текущих ошибок от частотных преобразователей и ситуация, когда один из регистров (влажность, СО2 и др.) вышел за допустимый диапазон, что является также аварийной ситуацией.
+
For example, there is a certain functional unit, for example, an air-handling unit, in which there is some common sign of an accident, there are several registers with codes for current errors from frequency converters and a situation where one of the registers exceeded alert limit (humidity, CO2, etc.) which is also an emergency situation.
[[Файл:Alerts.png | 800px | left]]
+
[[Файл:Alerts eng.png | 800px | left]]
 
<br clear = all>
 
<br clear = all>
  
Тогда скрипт, который обработает все эти ситуации и сформирует нужные флаги на своих местах может выглядеть так:
+
Then the script that will handle all these situations and generate the necessary flags in their places can look like this:
 
<syntaxhighlight lang = lua>
 
<syntaxhighlight lang = lua>
 
allAlerts = {          -- регистр    бит - код ошибки    тип операции
 
allAlerts = {          -- регистр    бит - код ошибки    тип операции
               {srcAlias = "pv1Status", bits = {[1] = 1}, "bit9"},
+
               {srcAlias = "pv1Status", bits = {[1] = 1}, checktype = "bit9"},
               {srcAlias = "vfdStatus_1", bits = {[2] =  10, [3] = 20, [4] = 30, [5] = 40}, "=="},
+
               {srcAlias = "vfdStatus_1", bits = {[2] =  10, [3] = 20, [4] = 30, [5] = 40}, checktype = "=="},
               {srcAlias = "vfdStatus_2",  bits = {[6] =  10, [7] = 20, [8] = 30, [9] = 40}, "=="},
+
               {srcAlias = "vfdStatus_2",  bits = {[6] =  10, [7] = 20, [8] = 30, [9] = 40}, checktype = "=="},
               {srcAlias = "pv1ZoneHumidity", bits = {[10] = 90}, ">="}
+
               {srcAlias = "pv1ZoneHumidity", bits = {[10] = 90}, checktype = ">="}
 
  }
 
  }
  
Строка 170: Строка 171:
 
function main (userId)
 
function main (userId)
  
 
+
                                DEBUG("Entered script")
 
local alertInput, alertOut = 0,0 -- временные переменные для чтения аварий и формирования результата  
 
local alertInput, alertOut = 0,0 -- временные переменные для чтения аварий и формирования результата  
 
local digit = 0 -- номер бита в исходном регистре, указывающий на аварию..
 
local digit = 0 -- номер бита в исходном регистре, указывающий на аварию..
Строка 180: Строка 181:
 
   for i,v in pairs(allAlerts) do  
 
   for i,v in pairs(allAlerts) do  
 
         alertInput = GetReg(v.srcAlias) -- читаем регистр
 
         alertInput = GetReg(v.srcAlias) -- читаем регистр
 
+
                                DEBUG("Регистр для анализа = ".." "..v.srcAlias.." = "..alertInput)
 
+
            for j,k in pairs(v.bits) do -- выполнится по числу элементов в под-табличке bits
for j,k in pairs(v.bits) do -- выполнится по числу элементов в под-табличке bits
+
 
                           -- проверяем тип операции - точное совпадение или другое условие  
 
                           -- проверяем тип операции - точное совпадение или другое условие  
                if (v[3] == "==") then  
+
                    if (v.checktype == "==") then  
 +
                                DEBUG("Тип сравнения - точное совпадение")
 
-- проверяем регистр на один из кодов ошибок
 
-- проверяем регистр на один из кодов ошибок
                  if (alertInput == k) then
+
                      if (alertInput == k) then
                      alertOut = setbit(alertOut, j)
+
                          alertOut = setbit(alertOut, bit(j))
                  else
+
                      else
                      alertOut = clearbit(alertOut, j)
+
                          alertOut = clearbit(alertOut, bit(j))
                  end
+
                      end
 
-- проверяем определенный бит
 
-- проверяем определенный бит
                elseif (string.find(v[3], "bit%d+") ~= nil) then             -- %d+    паттерн любая последовательность цифр
+
                    elseif (string.find(v.checktype, "bit%d+") ~= nil) then            
                        _,_,digit = tonumber(string.find(v[3], "bit(%d+)"))   -- выделить из паттерна цифру и преобразовать в число
+
            -- %d+    паттерн любая последовательность цифр
 +
                                                DEBUG("Тип сравнения - проверка бита")
 +
                            _, _, digit = string.find(v.checktype,"(%d+)")
 +
                            digit = tonumber(digit)
 +
                                                DEBUG("digit = "..digit)
 
                       if hasbit(alertInput, bit(digit)) then                -- если заданный бит установлен во входном регистре
 
                       if hasbit(alertInput, bit(digit)) then                -- если заданный бит установлен во входном регистре
                           alertOut = setbit(alertOut, j)
+
                           alertOut = setbit(alertOut, bit(j))
 
                       else  
 
                       else  
                           alertOut = clearbit(alertOut, j)
+
                           alertOut = clearbit(alertOut, bit(j))
 
                       end  
 
                       end  
                 else  
+
            elseif  (v.checktype == ">=") then   
 +
                 -- параметр больше чем значение
 +
                    if (alertInput >= k) then
 +
                      alertOut = setbit(alertOut, bit(j))
 +
                    else
 +
                      alertOut = clearbit(alertOut, bit(j))
 +
                  end
 +
            else  
 
                     --- другие операции
 
                     --- другие операции
                end   
+
            end  -- if checktype
        end -- for bits inside  
+
            end -- for bits inside  
  
 
end -- for allAlerts  
 
end -- for allAlerts  
Строка 228: Строка 240:
 
   return hasbit(x, p) and x - p or x -- тоже только снять бит
 
   return hasbit(x, p) and x - p or x -- тоже только снять бит
 
end
 
end
 
 
</syntaxhighlight>
 
</syntaxhighlight>
 
Если различных операций над значениями много: не только сравнение, но и например проверка конкретного бита и т.п. то можно детали реализации спрятать в функцию, которая получит на вход табличку с типом операции (установлен ли конкретный бит, код ошибки, параметр в границах и т.п.) и входным значением и поставит или сбросит нужный бит.  
 
Если различных операций над значениями много: не только сравнение, но и например проверка конкретного бита и т.п. то можно детали реализации спрятать в функцию, которая получит на вход табличку с типом операции (установлен ли конкретный бит, код ошибки, параметр в границах и т.п.) и входным значением и поставит или сбросит нужный бит.  
Строка 237: Строка 248:
  
 
for i,v in pairs(allAlerts) do  
 
for i,v in pairs(allAlerts) do  
     alertInput = GetReg(v.srcAlias) -- читаем регистр
+
     alertInput = GetReg(v.srcAlias) --  
     alertOut = MyGetAlerts(alertInput, alertOut, v) -- вызываем функцию, которая посмотрит что надо сделать по полю v[3], т.е. allAlerts[3]
+
     alertOut = MyGetAlerts(alertInput, alertOut, v) -- calling the function which will look what to do
 
end -- for  
 
end -- for  
 
</syntaxhighlight>
 
</syntaxhighlight>

Текущая версия на 12:38, 31 мая 2018

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

Bit operations

Convenient bit operations

You can define several convenient bit operations, remembering that in lua bits are numbered from 1. The link was taken from here, there are many other useful functions.

function bit(n)
  return 2 ^ (n - 1)  -- returns weight of the binary digit
end

function hasbit(x, p)
  return x % (p + p) >= p  -- return on / off state as true/false   if hasbit(value, bit(2)) then ...
end

function setbit(x, p)
  return hasbit(x, p) and x or x + p -- set bit # p in х examle  х = setbit(х, bit(p))
end

function clearbit(x, p)
  return hasbit(x, p) and x - p or x -- same as previous but clears bit
end

Convert binary table to number

This function can be useful for non-standard conversions when the number is stored in a specific format and it must be processed in parts using the specified position of the 1st bit and the length of the value field.

function getNumberFromTab(tab,start,length) -- 
    local result_str = "";
    
    for i=start,(start+length-1) do 
            result_str = result_str..tostring(tab[i]);
    end 
    return tonumber(result_str,2);
end -- getNumberFromTab

Convert number to binary table

In lua (in the version used in WebHMI) there is no built-in operation to get a string representation of a binary number. However, this feature is very useful in custom protocols and others when it is needed to redistribute or somehow treat the individual bits of the number. The text of the variant of such function:

function getBits(input_num,length)         -- works with defined length
    
local tab = {};                           -- empty table for reply 
local max_i = length - 1;                 
local remainder = input_num;              -- remainder of successive approximation 
  
for i=max_i,0,-1 do
     if remainder - 2^i >= 0 then
       table.insert(tab, "1")    ;               
       remainder =  remainder - 2^i;
     else 
       table.insert(tab, "0")    ;
     end 
end
return tab;
end -- getBits

Then you can rearrange the necessary bits in places, etc. and get the right number. Below is an example of exchanging bits 31 and 23 (with bit numbering from 0) in the response of the flow meter of VLR 2301/2304 produced by Asvega-U.

function main (userId)
    
  local input_num = GetReg(3);               -- 
  local bitTable = getBits(input_num,32);    -- table to process result
  local tmp_bit = "";                        -- 
  
  tmp_bit = bitTable[1]; -- exchange
  bitTable[1] = bitTable[9];                 
  bitTable[9] = tmp_bit;
  WriteReg(1, tonumber(table.concat(bitTable),2));       

end

Processing extended precision float numbers (double)

Some devices can store data in a format with increased accuracy double float. In the current version of WebHMI only 32 bit registers are supported, so to process a 64-bit number, you will need to use a script that will 'chain' the two registers into the original number, and then convert and write back into float. Registers must be of type double word uint. For the 'coupling' of numbers, you can use the function getBits described above to get 2 tables, and then add the first table with bits from the second using table.insert. In this example, a constant is used as the verification number for simplicity.

--------------------------------------
local test_var = 0xC1D312D000000000;
local NaN = tonumber("11111111111111111111111111111111",2);
--------------------------------------

function main (userId)
  -- getresult tab of 0 and 1 
local result_tab = getBits(test_var,64);  

WriteReg(11,table.concat(result_tab)); -- debug print into a string register 

local result_num = 0.0; -- to store the result 

local sign,exp,mantissa = 0,0,0;
local fraction_table = {}; -- table for fraction part 

-- get sign 
if result_tab[1] == "1" then 
    sign = -1;
else 
    sign = 1;
end 
-- get exponent
exp = getNumberFromTab(result_tab,2,11);
                DEBUG("exp = "..tostring(exp)); -- debug printing
-- mantissa
for i=13,64 do 
    table.insert(fraction_table,result_tab[i]);
end 
-- get mantissa bit - by - bit 
for j=1,52 do 
    if fraction_table[j]=="1" then 
    mantissa = mantissa +(2^(-1*j))
    end 
end 

mantissa = mantissa +1;
                DEBUG("m = "..tostring(mantissa)); -- debug printing 
result_num = sign*(2^(exp - 1023))*mantissa

-- exception handling
if exp == 0 then -- subnormals
   result_num = sign*(2^(-1022))*(mantissa-1);
end 

if exp == 0x7ff then -- nan 
   result_num = NaN;
end 
-- output the result into a single float register 
WriteReg(10, result_num);

end -- main

Grouping alerts into a single alert register

WebHMI has a alerts mechanism. I.e by the state of the bit in the alert register, you can automatically generate entries in the log about the time of occurrence, removal of the accident, acknowledgment by the operator.

However, in some cases, using this mechanism directly, as it is, may not be entirely convenient. For example, a certain bit or a combination thereof, different error codes (and they must be distinguished from each other), a value greater than or less than a threshold, etc. are considered emergency. If the register has already configured states for the emergency used for display, then it must be simply duplicated by setting the bit in the common emergency register. It may also be necessary to introduce an alarm delay, since the value may have a 'bounce' which must be filtered before generating an accident in the log. In addition, it is more convenient when all accidents are described in one place, rather than scattered over hundreds of registers. I.e. it is more rational to use one or more several 32-bit registers to describe all possible accidents, and a script in which these accidents will be written to this register under different conditions. Below is an example of such a script:

For example, there is a certain functional unit, for example, an air-handling unit, in which there is some common sign of an accident, there are several registers with codes for current errors from frequency converters and a situation where one of the registers exceeded alert limit (humidity, CO2, etc.) which is also an emergency situation.

Alerts eng.png


Then the script that will handle all these situations and generate the necessary flags in their places can look like this:

allAlerts = {          -- регистр    бит - код ошибки    тип операции
              {srcAlias = "pv1Status", bits = {[1] = 1}, checktype = "bit9"},
              {srcAlias = "vfdStatus_1", bits = {[2] =  10, [3] = 20, [4] = 30, [5] = 40}, checktype = "=="},
              {srcAlias = "vfdStatus_2",   bits = {[6] =  10, [7] = 20, [8] = 30, [9] = 40}, checktype = "=="},
              {srcAlias = "pv1ZoneHumidity", bits = {[10] = 90}, checktype = ">="}
 }

intAlertReg = "alertsReg" -- ОБЩИЙ  РЕГИСТР АВАРИЙ

function main (userId)

                                DEBUG("Entered script")
local alertInput, alertOut = 0,0 -- временные переменные для чтения аварий и формирования результата 
local digit = 0 -- номер бита в исходном регистре, указывающий на аварию..
--[[
Если назвать единообразно все  параметры, 
из которых надо формировать аварии и сложить их в одну структуру, 
тогда можно использовать только один цикл for, например так:
--]]
   for i,v in pairs(allAlerts) do 
         alertInput = GetReg(v.srcAlias) -- читаем регистр
                                DEBUG("Регистр для анализа = ".." "..v.srcAlias.." = "..alertInput)
            for j,k in pairs(v.bits) do -- выполнится по числу элементов в под-табличке bits
                          -- проверяем тип операции - точное совпадение или другое условие 
                    if (v.checktype == "==") then 
                                DEBUG("Тип сравнения - точное совпадение")
-- проверяем регистр на один из кодов ошибок
                       if (alertInput == k) then
                          alertOut = setbit(alertOut, bit(j))
                       else
                          alertOut = clearbit(alertOut, bit(j))
                       end
-- проверяем определенный бит
                    elseif (string.find(v.checktype, "bit%d+") ~= nil) then             
            -- %d+    паттерн любая последовательность цифр
                                                 DEBUG("Тип сравнения - проверка бита")
                            _, _, digit = string.find(v.checktype,"(%d+)")
                            digit = tonumber(digit)
                                                DEBUG("digit = "..digit)
                       if hasbit(alertInput, bit(digit)) then                 -- если заданный бит установлен во входном регистре
                          alertOut = setbit(alertOut, bit(j))
                       else 
                          alertOut = clearbit(alertOut, bit(j))
                       end 
            elseif  (v.checktype == ">=") then    
                -- параметр больше чем значение 
                    if (alertInput >= k) then
                      alertOut = setbit(alertOut, bit(j))
                    else
                      alertOut = clearbit(alertOut, bit(j))
                   end
            else 
                    --- другие операции
            end  -- if checktype 
            end -- for bits inside 

end -- for allAlerts 
                    -- теперь просто записать то что получилось в общий регистр аварий
WriteReg(intAlertReg,  alertOut)

end -- main 


----------------- ДОПОЛНИТЕЛЬНЫЕ ФУНКЦИИ ----------------------------------

function bit(n)
  return 2 ^ (n - 1)  -- возвращает число - вес разряда номер n
end

function hasbit(x, p)
  return x % (p + p) >= p  -- возвращает состояние бита p как true/false; if hasbit(value, bit(2)) then ...
end

function setbit(x, p)
  return hasbit(x, p) and x or x + p -- установить бит № p в х Пример использования:  х = setbit(х, bit(p))
end

function clearbit(x, p)
  return hasbit(x, p) and x - p or x -- тоже только снять бит
end

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


for i,v in pairs(allAlerts) do 
     alertInput = GetReg(v.srcAlias) -- 
     alertOut = MyGetAlerts(alertInput, alertOut, v) -- calling the function which will look what to do 
end -- for