Export translations
Перейти к:
навигация
,
поиск
Параметры
Группа
1-Wire
Allen Bradley DF1
API - Запись нового значения в регистр
API - Получение данных для графика
API - Получение данных для события
API - Получение данных о локальном времени
API - Получение лога регистров
API - Получение текущих значений регистров
API - Список блоков панелей
API - Список графиков
API - Список изображений
API - Список панелей
API - Список регистров
API - Список словарей
API - Список соединений
API - Список трендов
BACnet IP
Broadlink SP3S
Delta DVP
ModBus ASCII
ModBus RTU
Modbus RTU в виде custom protocol
ModBus TCP
Siemens PPI
Siemens S7 Communication
Test
Troubleshooting - Решение типовых проблем при работе в WebHMI
Аварии
Аннотация - функциональные возможности WebHMI
Битовые операции
Быстродействие при обмене данными
ВНИМАНИЕ! ДАННАЯ ВЕРСИЯ ВИКИ УСТАРЕЛА - см. docs.webhmi.com.ua
Внутренние регистры WebHMI
Демо-приложение для Android
Дополнительные СОМ порты
Доступ по ftp
Журнал регистров
Интеграция в другие системы
Использование MultiWan
Исторические графики
Как проверить уровень приема сигнала у 3G модема
Календарь
Назначение и применение
Настройка виртуального UART
Настройка связи с CDC-модемами на примере модема Huawei E3531
Настройка сетевых соединений
Обновление версии прошивки
Описание API
Описание внешних разъемов
Оптимизация производительности
Особенности работы с некоторыми модемами
Отладка сложных скриптов
Первое включение
Перевод на англ 2
Поддерживаемые протоколы
Подключение 3G модем ZTE K3806 Киевстар
Подключение WebHMI к Level2
Подключение внешних устройств
Подключение к Allen-Bradley MicroLogix 1200
Подключение к Kиевстар на примере модема ZTE MF100
Подключение к People.net
Подключение к S7-1200
Подключение к МТС Коннект
Подключение к ОВЕН160
Подключение к ПЛК с Codesys
Подключение к интернету через 3G модем
Полезные программы
Полезные советы
Пользовательские графики и тренды (Аналитика)
Пользовательские протоколы
Построение графиков в Level2
Пример доступа к данным из C/C++
Пример доступа к данным из Excel
Пример протокола ModBus ASCII
Пример протокола ModBus TCP
Примеры подключения к разным устройствам
Просмотр регистров по запросу
Работа с контроллером холодильного оборудования Danfoss AK-CC 550
Работа с регистрами
Рецепты
Сброс настроек
Синхронизация времени
Системные настройки и сервис
Скрипты
События
Соединения
Сравнение карт SD
Тренды
Удалённый сервисный доступ
Формирование отчетов
Функции управления соединениями
Функция Modbus/TCP сервер
Шаблоны дешбордов
Экраны
Язык
aa - Afar
ab - Abkhazian
ace - Achinese
ady - Adyghe
ady-cyrl - адыгабзэ
aeb - Tunisian Arabic
aeb-arab - تونسي
aeb-latn - Tûnsî
af - Afrikaans
ak - Akan
aln - Gheg Albanian
am - Amharic
an - Aragonese
ang - Old English
anp - Angika
ar - Arabic
arc - Aramaic
arn - Mapuche
arq - Algerian Arabic
ary - Moroccan Arabic
arz - Egyptian Arabic
as - Assamese
ase - American Sign Language
ast - Asturian
av - Avaric
avk - Kotava
awa - Awadhi
ay - Aymara
az - Azerbaijani
azb - تۆرکجه
ba - Bashkir
bar - Bavarian
bbc - Batak Toba
bbc-latn - Batak Toba
bcc - Southern Balochi
bcl - Bikol Central
be - Belarusian
be-tarask - Belarusian (Taraškievica orthography)
bg - Bulgarian
bgn - Western Balochi
bho - Bhojpuri
bi - Bislama
bjn - Banjar
bm - Bambara
bn - Bengali
bo - Tibetan
bpy - Bishnupriya
bqi - Bakhtiari
br - Breton
brh - Brahui
bs - Bosnian
bto - Iriga Bicolano
bug - Buginese
bxr - буряад
ca - Catalan
cbk-zam - Chavacano de Zamboanga
cdo - Min Dong Chinese
ce - Chechen
ceb - Cebuano
ch - Chamorro
cho - Choctaw
chr - Cherokee
chy - Cheyenne
ckb - Central Kurdish
co - Corsican
cps - Capiznon
cr - Cree
crh - Crimean Turkish
crh-cyrl - Crimean Turkish (Cyrillic script)
crh-latn - Crimean Turkish (Latin script)
cs - Czech
csb - Kashubian
cu - Church Slavic
cv - Chuvash
cy - Welsh
da - Danish
de - German
de-at - Austrian German
de-ch - Swiss High German
de-formal - German (formal address)
diq - Zazaki
dsb - Lower Sorbian
dtp - Central Dusun
dty - डोटेली
dv - Divehi
dz - Dzongkha
ee - Ewe
egl - Emilian
el - Greek
eml - Emiliano-Romagnolo
en - English
en-ca - Canadian English
en-gb - British English
eo - Esperanto
es - Spanish
et - Estonian
eu - Basque
ext - Extremaduran
fa - Persian
ff - Fulah
fi - Finnish
fit - Tornedalen Finnish
fj - Fijian
fo - Faroese
fr - French
frc - Cajun French
frp - Arpitan
frr - Northern Frisian
fur - Friulian
fy - Western Frisian
ga - Irish
gag - Gagauz
gan - Gan Chinese
gan-hans - Simplified Gan script
gan-hant - Traditional Gan script
gd - Scottish Gaelic
gl - Galician
glk - Gilaki
gn - Guarani
gom - Goan Konkani
gom-deva - Goan Konkani (Devanagari script)
gom-latn - Goan Konkani (Latin script)
got - Gothic
grc - Ancient Greek
gsw - Swiss German
gu - Gujarati
gv - Manx
ha - Hausa
hak - Hakka Chinese
haw - Hawaiian
he - Hebrew
hi - Hindi
hif - Fiji Hindi
hif-latn - Fiji Hindi (Latin script)
hil - Hiligaynon
ho - Hiri Motu
hr - Croatian
hrx - Hunsrik
hsb - Upper Sorbian
ht - Haitian Creole
hu - Hungarian
hy - Armenian
hz - Herero
ia - Interlingua
id - Indonesian
ie - Interlingue
ig - Igbo
ii - Sichuan Yi
ik - Inupiaq
ike-cans - Eastern Canadian (Aboriginal syllabics)
ike-latn - Eastern Canadian (Latin script)
ilo - Iloko
inh - Ingush
io - Ido
is - Icelandic
it - Italian
iu - Inuktitut
ja - Japanese
jam - Jamaican Creole English
jbo - Lojban
jut - Jutish
jv - Javanese
ka - Georgian
kaa - Kara-Kalpak
kab - Kabyle
kbd - Kabardian
kbd-cyrl - Адыгэбзэ
kg - Kongo
khw - Khowar
ki - Kikuyu
kiu - Kirmanjki
kj - Kuanyama
kk - Kazakh
kk-arab - Kazakh (Arabic script)
kk-cn - Kazakh (China)
kk-cyrl - Kazakh (Cyrillic script)
kk-kz - Kazakh (Kazakhstan)
kk-latn - Kazakh (Latin script)
kk-tr - Kazakh (Turkey)
kl - Kalaallisut
km - Khmer
kn - Kannada
ko - Korean
ko-kp - 한국어 (조선)
koi - Komi-Permyak
kr - Kanuri
krc - Karachay-Balkar
kri - Krio
krj - Kinaray-a
ks - Kashmiri
ks-arab - Kashmiri (Arabic script)
ks-deva - Kashmiri (Devanagari script)
ksh - Colognian
ku - Kurdish
ku-arab - كوردي (عەرەبی)
ku-latn - Kurdish (Latin script)
kv - Komi
kw - Cornish
ky - Kyrgyz
la - Latin
lad - Ladino
lb - Luxembourgish
lbe - лакку
lez - Lezghian
lfn - Lingua Franca Nova
lg - Ganda
li - Limburgish
lij - Ligurian
liv - Livonian
lmo - Lombard
ln - Lingala
lo - Lao
loz - Lozi
lrc - Northern Luri
lt - Lithuanian
ltg - Latgalian
lus - Mizo
luz - Southern Luri
lv - Latvian
lzh - Literary Chinese
lzz - Laz
mai - Maithili
map-bms - Basa Banyumasan
mdf - Moksha
mg - Malagasy
mh - Marshallese
mhr - Eastern Mari
mi - Maori
min - Minangkabau
mk - Macedonian
ml - Malayalam
mn - Mongolian
mo - молдовеняскэ
mr - Marathi
mrj - Western Mari
ms - Malay
mt - Maltese
mus - Creek
mwl - Mirandese
my - Burmese
myv - Erzya
mzn - Mazanderani
na - Nauru
nah - Nāhuatl
nan - Min Nan Chinese
nap - Neapolitan
nb - Norwegian Bokmål
nds - Low German
nds-nl - Low Saxon
ne - Nepali
new - Newari
ng - Ndonga
niu - Niuean
nl - Dutch
nl-informal - Nederlands (informeel)
nn - Norwegian Nynorsk
nov - Novial
nrm - Nouormand
nso - Northern Sotho
nv - Navajo
ny - Nyanja
oc - Occitan
olo - Livvi-Karelian
om - Oromo
or - Oriya
os - Ossetic
pa - Punjabi
pag - Pangasinan
pam - Pampanga
pap - Papiamento
pcd - Picard
pdc - Pennsylvania German
pdt - Plautdietsch
pfl - Palatine German
pi - Pali
pih - Norfuk / Pitkern
pl - Polish
pms - Piedmontese
pnb - Western Punjabi
pnt - Pontic
prg - Prussian
ps - Pashto
pt - Portuguese
pt-br - Brazilian Portuguese
qu - Quechua
qug - Chimborazo Highland Quichua
rgn - Romagnol
rif - Riffian
rm - Romansh
rmy - Romani
rn - Rundi
ro - Romanian
roa-tara - tarandíne
ru - Russian
rue - Rusyn
rup - Aromanian
ruq - Megleno-Romanian
ruq-cyrl - Megleno-Romanian (Cyrillic script)
ruq-latn - Megleno-Romanian (Latin script)
rw - Kinyarwanda
sa - Sanskrit
sah - Sakha
sat - Santali
sc - Sardinian
scn - Sicilian
sco - Scots
sd - Sindhi
sdc - Sassarese Sardinian
sdh - Southern Kurdish
se - Northern Sami
sei - Seri
ses - Koyraboro Senni
sg - Sango
sgs - Samogitian
sh - Serbo-Croatian
shi - Tachelhit
shi-latn - Tašlḥiyt
shi-tfng - ⵜⴰⵛⵍⵃⵉⵜ
si - Sinhala
sk - Slovak
sl - Slovenian
sli - Lower Silesian
sm - Samoan
sma - Southern Sami
sn - Shona
so - Somali
sq - Albanian
sr - Serbian
sr-ec - Serbian (Cyrillic script)
sr-el - Serbian (Latin script)
srn - Sranan Tongo
ss - Swati
st - Southern Sotho
stq - Saterland Frisian
su - Sundanese
sv - Swedish
sw - Swahili
szl - Silesian
ta - Tamil
tcy - Tulu
te - Telugu
tet - Tetum
tg - Tajik
tg-cyrl - Tajik (Cyrillic script)
tg-latn - Tajik (Latin script)
th - Thai
ti - Tigrinya
tk - Turkmen
tl - Tagalog
tly - Talysh
tn - Tswana
to - Tongan
tokipona - Toki Pona
tpi - Tok Pisin
tr - Turkish
tru - Turoyo
ts - Tsonga
tt - Tatar
tt-cyrl - Tatar (Cyrillic script)
tt-latn - Tatar (Latin script)
tum - Tumbuka
tw - Twi
ty - Tahitian
tyv - Tuvinian
tzm - Central Atlas Tamazight
udm - Udmurt
ug - Uyghur
ug-arab - Uyghur (Arabic script)
ug-latn - Uyghur (Latin script)
uk - Ukrainian
ur - Urdu
uz - Uzbek
uz-cyrl - ўзбекча
uz-latn - oʻzbekcha
ve - Venda
vec - Venetian
vep - Veps
vi - Vietnamese
vls - West Flemish
vmf - Main-Franconian
vo - Volapük
vot - Votic
vro - Võro
wa - Walloon
war - Waray
wo - Wolof
wuu - Wu Chinese
xal - Kalmyk
xh - Xhosa
xmf - Mingrelian
yi - Yiddish
yo - Yoruba
yue - Cantonese
za - Zhuang
zea - Zeelandic
zh - Chinese
zh-cn - Chinese (China)
zh-hans - Simplified Chinese
zh-hant - Traditional Chinese
zh-hk - Chinese (Hong Kong)
zh-mo - 中文(澳門)
zh-my - 中文(马来西亚)
zh-sg - Chinese (Singapore)
zh-tw - Chinese (Taiwan)
zu - Zulu
qqq - Message documentation
Format
Экспорт для оффлайнового перевод
Экспорт в родном формате
{{DISPLAYTITLE:Useful programs}}== Useful programs == === Running script upon rising or falling edge of the signal === <languages/> The script should be defined as running upon register change. Inside the script, there have to be checking of the current state and espective actions on rising edge (current state = 1) or falling edge (=0). Example: [[Файл:Rising edge.png | 1000 px | left]] <br clear = all > === Timer with ON-delay (TON) === TON timer counts while input = 1, and after delay time sets output to "1". <syntaxhighlight lang="Lua"> -- globals TIMER_DELAY = 20 -- timer delay 20 sec. tmrStartTime = 0 -- timer start time function main (userId) -- local vars local now = os.time(); -- current time local startBit = (GetReg("tmrStartBit") == 1) -- just bit (D301@WebHMI) -- CHECKING CONDITION ---------- if not startBit then tmrStartTime = now -- here start time is stored WriteReg("TON_out", 0) -- timer output bit register with alias"TON_out" return 0 ; else if (now - tmrStartTime) > TIMER_DELAY then -- end of countdown actions WriteReg("TON_out", 1) end -- if end -- if end -- main </syntaxhighlight> === Alarming (sound, relya, sms, viber, telegram) about connection error === 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. <syntaxhighlight lang= "lua"> cntdownFlag = false -- countdown flag timeStmp = 0 -- метка времени time stamp msgSent = false; -- message sent flag function main (userId) -- reading inputs local scan = GetReg(34); -- scan time local c0 = GetReg(42); -- bad connection number local SCANLIMIT = GetReg(886); -- scan time limit local SCANDELAY = GetReg(887); -- error reaction delay -- if (scan == nil) or (c0 == nil) or (SCANLIMIT == nil) or (SCANDELAY == nil) then ERROR("scan / c0 was read as nil"); return 0; end -- reading time local now = os.time() -- шаблон сообщения local msg1 = "Scan time long "..tostring(scan).." ".."ms, error in conn. "..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 is normal if (cntdownFlag == true) and msgSent then AddInfoMessage("Scan is normal now"); SendViberMessage(398044391, "Scan is normal now"); msgSent = false end cntdownFlag = false end end -- main </syntaxhighlight> 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 column 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: <br> at the beginning of the filtering, first N values of sample 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. <syntaxhighlight lang = "lua"> -- globals mav_len = 20; -- queue length queue_fill = 0; -- queue filling index av_sum = 0; -- accumulator for the moving average function main (userId) local in_value, tmp_var, out_value = GetReg(26), 0, 0; -- reading parameter if (queue_fill < mav_len) then -- queue is not filled av_sum = av_sum + in_value; -- accumulating sum queue_fill = queue_fill +1; -- and index else -- queue is full tmp_var = av_sum / mav_len; -- store one element av_sum = av_sum - tmp_var + in_value; -- subtract and add new end -- if (queue_fill == mav_len ) then out_value = av_sum / mav_len; -- calc. moving average else out_value = av_sum / queue_fill; -- mean average end WriteReg("Tout_mav", out_value); -- outside temperature end </syntaxhighlight> === PID - control === An example of implementing a PID controller in WebHMI: <syntaxhighlight lang = "lua"> G_LIMIT = 100 -- ограничение выхода регулятора -- function main (userId) -- локальные переменные local now = os.time() local nexTime = GetReg("nextPidTime") local CYCLE_TIME = GetReg("pidCycleTime") -- параметры регулятора local Kp = GetReg("Kp") -- Пропорц. коэф. (DS1400@WH Valve control) local Ti = GetReg("Ki") -- Интегральный коэф. (DS1404@WH Valve control) local Td = GetReg("Kd") -- Дифф. коэф. (DS1408@WH Valve control) local Err, dErr, iSum_Limit = 0, 0, 0 local Int_sum = GetReg("pidIntegral") -- интегральный накопитель local intPart = 0 -- инт. часть формулы local G = GetReg("pidOut") -- выход регулятора -- процесс local PV = GetReg(1436) -- Мощность (PWR0@Scylar 8 INT) local Sp = GetReg("targetPowerSp") DEBUG_("seconds left for PID cycle = "..tostring(nexTime - now)) -- условие работы local auto = (GetReg("auto_mode") == 1) -- АВТ. РЕЖИМ ВКЛЮЧЕН (ds1176@WH Global) local heatDemand = (GetReg("heatDemand") == 1) if auto then if heatDemand then -- РЕГУЛЯТОР if (now >= nexTime) then DEBUG_("PID compute cycle") WriteReg("nextPidTime", now + CYCLE_TIME) Err = Sp - PV -- вычисляем ошибку DEBUG_("sp pv Err = "..Sp.." "..PV.." "..Err) dErr = Err - GetReg("pidPrevError") -- вычисляем производную ошибки DEBUG_("dErr = "..dErr) -- проверяем интегральное насыщение iSum_Limit = (G_LIMIT * Ti / Kp) / 5 DEBUG_("iSum_Limit = "..iSum_Limit) -- ПИД - регулятор --проверка на 0 инт. составляющей DEBUG_("prev Int_sum = "..Int_sum) if (intPart <= iSum_Limit) and (intPart >= 0.0) then Int_sum = Int_sum + Err -- накапливаем интеграл ошибки DEBUG_("added error to Int_sum ") elseif Int_sum < 0 then Int_sum = 0 else Int_sum = iSum_Limit -- ограничиваем интегральную составляющую 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) -- проверка выхода за диапазон if G < 0 then G = 0 end if G > G_LIMIT then G = G_LIMIT end WriteReg("pidPrevError", Err) -- запомнить предыдущую ошибку для след. скана WriteReg("dErr", dErr) -- запомнить предыдущую ошибку для след. скана WriteReg("pidIntegral", intPart) -- запомнить интегральную составляющую WriteReg("pidOut", G) WriteReg("posSPinput", G) -- дать уставку на клапан 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 if (tmp == tostring(thisScriptID)) then DEBUG(str) return 0 end end end end -- DEBUG_ ------------------------------ </syntaxhighlight> 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 hour meter === The hour meter is convenient for automatically generating a message about the need for maintenance for the equipment unit, changing the master pump in group to equalize the operating time, and so on. An example of the implementation of the running hour meter on Lua in WebHMI (the program runs in each scan): <syntaxhighlight lang = "lua"> -- globals run_state = false; -- to store current state function main (userId) -- locals local check_mask = tonumber("0000100000000000",2); -- the mask to check running state bit in status register of the freq. drive FC 51 Danfoss local run_status = (bit.band(GetReg(109),check_mask) ~= 0); -- checking result as a bool var local now = os.time(); -- current system time local time_diff = 0; -- time difference between calls -- catching rising edge of the event if (not run_state) and run_status then WriteReg("P43StartTime", now); -- Drive start time №43 -- counting time if run_state then time_diff = (now - GetReg("P43StartTime")); -- get time difference WriteReg("P43RunTime", GetReg("P43RunTime")+time_diff); -- increase running hour meter WriteReg("P43StartTime", now); -- overwrite start time stamp end run_state = run_status end </syntaxhighlight> 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 running hours count. 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 | 800 px|left]] <br clear = all> For simplicity and clarity, it is better to split the scripts into functional modules that can be quickly analyzed and placed in the desired order in the program list. The first script looks at the errors and if they do not exist, the units (air conditioners) alternate in time. <syntaxhighlight lang = "lua"> CIRCULATION_TIME = 30; -- for testing purposed circulation time = 30 sec. function main (userId) --[[ если нет ошибок, то чередуем работу по времени циркуляции если есть ошибка на одном из кондиционеров, то он исключается из чередования если ошибки на обоих, то стоим на месте if there are no errors, then alternate the work on the time of circulation If there is an error on one of the air conditioners, it is excluded from the circulation if there are errors on both, then we stand in place --]] local acError1, acError2 = (GetReg("acError1") == 1), (GetReg("acError2") ==1) ; -- error on a/c #1 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 -- working with 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 </syntaxhighlight> The second script looks at which a/c 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. <syntaxhighlight lang = "lua"> function main (userId) --[[ turn selected a/c on dependting on pointer --]] local pointer = GetReg("activeAC"); -- active a/c (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 </syntaxhighlight> Also here you need a script that will rise error flags upon 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 full valve travel time), initializes the coordinate, and then runs through the specified position. <p> To determine intermediate positions, a calculated value is used, determined from the characteristics of the actuator's 'full stroke 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 which can be done using internal registers. </p> <br> Below one of the examples for 3-point valve control with 2 limit switches is provided The program split into 6 parts: [[Файл:3-point control.png|800px|left]] <br clear = all> ::'''v3 OpenClose Valve Manual''' - The script controls the drive in manual mode. Started by changing the number of the pressed button from the dashboard ::'''v4 Auto Valve Control''' - the main control script in the auto mode. It automatically performs the first initialization, and if the specified coordinate does not match the current one, it turns the drive in the desired direction, upon reaching the position, stops. Also, if the specified position is the same as the limit positions (0,100), the script continues to hold the command until the physical limit switch reached, thereby performing a periodic synchronization of the calculated position with the real position. ::'''v5 LatchStart_Time''' - script 'capturing' the initial position of the movement, is executed by changing the flag inMotionFlag, which is set by the previous programs v3 or v4. ::'''v6 CalcNewPosition''' - is executed 1 time per second. (on system time change), the script works as long as there is an inMotionFlag flag and re-counts the current time passed from the beginning of the movement to the current position (which in turn is used by the v4 program). ::'''v7 Limit sw reaction''' -When the limit switches are reached, it removes commands, and also removes the request from the manual control buttons. ::'''v8 Drop Cur Cmd on Manual mode''' - when switching to manual mode, turns off the current command. At the beginning of the program names, the order of the required execution order (v3..v8) is indicated, since the necessary order of program execution can change undesirably, 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> Text of the programs:</p> '''v3''' <syntaxhighlight lang = lua> ------- MANUAL CONTROL FROM SCREEN BUTTONS OpenClose Valve Manual ------------------- function main (userId) local button_value = GetReg("valveMan_code"); -- key code from buttons open / close local manual_mode = (GetReg("auto_mode") == 0) ; -- auto mode is on (ds1176@WH Global) if manual_mode then if (button_value == 10) then WriteReg("openCmd", 1); -- open command (DS1007@WH Valve control) WriteReg("closeCmd", 0); elseif (button_value == 5) then WriteReg("closeCmd", 1); -- close command (DS1008@WH Valve control) WriteReg("openCmd", 0) else -- invalid value - switch every command off DEBUG("Read button value as "..tostring(button_value)); WriteReg("openCmd", 0) WriteReg("closeCmd", 0) WriteReg("inMotion_flag",0) end --if -- capture start position and start time if (button_value == 10) or (button_value == 5) then WriteReg("startPosition", GetReg("curPosition")); -- starting position (DS1020@WebHMI 3point control) WriteReg("startPosTime", os.time()); -- start time stamp (DS1012@WebHMI 3point control) WriteReg("inMotion_flag",1) end end -- manual_mode end -- main ----------------------- </syntaxhighlight> '''v4''' <syntaxhighlight lang = lua> -- AUTO VALVE CONTROL ---- function main (userId) local now = os.time(); -- current time local timeStmp = GetReg("endPosTime"); -- end position time stamp (D1001@WebHMI Kitothemr) local auto_mode = (GetReg("auto_mode")==1) if auto_mode then -- check if very first run = 0 if (timeStmp == 0) and (not home_mode) then WriteReg("homingBit",1) DEBUG("timeStmp = 0, homing needed! "); end local home_mode = (GetReg("homingBit") == 1); -- homing bit (D1006@WebHMI Kitothemr) local full_close = GetReg("LLsw"); -- full close limit switch (D1010@WebHMI Kitothemr) if home_mode then -- go down till close switch to determine position 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 repeated homing WriteReg("homingBit",0); WriteReg("curPosition", 0); -- current position end return 0 -- exit while not the end 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; -- moving sign local posSV = GetReg("posSetpoint"); -- valve setpoint local posPV = GetReg("curPosition"); -- current position -- determine start position and time local startTime = GetReg("startPosTime"); local startPos = GetReg("startPosition"); local pathdone = 0; -- path done value local OpenSw = GetReg("HLsw"); -- full open switch local CloseSw = GetReg("LLsw"); -- full close switch DEBUG("posPV "..tostring(posPV).." pos SV"..tostring(posSV)); -- already in motion if inMotionFlag then DEBUG("in motion now") -- full close ? if (closingStatus) and (posPV <= posSV) then -- if 0 set then wait close limit switch, auto calibrate if (posSV == 0) and (not CloseSw) then WriteReg("closeCmd", 1); return 0 -- cmd will be off in limit switch reaction script end WriteReg("closeCmd", 0) WriteReg("inMotion_flag",0) end -- upper limit if (openingStatus) and (posPV >= posSV) then -- same as for close if (posSV == 100) and (not OpenSw) then WriteReg("openCmd", 1); return 0; -- cmd will be off in limit switch reaction script end WriteReg("openCmd", 0) WriteReg("inMotion_flag",0) end else -- was stopped if (posSV ~= posPV) then -- target_dir = (posSV - posPV)/math.abs(posSV - posPV); -- get direction 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); -- set the start flag for other scripts end end -- auto mode motion control end -- auto_mode end -- main </syntaxhighlight> '''v5''' <syntaxhighlight lang = lua> -- LatchStart_Time --- function main (userId) DEBUG("Entered latch start time and position"); local flag = (GetReg("inMotion_flag") == 1) ; -- in motion flag @WebHMI 3point control DEBUG("motion flag = "..tostring(flag)); if flag then DEBUG("now flag = 1"); WriteReg("startPosition", GetReg("curPosition")); -- start position stored (DS1020@WebHMI 3point control) WriteReg("startPosTime", os.time()); -- start time remembered (DS1012@WebHMI 3point control) end end </syntaxhighlight> '''v6''' <syntaxhighlight lang=lua> --- CalcNewPosition -- constants FULLPATH = 127 ; -- full path time K = 100 / FULLPATH ; -- time to position k 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 local posPV = GetReg("curPosition"); local startTime = GetReg("startPosTime"); local startPos = GetReg("startPosition"); local pathdone = 0; -- пройденный путь if inMotionFlag then if open_sts then cur_dir = 1; else cur_dir = -1; end pathdone = GetPathDone(startTime, now) varPos = startPos + cur_dir*pathdone 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); -- end -- auto mode motion control end -- main ------------- calculates path done --------------- function GetPathDone(startTime, curTime) local curPos = (curTime - startTime) * K; local remainder = curPos - math.floor(curPos); if (remainder >= 0.5) then -- округл. вниз curPos = math.floor(curPos) + 1; else curPos = math.floor(curPos) ; end return curPos; end </syntaxhighlight> '''v7''' <syntaxhighlight lang=lua> -- 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) end if full_close then WriteReg("curPosition", 0) WriteReg("closeCmd", 0) WriteReg("valveMan_code", 0) end WriteReg("inMotion_flag", 0) WriteReg("endPosTime", now) end -- code of current state 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 -------------------------------- </syntaxhighlight> '''v8''' <syntaxhighlight lang=lua> ------ v8 Drop Cur Cmd on Manual mode ---- function main (userId) local auto_on = (GetReg("auto_mode")==1); -- if not auto_on then DEBUG_("Dropped cmds in manua mode ! "); WriteReg("openCmd", 0) WriteReg("closeCmd", 0) WriteReg("inMotion_flag",0) end prev_mode = auto_on end -- main </syntaxhighlight> === Bit operation === ==== Удобные битовые операции ==== Можно определить несколько удобных битовых операций, помня о том что в lua биты нумеруются с 1. Ссылка взята [http://lua-users.org/wiki/BitwiseOperators отсюда], там же есть и другие полезные функции. <syntaxhighlight lang = lua> 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 </syntaxhighlight> ==== Преобразование битовой таблички в число ==== Данная функция может быть полезна для нестандартных преобразований, когда число хранится в специфичном формате и его необходимо обработать по частям, используя заданную позицию 1-го бита и длину поля значения. <syntaxhighlight lang = "lua"> 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 </syntaxhighlight> ==== Преобразование числа в битовую табличку ==== В lua (в версии, используемой в WebHMI) нет встроенной операции получения строкового представления двоичного числа. Однако эта функция очень полезна в пользовательсиких протоколах и др. задачах, где требуется перераспределить или каким то образом обработать отдельные биты числа. Текст варианта такой функции: <syntaxhighlight lang = "lua"> function getBits(input_num,length) -- работает с заданной длиной local tab = {}; -- пустая табличка для ответа local max_i = length - 1; local remainder = input_num; -- остаток порязрядного взешивания 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 </syntaxhighlight> Далее можно переставить нужные биты местами и т.п. и получить нужное число. Ниже приведен пример перестановки битов 31 и 23 битов (при нумерации битов с 0) в ответе счетчика расходомера ВЛР 2301/2304 производства Асвега-У <syntaxhighlight lang = "lua"> function main (userId) local input_num = GetReg(3); -- прочитать регистр с числом local bitTable = getBits(input_num,32); -- таблица для обработки результата local tmp_bit = ""; -- вспомогательный бит tmp_bit = bitTable[1]; bitTable[1] = bitTable[9]; -- 31 бит это 1 элемент так как таблица развернута слева-направо, номера элементов табл.в lua c 1 bitTable[9] = tmp_bit; -- конкатенация готовой таблицы в строку и преобразование в число из строки двоичного представления WriteReg(1, tonumber(table.concat(bitTable),2)); end </syntaxhighlight> === Обработка чисел с плавающей точкой двойной точности === Некоторые устройства могут хранить данные в формате с повышенной точностью double float. В WebHMI текущей версии поддерживаются только 32 битные регистры, поэтому чтобы обработать 64 битное число необходимо будет использовать скрипт, которые "сцепит" два регистра в исходное число, а затем преобразует и запишет в обратно в float. Регистры должны быть типа double uint. Для "сцепки" чисел можно использовать описанную выше функцию getBits чтобы получить 2 таблицы, а затем дополнить первую таблицу битами из второй используя table.insert. В этом примере в качестве проверочного числа для простоты используется константа. <syntaxhighlight lang = "lua"> -------------------------------------- local test_var = 0xC1D312D000000000; local NaN = tonumber("11111111111111111111111111111111",2); -------------------------------------- function main (userId) -- получить результирующую табличку "0" "1" local result_tab = getBits(test_var,64); WriteReg(11,table.concat(result_tab)); -- отладочная печать в регстр - строку local result_num = 0.0; -- для хранения результата local sign,exp,mantissa = 0,0,0; local fraction_table = {}; -- табл. для дробной части -- определить знак if result_tab[1] == "1" then sign = -1; else sign = 1; end -- экспоненту exp = getNumberFromTab(result_tab,2,11); DEBUG("exp = "..tostring(exp)); -- отл. печать -- мантиссу for i=13,64 do table.insert(fraction_table,result_tab[i]); end -- посчитать мантиссу по-разрядно !!! 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)); -- отл. печать result_num = sign*(2^(exp - 1023))*mantissa; -- Обработка исключений if exp == 0 then -- subnormals result_num = sign*(2^(-1022))*(mantissa-1); end if exp == 0x7ff then -- nan result_num = NaN; end -- Вывод результата в регистр типа float WriteReg(10, result_num); end -- main </syntaxhighlight> === Формирование общего регистра аварий по разным условиям === В WebHMI есть механизм аварий. Т.е. по состоянию бита в регистре можно автоматически генерировать записи в журнале о моменте возникновения, снятия аварии, квитировании ее оператором. Однако в некоторых случаях использовать данный механизм непосредственно, как есть, может не совсем быть удобно. Например - аварийным считается определенный бит или их комбинация, разные коды ошибок (и их надо отличать друг от друга), значение больше или меньше порога и т.д. Если регистр имеет уже настроенные состояния для аварийной ситуации, используемое для отображения, то его нужно просто продублировать, выставив бит в общем аварийном регистре. Также может потребоваться введение задержки на срабатывание аварии, так как значение может иметь "дребезг" который надо фильтровать, прежде чем генерировать аварию в журнале. Кроме этого удобнее, когда все аварии описаны в одном месте, а не разбросаны по сотням регистров. Т.е. рациональнее использовать один или несколько несколько 32-битных регистров для описания всех возможных аварий, и скрипт в котором эти аварии будут записываться в этот регистр по разным условиям. Ниже приведен пример такого скрипта: Допустим, есть некая функциональная единица, например приточная вентиляционная установка, в которой есть некий общий признак аварии, есть несколько регистров с кодами текущих ошибок от частотных преобразователей и ситуация, когда один из регистров (влажность, СО2 и др.) вышел за допустимый диапазон, что является также аварийной ситуацией. [[Файл:Alerts.png | 800px | left]] <br clear = all> Тогда скрипт, который обработает все эти ситуации и сформирует нужные флаги на своих местах может выглядеть так: <syntaxhighlight lang = lua> 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" -- ОБЩИЙ РЕГИСТР АВАРИЙ function main (userId) local alertInput, alertOut = 0,0 -- временные переменные для чтения аварий и формирования результата local digit = 0 -- номер бита в исходном регистре, указывающий на аварию.. --[[ Если назвать единообразно все параметры, из которых надо формировать аварии и сложить их в одну структуру, тогда можно использовать только один цикл for, например так: --]] for i,v in pairs(allAlerts) do alertInput = GetReg(v.srcAlias) -- читаем регистр for j,k in pairs(v.bits) do -- выполнится по числу элементов в под-табличке bits -- проверяем тип операции - точное совпадение или другое условие if (v[3] == "==") then -- проверяем регистр на один из кодов ошибок if (alertInput == k) then alertOut = setbit(alertOut, j) else alertOut = clearbit(alertOut, j) end -- проверяем определенный бит elseif (string.find(v[3], "bit%d+") ~= nil) then -- %d+ паттерн любая последовательность цифр _,_,digit = tonumber(string.find(v[3], "bit(%d+)")) -- выделить из паттерна цифру и преобразовать в число if hasbit(alertInput, bit(digit)) then -- если заданный бит установлен во входном регистре alertOut = setbit(alertOut, j) else alertOut = clearbit(alertOut, j) end else --- другие операции end 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 </syntaxhighlight> Если различных операций над значениями много: не только сравнение, но и например проверка конкретного бита и т.п. то можно детали реализации спрятать в функцию, которая получит на вход табличку с типом операции (установлен ли конкретный бит, код ошибки, параметр в границах и т.п.) и входным значением и поставит или сбросит нужный бит. <syntaxhighlight lang = lua> for i,v in pairs(allAlerts) do alertInput = GetReg(v.srcAlias) -- читаем регистр alertOut = MyGetAlerts(alertInput, alertOut, v) -- вызываем функцию, которая посмотрит что надо сделать по полю v[3], т.е. allAlerts[3] end -- for </syntaxhighlight>
Навигация
Персональные инструменты
русский
127.0.0.1
Обсуждение для этого IP-адреса
Войти
Пространства имён
Служебная страница
Варианты
Просмотры
Ещё
Поиск
Навигация
Заглавная страница
Свежие правки
Случайная статья
Справка
Инструменты
Спецстраницы
Версия для печати