Битовые операции/en — различия между версиями
Строка 1: | Строка 1: | ||
+ | <languages/> | ||
<languages/> | <languages/> | ||
=== Bit operations === | === Bit operations === | ||
− | ==== | + | ==== Удобные битовые операции ==== |
− | + | Можно определить несколько удобных битовых операций, помня о том что в lua биты нумеруются с 1. Ссылка взята [http://lua-users.org/wiki/BitwiseOperators отсюда], там же есть и другие полезные функции. | |
− | + | ||
<syntaxhighlight lang = lua> | <syntaxhighlight lang = lua> | ||
Строка 25: | Строка 25: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
− | ==== | + | ==== Преобразование битовой таблички в число ==== |
− | + | Данная функция может быть полезна для нестандартных преобразований, когда число хранится в специфичном формате и его необходимо обработать по частям, используя заданную позицию 1-го бита и длину поля значения. | |
<syntaxhighlight lang = "lua"> | <syntaxhighlight lang = "lua"> | ||
Строка 155: | Строка 155: | ||
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. | 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> | ||
Версия 07:35, 16 мая 2018
Содержание
Bit operations
Удобные битовые операции
Можно определить несколько удобных битовых операций, помня о том что в lua биты нумеруются с 1. Ссылка взята отсюда, там же есть и другие полезные функции.
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
Преобразование битовой таблички в число
Данная функция может быть полезна для нестандартных преобразований, когда число хранится в специфичном формате и его необходимо обработать по частям, используя заданную позицию 1-го бита и длину поля значения.
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.
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}, "bit9"},
{srcAlias = "vfdStatus_1", bits = {[2] = 10, [3] = 20, [4] = 30, [5] = 40}, "=="},
{srcAlias = "vfdStatus_2", bits = {[6] = 10, [7] = 20, [8] = 30, [9] = 40}, "=="},
{srcAlias = "pv1ZoneHumidity", bits = {[10] = 90}, ">="}
}
intAlertReg = "alertsReg" -- COMMON ALERT REGISTER
function main (userId)
local alertInput, alertOut = 0,0 -- temporary vars to read alerts and form result
local digit = 0 -- номер бита в исходном регистре, указывающий на аварию..
--[[
If we name all the parameters uniformly,
which are sources of alerts and combine them into one structure,
then you can use only one "for" loop, for example:
--]]
for i,v in pairs(allAlerts) do
alertInput = GetReg(v.srcAlias) -- reading register
for j,k in pairs(v.bits) do
-- check what condition to look for
if (v[3] == "==") then
-- check error code
if (alertInput == k) then
alertOut = setbit(alertOut, j)
else
alertOut = clearbit(alertOut, j)
end
-- check certain bit
elseif (string.find(v[3], "bit%d+") ~= nil) then -- %d+ is a pattern for a sequence of digits
_,_,digit = tonumber(string.find(v[3], "bit(%d+)")) -- select digit and converto number
if hasbit(alertInput, bit(digit)) then -- if this bit set in input (source) register
alertOut = setbit(alertOut, j)
else
alertOut = clearbit(alertOut, j)
end
else
--- other operation
end
end -- for bits inside
end -- for allAlerts
-- now just write down what happened in the general register of accidents
WriteReg(intAlertReg, alertOut)
end -- main
----------------- extra functions ----------------------------------
function bit(n)
return 2 ^ (n - 1)
end
function hasbit(x, p)
return x % (p + p) >= p --
end
function setbit(x, p)
return hasbit(x, p) and x or x + p --
end
function clearbit(x, p)
return hasbit(x, p) and x - p or x -- also clear bit
end
If there are many different operations on the values: not only comparison, but also for example checking a particular bit, etc. you can hide the implementation details in a function that will receive a sign with the operation type (whether a particular bit, an error code, a parameter in the boundaries, etc.) and an input value is set and will put or reset the desired bit.
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