# flb_regEl

ID: pidUnif
NAME: PID unified
uk#NAME: ПІД уніфікований
ru#NAME: ПИД унифицированный
DESCR:
Composite-unified analog and pulse PID. At the heart of the regulator is core a standard analog PID controller from the library FLibComplex1 and the implementation of the PWM for the pulse part.

Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.0
License: GPLv2

uk#DESCR:
Суміщений-уніфікований аналоговий та імпульсний ПІД-регулятор. У основі регулятора лежить ядро стандартного аналогового ПІД-регулятора з бібліотеки "FLibComplex1" та реалізація ШІМ для імпульсної частини.

Автор: Роман Савоченко <roman@oscada.org>
Версія: 1.0.0
Ліцензія: GPLv2

ru#DESCR:
Совмещённый-унифицированный аналоговый и импульсный ПИД-регулятор. В основе регулятора лежит ядро стандартного аналогового ПИД-регулятора из библиотеки "FLibComplex1" и реализация ШИМ для импульсной части.

Автор: Роман Савоченко <roman@oscada.org>
Версия: 1.0.0
Лицензия: GPLv2

FORMULA:
//Call standard analog PID
outA = Special.FLibComplex1.pid(var,sp,max,min,manIn,auto,casc,Kp,Ti,Kd,Td,Tzd,Hup,Hdwn,Zi,followSp,K1,in1,K2,in2,K3,in3,K4,in4,f_frq,int,dif,lag);
if(analog) return outA;

if(f_start || f_stop) { impQup = impQdwn = false; return 0; }

//Call impuls generator
outA = 2*(outA-50);
if(perLag <= 0 && ((outA*KImpRfact) >= (100*TImpMin/TImpPer) || (-outA/KImpRfact) >= (100*TImpMin/TImpPer) || (!auto && abs(outA) > 0.1)))
{
	impLag = max(abs(outA)*TImpPer/100, TImpMin);
	impLag = (outA>0) ? impLag*KImpRfact : impLag/KImpRfact;
	perLag = TImpPer;
	if(outA > 0) impQup = true; else impQdwn = true;  
}
else if(perLag > 0) {
	perLag -= 1e3/f_frq;
	if((TImpPer-perLag) >= impLag) {
		impQup = impQdwn = false;
		if(!auto) perLag = 0;
	}
}

if(!auto) manIn = 50;

if(impQupTm > 1 || (impQupTm && !impQup)) impQupTm--;
if(impQup && !impQupTm) impQupTm = 2*f_frq;
if(impQdwnTm > 1 || (impQdwnTm && !impQdwn)) impQdwnTm--;
if(impQdwn && !impQdwnTm) impQdwnTm = 2*f_frq;

if(impAnImit) {
	if(impQup)	out += 100/(impAnOut*f_frq);
	if(impQdwn)	out -= 100/(impAnOut*f_frq);
	return max(0,min(100,out));
}
return impAnOut;


ID: pidUnifD
NAME: PID dynamic
uk#NAME: ПІД динамічний
ru#NAME: ПИД динамический
DESCR:
Completely identical to the unified PID-regulator, but it is implemented dynamically on JavaLikeCalc. The dynamic implementation allows you to easily adapt the regulator to the desired requirements, simply editing it.

Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.0
License: GPLv2

uk#DESCR:
Цілковито ідентичний уніфікованому ПІД-регулятору, але реалізований динамічно на JavaLikeCalc. Динамічна реалізація дозволяє легко адаптувати регулятор під потрібні вимоги, просто відредагувавши його.

Автор: Роман Савоченко <roman@oscada.org>
Версія: 1.0.0
Ліцензія: GPLv2

ru#DESCR:
Полностью идентичный унифицированному ПИД-регулятору, но реализованный динамически на JavaLikeCalc. Динамическая реализация позволяет легко адаптировать регулятор под нужные требования, просто отредактировав его.

Автор: Роман Савоченко <roman@oscada.org>
Версия: 1.0.0
Лицензия: GPLv2

FORMULA:
if(f_start) {
	auto_ = auto;
	outA = out;
	impQupTm_ = impQdwnTm_ = 0;
	impQup_ = impQdwn_ = 0;
}

//Call standard analog PID
Kzd = min(1e3/(f_frq*Tzd),1);
Kint = min(1e3/(f_frq*Ti),1);
Kdif = min(1e3/(f_frq*Td),1);

//Scale error
if(var.isEVal() || max <= min) return out;
sp = max(min,min(max,sp));

//Prepare values
spv = 100*(sp-min)/(max-min);
val = 100*(var-min)/(max-min);
val += K1*in1 + K2*in2;
val = min(100,max(-100,val));

//Error
err = spv - val;
err = (abs(err)<Zi) ? 0 : ((err>0) ? err-Zi : err+Zi );
err *= Kp;
err = min(100,max(-100,err));

integ += Kint*err;						//Integral
difer -= Kdif*(difer-val);			//Differential wait
dlag  += Kzd*((difer-val)-dlag);	//Differential lag

//Automatic mode enabled
if(auto) {
	outA_ = err + integ + Kd*dlag + K3*in3 + K4*in4;
	if(QO) outA_ = min(outA, outA_);
	if(QZ) outA_ = max(outA, outA_);
	outA = outA_;
}
else {
	casc = false;
	if(followSp) sp = var;
	outA = manIn;
}

//Check output limits
outA = manIn = min(Hup,max(Hdwn,outA));

//Fix integral for manual and limits
integ = outA - err - Kd*dlag - K3*in3 - K4*in4;

if(analog) return outA;

if(f_start || f_stop) { impQup = impQdwn = false; return 0; }

//Call impuls generator
outA_ = (impAnOut < 0) ? 2*(outA-50) : (outA-impAnOut);
if(perLag <= 0 && ((outA_*KImpRfact) >= (100*TImpMin/TImpPer) || (-outA_/KImpRfact) >= (100*TImpMin/TImpPer)))
{
	impLag = max(abs(outA_)*TImpPer/100,TImpMin);
	impLag = (outA_>0) ? impLag*KImpRfact : impLag/KImpRfact;
	perLag = TImpPer;
	if(outA_ > 0) impQup = !QO; else impQdwn = !QZ;
}
else if(perLag > 0) {
	perLag -= 1e3/f_frq;
	if((TImpPer-perLag) >= impLag) {
		impQup = impQdwn = false;
		if(!auto) perLag = 0;
	}
}

if(!auto && auto != auto_)	manIn = (impAnOut < 0) ? 50 : impAnOut;
auto_ = auto;

if(impQup && !impQup_) impQupTm_ = 2*f_frq;
impQupTm_ = max(0,impQupTm_-1);
impQupTm = (impQup || impQupTm_ > 0.1);
impQup_ = impQup;
if(impQdwn && !impQdwn_) impQdwnTm_ = 2*f_frq;
impQdwnTm_ = max(0,impQdwnTm_-1);
impQdwnTm = (impQdwn || impQdwnTm_ > 0.1);
impQdwn_ = impQdwn;

if(impAnImit > 0) {
	if(impQup)	out += 100/(impAnImit*f_frq);
	if(impQdwn)	out -= 100/(impAnImit*f_frq);
	return max(0, min(100,out));
}
return max(0, min(100,impAnOut));


ID: pidImp
NAME: PID pulse
uk#NAME: ПІД імпульсний
ru#NAME: ПИД импульсный
DESCR:
Specialized pulse PID regulator is implemented on a special algorithm with compensation of double integration.

Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.0
License: GPLv2

uk#DESCR:
Спеціалізований імпульсний ПІД-регулятор реалізований за особливим алгоритмом із компенсацією подвійного інтегрування.

Автор: Роман Савоченко <roman@oscada.org>
Версія: 1.0.0
Ліцензія: GPLv2

ru#DESCR:
Специализированный импульсный ПИД регулятор реализованный по особому алгоритму с компенсацией двойного интегрирования.

Автор: Роман Савоченко <roman@oscada.org>
Версия: 1.0.0
Лицензия: GPLv2

FORMULA:
//Call standard analog PID
Kzd = min(1e3/(f_frq*Tzd), 1);
Kint = min(1e3/(f_frq*Ti), 1);
Kdif = min(1e3/(f_frq*Td), 1);

//Scale error
if(max <= min) return 0;

//Prepare values
spv = 100*(sp-min)/(max-min);
val = 100*(var-min)/(max-min);
val += K1*in1 + K2*in2;
val = min(100,max(-100,val));

//Error
err = spv - val;
err = (abs(err)<Zi) ? 0 : ((err>0) ? err-Zi : err+Zi );
err *= Kp;
err = min(100,max(-100,err));
errP = err-err1;
errD = err1-err2;
err2 = err1; err1 = err;

integ += Kint*err;							//Integral
difer -= Kdif*(difer-errD);				//Differecial wait
dlag  += Kzd*((errP-difer)-dlag);	//Differecial lag

//Automatic mode enabled
if(auto) { outPID = errP + Ki*err + integ + Kd*dlag; outA = 50 + outPID + K3*in3 + K4*in4; }
else {
	outPID = 0;
	casc = false;
	if(followSp) sp = var;
	outA = manIn;
}

//Check output limits
outA = manIn = min(Hup, max(Hdwn,outA));

//Fix integral for manual and limits
//integ = outA - errP - Kd*dlag - K3*in3 - K4*in4;

if(f_start || f_stop) { impQup = impQdwn = false; return 0; }

//Call impuls generator
outA = 2*(outA-50);
if(perLag <= 0 && ((outA*KImpRfact) >= (100*TImpMin/TImpPer) || (-outA/KImpRfact) >= (100*TImpMin/TImpPer) || (!auto && abs(outA) > 0.1)))
{
	impLag = max(abs(outA)*TImpPer/100,TImpMin);
	impLag = (outA > 0) ? impLag*KImpRfact : impLag/KImpRfact;
	perLag = TImpPer;
	if(outA > 0) impQup = true; else impQdwn = true;  
}
else if(perLag > 0) {
	perLag -= 1e3/f_frq;
	if((TImpPer-perLag) >= impLag) {
		impQup = impQdwn = false;
		if(!auto) perLag = 0;
  }
}

if(!auto) manIn = 50;

if(impQupTm > 1 || (impQupTm && !impQup)) impQupTm--;
if(impQup && !impQupTm) impQupTm = 2*f_frq;
if(impQdwnTm > 1 || (impQdwnTm && !impQdwn)) impQdwnTm--;
if(impQdwn && !impQdwnTm) impQdwnTm = 2*f_frq;

if(impQup || impQdwn) integ = 0;

if(impAnImit) {
	if(impQup) out += 100/(impAnOut*f_frq);
	if(impQdwn) out -= 100/(impAnOut*f_frq);
	return max(0, min(100,out));
}
return impAnOut;


# flb_doc

ID: getVal
NAME: Getting value from the value archive
uk#NAME: Отримання значення з архіву значень
ru#NAME: Получение значения из архива значений
DESCR:
Query the value for the specified time (time:uTime) from the assigned archiver archtor (FSArch.1m) and issuing results with the specified number of digits prec after the dot.

The function works with the DAQ-parameter attribute objects to which the archive is attached and the address addr is written to it in full path, like "/DAQ/LogicLev/gen/prm_F3/a_var". For the empty archtor, the request will be made generally to everyone with the archive buffer periodicity!

In the process of obtaining a value, the time of this value may be different from the requested one, if the archive is not currently present and returns the closest value, preventing why you may by setting strong. When you need to get the closest valid value in a controlled way, for counters to example, you have the "tryTo" time argument to which to look for the closest valid value. The time in tryTo may be both lesser and bigger than the request time.

Initially, the result is presented as a string of real value with a certain number of digits prec after the dot, and with the value "Empty" for not valid values, allowing it to be inserted into the document without further processing. However, at requiring of additional processing and specific formatting, the result can be obtained with an unformatted real value, for prec less than 0.

Author: Roman Savochenko <roman@oscada.org>
Version: 1.1.2
License: GPLv2

uk#DESCR:
Запит значення за визначений час (time:uTime) із пов'язаного архіватору archtor (FSArch.1m) та формування результату із визначеною кількістю знаків prec після коми.

Функція працює із об'єктами атрибутів DAQ-параметрів до яких підключено архів, та адреса addr до нього записується повним шляхом на кшталт "/DAQ/LogicLev/gen/prm_F3/a_var". Для порожнього archtor запит буде здійснюватися загально до усіх із періодичністю буферу архіву.

У процесі отримання значення, час цього значення може відрізнятися від запитаного, якщо архів на цей час відсутній та повертається найближче, запобігти чому можна встановивши strong. Коли-ж таки потрібно контрольовано отримати найближче достовірне значення, наприклад для лічильників, то передбачено аргумент часу tryTo до якого шукати найближче достовірне значення. Час у tryTo може бути як більше, так і менше за час запиту.

Первинно, результат надається у вигляді рядка реального значення із визначеною кількістю знаків prec після коми, та із значенням "Порожньо" для недостовірних значень, що дозволяє вставляти його до документу без додаткової обробки. Однак, за потреби додаткової обробки та специфічного форматування, результат можна отримати неформатованим реальним значенням, для prec менше 0.

Автор: Роман Савоченко <roman@oscada.org>
Версія: 1.1.2
Ліцензія: GPLv2

ru#DESCR:
Запрос значения за определённое время (time:uTime) из связанного архиватора archtor (FSArch.1m) и формирование результата с определённым количеством знаков prec после запятой.

Функция работает с объектами атрибутов DAQ-параметров к которым подключён архив, и адрес addr к нему записывается полным путём вроде "/DAQ/LogicLev/gen/prm_F3/a_var". Для пустого archtor запрос будет осуществляться в целом ко всем с периодичностью буфера архива.

В процессе получения значения, время этого значения может отличаться от запрошенного, если архив на это время отсутствует и возвращается ближайшее, предотвратить что можно установив strong. Когда таки необходимо контролировано получить ближайшее достоверное значение, например для счётчиков, то предусмотрен аргумент времени tryTo до которого искать ближайшее достоверное значение. Время в tryTo может быть как больше, так и меньше времени запроса.

Первично, результат предоставляется в виде строки вещественного значения с определённым количеством знаков prec после запятой, и с значением "Пусто" для недостоверных значений, что позволяет вставлять его в документ без дополнительной обработки. Однако, при необходимости дополнительной обработки и специфического форматирования, результат можно получить неформатированным вещественным значением, для prec менше 0.

Автор: Роман Савоченко <roman@oscada.org>
Версия: 1.1.2
Лицензия: GPLv2

FORMULA:
srcTime = time*1e6 + uTime;
aO = SYS.nodeAt(addr).arch();
if(aO) {
	if(!tryTo || tryTo == srcTime)	val = aO.getVal(srcTime, false, archtor);
	else {
		if((aPer=aO.period(archtor)) == 0)	val = EVAL;
		else if(srcTime < tryTo)
			for(srcTime1 = srcTime; srcTime < tryTo && (val=aO.getVal(srcTime1,false,archtor)).isEVal(); )
			{ srcTime += aPer; srcTime1 = srcTime; }
		else
			for(srcTime1 = srcTime; srcTime > tryTo && (val=aO.getVal(srcTime1,false,archtor)).isEVal(); )
			{ srcTime -= aPer; srcTime1 = srcTime; }
	}
	if(strong && (srcTime*1e-6) != time)	val = EVAL;
} else val = EVAL;
if(prec < 0)	return val;
if(val.isEVal())	return tr("Empty");
if(typeof(val) == "real")	return val.toFixed(prec);
return val;


ID: sumVal
NAME: Sum values of the value archive
uk#NAME: Підсумок значень архіву значень
ru#NAME: Сумма значений архива значений
DESCR:
Calculate the summary value over a specified period of the time [bTime...eTime], for the archiver archtor (FSArch.1m) data and issuing results with the specified number of digits prec after the dot.

The function works with the DAQ-parameter attribute objects to which the archive is attached and the address addr is written to it in full path, like "/DAQ/LogicLev/gen/prm_F3/a_var". For the empty archtor, the request will be made generally to everyone with the archive buffer periodicity!

In the process of calculation, the data in the archive is checked for validity.

Initially, the result is presented as a string of real value with a certain number of digits prec after the dot, and with the value "Empty" for not valid values, allowing it to be inserted into the document without further processing. However, at requiring of additional processing and specific formatting, the result can be obtained with an unformatted real value, for prec less than 0.

Author: Roman Savochenko <roman@oscada.org>
Version: 1.1.1
License: GPLv2

uk#DESCR:
Обчислення суми значень за вказаний період часу [bTime...eTime], для даних архіватору archtor (FSArch.1m) та формування результату із визначеною кількістю знаків prec після коми.

Функція працює із об'єктами атрибутів DAQ-параметрів до яких підключено архів, та адреса addr до нього записується повним шляхом на кшталт "/DAQ/LogicLev/gen/prm_F3/a_var". Для порожнього archtor запит буде здійснюватися загально до усіх із періодичністю буферу архіву.

У процесі підрахунку здійснюється перевірка даних у архіві на предмет достовірності.

Первинно, результат надається у вигляді рядка реального значення із визначеною кількістю знаків prec після коми, та із значенням "Порожньо" для недостовірних значень, що дозволяє вставляти його до документу без додаткової обробки. Однак, за потреби додаткової обробки та специфічного форматування, результат можна отримати неформатованим реальним значенням, для prec менше 0.

Автор: Роман Савоченко <roman@oscada.org>
Версія: 1.1.1
Ліцензія: GPLv2

ru#DESCR:
Вычисление суммы значений за указанный период времени [bTime...eTime], для данных архиватора archtor (FSArch.1m) и формирование результата с определённым количеством знаков prec после запятой.

Функция работает с объектами атрибутов DAQ-параметров к которым подключён архив, и адрес addr к нему записывается полным путём вроде "/DAQ/LogicLev/gen/prm_F3/a_var". Для пустого archtor запрос будет осуществляться в целом ко всем с периодичностью буфера архива.

В процессе расчёта осуществляется проверка данных в архиве на предмет достоверности.

Первично, результат предоставляется в виде строки вещественного значения с определённым количеством знаков prec после запятой, и с значением "Пусто" для недостоверных значений, что позволяет вставлять его в документ без дополнительной обработки. Однако, при необходимости дополнительной обработки и специфического форматирования, результат можно получить неформатированным вещественным значением, для prec менше 0.

Автор: Роман Савоченко <roman@oscada.org>
Версия: 1.1.1
Лицензия: GPLv2

FORMULA:
val = 0;
aO = SYS.nodeAt(addr).arch();
if(aO) {
	bTime = bTime*1e6; eTime = eTime*1e6;
	buf_beg = max(aO.begin(archtor), bTime); buf_end = min(aO.end(archtor), eTime);
	while(true) {
		r_val = aO.getVal(buf_beg, true, archtor);
		if(buf_beg > buf_end)	break;
		if(!r_val.isEVal())	val += r_val;
		if(buf_beg == 0 || buf_beg >= buf_end) break;
		buf_beg++;
	}
}
if(prec < 0)	return val;
if(val.isEVal())	return tr("Empty");
if(typeof(val) == "real")	return val.toFixed(prec);
return val;


ID: averVal
NAME: Average value of the value archive
uk#NAME: Середнє значення архіву значень
ru#NAME: Среднее значение архива значений
DESCR:
Calculate the average value over a specified period of the time [bTime...eTime], for the archiver archtor (FSArch.1m) data and issuing results with the specified number of digits prec after the dot.

The function works with the DAQ-parameter attribute objects to which the archive is attached and the address addr is written to it in full path, like "/DAQ/LogicLev/gen/prm_F3/a_var". For the empty archtor, the request will be made generally to everyone with the archive buffer periodicity!

In the process of calculation, the data in the archive is checked for validity and the value of the percentage of reliable data actPerc that participated in the result is formed. In general, you should use intermediate archivers — medium quality archivers, so that there is not too much data in a given interval to reduce performance, and that it does not have one value at all.

Initially, the result is presented as a string of real value with a certain number of digits prec after the dot, and with the value "Empty" for not valid values, allowing it to be inserted into the document without further processing. However, at requiring of additional processing and specific formatting, the result can be obtained with an unformatted real value, for prec less than 0.

Author: Roman Savochenko <roman@oscada.org>
Version: 1.1.1
License: GPLv2

uk#DESCR:
Обчислення середнього значення за вказаний період часу [bTime...eTime], для даних архіватору archtor (FSArch.1m) та формування результату із визначеною кількістю знаків prec після коми.

Функція працює із об'єктами атрибутів DAQ-параметрів до яких підключено архів, та адреса addr до нього записується повним шляхом на кшталт "/DAQ/LogicLev/gen/prm_F3/a_var". Для порожнього archtor запит буде здійснюватися загально до усіх із періодичністю буферу архіву.

У процесі підрахунку здійснюється перевірка даних у архіві на предмет достовірності та формується значення відсотку достовірних даних actPerc, що прийняли участь у отримані результату. Загалом, варто використовувати проміжні архіватори — архіватори середньої якості, щоб даних у ньому, за визначений інтервал, не було забагато для зниження продуктивності, та щоб це не було взагалі одне значення.

Первинно, результат надається у вигляді рядка реального значення із визначеною кількістю знаків prec після коми, та із значенням "Порожньо" для недостовірних значень, що дозволяє вставляти його до документу без додаткової обробки. Однак, за потреби додаткової обробки та специфічного форматування, результат можна отримати неформатованим реальним значенням, для prec менше 0.

Автор: Роман Савоченко <roman@oscada.org>
Версія: 1.1.1
Ліцензія: GPLv2

ru#DESCR:
Вычисление среднего значения за указанный период времени [bTime...eTime], для данных архиватора archtor (FSArch.1m) и формирование результата с определённым количеством знаков prec после запятой.

Функция работает с объектами атрибутов DAQ-параметров к которым подключён архив, и адрес addr к нему записывается полным путём вроде "/DAQ/LogicLev/gen/prm_F3/a_var". Для пустого archtor запрос будет осуществляться в целом ко всем с периодичностью буфера архива.

В процессе расчёта осуществляется проверка данных в архиве на предмет достоверности и формируется значение процента достоверных данных actPerc, которые приняли участие в получении результата. В целом, стоит использовать промежуточные архиваторы — архиваторы среднего качества, чтобы данных в нём, за указанный интервал, не было очень много для понижения производительности, и чтобы это не было вообще одне значение.

Первично, результат предоставляется в виде строки вещественного значения с определённым количеством знаков prec после запятой, и с значением "Пусто" для недостоверных значений, что позволяет вставлять его в документ без дополнительной обработки. Однако, при необходимости дополнительной обработки и специфического форматирования, результат можно получить неформатированным вещественным значением, для prec менше 0.

Автор: Роман Савоченко <roman@oscada.org>
Версия: 1.1.1
Лицензия: GPLv2

FORMULA:
val = EVAL;
actPerc = 0;
aO = SYS.nodeAt(addr).arch();
if(aO) {
	bTime = bTime*1e6; eTime = eTime*1e6;
	vCnt = (eTime-bTime)/aO.period(archtor)+1;
	buf_beg = max(aO.begin(archtor), bTime); buf_end = min(aO.end(archtor), eTime);
	for(vCntA = 0; true; ) {
  		buf_beg1 = buf_beg;
		r_val = aO.getVal(buf_beg, true, archtor);
		if(buf_beg > buf_end)	break;
		if(!r_val.isEVal())	vCntA++;
		if(val.isEVal())	val = r_val;
		else if(!r_val.isEVal())
			val = (val*(buf_beg-bTime) + r_val*(buf_beg-buf_beg1))/(buf_beg - bTime + buf_beg - buf_beg1);
		if(buf_beg == 0 || buf_beg >= buf_end) break;
		buf_beg++;
	}
	//SYS.messInfo("averVal", "vCntA="+vCntA+"("+vCnt+"); archtor="+archtor); 
	actPerc = vCntA ? min(100,100*vCntA/vCnt) : 0;
}
if(prec < 0)	return val;
if(val.isEVal())	return tr("Empty");
if(typeof(val) == "real")	return val.toFixed(prec);
return val;


ID: diagram
NAME: Diagram
uk#NAME: Діаграма
ru#NAME: Диаграмма
DESCR:
Building trends diagram to SVG, which may be builtin next to the XHTML-document, for specified period of the time [(end-size)...end] and from the sources srcs.

The diagram forming code is based on code of the primitive "Diagram" of the visualisers, and which was quite simply migrated from C++ to the JavaLikeCalc embedded language. Currently, only trends have been moved-implemented!

The appearance of the diagram is determined by its dimensions hsz*vsz and the source SVG-template tmpl, a number attributes of the root tag of which has special meaning and corresponding to the primitive "Diagram":
- Scale: Markers: font (sclMarkFont), by default "Arial 10".
- Scale: horizontal (sclHor), by default "Markers (2)" and generally provided: "No draw (0)", "Grid (1)", "Markers (2)", "Grid and markers (3)".
- Scale: vertical (sclVer), by default "Grid and markers (3)" and generally provided: "No draw (0)", "Grid (1)", "Markers (2)", "Grid and markers (3)", "Grid (log) (5)", "Markers (log) (6)", "Grid and markers (log) (7)".
- Scale: color (sclColor), by default "gray".
- Scale: Markers: color (sclMarkColor), by default "gray".

Currently, only value archives can be used as a data source, either directly to them or through the DAQ-parameter attribute with the archive. The number of data sources is not directly limited and is written in separate lines of the argument srcs in the format "{addr}:{min}:{max}:{color}[:{scale}[:{width}]]", where:
- addr — address to the value archive or attributes of the DAQ-parameter, for example: "/Archive/va_CPULoad_load", "/DAQ/LogicLev/gen/F3/var";
- min, max — minimum and maximum of the scale, in the scale absence (min >= max), it will be calculated from the real data;
- color — colour of the trend curve;
- scale — separate definition of the trend scale, by default "Global (0)" and generally provided: "Global (0)", "Markers (2)", "Grid and markers (3)", "Markers (log) (6)", "Grid and markers (log) (7)".
- width — width of the trend curve, by default 2.

Author: Roman Savochenko <roman@oscada.org>
Sponsored by: Magomed, SAVTECH
Version: 1.4.0
License: GPLv2

uk#DESCR:
Побудова діаграми трендів у SVG, яка може надалі вбудовуватися у XHTML-документу, для даних за вказаний період часу [(end-size)...end] та із джерел srcs.

Код формування діаграми засновано на коді примітиву "Діаграма" візуалізаторів, та який було доволі просто перенесено із мови C++ на вбудовану мову JavaLikeCalc. Наразі перенесено-реалізовано лише тренди!

Вигляд діаграми визначається її розмірами hsz*vsz та початковим SVG-шаблоном tmpl, низка атрибутів кореневого тегу якого мають особливе значення та які відповідають примітиву "Діаграма":
- Шкала: Маркери: шрифт (sclMarkFont), по замовченню "Arial 10".
- Шкала: горизонтальна (sclHor), по замовченню "Маркери (2)" та загалом передбачено: "Не зображувати (0)", "Ґратка (1)", "Маркери (2)", "Ґратка та маркери (3)".
- Шкала: вертикальна (sclVer), по замовченню "Ґратка та маркери (3)" та загалом передбачено: "Не зображувати (0)", "Ґратка (1)", "Маркери (2)", "Ґратка та маркери (3)", "Ґратка (лог) (5)", "Маркери (лог) (6)", "Ґратка та маркери (лог) (7)".
- Шкала: колір (sclColor), по замовченню "gray".
- Шкала: Маркери: колір (sclMarkColor), по замовченню "gray".

У якості джерела даних наразі можуть використовуватися лише архіви значень, прямою адресою до них або через атрибут DAQ-параметру з архівом. Кількість джерел даних прямо не обмежене та вони записуються окремими рядками аргументу srcs із форматом "{addr}:{min}:{max}:{color}[:{scale}[:{width}]]", де:
- addr — адреса до архіву значень або атрибуту DAQ-параметру, наприклад: "/Archive/va_CPULoad_load", "/DAQ/LogicLev/gen/F3/var";
- min, max — мінімум та максимум шкали, за відсутності шкали (min >= max) вона буде вираховуватися із реальних даних;
- color — колір кривої тренду;
- scale — окреме визначення шкали тренду, по замовченню "Глобально (0)" та загалом передбачено: "Глобально (0)", "Маркери (2)", "Ґратка та маркери (3)", "Маркери (лог) (6)", "Ґратка та маркери (лог) (7)".
- width — ширина кривої тренду, по замовченню 2.

Автор: Роман Савоченко <roman@oscada.org>
Спонсоровано: Магомед, SAVTECH
Версія: 1.4.0
Ліцензія: GPLv2

ru#DESCR:
Построение диаграммы трендов в SVG, которая может далее встраиваться в XHTML-документ, для данных за указанный период времени [(end-size)...end] и из источников srcs.

Код формирования диаграммы основан на коде примитива "Диаграмма" визуализаторов, и который был довольно просто перенесен с языка C++ на встроенный язык JavaLikeCalc. Сейчас перенесено-реализовано только тренды!

Вид диаграммы определяется её размерами hsz*vsz и исходным SVG-шаблоном tmpl, ряд атрибутов кореневого тега которого имеют особое значение и которые соответствуют примитиву "Диаграмма":
- Шкала: Маркеры: шрифт (sclMarkFont), по умолчанию "Arial 10".
- Шкала: горизонтальная (sclHor), по умолчанию "Маркеры (2)" и в целом предусмотрено: "Не рисовать (0)", "Решетка (1)", "Маркеры (2)", "Решетка и маркеры (3)".
- Шкала: вертикальная (sclVer), по умолчанию "Решетка и маркеры (3)" и в целом предусмотрено: "Не рисовать (0)", "Решетка (1)", "Маркеры (2)", "Решетка и маркеры (3)", "Решетка (лог) (5)", "Маркеры (лог) (6)", "Решетка и маркеры (лог) (7)".
- Шкала: цвет (sclColor), по умолчанию "gray".
- Шкала: Маркеры: цвет (sclMarkColor), по умолчанию "gray".

В качестве источника данных на данный момент могут использоваться только архивы значений, прямым адресом к ним или через атрибут DAQ-параметра с архивом. Количество источников данных прямо не ограничено и они записываются отдельными строками аргумента srcs с форматом "{addr}:{min}:{max}:{color}[:{scale}[:{width}]]", где:
- addr — адрес к архиву значений или атрибуту DAQ-параметра, например: "/Archive/va_CPULoad_load", "/DAQ/LogicLev/gen/F3/var";
- min, max — минимум и максимум шкалы, при отсутствии шкалы (min >= max) она будет вычисляться из реальных данных;
- color — цвет кривой тренда;
- scale — отдельное определение шкалы тренда, по умолчанию "Глобально (0)" и в целом предусмотрено: "Глобально (0)", "Маркеры (2)", "Решетка и маркеры (3)", "Маркеры (лог) (6)", "Решетка и маркеры (лог) (7)".
- width — ширина кривой тренда, по умолчанию 2.

Автор: Роман Савоченко <roman@oscada.org>
Спонсировано: Магомед, SAVTECH
Версия: 1.4.0
Лицензия: GPLv2

FORMULA:
function strChars(inS) {
	for(inSz = 0, off = 0; off < inS.length; inSz++)
		inS.charAt(off, "UTF-8");
	return inSz;
}

im = SYS.XMLNode();
im.load(tmpl, false, 3);
if(!(drawArea=im.getElementBy("drawArea")) || !(scVer=im.getElementBy("scVer")) || !(scHor=im.getElementBy("scHor")) ||
		(styles=im.getElementsBy("style")[0]).isEVal())
	return "";

//Constant
FD_GRD = 0x1;
FD_MARKS = 0x2;
FD_LOG = 0x4;

if(!end)	end = SYS.time();
begin = end - size;
reqPer = size/vsz;

//Getting the trends data
trends = new Array();
for(off = 0; (tEl=srcs.parseLine(0,off)).length; ) {
	elO = new Object();
	elO.addr = tEl.parse(0, ":");
	elO.min = tEl.parse(1, ":"); elO.max = tEl.parse(2, ":");
	elO.color = tEl.parse(3, ":");
	elO.scale = tEl.parse(4, ":").toInt();
	elO.width = max(1, tEl.parse(5,":").toInt());
	elO.beg = begin*1e6; elO.per = reqPer*1e6;
	if((elO.srcO=SYS.nodeAt(elO.addr))) {
		if(typeof(elO.srcO) == "TCntrNode:TVal")	elO.srcO = elO.srcO.arch();
		if(typeof(elO.srcO) != "TCntrNode:TVArchive")	continue;
		elO.val = elO.srcO.getVals(elO.beg, end*1e6, elO.per, arch);
	}
	else if(!(dDt=SYS.XMLNode()).load(elO.addr).toInt() && dDt.name() == "d") {
		elO.val = new Array();
		isSec = dDt.attr("s").toInt();
		isAprox = dDt.attr("aprox").toInt();
		elO.per = dDt.attr("per").toInt() * (isSec?1e6:1);
		for(prevPos = 0, prevVal = EVAL, maxPos = floor(1e6*(end-begin)/elO.per), off1 = 0; true; ) {
			if((svl=dDt.text().parse(0,",",off1)).length)
				curPos = svl.parse(0,"=").toInt(), curVal = ((curVal=svl.parse(1,"="))==EVAL)?EVAL:curVal.toReal();
			else curPos = maxPos+1;
			for(stPos = prevPos; prevPos < curPos; prevPos++)
				if(isAprox && !prevVal.isEVal() && !curVal.isEVal())
					elO.val.push(prevVal+(curVal-prevVal)*(prevPos-stPos)/(curPos-stPos));
				else elO.val.push(prevVal);
			if(prevPos > maxPos)	break;
			prevVal = curVal;
		}
	}
	else continue;

	trends.push(elO);
}

im.setAttr("width", hsz).setAttr("height", vsz).setAttr("viewBox", "0 0 "+hsz+" "+vsz);

//Getting the labels font
if(!(tVl=im.attr("sclMarkFont")).length)	tVl = "Arial "+max(5,floor(hsz/50));
off = 0;
styles.setText("\ntext { font-family: "+tVl.parse(0," ",off)+"; font-size: "+(mrkFontSize=tVl.parse(0," ",off).toInt())+"px; "
		"font-weight: "+(tVl.parse(0," ",off).toInt()?"bold":"normal")+"; font-style: "+(tVl.parse(0," ",off).toInt()?"italic":"normal")+"; }" + styles.text());
sclHor = (tVl=im.attr("sclHor")).length ? tVl.toInt() : FD_MARKS;
sclVer = (tVl=im.attr("sclVer")).length ? tVl.toInt() : FD_MARKS|FD_GRD;
if(!(sclColor=im.attr("sclColor")).length)			sclColor = "gray";
if(!(sclMarkColor=im.attr("sclMarkColor")).length)	sclMarkColor = "gray";

mrkFontWRange = 0.7;
mrkWidth = 5*mrkFontSize*mrkFontWRange;
mrkHeight = mrkFontSize;

tArMrg = 2;
tArX	= tArMrg;
tArW	= hsz - tArX - tArMrg;
tArY	= tArMrg;
tArH	= vsz - tArY - tArMrg;
if(sclHor&FD_MARKS) {
	if(tArH < 100) sclHor = sclHor & ~(FD_MARKS);
	else tArH -= mrkHeight*2;
}

//Calculating the vertical scale
vsPerc = true;			//Vertical scale percent mode
isLog = sclVer&FD_LOG;		//Logarithmic scale
vsMax = -3e300, vsMin = 3e300;	//Trend's vertical scale border

// Get main scale for non individual parameters
prmInGrp = 0, prmGrpLast = -1;
for(iP = 0, mainPerc = false; iP < trends.length; iP++) {
	cP = trends[iP];
	if(!cP.val.length || !cP.color.length) continue;
	cP.adjU = -3e300, cP.adjL = 3e300;
	if(cP.max > cP.min)	cP.adjL = cP.min, cP.adjU = cP.max;
	else {
		for(iDt = 0; iDt < cP.val.length; iDt++)
			if(!cP.val[iDt].isEVal()) { cP.adjL = min(cP.adjL, cP.val[iDt]); cP.adjU = max(cP.adjU, cP.val[iDt]); }
		// Value range expanding
		//  No value
		if(cP.adjU == -3e300)	cP.adjU = 1, cP.adjL = 0;
		// Range and absolute value are very small
		else if((cP.adjU-cP.adjL) < 1e-30 && abs(cP.adjU) < 1e-30)	cP.adjU += 0.5, cP.adjL -= 0.5;
		// Only range is very small
		else if((cP.adjU-cP.adjL) < 1e-30)
			cP.adjL -= (iP/trends.length)*abs(cP.adjU), cP.adjU += (1-iP/trends.length)*abs(cP.adjU);
		// Range smaller relatively to absolute value
		else if((cP.adjU-cP.adjL) / abs(cP.adjL+(cP.adjU-cP.adjL)/2) < 0.001) {
			wnt_dp = 0.001*abs(cP.adjL+(cP.adjU-cP.adjL)/2)-(cP.adjU-cP.adjL);
			cP.adjL -= (iP/trends.length)*wnt_dp, cP.adjU += (1-iP/trends.length)*wnt_dp;
		}
	}

	cP.wScale = cP.scale&(sclVer|FD_LOG);
	if(cP.wScale&(FD_MARKS|FD_GRD))	continue;

	//  Check for value border allow
	if(!mainPerc && (vsMin > vsMax || max(abs((vsMax-cP.adjL)/(vsMax-vsMin)-1),abs((cP.adjU-vsMin)/(vsMax-vsMin)-1)) < 0.2))
	{ vsMin = min(vsMin, cP.adjL); vsMax = max(vsMax, cP.adjU); }
	else { vsMax = -3e300; vsMin = 3e300; mainPerc = true; }

	prmInGrp++; prmGrpLast = iP;
}

// Checking for individual parameters and for the possibility to merge their to groups or create a new one for no group
prmIndiv = 0, prmIndivSc = -1, prmsInd = new Array();
for(iP = 0; iP < trends.length; iP++) {
	cP = trends[iP];
	cP.isIndiv = false;
	if(!cP.val.length || !cP.color.length || !(cP.wScale&(FD_MARKS|FD_GRD))) continue;
	// Checking for include to the present one or create a new group and exclude from individual ones
	if((!prmInGrp || (vsMin < vsMax && max(abs((vsMax-cP.adjL)/(vsMax-vsMin)-1),abs((cP.adjU-vsMin)/(vsMax-vsMin)-1)) < 0.2)) &&
			(cP.scale&FD_LOG) == (sclVer&FD_LOG))
	{
		vsMin = min(vsMin, cP.adjL); vsMax = max(vsMax, cP.adjU);
		prmInGrp++; prmGrpLast = iP;
		continue;
	}
	cP.isIndiv = true;
	prmIndiv++;
	if(prmIndivSc < 0 && (cP.scale&FD_GRD)) prmIndivSc = iP;
	else prmsInd.push(iP);
	if(cP.scale&FD_LOG) {
		cP.adjU = lg(max(1e-100,cP.adjU)); cP.adjL = lg(max(1e-100,cP.adjL));
		if((cP.adjU-cP.adjL) / abs(cP.adjL+(cP.adjU-cP.adjL)/2) < 0.0001) {
			wnt_dp = 0.0001*abs(cP.adjL+(cP.adjU-cP.adjL)/2)-(cP.adjU-cP.adjL);
			cP.adjL -= wnt_dp/2; cP.adjU += wnt_dp/2;
		}
	}
}
if(prmInGrp) prmsInd.push(-1);
if(prmIndivSc >= 0) prmsInd.push(prmIndivSc);

// Final main scale adapting
if(vsMin > vsMax) { vsPerc = true; vsMax = 100; vsMin = isLog ? pow(10,min(0,2-(tArH/150))) : 0; }
else vsPerc = false;
if(isLog) {
	vsMax = lg(max(1e-100,vsMax)); vsMin = lg(max(1e-100,vsMin));
	if((vsMax-vsMin) / abs(vsMin+(vsMax-vsMin)/2) < 0.0001) {
		wnt_dp = 0.0001*abs(vsMin+(vsMax-vsMin)/2)-(vsMax-vsMin);
		vsMin -= wnt_dp/2; vsMax += wnt_dp/2;
	}
}

//Draw main and individual vertical scales
vmax_ln = tArH / ((sclVer&FD_MARKS)?(2*mrkHeight):15);
for(iP = 0; vmax_ln >= 2 && iP < prmsInd.length; iP++) {	//prmsInd[i]=-1 - for main scale
	vDiv = 1;
	if(prmsInd[iP] < 0) {	//Main scale process
		// Draw the environment
		vsPercT = vsPerc;
		isLogT = isLog;
		sclVerT = sclVer;
		clrGridT = sclColor;
		clrMrk = sclMarkColor;
		if(prmInGrp == 1 && prmGrpLast >= 0)	//Set color for single parameter in main group
			clrGridT = clrMrk = trends[prmGrpLast].color;
		// Rounding
		v_len = vsMax - vsMin;
		while(v_len > vmax_ln)						{ vDiv *= 10; v_len /= 10; }
		while(v_len && v_len < vmax_ln/10)	{ vDiv /= 10; v_len *= 10; }
		vsMin = floor(vsMin/vDiv)*vDiv; vsMax = ceil(vsMax/vDiv)*vDiv;
		while(!isLogT && ((vsMax-vsMin)/vDiv) < vmax_ln/2) vDiv /= 2;
		vsMinT = vsMin; vsMaxT = vsMax;
	}
	else {	//Individual scale process
		cP = trends[prmsInd[iP]];
		// Draw environment
		vsPercT = false;
		isLogT = cP.scale&FD_LOG;
		sclVerT = cP.wScale;
		clrGridT = clrMrk = cP.color;
		// Rounding
		v_len = cP.adjU - cP.adjL;
		while(v_len > vmax_ln)						{ vDiv *= 10; v_len /= 10; }
		while(v_len && v_len < vmax_ln/10)	{ vDiv /= 10; v_len *= 10; }
		cP.adjL = floor(cP.adjL/vDiv)*vDiv; cP.adjU = ceil(cP.adjU/vDiv)*vDiv;
		while(!isLogT && ((cP.adjU-cP.adjL)/vDiv) < vmax_ln/2) vDiv /= 2;
		vsMinT = cP.adjL; vsMaxT = cP.adjU;
	}
	if(iP < (prmsInd.length-1))	sclVerT = sclVerT&(~FD_GRD);	//Hide grid for not last scale

	// Draw vertical grid and markers
	markWdth = 0;
	if(sclVerT&(FD_MARKS|FD_GRD)) {
		scVer.childAdd("rect").setAttr("fill",clrGridT).setAttr("x",tArX-1).setAttr("y",tArY).setAttr("width",1).setAttr("height",tArH);
		for(iV = floor((vsMinT/vDiv)+0.5)*vDiv; (vsMaxT-iV)/vDiv > -0.1; iV += vDiv) {
			//  Draw grid
			v_pos = tArY + tArH - tArH*(iV-vsMinT)/(vsMaxT-vsMinT);
			if(sclVerT&FD_GRD)	scVer.childAdd("rect").setAttr("fill",sclColor).setAttr("x",tArX).setAttr("y",floor(v_pos+0.5)).setAttr("width",tArW).setAttr("height",1);
			else	scVer.childAdd("rect").setAttr("fill",clrGridT).setAttr("x",tArX-3).setAttr("y",floor(v_pos+0.5)).setAttr("width",6).setAttr("height",1);
			//  Draw markers
			if(sclVerT&FD_MARKS) {
				isPerc = vsPercT && ((vsMaxT-iV-vDiv)/vDiv <= -0.1);
				isMax = (v_pos-1-mrkHeight) < tArY;
				labVal = (isLogT?pow(10,iV):iV).toPrecision(5) + (isPerc?" %":"");
				scVer.childAdd("text").setAttr("text-anchor","start").setAttr("stroke","none")
						.setAttr("fill", clrMrk)
						.setAttr("x",tArX+2).setAttr("y",floor(v_pos-1+(isMax?mrkHeight:0)+0.5)).setText(labVal);
				markWdth = max(markWdth, labVal.length*mrkFontSize*mrkFontWRange);
			}
		}
	}
	if(iP < (prmsInd.length-1)) { tArX += markWdth?(markWdth+5):0; tArW -= markWdth?(markWdth+5):0; }
}

//Calc and draw the horizontal scale
hDiv = 1;			//Horisontal scale divisor
hmax_ln = tArW / ((sclHor&FD_MARKS)?mrkWidth:15);
if(hmax_ln >= 2) {
	hvLev = 0;
	hLen = end - begin;

	if(hLen/2635200 >= 5)		{ hvLev = 7; hDiv = 2635200; }	//Month a unstrict interval !!!! to implement !!!!
	else if(hLen/86400 >= 5)	{ hvLev = 6; hDiv =   86400; }	//More days and no time in the scale
	else if(hLen/86400 >= 2)	{ hvLev = 5; hDiv =   86400; }	//Days
	else if(hLen/3600 >= 2)	{ hvLev = 4; hDiv =    3600; }	//Hours
	else if(hLen/60 >= 2)		{ hvLev = 3; hDiv =      60; }	//Minutes
	else if(hLen/1 >= 2)		{ hvLev = 2; hDiv =       1; }	//Seconds
	else if(hLen/0.001 >= 2)	{ hvLev = 1; hDiv =   0.001; }	//Milliseconds

	// Draw the horisontal grid and markers
	if(sclHor&(FD_MARKS|FD_GRD)) {
		tm_t = 0;
		lab_tm = lab_dt = "";

		//  Draw generic grid line
		scHor.childAdd("rect").setAttr("fill",sclColor).setAttr("x",floor(tArX+0.5)).setAttr("y",floor(tArY+tArH+0.5)).setAttr("width",floor(tArW+0.5)).setAttr("height",1);

		//  Draw full trend's data and time to the trend end position
		begMarkBrd = -5;
		endMarkBrd = tArX + tArW;
		if((sclHor&FD_MARKS) && mrkHeight) {
			lab_dt = SYS.strftime(end, "%d-%m-%Y");
			if((end%60) == 0 && (end-floor(end)) == 0)
				lab_tm = SYS.strftime(end, "%H:%M");
			else if((end-floor(end)) == 0)
				lab_tm = SYS.strftime(end, "%H:%M:%S");
			else lab_tm = SYS.strftime(end, "%H:%M:%S")+"."+(end-floor(end));

			markY = tArY + tArH + mrkHeight;
			if(hvLev < 6) {
				scHor.childAdd("text").setAttr("text-anchor","end").setAttr("stroke","none")
					.setAttr("fill",sclMarkColor)
					.setAttr("x",tArX+tArW).setAttr("y",markY).setText(lab_tm);
				endMarkBrd = min(endMarkBrd, tArX + tArW - mrkFontSize*lab_tm.length*mrkFontWRange);
				markY += mrkHeight;
			}
			scHor.childAdd("text").setAttr("text-anchor","end").setAttr("stroke","none")
				.setAttr("fill",sclMarkColor)
				.setAttr("x",tArX+tArW).setAttr("y",markY).setText(lab_dt);
			endMarkBrd = min(endMarkBrd, tArX + tArW - mrkFontSize*lab_dt.length*mrkFontWRange);
		}


		hDiv_ = hDiv;
		while(hLen/hDiv_ > hmax_ln)	hDiv_ *= 10;
		while(hLen/hDiv_ < hmax_ln/5 && (1e6*hDiv_/5)%(1e6*hDiv) == 0) hDiv_ /= 5;
		while(hLen/hDiv_ < hmax_ln/2 && (hvLev < 6 || (1e6*hDiv_/2)%(1e6*hDiv) == 0)) hDiv_ /= 2;
		hDiv = hDiv_;

		UTChourDt = SYS.strptimegm(SYS.strftime(end)) - end;

		//  Draw grid and/or markers
		first_m = true;
		ttm1 = new Object();
		for(iH = begin; true; ) {
			//   Draw grid
			h_pos = tArX + tArW*(iH-begin)/(end-begin);
			if(sclHor&FD_GRD)
				scHor.childAdd("rect").setAttr("fill",sclColor).setAttr("x",floor(h_pos+0.5)).setAttr("y",floor(tArY+0.5)).setAttr("width",1).setAttr("height",tArH);
			else scHor.childAdd("rect").setAttr("fill",sclColor).setAttr("x",floor(h_pos+0.5)).setAttr("y",floor(tArY+tArH-3+0.5)).setAttr("width",1).setAttr("height",6);

			//   Draw markers
			if((sclHor&FD_MARKS) && (!(1e6*(iH+UTChourDt)%(1e6*hDiv)) || hvLev >= 7) && iH != end) {
				if(first_m)	SYS.localtime(begin-(end-begin), ttm1.sec, ttm1.min, ttm1.hour, ttm1.mday, ttm1.mon);
				ttm = new Object();
				SYS.localtime(iH, ttm.sec, ttm.min, ttm.hour, ttm.mday, ttm.mon);

				chLev = 0;
				if(ttm.year-ttm1.year)			chLev = 6;
				else if(ttm.mon-ttm1.mon) 	chLev = 5;
				else if(ttm.mday-ttm1.mday)	chLev = 4;
				else if(ttm.hour-ttm1.hour)	chLev = 3;
				else if(ttm.min-ttm1.min)		chLev = 2;
				else if(ttm.sec-ttm1.sec)		chLev = 1;

				//Checking for the data presence
				lab_dt = lab_tm = "";
				//Date
				if(hvLev == 7)
					lab_dt = SYS.strftime(iH, (chLev>=6)?"%B %Y":"%B");
				else if(/*hvLev == 5 ||*/ chLev >= 4)
					lab_dt = SYS.strftime(iH, (chLev>=5)?"%d-%m-%Y":"%d");
				//Hours and minuts
				if((hvLev == 4 || hvLev == 3 || ttm.hour || ttm.min) && !ttm.sec)
					lab_tm = SYS.strftime(iH, "%H:%M");
				//Seconds
				else if((hvLev == 2 || ttm.sec) && !((iH-floor(iH))*1e6))
					lab_tm = (chLev >= 2) ? SYS.strftime(iH, "%H:%M:%S") : (iH%60).toPrecision(6)+tr("s");
				//Milliseconds
				else if(hvLev <= 1 || (iH-floor(iH))*1e6)
					lab_tm = (chLev >= 2) ? SYS.strftime(iH, "%H:%M:%S")+(iH-floor(iH)).toPrecision(6).slice(1) :
								(chLev >= 1) ? ((iH%60)+(iH-floor(iH))).toPrecision(6)+tr("s") :
													(1e3*iH).toPrecision(6)+tr("ms");

				//SYS.messInfo("DG", "lab_tm="+lab_tm+"; hvLev="+hvLev+"; chLev="+chLev+"; iH="+iH);

				endPosTm = endPosDt = begMarkBrd, markY = tArY + tArH + mrkHeight;
				if(hvLev < 6) {
					if(lab_tm.length) {
						wdth = mrkFontSize*strChars(lab_tm)*mrkFontWRange;
						tpos = max(h_pos-wdth/2, 0);
						if(first_m || (tpos+wdth) < endMarkBrd && tpos > begMarkBrd) {
							if(first_m)	tpos = max(begMarkBrd, tpos);
							scHor.childAdd("text").setAttr("text-anchor",(first_m?"start":"middle")).setAttr("stroke","none")
								.setAttr("fill",sclMarkColor)
								.setAttr("x",floor(tpos+(first_m?0:wdth/2)+0.5)).setAttr("y",floor(markY+0.5)).setText(lab_tm);
							endPosTm = tpos + wdth;
						}
					}
					markY += mrkHeight;
				}
				if(lab_dt.length) {
					wdth = mrkFontSize*strChars(lab_dt)*mrkFontWRange;
					tpos = max(h_pos-wdth/2, 0);
					if(first_m || (tpos+wdth) < endMarkBrd && tpos > begMarkBrd) {
						if(first_m)	tpos = max(begMarkBrd, tpos);
						scHor.childAdd("text").setAttr("text-anchor",(first_m?"start":"middle")).setAttr("stroke","none")
							.setAttr("fill",sclMarkColor)
							.setAttr("x",floor(tpos+(first_m?0:wdth/2)+0.5)).setAttr("y",floor(markY+0.5)).setText(lab_dt);
						endPosDt = tpos + wdth;
					}
				}
				begMarkBrd = max(begMarkBrd, max(endPosTm,endPosDt));
				ttm1 = ttm;
				first_m = false;
			}

			//   Next
			if(iH >= end) break;
			if(hvLev >= 7) {	//Per month
				curYear = curMonth = 0;
				SYS.localtime(iH, 0, 0, 0, 0, curMonth, curYear);
				iH = SYS.mktime(0, 0, 0, 1, curMonth+1, curYear);
			} else iH = floor((iH+UTChourDt)/hDiv)*hDiv + hDiv - UTChourDt;
			iH = min(end, iH);
		}
	}
}

//Drawing the trends
for(iTr = 0; iTr < trends.length; iTr++) {
	cP = trends[iTr];

	vsPercT = cP.isIndiv ? false : vsPerc;
	isLogT = cP.isIndiv ? (cP.wScale&FD_LOG) : isLog;
	vsMaxT = cP.isIndiv ? cP.adjU : vsMax;
	vsMinT = cP.isIndiv ? cP.adjL : vsMin;

	// Prepare border for percent trend
	bordL = cP.min;
	bordU = cP.max;
	if(vsPercT && bordL >= bordU) { bordL = cP.adjL; bordU = cP.adjU; }
		/*bordU = -3e300, bordL = 3e300;
		for(iDt = 0; iDt < cP.val.length; iDt++) {
			c_val = cP.val[iDt];
			if(!c_val.isEVal()) { bordL = min(bordL, c_val); bordU = max(bordU, c_val); }
		}
		if((vMarg=(bordU-bordL)/10) == 0) vMarg = 0.5;
		bordL -= vMarg;
		bordU += vMarg;
	}*/

	trPath = 0; prev_vl = EVAL; prev_pos = 0;
	for(iDt = 0; iDt < cP.val.length; iDt++) {
		c_val = cP.val[iDt];
		if(vsPercT && !c_val.isEVal()) c_val = 100*(c_val-bordL)/(bordU-bordL);
		c_tm = 1e-6*(cP.beg + iDt*cP.per);
		if(c_tm < begin)	continue;
		c_pos = floor(tArX + tArW*(c_tm-begin)/(end-begin));
		if(!c_val.isEVal()) {
			c_vpos = max(tArY,min(tArY+tArH,floor(tArY + tArH - tArH*((isLogT?lg(max(1e-100,c_val)):c_val)-vsMinT)/(vsMaxT-vsMinT))));
			if(!trPath) trPath = drawArea.childAdd("path").setAttr("stroke", cP.color).setAttr("stroke-width",cP.width);
			if(prev_vl.isEVal()) trPath.setAttr("d", trPath.attr("d")+"M"+c_pos+","+c_vpos);
			else trPath.setAttr("d",trPath.attr("d")+"L"+c_pos+","+c_vpos);
		}
		else if(!prev_vl.isEVal()) {
			c_vpos = max(tArY,min(tArY+tArH,floor(tArY + tArH - tArH*((isLogT?lg(max(1e-100,prev_vl)):prev_vl)-vsMinT)/(vsMaxT-vsMinT))));
			trPath.setAttr("d", trPath.attr("d")+"L"+prev_pos+".1,"+c_vpos+".1");
		}
		prev_vl = c_val; prev_pos = c_pos;
	}
}

return im.save();


# tmplib_LowDevLib

ID: 1602A
NAME: GPIO|I2C: 1602A(HD44780)
DESCR:
LCD Module 1602A, STN, BLUB, 16 Character x 2 Line,  5 x 8 Dots, by the direct (Raspberry PI BCM2835 GPIO) or I2C (PCF8574) wiring.
Conditions: Default planing policy but realtime one preferred.
Author: Roman Savochenko <roman@oscada.org>
Version: 1.1.1

PROGRAM:
JavaLikeCalc.JavaScript
using Special.FLibSYS;

//Set link to fast external functions
if(f_start)	f_err = "0", transport_ = ln1_ = ln2_ = "";

t_err = "0";

//Check for the transport change and the link update
if(transport != transport_)	{
	transport_ = transport;
	if(!(tr=SYS.Transport.Serial.nodeAt("out_"+transport)))
		function put = transport+".fnc_put";
}

//Write a byte function declaration
function byte(tr, vl, md, dl) {
	if(md == EVAL_BOOL) md = false;
	if(dl == EVAL_BOOL) dl = 50e-6;
	if(tr) {
		//RS(md) | RW(false) | E(false) | BackLight(true) | D4 | D5 | D6 | D7
		com = (md?0x01:0) | 0x8 | (vl&0xF0);
		tr.messIO(SYS.strFromCharCode(addr,com), 0, 0);			tmSleep(dl);
		tr.messIO(SYS.strFromCharCode(addr,com|0x04), 0, 0);	tmSleep(dl);	//E toggle
		tr.messIO(SYS.strFromCharCode(addr,com), 0, 0);			tmSleep(dl);
		//RS(md) | RW(false) | E(false) | BackLight(true) | D0 | D1 | D2 | D3
		com = (md?0x01:0) | 0x8 | ((vl<<4)&0xF0);
		tr.messIO(SYS.strFromCharCode(addr,com), 0, 0);			tmSleep(dl);
		tr.messIO(SYS.strFromCharCode(addr,com|0x04), 0, 0);	tmSleep(dl);	//E toggle
		tr.messIO(SYS.strFromCharCode(addr,com), 0, 0);			tmSleep(dl);
	}
	else if(put) {
		put(RS, md);
		put(D4, vl&0x10); put(D5, vl&0x20); put(D6, vl&0x40); put(D7, vl&0x80);
		tmSleep(dl); put(E, true); tmSleep(dl); put(E, false); tmSleep(dl);
		put(D4, vl&0x01); put(D5, vl&0x02); put(D6, vl&0x04); put(D7, vl&0x08);
		tmSleep(dl); put(E, true); tmSleep(dl); put(E, false); tmSleep(dl);
	}
}

//Main processing
if(!tr && !put)
	t_err = "1:"+tr("Error for '%1' as an output transport of I2C or a link to external functions of GPIO.").replace("%1",transport);
else if(ln1 != ln1_ || ln2 != ln2_) {
	ln1_ = ln1; ln2_ = ln2;	

	//Init
	byte(tr, 0x33); byte(tr, 0x32); byte(tr, 0x28); byte(tr, 0x0C); byte(tr, 0x06); byte(tr, 0x01);
	//Line 1
	if(ln1.length) {
		tmSleep(2e-3);
		byte(tr, 0x80);
		for(iC = 0; iC < min(16,ln1.length); iC++)
			byte(tr, ln1.charCodeAt(iC), true);
	}
	//Line 2
	if(ln2.length) {
		tmSleep(2e-3);
		byte(tr, 0xC0);
		for(iC = 0; iC < min(16,ln2.length); iC++)
			byte(tr, ln2.charCodeAt(iC), true);
	}
}

f_err = t_err;


ID: 1W_DS9097
NAME: One Wire by DS9097
DESCR:
One Wire sensors bus implementing by 1Wire-adapter DS9097. Supported direct and parasite powering for the temperature sensors.
Supported 1Wire-devices: DS1820, DS1820/DS18S20/DS1920 (not tested), DS1822 (not tested), DS2413, DS2408, DS2450, DS2438.
Author: Roman Savochenko <roman@oscada.org>
Version: 1.2.1

PROGRAM:
JavaLikeCalc.JavaScript
//Functions
function reset(tr) {
	tr.addr(tr.addr().parse(0,":")+":9600:8N1"); tr.start(true);
	rez = tr.messIO(SYS.strFromCharCode(0xF0));
	tr.addr(tr.addr().parse(0,":")+":115200:8N1"); tr.timings("100:2-0"); tr.start(true);
	tr.messIO("");
	return rez.length && rez.charCodeAt(0) != 0xF0;
}

function io(tr, mess, bits) {
	if(bits == EVAL) bits = mess.length * 8;
	req = "";
	for(iB = 0; iB < ceil(bits/8); iB++) {
		vB = mess.charCodeAt(iB);
		bB = ((iB+1)*8 > bits) ? bits-floor(bits/8)*8 : 8;
		for(iBi = 0; iBi < bB; iBi++) req += SYS.strFromCharCode((vB&(1<<iBi)) ? 0xFF : 0x00);
	}
	//SYS.messInfo("OneWire","bits="+bits+"; req="+SYS.strDecode(req,"Bin"," "));
	for(resp = tr.messIO(req); resp.length < req.length && (tresp=tr.messIO("")).length; resp += tresp) ;
	//SYS.messInfo("OneWire","bits="+bits+"; resp="+SYS.strDecode(resp,"Bin"," "));
	rez = "";
	for(iR = 0; iR < resp.length; ) {
		vB = 0;
		for(ib = 0; ib < 8 && iR < resp.length; iR++, ib++)
			if(resp.charCodeAt(iR)&1) vB = vB | (1<<ib);
		rez += SYS.strFromCharCode(vB);
	}		
	return rez;
}

function scan(tr, sn, lstDiscr) {
	if(!reset(tr)) return "";
	io(tr, SYS.strFromCharCode(0xF0));
	for(nByte = 0, nBit = 1, tBit = 0, sDir = false, mskByte = 1, lstZero = 0; nByte < 8; ) {
		tBit = io(tr, SYS.strFromCharCode(0xFF), 1).charCodeAt(0) << 1;
		tBit = tBit | io(tr, SYS.strFromCharCode(0xFF), 1).charCodeAt(0);
		//SYS.messInfo("OneWire","nByte="+nByte+"; nBit="+nBit+"; tBit="+tBit);
		if(tBit == 3) break;
		if(tBit > 0) sDir = !(tBit&0x01);
		else {
			//SYS.messInfo("OneWire","nByte="+nByte+"; nBit="+nBit+"; lstDiscr="+lstDiscr);
			if(nBit < lstDiscr) sDir = sn[nByte]&mskByte;
			else sDir = (nBit == lstDiscr);
			if(!sDir) lstZero = nBit;
			//if(lstZero < 9) lstDiscr = lstZero;
			//SYS.messInfo("OneWire","nByte="+nByte+"; nBit="+nBit+"; sDir="+sDir+"; mskByte="+mskByte);
		}
		if(sDir) sn[nByte] = sn[nByte] | mskByte;
		else sn[nByte] = sn[nByte] & (~mskByte);
		tr.messIO(SYS.strFromCharCode(sDir?0xFF:0x00));
		nBit++;
     mskByte = (mskByte<<1)&0xFF;
		if(!mskByte) { nByte++; mskByte = 1; }
	}

	rez = "";
	if(nByte == 8) {
		for(iB = 0; iB < nByte; iB++) rez += SYS.strFromCharCode(sn[iB]);
		if(!Special.FLibSYS.CRC(rez,0x8C,8,0))	lstDiscr = lstZero;
		else { rez = ""; lstDiscr = 0; }
	}

	return rez;
}

//Set transport and init
if(f_start) {
	tmResc_ = tmResc;
	devLs = new Object();
	transport_ = transport;
	tr = SYS.Transport.Serial.nodeAt("out_"+transport);
}
if(f_stop) {
	for(var devID in devLs) {
		if((dO=devLs[devID]).isEVal()) continue;
		aLs = dO.dP.nodeList("a_");
		for(iA = 0; iA < aLs.length; iA++)
			if(!dO.dP[aLs[iA]].isCfg()) dO.dP[aLs[iA]].set(EVAL, 0, 0, true);
	}
	devLs = tr = false;
	return;
}

t_err = "0";

//Check for the transport change and connect
if(!tr || transport != transport_)	{
	tr = SYS.Transport.Serial.nodeAt("out_"+transport);
	transport_ = transport;
}
if(!tr)	t_err = "1:"+tr("Output transport '%1' error.").replace("%1",transport);
else {
	//Generic information update.
	if(f_start || (tmResc && (tmResc_=tmResc_-1/f_frq) <= 0)) {
		tmResc_ = tmResc;
		tmSc = SYS.time();
		// Check for power
		if(!reset(tr) ||
		 	io(tr,(req=SYS.strFromCharCode(0xCC))) != req ||
			io(tr,(req=SYS.strFromCharCode(0xB4))) != req ||
			!(resp=io(tr,SYS.strFromCharCode(1),1)).length) power = EVAL;
		else power = resp.charCodeAt(0);
		// Scan for allowed devices on the bus.
		sn = new Array(0, 0, 0, 0, 0, 0, 0, 0);
		for(devID = "start", lstDiscr = -1; devID.length && lstDiscr; ) {
			if(lstDiscr < 0) lstDiscr = 0;
			if((devID=scan(tr,sn,lstDiscr)).length)	{
				//SYS.messInfo("OneWire","devID="+SYS.strDecode(devID,"Bin"," "));
				if((dO=devLs[devID]).isEVal()) {
					devLs[devID] = dO = new Object();
					devFamily = devID.charCodeAt(0)&0x7F; devNm = tr("1W device"); dO.isT = false;
					if(devFamily == 0x10)			{ devNm = "DS1820/DS18S20/DS1920"; dO.isT = true; }
					else if(devFamily == 0x20)	devNm = "DS2450";		//4AI
					else if(devFamily == 0x22)	{ devNm = "DS1822"; dO.isT = true; }
					else if(devFamily == 0x26)	devNm = "DS2438";		//SmartBat
					else if(devFamily == 0x28)	{ devNm = "DS18B20"; dO.isT = true; }
					else if(devFamily == 0x29)	devNm = "DS2408";		//8DIO
					else if(devFamily == 0x3A)	devNm = "DS2413";		//2DIO

					// Create the device's parameter object
					decId = SYS.strDecode(devID,"Bin","");
					SYS.cntrReq(SYS.XMLNode("add").setAttr("path",this.nodePath()+"/%2fbr%2fprm_").setAttr("id",decId).setText(devNm+": "+decId));
					SYS.cntrReq(SYS.XMLNode("set").setAttr("path",this.nodePath()+"/prm_"+decId+"/%2fprm%2fst%2fen").setText("1"));
					// Create the device's proper attributes
					dO.dP = dP = this["prm_"+decId];
					if(dO.isT) {	//Temperature
						dP.attrAdd("val", tr("Temperature, °С"), "real|ro");
						dP.attrAdd("m", tr("User cell [0...65535]"), "integer");
						dP.attrAdd("res", tr("Resolution")+" [9...12]", "integer");
					}
					else if(devFamily == 0x20)		//DS2450
						for(iN = 0; iN < 4; iN++) {
							dP.attrAdd("ai"+iN, tr("AI, V")+iN, "real|ro");
							dP.attrAdd("res"+iN, tr("Resolution")+iN+" [1...16]", "integer");
							dP.attrAdd("md"+iN, tr("Mode")+iN, "integer|sel", "0;1;2\n2.55V;5.10V;DO");
							dP.attrAdd("do"+iN, tr("DO")+iN, "boolean");
						}
					else if(devFamily == 0x26) {	//DS2438
						dP.attrAdd("t", tr("Temperature, °С"), "real|ro");
						dP.attrAdd("v", tr("Voltage, V"), "real|ro");
						dP.attrAdd("i", tr("Current, A"), "real|ro");
						dP.attrAdd("ICA", tr("Current Accumulator"), "integer");
						dP.attrAdd("thr", tr("Accumulation threshold"), "integer|sel", "0;1;2;3\nNone;±2LSB;±4LSB;±8LSB");
						dP.attrAdd("ETM", tr("Elapsed time meter"), "integer");
						dP.attrAdd("TM_DISC", tr("Disconnect time"), "integer");
						dP.attrAdd("TM_END", tr("End of charge time"), "integer");
						dP.attrAdd("OFF", tr("Current Offset"), "integer");
						dP.attrAdd("IAD", tr("Enable Current A/D"), "boolean");
						dP.attrAdd("CA", tr("Enable Current Accumulator"), "boolean");
						dP.attrAdd("EE", tr("Enable Current Accumulator Shadow"), "boolean");
						dP.attrAdd("AD", tr("Voltage A/D (0-VAD,1-VDD)"), "boolean");
					}
					else if(devFamily == 0x29)		//DS2408
						for(iD = 0; iD < 8; iD++) {
							dP.attrAdd("di"+iD, tr("DI")+iD, "boolean|ro");
							dP.attrAdd("do"+iD, tr("DO")+iD, "boolean");
						}
					else if(devFamily == 0x3A)		//DS2413
						for(iD = 0; iD < 2; iD++) {
							dP.attrAdd("di"+iD, tr("DI")+iD, "boolean|ro");
							dP.attrAdd("do"+iD, tr("DO")+iD, "boolean");
						}
				}
				if(dO.tmSc == tmSc)	break;	//Somthing wrong into the scan but repeat, interruption
				dO.tmSc = tmSc;
				dO.try = tryEVAL;
			}
		}
		//Check for removed devices
		for(devID in devLs) {
			if((dO=devLs[devID]).isEVal()) continue;
			if(dO.tmSc != tmSc)	{
				devLs[devID] = EVAL;
				aLs = dO.dP.nodeList("a_");
				for(iA = 0; iA < aLs.length; iA++)
					if(!dO.dP[aLs[iA]].isCfg())	dO.dP[aLs[iA]].set(EVAL, 0, 0, true);
			}
		}
	}
	else {
		//Process devices for reading current value and set modifable
		for(devID in devLs) {
			if((dO=devLs[devID]).isEVal()) continue;
			devFamily = devID.charCodeAt(0)&0x7F;
			if(dO.isT) {	//Temperature
				dP = dO.dP;
				//SYS.messInfo("OneWire","devID="+SYS.strDecode(devID,"Bin"," "));
				//Check for a modifications
				isMdf = false;
				m_ = dP.m.get(), res_ = max(9,min(12,dP.res.get()));
				if(!dO.m.isEVal() && !dO.res.isEVal() && (m_ != dO.m || res_ != dO.res)) {
					//Set scratchpad
					if(!reset(tr)) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x4E,m_&0xFF,(m_>>8)&0xFF,((res_-9)<<5)|0x1F))) != req) continue;
					//Copy scratchpad part to the EEPROM
					if(!reset(tr)) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x48))) != req) continue;
					isMdf = true;
				}

				//Read current
				// Call the transmission
				if(!reset(tr)) continue;
				if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
				if(io(tr,(req=SYS.strFromCharCode(0x44))) != req) continue;
				if(!power) SYS.sleep(0.094*pow(2,(dO.res.isEVal()?12:dO.res)-9));
				// Get scratchpad
				if(!reset(tr)) continue;
				if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
				io(tr, SYS.strFromCharCode(0xBE));
				resp = io(tr, SYS.strFromCharCode(0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF));
				//SYS.messInfo("OneWire","resp="+SYS.strDecode(resp,"Bin"," "));
				if(!Special.FLibSYS.CRC(resp,0x8C,8,0)) {
					val = resp.charCodeAt(1)*256 + resp.charCodeAt(0);
					if(val > 32767) val -= 65536;
					dP.val.set(val/16, 0, 0, true);
					if(dO.m.isEVal() || dO.res.isEVal() || isMdf) {
						dO.m = resp.charCodeAt(3)*256 + resp.charCodeAt(2); dP.m.set(dO.m, 0, 0, true);
						dO.res = ((resp.charCodeAt(4)>>5)&0x3)+9; dP.res.set(dO.res, 0, 0, true);
					}
					dO.try = 0;
				}
				else if((dO.try=dO.try+1) > tryEVAL) {
					dP.val.set(EVAL, 0, 0, true); dP.m.set(EVAL, 0, 0, true); dP.res.set(EVAL, 0, 0, true);
					dO.m = dO.res = EVAL;
				}
			}
			else if(devFamily == 0x20) {	//DS2450
				dP = dO.dP;
				//Check for a modifications
				for(iN = 0; iN < 4; iN++) {
					if(dO["st1"+iN].isEVal())	continue;
					if((tRes=max(0,min(16,dP["res"+iN].get()))) == 16)	tRes = 0;
					tSt = (dO["st1"+iN]&(~0xCF)) + tRes + ((dP["md"+iN].get()==2)?0x80:0) + (dP["do"+iN].get()?0x40:0);
					if(tSt != dO["st1"+iN]) {	//Same write and check the result
						if(!reset(tr)) continue;
						if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
						if(io(tr,(req=SYS.strFromCharCode(0x55,8+iN*2,0,tSt))) != req) continue;
						resp = io(tr, SYS.strFromCharCode(0xFF,0xFF,0xFF));
						if(((~((resp.charCodeAt(1)<<8) + resp.charCodeAt(0)))&0xFFFF) == Special.FLibSYS.CRC(req,0xA001,16,0) && resp.charCodeAt(2) == tSt)
							dO["st1"+iN] = tSt;
					}
					tSt = (dO["st2"+iN]&(~0x01)) + (dP["md"+iN].get()?1:0);
					if(tSt != dO["st2"+iN]) {	//Same write and check the result
						if(!reset(tr)) continue;
						if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
						if(io(tr,(req=SYS.strFromCharCode(0x55,8+iN*2+1,0,tSt))) != req) continue;
						resp = io(tr, SYS.strFromCharCode(0xFF,0xFF,0xFF));
						if(((~((resp.charCodeAt(1)<<8) + resp.charCodeAt(0)))&0xFFFF) == Special.FLibSYS.CRC(req,0xA001,16,0) && resp.charCodeAt(2) == tSt)
							dO["st2"+iN] = tSt;
					}
				}

				//Read control status data
				if(dP["res0"].get().isEVal()) {
					if(!reset(tr)) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
					if(io(tr,(req=SYS.strFromCharCode(0xAA,0x08,0x00))) != req) continue;
					resp = io(tr, SYS.strFromCharCode(0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF));
					CRC_OK = (~((resp.charCodeAt(resp.length-1)<<8) + resp.charCodeAt(resp.length-2)))&0xFFFF;
					CRC_OK = (Special.FLibSYS.CRC(SYS.strFromCharCode(0xAA,0x08,0x00)+resp.slice(0,-2),0xA001,16,0) == CRC_OK);
					for(iN = 0; iN < 4; iN++) {
						dP["res"+iN].set(CRC_OK?((tVl=resp.charCodeAt(iN*2)&0xF)?tVl:16):EVAL, 0, 0, true);
						dP["md"+iN].set(CRC_OK?((resp.charCodeAt(iN*2)&0x80)?2:resp.charCodeAt(iN*2+1)&1):EVAL, 0, 0, true);
						dP["do"+iN].set(CRC_OK?((resp.charCodeAt(iN*2)&0x40)?true:false):EVAL, 0, 0, true);
						dO["st1"+iN] = resp.charCodeAt(iN*2); dO["st2"+iN] = resp.charCodeAt(iN*2+1);
					}
				} else CRC_OK = true;

				//Read AI value
				if(CRC_OK) {
					if(!reset(tr)) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
					if(io(tr,(req=SYS.strFromCharCode(0xAA,0x00,0x00))) != req) continue;
					resp = io(tr, SYS.strFromCharCode(0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF));
					//SYS.messInfo("OneWire","resp="+SYS.strDecode(resp,"Bin"," ")/*+"; CRC="+Special.FLibSYS.CRC(SYS.strFromCharCode(0x3C,0x08,0x00)/*+resp.slice(0,-2),0xA001,16,0).toString(16)*/);
					CRC_OK = (~((resp.charCodeAt(resp.length-1)<<8) + resp.charCodeAt(resp.length-2)))&0xFFFF;
					CRC_OK = (Special.FLibSYS.CRC(SYS.strFromCharCode(0xAA,0x00,0x00)+resp.slice(0,-2),0xA001,16,0) == CRC_OK);
				}
				for(iN = 0; iN < 4; iN++)
					if(CRC_OK) {
						tVl = (resp.charCodeAt(iN*2+1)<<8) + resp.charCodeAt(iN*2);
						vlMax = dP["md"+iN].get() ? 5.1 : 2.55;
						dP["ai"+iN].set(tVl*vlMax/65536, 0, 0, true);
					} else dP["ai"+iN].set(EVAL, 0, 0, true);

				//Start A/D conversion for prepare data for next iteration
				if(CRC_OK) {
					if(!reset(tr)) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x3C,0x0F,0x00))) != req) continue;
					resp = io(tr, SYS.strFromCharCode(0xFF,0xFF));
				}
			}
			else if(devFamily == 0x26) {	//DS2438
				dP = dO.dP;
				//Check for a modifications
				//????

				//Recall memory page 0
				if(!reset(tr)) continue;
				if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
				if(io(tr,(req=SYS.strFromCharCode(0xB8,0))) != req) continue;
				//Read current state
				if(!reset(tr)) continue;
				if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
				if(io(tr,(req=SYS.strFromCharCode(0xBE,0))) != req) continue;
				resp = io(tr, SYS.strFromCharCode(0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF));
				CRC_OK = !Special.FLibSYS.CRC(resp, 0x8C, 8, 0);
				if(CRC_OK) {
					if((tVl=(resp.charCodeAt(2)<<8)+resp.charCodeAt(1)) >= 32767) tVl -= 65536;
					dP["t"].set(tVl/256, 0, 0, true);
					if((tVl=(resp.charCodeAt(6)<<8)+resp.charCodeAt(5)) >= 32767) tVl -= 65536;
					dP["i"].set(tVl, 0, 0, true);
				}
				else {
					dP["t"].set(EVAL, 0, 0, true);
					dP["i"].set(EVAL, 0, 0, true);
				}
				dP["v"].set(CRC_OK?((resp.charCodeAt(4)<<8)+resp.charCodeAt(3))/100:EVAL, 0, 0, true);
				dP["IAD"].set(CRC_OK?(resp.charCodeAt(0)&0x01):EVAL, 0, 0, true);
				dP["CA"].set(CRC_OK?(resp.charCodeAt(0)&0x02):EVAL, 0, 0, true);
				dP["EE"].set(CRC_OK?(resp.charCodeAt(0)&0x04):EVAL, 0, 0, true);
				dP["AD"].set(CRC_OK?(resp.charCodeAt(0)&0x08):EVAL, 0, 0, true);
				dP["thr"].set(CRC_OK?(resp.charCodeAt(7)>>6):EVAL, 0, 0, true);

				if(CRC_OK) {
					if(!reset(tr)) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
					if(io(tr,(req=SYS.strFromCharCode(0xBE,1))) != req) continue;
					resp = io(tr, SYS.strFromCharCode(0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF));
					CRC_OK = !Special.FLibSYS.CRC(resp, 0x8C, 8, 0);
					SYS.messInfo("OneWire","resp="+SYS.strDecode(resp,"Bin"," "));
				}
				dP["ETM"].set(CRC_OK?((resp.charCodeAt(3)<<24)+(resp.charCodeAt(2)<<16)+(resp.charCodeAt(1)<<8)+resp.charCodeAt(0)):EVAL, 0, 0, true);
				dP["ICA"].set(CRC_OK?resp.charCodeAt(4):EVAL, 0, 0, true);
				if(CRC_OK) {
					if((tVl=(resp.charCodeAt(6)<<8)+resp.charCodeAt(5)) >= 32767) tVl -= 65536;
					dP["OFF"].set(tVl, 0, 0, true);
				} else dP["OFF"].set(EVAL, 0, 0, true);

				if(CRC_OK) {
					if(!reset(tr)) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
					if(io(tr,(req=SYS.strFromCharCode(0xBE,2))) != req) continue;
					resp = io(tr, SYS.strFromCharCode(0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF));
					CRC_OK = !Special.FLibSYS.CRC(resp, 0x8C, 8, 0);
					SYS.messInfo("OneWire","resp="+SYS.strDecode(resp,"Bin"," "));
				}
				dP["TM_DISC"].set(CRC_OK?((resp.charCodeAt(3)<<24)+(resp.charCodeAt(2)<<16)+(resp.charCodeAt(1)<<8)+resp.charCodeAt(0)):EVAL, 0, 0, true);
				dP["TM_END"].set(CRC_OK?((resp.charCodeAt(7)<<24)+(resp.charCodeAt(6)<<16)+(resp.charCodeAt(5)<<8)+resp.charCodeAt(4)):EVAL, 0, 0, true);

				//Start T ant V conversion for prepare the data for next iteration
				if(CRC_OK) {
					if(!reset(tr)) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x44))) != req) continue;
					if(!reset(tr)) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
					if(io(tr,(req=SYS.strFromCharCode(0xB4))) != req) continue;
				}
			}
			else if(devFamily == 0x29) {	//DS2408
				dP = dO.dP;
				//Check for a modifications
				isMdf = false; tVl = 0; do_ = new Array();
				for(iD = 0; iD < 8; iD++) {
					if(dO["do"+iD].isEVal())	{ isMdf = false; break; }
					do_[iD] = dP["do"+iD].get();
					if(do_[iD] != dO["do"+iD])	isMdf = true;
					if(do_[iD])	tVl = tVl | (1<<iD);
				}
				if(isMdf) {
					if(!reset(tr)) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x5A,tVl,~tVl))) != req) continue;
					// Verify the correct writing, pass by that can be wrong
					//resp = io(tr, SYS.strFromCharCode(0xFF,0xFF));
					//SYS.messInfo("OneWire","Write resp="+SYS.strDecode(resp,"Bin"," "));
				}

				//Read current state
				if(!reset(tr)) continue;
				if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
				if(io(tr,(req=SYS.strFromCharCode(0xF0,0x88,0x00))) != req) continue;
				resp = io(tr, SYS.strFromCharCode(0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF));
				//SYS.messInfo("OneWire","resp="+SYS.strDecode(resp,"Bin"," ")+"; CRC="+Special.FLibSYS.CRC(SYS.strFromCharCode(0xF0,0x88,0x00)+resp.slice(0,-2),0xA001,16,0).toString(16));
				CRC_OK = (~((resp.charCodeAt(resp.length-1)<<8) + resp.charCodeAt(resp.length-2)))&0xFFFF;
				CRC_OK = (Special.FLibSYS.CRC(SYS.strFromCharCode(0xF0,0x88,0x00)+resp.slice(0,-2),0xA001,16,0) == CRC_OK);
				for(iD = 0; iD < 8; iD++) {
					dP["di"+iD].set(CRC_OK?resp.charCodeAt(0)&(1<<iD):EVAL, 0, 0, true);
					if(dO["do"+iD].isEVal() || isMdf || !CRC_OK) {
						dO["do"+iD] = CRC_OK ? ((resp.charCodeAt(1)&(1<<iD))?true:false) : EVAL;
						dP["do"+iD].set(dO["do"+iD], 0, 0, true);
					}
				}
			}
			else if(devFamily == 0x3A) {	//DS2413
				dP = dO.dP;
				//SYS.messInfo("OneWire","devID="+SYS.strDecode(devID,"Bin"," "));
				//Check for a modifications
				isMdf = false;
				do0_ = dP.do0.get(), do1_ = dP.do1.get();
				if(!dO.do0.isEVal() && !dO.do1.isEVal() && (do0_ != dO.do0 || do1_ != dO.do1)) {
					tVl = (do1_<<1) | do0_;
					if(!reset(tr)) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x5A,tVl,(~tVl)&0xFF))) != req) continue;
					isMdf = true;
				}

				//Read current state
				if(!reset(tr)) continue;
				if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
				if(io(tr,(req=SYS.strFromCharCode(0xF5))) != req) continue;
				resp = io(tr, SYS.strFromCharCode(0xFF));
				//SYS.messInfo("OneWire","resp="+SYS.strDecode(resp,"Bin"," "));
				if(((tVl=resp.charCodeAt(0))&0xF) == ((~(tVl>>4))&0xF)) {
					dP.di0.set(tVl&0x01, 0, 0, true); dP.di1.set(tVl&0x04, 0, 0, true);
					if(dO.do0.isEVal() || dO.do1.isEVal() || isMdf) {
						dO.do0 = (tVl&0x02)?true:false; dP.do0.set(dO.do0, 0, 0, true);
						dO.do1 = (tVl&0x08)?true:false; dP.do1.set(dO.do1, 0, 0, true);
					}
				}
				else {
					dP.di0.set(EVAL, 0, 0, true); dP.di1.set(EVAL, 0, 0, true);
					dP.do0.set(EVAL, 0, 0, true); dP.do1.set(EVAL, 0, 0, true);
					dO.do0 = dO.do1 = EVAL;
				}
			}
		}
	}
}

f_err = t_err;


ID: 1W_DS9097U
NAME: One Wire by DS9097U
DESCR:
One Wire sensors bus implementing by 1Wire-adapter DS9097U. Supported direct and parasite powering for the temperature sensors.
Supported 1Wire-devices: DS1820, DS1820/DS18S20/DS1920 (not tested), DS1822 (not tested), DS2413, DS2408, DS2450, DS2438.
Author: Roman Savochenko <roman@oscada.org>
Version: 1.2.1

PROGRAM:
JavaLikeCalc.JavaScript
//Functions
function reset(tr) {
	req = (isData?SYS.strFromCharCode(0xE3):"") +
				SYS.strFromCharCode(0xC5);	//CMD_COMM(0x81) | FUNCTSEL_RESET(0x40) | SPEEDSEL_FLEX(0x04)
	isData = false;
	resp = tr.messIO(req);
	return (resp.charCodeAt(0)&0x03) == 0x01;	//RB_PRESENCE
}

function io(tr, mess, bits) {
	if(bits == EVAL) {
		req = isData ? "" : SYS.strFromCharCode(0xE1);	//MODE_DATA
		isData = true;
		for(iB = 0; iB < mess.length; iB++) {
			req += SYS.strFromCharCode(tVl=mess.charCodeAt(iB));
			if(tVl == 0xE3) req += SYS.strFromCharCode(0xE3);	//duplication to the COMMAND mode
		}
		for(resp = tr.messIO(req); resp.length && resp.length < mess.length && (tresp=tr.messIO("")).length; resp += tresp) ;
		return resp;
	}
	else {
		req = (isData?SYS.strFromCharCode(0xE3):"") + SYS.strFromCharCode(0x3F);	//MODE_COMMAND, CMD_CONFIG(0x01) | PARMSEL_5VPULSE(0x30) | PARMSET_infinite(0x0E)
		isData = false;
		for(iB = 0; iB < ceil(bits/8); iB++) {
			vB = mess.charCodeAt(iB);
			bB = ((iB+1)*8 > bits) ? bits-floor(bits/8)*8 : 8;
			for(iBi = 0; iBi < bB; iBi++)
				//{BITPOL_ONE(0x10)|BITPOL_ZERO(0x00)}|CMD_COMM(0x81)|FUNCTSEL_BIT(0x00)|SPEEDSEL_FLEX(0x04)|{PRIME5V_TRUE(0x02)|PRIME5V_FALSE(0x00)}
				req += SYS.strFromCharCode(((vB&(1<<iBi))?0x10:0x00)|0x85/*|((iB==(ceil(bits/8)-1)&&iBi==(bB-1))?0x02:0x00)*/);
		}
		for(resp = tr.messIO(req); resp.length && resp.length < (bits+1) && (tresp=tr.messIO("")).length; resp += tresp) ;
		rez = "";
		if(resp.length && !(resp.charCodeAt(0)&0x81))
			for(iR = 1;  iR < resp.length; ) {
				vB = 0;
				for(ib = 0; ib < 8 && iR < resp.length; iR++, ib++)
					if(resp.charCodeAt(iR)&1) vB = vB | (1<<ib);
				rez += SYS.strFromCharCode(vB);
			}
	}
	return rez;
}

function scan(tr, sn, lstDiscr) {
	if(!reset(tr)) return "";
	
	//Set the bits in the added buffer
	asn = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
	for(i = 0; lstDiscr >= 0 && i < 64; i++)
		if((i < (lstDiscr-1) && sn[floor(i/8)]&(1<<(i%8))) || i == (lstDiscr-1))
			asn[(i*2+1)/8] = asn[(i*2+1)/8] | (1<<((i*2+1)%8));

	req = isData ? "" : SYS.strFromCharCode(0xE1);	//MODE_DATA
	req += SYS.strFromCharCode(0xF0, 0xE3, 0xB5, 0xE1,
				asn[0], asn[1], asn[2], asn[3], asn[4], asn[5], asn[6], asn[7],
				asn[8], asn[9], asn[10], asn[11], asn[12], asn[13], asn[14], asn[15],
				0xE3, 0xA5);
	isData = false;
	for(resp = tr.messIO(req); resp.length && resp.length < 17 && (tresp=tr.messIO("")).length; resp += tresp) ;

	tmpLastDesc = -1;
	for(i = 0; i < 64; i++) {
		if(resp.charCodeAt(floor((i*2+1)/8)+1)&(1<<((i*2+1)%8)))	sn[floor(i/8)] = sn[floor(i/8)] | (1<<(i%8));
		else sn[floor(i/8)] = sn[floor(i/8)] & (~(1<<(i%8)));
		// Check LastDiscrepancy
		if((resp.charCodeAt(floor((i*2)/8)+1)&(1<<((i*2)%8))) && !(resp.charCodeAt(floor((i*2+1)/8)+1)&(1<<((i*2+1)%8))))
			tmpLastDesc = i + 1;
	}

	rez = "";
	for(iB = 0; iB < 8; iB++) rez += SYS.strFromCharCode(sn[iB]);
	if(sn[0] && lstDiscr != 63 && !Special.FLibSYS.CRC(rez,0x8C,8,0))	lstDiscr = tmpLastDesc;
	else { rez = ""; lstDiscr = 0; }

	return rez;
}

//Set transport and init
if(f_start) {
	tmResc_ = 0;
	devLs = new Object();
	transport_ = transport;
	tr = SYS.Transport.Serial.nodeAt("out_"+transport);
	DS2480 = isData = false;
}
if(f_stop) {
	for(var devID in devLs) {
		if((dO=devLs[devID]).isEVal()) continue;
		aLs = dO.dP.nodeList("a_");
		for(iA = 0; iA < aLs.length; iA++)
			if(!dO.dP[aLs[iA]].isCfg()) dO.dP[aLs[iA]].set(EVAL, 0, 0, true);
	}
	devLs = tr = false;
	return;
}

t_err = "0";

//Check for the transport change and connect
if(!tr || transport != transport_)	{
	tr = SYS.Transport.Serial.nodeAt("out_"+transport);
	transport_ = transport;
	DS2480 = false;
}
//Try DS2480
if(tr && !DS2480)	{
	tr.addr(tr.addr().parse(0,":")+":9600:8N1");	tr.timings("500:20");
	if(tr.start(true)) {
		tr.sendbreak(); SYS.sleep(2e-3);
		tr.messIO(SYS.strFromCharCode(0xC1));	SYS.sleep(4e-3);	//Send timing byte
		//PDSRC=1.37Vus; W1LT=10us; DSO/WORT=8us;
		//read the baud rate (to test command block); do 1 bit operation (to test 1-Wire block)
		req = SYS.strFromCharCode(0x17, 0x45, 0x5B, 0x0F, 0x91);
		for(resp = tr.messIO(req); resp.length && resp.length < 5 && (tresp=tr.messIO("")).length; resp += tresp) ;
		if(resp.length == 5 && (resp.charCodeAt(3)&0xF1) == 0x00 && (resp.charCodeAt(3)&0x0E) == 0x00/*9600*/ &&
				(resp.charCodeAt(4)&0xF0) == 0x90 && (resp.charCodeAt(4)&0x0C) == 0x00/*9600*/)
			DS2480 = true;
	}
	isData = false;
}
if(!tr)	t_err = "1:"+tr("Output transport '%1' error.").replace("%1", transport);
else if(!DS2480)	t_err = "2:"+tr("DS2480 is not detected.");
else {
	//Generic information update.
	if(f_start || (tmResc && (tmResc_=tmResc_-1/f_frq) <= 0)) {
		tmResc_ = tmResc;
		tmSc = SYS.time();
		// Check for power
		if(!reset(tr) ||
		 	io(tr,(req=SYS.strFromCharCode(0xCC))) != req ||
			io(tr,(req=SYS.strFromCharCode(0xB4))) != req ||
			!(resp=io(tr,SYS.strFromCharCode(1),1)).length) power = EVAL;
		else power = resp.charCodeAt(0);
		// Scan for allowed devices on the bus.
		sn = new Array(0, 0, 0, 0, 0, 0, 0, 0);
		for(devID = "start", lstDiscr = 0; devID.length && lstDiscr >= 0; ) {
			if((devID=scan(tr,sn,lstDiscr)).length)	{
				//SYS.messInfo("OneWire","lstDiscr="+lstDiscr+"; devID="+SYS.strDecode(devID,"Bin"," "));
				if((dO=devLs[devID]).isEVal()) {
					devLs[devID] = dO = new Object();
					devFamily = devID.charCodeAt(0)&0x7F; devNm = tr("1W device"); dO.isT = false;
					if(devFamily == 0x10)			{ devNm = "DS1820/DS18S20/DS1920"; dO.isT = true; }
					else if(devFamily == 0x20)	devNm = "DS2450";		//4AI
					else if(devFamily == 0x22)	{ devNm = "DS1822"; dO.isT = true; }
					else if(devFamily == 0x26)	devNm = "DS2438";		//SmartBat
					else if(devFamily == 0x28)	{ devNm = "DS18B20"; dO.isT = true; }
					else if(devFamily == 0x29)	devNm = "DS2408";		//8DIO
					else if(devFamily == 0x3A)	devNm = "DS2413";		//2DIO

					// Create the device's parameter object
					decId = SYS.strDecode(devID,"Bin","");
					SYS.cntrReq(SYS.XMLNode("add").setAttr("path",this.nodePath()+"/%2fbr%2fprm_").setAttr("id",decId).setText(devNm+": "+decId));
					SYS.cntrReq(SYS.XMLNode("set").setAttr("path",this.nodePath()+"/prm_"+decId+"/%2fprm%2fst%2fen").setText("1"));
					// Create the device's proper attributes
					dO.dP = dP = this["prm_"+decId];
					if(dO.isT) {	//Temperature
						dP.attrAdd("val", tr("Temperature, °С"), "real|ro");
						dP.attrAdd("m", tr("User cell [0...65535]"), "integer");
						dP.attrAdd("res", tr("Resolution")+" [9...12]", "integer");
					}
					else if(devFamily == 0x20)		//DS2450
						for(iN = 0; iN < 4; iN++) {
							dP.attrAdd("ai"+iN, tr("AI, V")+iN, "real|ro");
							dP.attrAdd("res"+iN, tr("Resolution")+iN+" [1...16]", "integer");
							dP.attrAdd("md"+iN, tr("Mode")+iN, "integer|sel", "0;1;2\n2.55V;5.10V;DO");
							dP.attrAdd("do"+iN, tr("DO")+iN, "boolean");
						}
					else if(devFamily == 0x26) {	//DS2438
						dP.attrAdd("t", tr("Temperature, °С"), "real|ro");
						dP.attrAdd("v", tr("Voltage, V"), "real|ro");
						dP.attrAdd("i", tr("Current, A"), "real|ro");
						dP.attrAdd("ICA", tr("Current Accumulator"), "integer");
						dP.attrAdd("thr", tr("Accumulation threshold"), "integer|sel", "0;1;2;3\nNone;±2LSB;±4LSB;±8LSB");
						dP.attrAdd("ETM", tr("Elapsed time meter"), "integer");
						dP.attrAdd("TM_DISC", tr("Disconnect time"), "integer");
						dP.attrAdd("TM_END", tr("End of charge time"), "integer");
						dP.attrAdd("OFF", tr("Current Offset"), "integer");
						dP.attrAdd("IAD", tr("Enable Current A/D"), "boolean");
						dP.attrAdd("CA", tr("Enable Current Accumulator"), "boolean");
						dP.attrAdd("EE", tr("Enable Current Accumulator Shadow"), "boolean");
						dP.attrAdd("AD", tr("Voltage A/D (0-VAD,1-VDD)"), "boolean");
					}
					else if(devFamily == 0x29)		//DS2408
						for(iD = 0; iD < 8; iD++) {
							dP.attrAdd("di"+iD, tr("DI")+iD, "boolean|ro");
							dP.attrAdd("do"+iD, tr("DO")+iD, "boolean");
						}
					else if(devFamily == 0x3A)		//DS2413
						for(iD = 0; iD < 2; iD++) {
							dP.attrAdd("di"+iD, tr("DI")+iD, "boolean|ro");
							dP.attrAdd("do"+iD, tr("DO")+iD, "boolean");
						}
				}
				if(dO.tmSc == tmSc)	break;	//Somthing wrong into the scan but repeat, interruption
				dO.tmSc = tmSc;
				dO.try = tryEVAL;
			}
		}
		//Check for removed devices
		for(devID in devLs) {
			if((dO=devLs[devID]).isEVal()) continue;
			if(dO.tmSc != tmSc)	{
				devLs[devID] = EVAL;
				aLs = dO.dP.nodeList("a_");
				for(iA = 0; iA < aLs.length; iA++)
					if(!dO.dP[aLs[iA]].isCfg())	dO.dP[aLs[iA]].set(EVAL, 0, 0, true);
			}
		}
	}
	else {
		//Process devices for reading current value and set modifable
		for(devID in devLs) {
			if((dO=devLs[devID]).isEVal()) continue;
			devFamily = devID.charCodeAt(0)&0x7F;
			if(dO.isT) {	//Temperature
				dP = dO.dP;
				//SYS.messInfo("OneWire","devID="+SYS.strDecode(devID,"Bin"," "));
				//Check for a modifications
				isMdf = false;
				m_ = dP.m.get(), res_ = max(9,min(12,dP.res.get()));
				if(!dO.m.isEVal() && !dO.res.isEVal() && (m_ != dO.m || res_ != dO.res)) {
					//Set scratchpad
					if(!reset(tr)) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x4E,m_&0xFF,(m_>>8)&0xFF,((res_-9)<<5)|0x1F))) != req) continue;
					//Copy scratchpad part to the EEPROM
					if(!reset(tr)) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x48))) != req) continue;
					isMdf = true;
				}

				//Read current
				// Call the transmission
				if(!reset(tr)) continue;
				if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
				if(io(tr,(req=SYS.strFromCharCode(0x44))) != req) continue;
				if(!power) SYS.sleep(0.094*pow(2,(dO.res.isEVal()?12:dO.res)-9));
				// Get scratchpad
				if(!reset(tr)) continue;
				if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
				/*out*/io(tr, SYS.strFromCharCode(0xBE));
				resp = io(tr, SYS.strFromCharCode(0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF));
				//SYS.messInfo("OneWire","resp="+SYS.strDecode(resp,"Bin"," "));
				if(!Special.FLibSYS.CRC(resp,0x8C,8,0)) {
					val = resp.charCodeAt(1)*256 + resp.charCodeAt(0);
					if(val > 32767) val -= 65536;
					dP.val.set(val/16, 0, 0, true);
					if(dO.m.isEVal() || dO.res.isEVal() || isMdf) {
						dO.m = resp.charCodeAt(3)*256 + resp.charCodeAt(2); dP.m.set(dO.m, 0, 0, true);
						dO.res = ((resp.charCodeAt(4)>>5)&0x3)+9; dP.res.set(dO.res, 0, 0, true);
					}
					dO.try = 0;
				}
				else if((dO.try=dO.try+1) > tryEVAL) {
					dP.val.set(EVAL, 0, 0, true); dP.m.set(EVAL, 0, 0, true); dP.res.set(EVAL, 0, 0, true);
					dO.m = dO.res = EVAL;
				}
			}
			else if(devFamily == 0x20) {	//DS2450
				dP = dO.dP;
				//Check for a modifications
				for(iN = 0; iN < 4; iN++) {
					if(dO["st1"+iN].isEVal())	continue;
					if((tRes=max(0,min(16,dP["res"+iN].get()))) == 16)	tRes = 0;
					tSt = (dO["st1"+iN]&(~0xCF)) + tRes + ((dP["md"+iN].get()==2)?0x80:0) + (dP["do"+iN].get()?0x40:0);
					if(tSt != dO["st1"+iN]) {	//Same write and check the result
						if(!reset(tr)) continue;
						if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
						if(io(tr,(req=SYS.strFromCharCode(0x55,8+iN*2,0,tSt))) != req) continue;
						resp = io(tr, SYS.strFromCharCode(0xFF,0xFF,0xFF));
						if(((~((resp.charCodeAt(1)<<8) + resp.charCodeAt(0)))&0xFFFF) == Special.FLibSYS.CRC(req,0xA001,16,0) && resp.charCodeAt(2) == tSt)
							dO["st1"+iN] = tSt;
					}
					tSt = (dO["st2"+iN]&(~0x01)) + (dP["md"+iN].get()?1:0);
					if(tSt != dO["st2"+iN]) {	//Same write and check the result
						if(!reset(tr)) continue;
						if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
						if(io(tr,(req=SYS.strFromCharCode(0x55,8+iN*2+1,0,tSt))) != req) continue;
						resp = io(tr, SYS.strFromCharCode(0xFF,0xFF,0xFF));
						if(((~((resp.charCodeAt(1)<<8) + resp.charCodeAt(0)))&0xFFFF) == Special.FLibSYS.CRC(req,0xA001,16,0) && resp.charCodeAt(2) == tSt)
							dO["st2"+iN] = tSt;
					}
				}

				//Read control status data
				if(dP["res0"].get().isEVal()) {
					if(!reset(tr)) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
					if(io(tr,(req=SYS.strFromCharCode(0xAA,0x08,0x00))) != req) continue;
					resp = io(tr, SYS.strFromCharCode(0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF));
					CRC_OK = (~((resp.charCodeAt(resp.length-1)<<8) + resp.charCodeAt(resp.length-2)))&0xFFFF;
					CRC_OK = (Special.FLibSYS.CRC(SYS.strFromCharCode(0xAA,0x08,0x00)+resp.slice(0,-2),0xA001,16,0) == CRC_OK);
					for(iN = 0; iN < 4; iN++) {
						dP["res"+iN].set(CRC_OK?((tVl=resp.charCodeAt(iN*2)&0xF)?tVl:16):EVAL, 0, 0, true);
						dP["md"+iN].set(CRC_OK?((resp.charCodeAt(iN*2)&0x80)?2:resp.charCodeAt(iN*2+1)&1):EVAL, 0, 0, true);
						dP["do"+iN].set(CRC_OK?((resp.charCodeAt(iN*2)&0x40)?true:false):EVAL, 0, 0, true);
						dO["st1"+iN] = resp.charCodeAt(iN*2); dO["st2"+iN] = resp.charCodeAt(iN*2+1);
					}
				} else CRC_OK = true;

				//Read AI value
				if(CRC_OK) {
					if(!reset(tr)) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
					if(io(tr,(req=SYS.strFromCharCode(0xAA,0x00,0x00))) != req) continue;
					resp = io(tr, SYS.strFromCharCode(0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF));
					//SYS.messInfo("OneWire","resp="+SYS.strDecode(resp,"Bin"," ")/*+"; CRC="+Special.FLibSYS.CRC(SYS.strFromCharCode(0x3C,0x08,0x00)/*+resp.slice(0,-2),0xA001,16,0).toString(16)*/);
					CRC_OK = (~((resp.charCodeAt(resp.length-1)<<8) + resp.charCodeAt(resp.length-2)))&0xFFFF;
					CRC_OK = (Special.FLibSYS.CRC(SYS.strFromCharCode(0xAA,0x00,0x00)+resp.slice(0,-2),0xA001,16,0) == CRC_OK);
				}
				for(iN = 0; iN < 4; iN++)
					if(CRC_OK) {
						tVl = (resp.charCodeAt(iN*2+1)<<8) + resp.charCodeAt(iN*2);
						vlMax = dP["md"+iN].get() ? 5.1 : 2.55;
						dP["ai"+iN].set(tVl*vlMax/65536, 0, 0, true);
					} else dP["ai"+iN].set(EVAL, 0, 0, true);

				//Start A/D conversion for prepare data for next iteration
				if(CRC_OK) {
					if(!reset(tr)) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x3C,0x0F,0x00))) != req) continue;
					resp = io(tr, SYS.strFromCharCode(0xFF,0xFF));
				}
			}
			else if(devFamily == 0x26) {	//DS2438
				dP = dO.dP;
				//Check for a modifications
				//????

				//Recall memory page 0
				if(!reset(tr)) continue;
				if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
				if(io(tr,(req=SYS.strFromCharCode(0xB8,0))) != req) continue;
				//Read current state
				if(!reset(tr)) continue;
				if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
				if(io(tr,(req=SYS.strFromCharCode(0xBE,0))) != req) continue;
				resp = io(tr, SYS.strFromCharCode(0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF));
				CRC_OK = !Special.FLibSYS.CRC(resp, 0x8C, 8, 0);
				if(CRC_OK) {
					if((tVl=(resp.charCodeAt(2)<<8)+resp.charCodeAt(1)) >= 32767) tVl -= 65536;
					dP["t"].set(tVl/256, 0, 0, true);
					if((tVl=(resp.charCodeAt(6)<<8)+resp.charCodeAt(5)) >= 32767) tVl -= 65536;
					dP["i"].set(tVl, 0, 0, true);
				}
				else {
					dP["t"].set(EVAL, 0, 0, true);
					dP["i"].set(EVAL, 0, 0, true);
				}
				dP["v"].set(CRC_OK?((resp.charCodeAt(4)<<8)+resp.charCodeAt(3))/100:EVAL, 0, 0, true);
				dP["IAD"].set(CRC_OK?(resp.charCodeAt(0)&0x01):EVAL, 0, 0, true);
				dP["CA"].set(CRC_OK?(resp.charCodeAt(0)&0x02):EVAL, 0, 0, true);
				dP["EE"].set(CRC_OK?(resp.charCodeAt(0)&0x04):EVAL, 0, 0, true);
				dP["AD"].set(CRC_OK?(resp.charCodeAt(0)&0x08):EVAL, 0, 0, true);
				dP["thr"].set(CRC_OK?(resp.charCodeAt(7)>>6):EVAL, 0, 0, true);

				if(CRC_OK) {
					if(!reset(tr)) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
					if(io(tr,(req=SYS.strFromCharCode(0xBE,1))) != req) continue;
					resp = io(tr, SYS.strFromCharCode(0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF));
					CRC_OK = !Special.FLibSYS.CRC(resp, 0x8C, 8, 0);
					SYS.messInfo("OneWire","resp="+SYS.strDecode(resp,"Bin"," "));
				}
				dP["ETM"].set(CRC_OK?((resp.charCodeAt(3)<<24)+(resp.charCodeAt(2)<<16)+(resp.charCodeAt(1)<<8)+resp.charCodeAt(0)):EVAL, 0, 0, true);
				dP["ICA"].set(CRC_OK?resp.charCodeAt(4):EVAL, 0, 0, true);
				if(CRC_OK) {
					if((tVl=(resp.charCodeAt(6)<<8)+resp.charCodeAt(5)) >= 32767) tVl -= 65536;
					dP["OFF"].set(tVl, 0, 0, true);
				} else dP["OFF"].set(EVAL, 0, 0, true);

				if(CRC_OK) {
					if(!reset(tr)) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
					if(io(tr,(req=SYS.strFromCharCode(0xBE,2))) != req) continue;
					resp = io(tr, SYS.strFromCharCode(0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF));
					CRC_OK = !Special.FLibSYS.CRC(resp, 0x8C, 8, 0);
					SYS.messInfo("OneWire","resp="+SYS.strDecode(resp,"Bin"," "));
				}
				dP["TM_DISC"].set(CRC_OK?((resp.charCodeAt(3)<<24)+(resp.charCodeAt(2)<<16)+(resp.charCodeAt(1)<<8)+resp.charCodeAt(0)):EVAL, 0, 0, true);
				dP["TM_END"].set(CRC_OK?((resp.charCodeAt(7)<<24)+(resp.charCodeAt(6)<<16)+(resp.charCodeAt(5)<<8)+resp.charCodeAt(4)):EVAL, 0, 0, true);

				//Start T ant V conversion for prepare the data for next iteration
				if(CRC_OK) {
					if(!reset(tr)) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x44))) != req) continue;
					if(!reset(tr)) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
					if(io(tr,(req=SYS.strFromCharCode(0xB4))) != req) continue;
				}
			}
			else if(devFamily == 0x29) {	//DS2408
				dP = dO.dP;
				//Check for a modifications
				isMdf = false; tVl = 0; do_ = new Array();
				for(iD = 0; iD < 8; iD++) {
					if(dO["do"+iD].isEVal())	{ isMdf = false; break; }
					do_[iD] = dP["do"+iD].get();
					if(do_[iD] != dO["do"+iD])	isMdf = true;
					if(do_[iD])	tVl = tVl | (1<<iD);
				}
				if(isMdf) {
					if(!reset(tr)) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x5A,tVl,~tVl))) != req) continue;
					// Verify the correct writing, pass by that can be wrong
					//resp = io(tr, SYS.strFromCharCode(0xFF,0xFF));
					//SYS.messInfo("OneWire","Write resp="+SYS.strDecode(resp,"Bin"," "));
				}

				//Read current state
				if(!reset(tr)) continue;
				if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
				if(io(tr,(req=SYS.strFromCharCode(0xF0,0x88,0x00))) != req) continue;
				resp = io(tr, SYS.strFromCharCode(0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF));
				//SYS.messInfo("OneWire","resp="+SYS.strDecode(resp,"Bin"," "));//+"; CRC="+Special.FLibSYS.CRC(SYS.strFromCharCode(0xF0,0x88,0x00)+resp.slice(0,-2),0xA001,16,0).toString(16));
				CRC_OK = (~((resp.charCodeAt(resp.length-1)<<8) + resp.charCodeAt(resp.length-2)))&0xFFFF;
				CRC_OK = (Special.FLibSYS.CRC(SYS.strFromCharCode(0xF0,0x88,0x00)+resp.slice(0,-2),0xA001,16,0) == CRC_OK);
				for(iD = 0; iD < 8; iD++) {
					dP["di"+iD].set(CRC_OK?resp.charCodeAt(0)&(1<<iD):EVAL, 0, 0, true);
					if(dO["do"+iD].isEVal() || isMdf || !CRC_OK) {
						dO["do"+iD] = CRC_OK ? ((resp.charCodeAt(1)&(1<<iD))?true:false) : EVAL;
						dP["do"+iD].set(dO["do"+iD], 0, 0, true);
					}
				}
			}
			else if(devFamily == 0x3A) {	//DS2413
				dP = dO.dP;
				//SYS.messInfo("OneWire","devID="+SYS.strDecode(devID,"Bin"," "));
				//Check for a modifications
				isMdf = false;
				do0_ = dP.do0.get(), do1_ = dP.do1.get();
				if(!dO.do0.isEVal() && !dO.do1.isEVal() && (do0_ != dO.do0 || do1_ != dO.do1)) {
					tVl = (do1_<<1) | do0_;
					if(!reset(tr)) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
					if(io(tr,(req=SYS.strFromCharCode(0x5A,tVl,(~tVl)&0xFF))) != req) continue;
					isMdf = true;
				}

				//Read current state
				if(!reset(tr)) continue;
				if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
				if(io(tr,(req=SYS.strFromCharCode(0xF5))) != req) continue;
				resp = io(tr, SYS.strFromCharCode(0xFF));
				//SYS.messInfo("OneWire","resp="+SYS.strDecode(resp,"Bin"," "));
				if(((tVl=resp.charCodeAt(0))&0xF) == ((~(tVl>>4))&0xF)) {
					dP.di0.set(tVl&0x01, 0, 0, true); dP.di1.set(tVl&0x04, 0, 0, true);
					if(dO.do0.isEVal() || dO.do1.isEVal() || isMdf) {
						dO.do0 = (tVl&0x02)?true:false; dP.do0.set(dO.do0, 0, 0, true);
						dO.do1 = (tVl&0x08)?true:false; dP.do1.set(dO.do1, 0, 0, true);
					}
				}
				else {
					dP.di0.set(EVAL, 0, 0, true); dP.di1.set(EVAL, 0, 0, true);
					dP.do0.set(EVAL, 0, 0, true); dP.do1.set(EVAL, 0, 0, true);
					dO.do0 = dO.do1 = EVAL;
				}
			}
		}
	}
}

f_err = t_err;


ID: BMP180
NAME: I2C: BMP180
DESCR:
I2C Pressure and Temperature sensor. Connect through a Serial output transport into the I2C mode.
Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.1

PROGRAM:
JavaLikeCalc.JavaScript
//Initial set
if(f_start) {
	f_err = "0";
	transport_ = transport;
	tr = SYS.Transport.Serial.nodeAt("out_"+transport);
	AC1 = 0;
}

t_err = "0";

//Check for the transport change and connect
if(!tr || transport != transport_)	{
	tr = SYS.Transport.Serial.nodeAt("out_"+transport);
	transport_ = transport;
}
if(!tr)	t_err = "1:"+tr("Output transport '%1' error.").replace("%1", transport);
else if(addr < 0 || addr > 119)	t_err = "2:"+tr("Device address '%1' out of range [0...119].").replace("%1",addr);
else {
	resp = Special.FLibSYS.IO("", "", "b");
	//Calibration data, from register 0xAA and to 0xBF
	if(!AC1) {
		resp.string = tr.messIO(SYS.strFromCharCode(addr,0xAA), 0, 22);
		if(resp.length != 22) t_err = "3:"+tr("Wrong or empty respond to the calibration request.");
		else {
			AC1 = resp.read("int16",1);	AC2 = resp.read("int16",1);	AC3 = resp.read("int16",1);
			AC4 = resp.read("uint16",1); AC5 = resp.read("uint16",1);	AC6 = resp.read("uint16",1);
			B1 = resp.read("int16",1);	B2 = resp.read("int16",1);
			MB = resp.read("int16",1);	MC = resp.read("int16",1);	MD = resp.read("int16",1);
			//SYS.messInfo("BMP180","AC1="+AC1+"; AC2="+AC2+"; AC3="+AC3+"; AC4="+AC4+"; AC5="+AC5+"; AC6="+AC6);
		}
	}

	//Read and calculate temperature
	if(AC1 && !t_err.toInt()) {
		// Start conversion
		tr.messIO(SYS.strFromCharCode(addr,0xF4,0x2E), 0, 0);
		SYS.sleep(4.5e-3);
		// Read value
		resp.pos = 0; resp.string = tr.messIO(SYS.strFromCharCode(addr,0xF6), 0, 2);
		if(resp.length != 2) t_err = "3:"+tr("Wrong or empty respond to the temperature data.");
		else {
			UT = resp.read("int16",1);
			X1 = (UT-AC6)*AC5/pow(2,15);	X2 = MC*pow(2,11)/(X1+MD);
			B5 = X1 + X2;
			t = 0.1*((B5+8)/pow(2,4));
			//SYS.messInfo("BMP180","UT="+UT+"; X1="+X1+"; X2="+X2+"; B5="+B5+"; t="+t);
		}
	}
	
	//Read and calculate pressure
	if(AC1 && !t_err.toInt()) {
		oss = max(0, min(3,oss));
		// Start conversion
		tr.messIO(SYS.strFromCharCode(addr,0xF4,0x34+(oss<<6)), 0, 0);
		SYS.sleep((1.5+3*pow(2,oss))*1e-3);
		// Read value
		resp.pos = 0; resp.string = tr.messIO(SYS.strFromCharCode(addr,0xF6), 0, 4);
		UP = resp.read("int32",1)>>(16-oss);
		B6 = B5 - 4000;
		X1 = (B2*(B6*floor(B6/pow(2,12))))/pow(2,11);
		X2 = AC2*B6/pow(2,11);
		X3 = X1 + X2;
		B3 = (((AC1*4+X3)<<oss)+2)/4;
		X1 = AC3*B6/pow(2,13);
		X2 = (B1*(B6*B6/pow(2,12)))/pow(2,16);
		X3 = ((X1+X2)+2)/pow(2,2);
		B4 = AC4*(X3+32768)/pow(2,15);
		B7 = (UP-B3)*(50000>>oss);
		p = (B7 < 0x80000000) ? (B7*2)/B4 : (B7/B4)*2;
		X1 = (p/pow(2,8))*(p/pow(2,8));
		X1 = (X1*3038)/pow(2,16);
		X2 = (-7357*p)/pow(2,16);
		p = p + (X1+X2+3791)/pow(2,4);
	}
}

if(t_err.toInt() && !f_err.toInt()) t = p = EVAL;
f_err = t_err;


ID: DHT
NAME: GPIO: DHT11,22 (AM23XX)
DESCR:
Digital Temperature and Humidity Sensor for models: DHT11, DHT12, AM2302, AM2320, ...
The module designed for the sensors connect through GPIO, mostly it's Raspberry PI BCM2835 GPIO.
Conditions: Exclusively realtime planing in the priority 199 (FIFO-99).
Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.0

PROGRAM:
JavaLikeCalc.JavaScript
using Special.FLibSYS;

//Set link to fast external functions
if(f_start)	f_err = "0", addr_ = "";

t_err = "0";

//Check for the address change and link
if(addr != addr_)	{
	addr_ = addr;
	function mode = addr+".fnc_mode";
	function get = addr+".fnc_get";
	function put = addr+".fnc_put";
}

//Read sensor's data function declaration
function read() {
	vl = 0;						//Meassured value
	cntHoldMax = 100;		//Maximum wait counter

	//Call the device to a respond
	mode(pin, 4); put(pin, true); tmSleep(500e-3);	//Set pin to output mode and next to true for 500ms
	put(pin, false); tmSleep(20e-3);							//Set output to false for 20ms
	mode(pin, 2);														//Set pin to input mode

	//Read
	// Wait for pull pin low.
	for(cntHold = 0; get(pin); cntHold++)
		if(cntHold > cntHoldMax) return 0;

	//Meassure the typical pulse length
	for(cntHold = 0; !get(pin); cntHold++)
		if(cntHold > cntHoldMax) return 0;
	for(cntHold = 0; get(pin); cntHold++)
		if(cntHold > cntHoldMax) return 0;
	cntPulse = cntHold;

	//Read meassured value
	for(iB = 0; iB < 40; iB++) {
		//stg = iB;
		for(cntHold = 0; !get(pin); cntHold++)
			if(cntHold > cntHoldMax) return 0;
		for(cntHold = 0; get(pin); cntHold++)
			if(cntHold > cntHoldMax) return 0;
		vl = vl << 1;
		if(cntHold > cntPulse/2)	vl = vl | 1;
	}

	return vl;
}

//Main processing
if(!mode)	t_err = "1:"+tr("No link to external functions on '%1'.").replace("%1",addr);
else if(!(dev == 0 || dev == 1))	t_err = "2:"+tr("Unknown device %1 [0-DHT11, 1-DHT22].").replace("%1",dev.toString());
else if(tries < 1 || tries > 5)	t_err = "3:"+tr("Tries number %1 out of range [1...5].").replace("%1",tries.toString());
else {
	for(i = 0; i < tries; i++) {
		if(i) tmSleep(2);	//Retry after two seconds
		if((vl=read()) && (vl&0xFF) == ((((vl>>8)&0xFF)+((vl>>16)&0xFF)+((vl>>24)&0xFF)+((vl>>32)&0xFF))&0xFF)) {
			//SYS.messInfo("/DHT","vl="+vl.toString(16));
			if(dev == 1) {
				h = 0.1*(((vl>>32)&0xFF)*256+((vl>>24)&0xFF));
				t = 0.1*(((vl>>16)&0xFF)*256+((vl>>8)&0xFF));
				break;
			}
			h = ((vl>>32)&0xFF) + 20; t = (vl>>16)&0xFF;
		}
	}
	if(i >= tries)	t_err = "10:"+tr("Get data after %1 tries error.").replace("%1",tries.toString());
}

if(t_err.toInt() && !f_err.toInt()) t = h = EVAL;
f_err = t_err;


ID: DS3231
NAME: I2C: DS1307,DS3231
DESCR:
I2C RTC chips DS1307,DS3231 with Temperature sensor and calibration for DS3231. Connects through a Serial output transport into the I2C mode.
Author: Roman Savochenko <roman@oscada.org>
Version: 1.1.0

PROGRAM:
JavaLikeCalc.JavaScript
//Set transport
if(f_start) {
	f_err = "0";
	transport_ = transport;
	tr = SYS.Transport.Serial.nodeAt("out_"+transport);
	mode_ = -1;
	tm_ = tm = "";
	agOff_ = 0;
	p32k_ = false;
	pSQW_ = pSQW = false;
	pSQWf_ = pSQWf = 0;
}

t_err = "0";

//Check for the transport change and connect
if(!tr || transport != transport_)	{
	tr = SYS.Transport.Serial.nodeAt("out_"+transport);
	transport_ = transport;
}
if(!tr)	t_err = "1:"+tr("Output transport '%1' error.").replace("%1", transport);
else if(addr < 0 || addr > 119)	t_err = "2:"+tr("Device address '%1' out of range [0...119].").replace("%1",addr);
else {
	//Check for modification
	if(mode != mode_) {
		if(mode == 0) {
			this.attrAdd("pSQWf", "", "integer|sel", "0;1;2;3\n1Hz;1.024kHz;4.096kHz;8.192kHz");
			this.attrAdd("agOff", "Aging offset, [-128...127]", "integer");
			this.attrAdd("t", "T, °С", "real|ro");
			this.attrAdd("p32k", "Enable 32768Hz", "boolean");
		}
		else {
			this.attrAdd("pSQWf", "", "integer|sel", "0;1;2;3\n1Hz;4.096kHz;8.192kHz;32.768kHz");
			this.attrDel("agOff");
			this.attrDel("t");
			this.attrDel("p32k");
		}
		mode_ = mode;
	}
	if(tm != tm_) {
		off = 0; sdt = tm.parse(0,"T",off); stm = tm.parse(0,"T",off);
		off = 0; year = sdt.parse(0,"-",off).toInt(16); month = sdt.parse(0,"-",off).toInt(16); day = sdt.parse(0,"-",off).toInt(16);
		off = 0; hour = stm.parse(0,":",off).toInt(16); min = stm.parse(0,":",off).toInt(16); sec = stm.parse(0,":",off).toInt(16);
		tr.messIO(SYS.strFromCharCode(addr,0,sec,min,hour,0,day,month+((year&0xF00)?0x80:0),year&0xFF), 0, 0);
		tm_ = tm;
	}
	if(mode == 0 && !(tVl=this.agOff.get()).isEVal() && tVl != agOff_) {
		tr.messIO(SYS.strFromCharCode(addr,16,tVl), 0, 0);
		agOff_ = tVl;
	}
	if(mode == 0 && !(tVl=this.p32k.get()).isEVal() && tVl != p32k_) {
		resp = tr.messIO(SYS.strFromCharCode(addr,15), 0, 1);
		if(resp.length != 1) t_err = "3:"+tr("Wrong or empty response.");
		else {
			cntrB = resp.charCodeAt(0);
			cntrB = tVl ?	cntrB|0x08 : cntrB&(~0x08);
			tr.messIO(SYS.strFromCharCode(addr,15,cntrB), 0, 0);
		}
		p32k_ = tVl;
	}
	if(pSQW != pSQW_) {
		if(mode == 0) {
			resp = tr.messIO(SYS.strFromCharCode(addr,14), 0, 1);
			if(resp.length != 1) t_err = "3:"+tr("Wrong or empty response.");
			else {
				cntrB = resp.charCodeAt(0);
				cntrB = pSQW ? cntrB&(~0x04) : cntrB|0x04;
				tr.messIO(SYS.strFromCharCode(addr,14,cntrB), 0, 0);
			}
		}
		else {
			resp = tr.messIO(SYS.strFromCharCode(addr,7), 0, 1);
			if(resp.length != 1) t_err = "3:"+tr("Wrong or empty response.");
			else {
				cntrB = resp.charCodeAt(0);
				cntrB = pSQW ? cntrB|0x10 : cntrB&(~0x10);
				tr.messIO(SYS.strFromCharCode(addr,7,cntrB), 0, 0);
			}
		}
		pSQW_ = pSQW;
	}
	if(pSQWf != pSQWf_) {
		pSQWf = max(0, min(3,pSQWf));
		if(mode == 0) {
			resp = tr.messIO(SYS.strFromCharCode(addr,14), 0, 1);
			if(resp.length != 1) t_err = "3:"+tr("Wrong or empty response.");
			else {
				cntrB = (resp.charCodeAt(0)&(~0x18)) + (pSQWf<<3);
				tr.messIO(SYS.strFromCharCode(addr,14,cntrB), 0, 0);
			}
		}
		else {
			resp = tr.messIO(SYS.strFromCharCode(addr,7), 0, 1);
			if(resp.length != 1) t_err = "3:"+tr("Wrong or empty response.");
			else {
				cntrB = (resp.charCodeAt(0)&(~0x3)) + pSQWf;
				tr.messIO(SYS.strFromCharCode(addr,7,cntrB), 0, 0);
			}
		}
		pSQWf_ = pSQWf;
	}

	//Get current
	rSz = (mode == 0) ? 19 : 8;
	resp = tr.messIO(SYS.strFromCharCode(addr,0), 0, rSz);
	if(resp.length != rSz) t_err = "3:"+tr("Wrong or empty response.");
	else {
		// Get current time
		tm_ = tm = (20+(resp.charCodeAt(5)>>7)).toString(10,2)+resp.charCodeAt(6).toString(16,2)+"-" +
				(resp.charCodeAt(5)&0x7F).toString(16,2)+"-" + 
				resp.charCodeAt(4).toString(16,2)+"T" +
				(resp.charCodeAt(2)&0x3F).toString(16,2)+":" +
				resp.charCodeAt(1).toString(16,2)+":" + 
				resp.charCodeAt(0).toString(16,2);

		if(mode == 0) {
			// Aging offset
			if((tVl=resp.charCodeAt(16)) >= 128)	tVl = tVl-256;
			this.agOff.set(agOff_=tVl, 0, 0, true);

			// Miscellaneous
			this.p32k.set((p32k_=resp.charCodeAt(15)&0x08), 0, 0, true);

			pSQW = pSQW_ = !(resp.charCodeAt(14)&0x04);
			pSQWf = pSQWf_ = (resp.charCodeAt(14)>>3)&0x3;

			// Get temperature
			tVl = (resp.charCodeAt(17)<<2)+(resp.charCodeAt(18)>>6);
			if(tVl >= 128) tVl = tVl-256;
			this.t.set(tVl/4, 0, 0, true);
		}
		else {
			pSQW = pSQW_ = resp.charCodeAt(7)&0x10;
			pSQWf = pSQWf_ = resp.charCodeAt(7)&0x3;
		}
	}
}

if(t_err.toInt() && !f_err.toInt()) t = p = EVAL;
f_err = t_err;


ID: PCF8574
NAME: I2C: PCF8574
DESCR:
I2C 8-bit 8DIO. Connect through a Serial output transport into the I2C mode.
Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.1

PROGRAM:
JavaLikeCalc.JavaScript
//Set transport
if(f_start) {
	f_err = "0";
	transport_ = transport;
	tr = SYS.Transport.Serial.nodeAt("out_"+transport);
	di0 = di1 = di2 = di3 = di4 = di5 = di6 = di7 = EVAL;
	do0 = do1 = do2 = do3 = do4 = do5 = do6 = do7 = true;
	do0_ = do1_ = do2_ = do3_ = do4_ = do5_ = do6_ = do7_ = true;
}

t_err = "0";

//Check for the transport change and connect
if(!tr || transport != transport_)	{
	tr = SYS.Transport.Serial.nodeAt("out_"+transport);
	transport_ = transport;
}
if(!tr)	t_err = "1:"+tr("Output transport '%1' error.").replace("%1", transport);
else if(addr < 0 || addr > 119)	t_err = "2:"+tr("Device address '%1' out of range [0...119].").replace("%1",addr);
else {
	if(do0 != do0_ || do1 != do1_ || do2 != do2_ || do3 != do3_ || do4 != do4_ || do5 != do5_ || do6 != do6_ || do7 != do7_) {
		tr.messIO(SYS.strFromCharCode(addr,(do0?0x01:0)|(do1?0x02:0)|(do2?0x04:0)|(do3?0x08:0)|
																(do4?0x10:0)|(do5?0x20:0)|(do6?0x40:0)|(do7?0x80:0)), 0, 0);
		do0_ = do0; do1_ = do1; do2_ = do2; do3_ = do3; do4_ = do4; do5_ = do5; do6_ = do6; do7_ = do7;
	}
	rez = tr.messIO(SYS.strFromCharCode(addr), 0, 1);
	if(rez.length) {
		vl = rez.charCodeAt(0);
		di0 = (vl&0x01); di1 = (vl&0x02); di2 = (vl&0x04); di3 = (vl&0x08);
		di4 = (vl&0x10); di5 = (vl&0x20); di6 = (vl&0x40); di7 = (vl&0x80);
	}
	else t_err = "3:"+tr("No read result.");
}

if(t_err.toInt() && !f_err.toInt())
	di0 = di1 = di2 = di3 = di4 = di5 = di6 = di7 = EVAL;

f_err = t_err;


ID: PCF8591
NAME: I2C: PCF8591
DESCR:
I2C 8-bit 4xA/D and D/A converter. Connect through a Serial output transport into the I2C mode.
Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.1

PROGRAM:
JavaLikeCalc.JavaScript
//Set transport
if(f_start) {
	f_err = "0";
	transport_ = transport;
	tr = SYS.Transport.Serial.nodeAt("out_"+transport);
}

t_err = "0";

//Check for the transport change and connect
if(!tr || transport != transport_)	{
	tr = SYS.Transport.Serial.nodeAt("out_"+transport);
	transport_ = transport;
}
if(!tr)	t_err = "1:"+tr("Output transport '%1' error.").replace("%1", transport);
else if(addr < 0 || addr > 119)	t_err = "2:"+tr("Device address '%1' out of range [0...119].").replace("%1",addr);
else {
	for(i = 0; i < 4 && !t_err.toInt(); i++) {
		//Select next channel and the output
		tr.messIO(SYS.strFromCharCode(addr,0x40|((i+1)&0x3),255*max(0,min(1,ao/vRef))), 0, 0);
		SYS.sleep(1e-3);
		rez = tr.messIO("", 0, 1);
		if(rez.length)	arguments["ai"+i] = vRef*(rez.charCodeAt(0)/255);
		else t_err = "3:"+tr("No read result.");
	}
}

if(t_err.toInt() && !f_err.toInt())
	for(i = 0; i < 4; i++)
		arguments["ai"+i] = EVAL;

f_err = t_err;


ID: BME280
NAME: I2C: BME280
DESCR:
I2C Pressure, Temperature and Humidity sensor. Connect through a Serial output transport into the I2C mode.
Author: Arcadiy Kisel, Roman Savochenko <roman@oscada.org>
Version: 1.0.1

PROGRAM:
JavaLikeCalc.JavaScript
//Initial set
if(f_start) {
	f_err = "0";
	transport_ = transport;
	tr = SYS.Transport.Serial.nodeAt("out_"+transport);
	dig_T1 = 0;
}

t_err = "0";

//Check for the transport change and connect
if(!tr || transport != transport_)	{
	tr = SYS.Transport.Serial.nodeAt("out_"+transport);
	transport_ = transport;
}
if(!tr)	t_err = "1:"+tr("Output transport '%1' error.").replace("%1",transport);
else if(addr < 0 || addr > 119)	t_err = "2:"+tr("Device address '%1' out of range [0...119].").replace("%1",addr);
else {
	resp = Special.FLibSYS.IO("", "", "l");
	//Calibration data, from register 0x88 and count 24 
	if(!dig_T1) {
		resp.string = tr.messIO(SYS.strFromCharCode(addr,0x88), 0, 24); //start address 88, count = 24
		if(resp.length != 24)	t_err = "3:"+tr("Wrong or empty respond to the calibration T1-3 or P1-9 request.");
		else {
			dig_T1 = resp.read("uint16", 1);	dig_T2 = resp.read("int16", 1);	dig_T3 = resp.read("int16", 1);
			dig_P1 = resp.read("uint16", 1);	dig_P2 = resp.read("int16", 1);	dig_P3 = resp.read("int16", 1);
			dig_P4 = resp.read("int16", 1);	dig_P5 = resp.read("int16", 1);	dig_P6 = resp.read("int16", 1);
			dig_P7 = resp.read("int16", 1);	dig_P8 = resp.read("int16", 1);	dig_P9 = resp.read("int16", 1);
		}
		resp.pos = 0;
		resp.string = tr.messIO(SYS.strFromCharCode(addr,0xA1), 0, 1); //start address A1, count = 1
		if(resp.length != 1) t_err = "3:"+tr("Wrong or empty respond to the calibration H1 request.");
		else dig_H1 = resp.read("uint8",1);
		resp.pos = 0;
		resp.string = tr.messIO(SYS.strFromCharCode(addr,0xE1), 0, 7); //start address E1, count = 8
		if(resp.length != 7) t_err = "3:"+tr("Wrong or empty respond to the calibration H2-H6 request.");
		else {
			dig_H2 = resp.read("int16", 1);
			dig_H3 = resp.read("uint8", 1);

			E4 = resp.read("int8", 1);
			E5 = resp.read("int8", 1);
			E6 = resp.read("int8", 1);
			// dig_H4, dig_H5 is signed. may be bug here, when <0
			dig_H4 = (E4 << 4) | (E5 & 0x0F);
			dig_H5 = (E6 << 4) | ((E5 >> 4) & 0x0F);
			dig_H6 = resp.read("int8", 1);
		}
	}

	oss = max(0, min(7,oss));

	//Pressure and Temperature
	tr.messIO(SYS.strFromCharCode(addr,0xF4,(oss<<5)|(oss<<2)|0x3), 0, 0);
	
	//Humidity
	tr.messIO(SYS.strFromCharCode(addr,0xF2,oss), 0, 0);	

	SYS.sleep((1.25 + (2.3*oss) + 2*(2.3*oss + 0.575))*1e-3); 

	//Read and calculate temperature
	if(dig_T1 && !t_err.toInt()) {
		// Read value
		resp = Special.FLibSYS.IO("", "", "b");
		resp.pos = 0;
		resp.string = tr.messIO(SYS.strFromCharCode(addr,0xFA), 0, 4);
		if(resp.length != 4) t_err = "3:"+tr("Wrong or empty respond to the temperature data.");
		else {
			adc_T = resp.read("uint32", 1) >> 12; //8 бит т.к. прочли четвертый "лишний" байт. 4 бит по даташиту
			var1 = (adc_T/16384 - dig_T1/1024)*dig_T2;
			var2 = ((adc_T/131072 - dig_T1/8192)*(adc_T/131072 - dig_T1/8192))*dig_T3;
			t_fine = var1 + var2; //for pressure
			t = (var1 + var2)/5120;
		}
	}

	//Read and calculate pressure
	if(dig_T1 && !t_err.toInt()) {
		// Read value
		resp.pos = 0; 
		resp.string = tr.messIO(SYS.strFromCharCode(addr,0xF7), 0, 4);
		if(resp.length != 4) t_err = "3:"+tr("Wrong or empty respond to the pressure data.");
		else {
			adc_P = resp.read("uint32", 1) >> 12; //8 бит т.к. прочли четвертый "лишний" байт. 4 бит по даташиту
			
			var1 = t_fine/2 - 64000;
			var2 = var1*var1*dig_P6/32768;
			var2 = var2 + var1*dig_P5*2;
			var2 = var2/4 + dig_P4*65536;
			var1 = (dig_P3*var1*var1/524288 + dig_P2*var1)/524288;
			var1 = (1 + var1/32768)*dig_P1;
			if(var1 == 0)	p = EVAL_REAL; // avoid exception caused by division by zero
			else {
				p = 1048576 - adc_P;
				p = (p - (var2/4096))*6250/var1;
				var1 = dig_P9*p*p/2147483648;
				var2 = p*dig_P8/32768;
				p = p + (var1 + var2 + dig_P7)/16;
			}
		}
	}
	
	//Read and calculate humidity
	if(dig_T1 && !t_err.toInt()) {
		resp.pos = 0;
		resp.string = tr.messIO(SYS.strFromCharCode(addr,0xFD), 0, 2);
		if(resp.length != 2) t_err = "3:"+tr("Wrong or empty respond to the humidity data.");
		else {
			adc_H = resp.read("uint16", 1);
			
			h = t_fine - 76800;
			h = (adc_H - (dig_H4*64 + dig_H5/16384*h))*(dig_H2/65536*(1 + dig_H6/67108864*h*(1 + dig_H3/67108864*h)));
			h = max(0, min(100, h*(1 - dig_H1*h/524288)));
		}
	}	
}

if(t_err.toInt() && !f_err.toInt()) t = p = h = EVAL;

f_err = t_err;


ID: SHT3x
NAME: I2C: SHT3x
DESCR:
Digital Temperature and Humidity Sensor for the models: SHT30
Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.0

PROGRAM:
JavaLikeCalc.JavaScript
//Set transport
if(f_start) {
	f_err = "0";
	transport_ = transport;
	tr = SYS.Transport.Serial.nodeAt("out_"+transport);
	H = T = EVAL;
	return;
}
if(f_stop)	return;

t_err = "0";

//Check for the transport change and connect
if(!tr || transport != transport_)	{
	tr = SYS.Transport.Serial.nodeAt("out_"+transport);
	transport_ = transport;
}
if(!tr)	t_err = "1:"+tr("Output transport '%1' error.").replace("%1", transport);
else if(addr < 0 || addr > 119)	t_err = "2:"+tr("Device address '%1' out of range [0...119].").replace("%1",addr);
else {
	if(H.isEVal()) {
		if(f_frq < 0.5)		spdCfg = 0x2032;	//0.5mps
		else if(f_frq < 1)	spdCfg = 0x2130;	//1.0mps
		else if(f_frq < 2)	spdCfg = 0x2236;	//2.0mps
		else if(f_frq < 4)	spdCfg = 0x2334;	//4.0mps
		else spdCfg = 0x2737;						//10.0mps
		//SYS.messInfo("SMH", "spdCfg="+spdCfg.toString(16)+"; frq="+f_frq+"; tm="+(1/max(0.5,f_frq)));
		tr.messIO(SYS.strFromCharCode(addr,(spdCfg>>8)&0xFF,spdCfg&0xFF), 0, 0);	//10 mps
		SYS.sleep(1/max(0.5,f_frq));
	}

	rez = tr.messIO(SYS.strFromCharCode(addr), 0, 6);
	if(rez.length == 6) {
		T = 175*(((rez.charCodeAt(0)<<8)+rez.charCodeAt(1))/65535) - 45;
		H = 100*(((rez.charCodeAt(3)<<8)+rez.charCodeAt(4))/65535);
	}
	else { t_err = "3:"+tr("No data."); H = T = EVAL; }
}

f_err = t_err;


ID: ADS111x
NAME: I2C: ADS101x, ADS111x
DESCR:
I2C 12/16-bit 4xA/D converter. Connect through a Serial output transport into the I2C mode.
Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.1

PROGRAM:
JavaLikeCalc.JavaScript
//Set transport
if(f_start) {
	f_err = "0";
	transport_ = transport;
	tr = SYS.Transport.Serial.nodeAt("out_"+transport);
}

t_err = "0";

//Check for the transport change and connect
if(!tr || transport != transport_)	{
	tr = SYS.Transport.Serial.nodeAt("out_"+transport);
	transport_ = transport;
}
if(!tr)	t_err = "1:"+tr("Output transport '%1' error.").replace("%1", transport);
else if(addr < 0 || addr > 119)	t_err = "2:"+tr("Device address '%1' out of range [0...119].").replace("%1",addr);
else {
	for(i = 0; i < 4 && !t_err.toInt(); i++) {
		//Selecting the channel and setting for needed options 
		sw = 0xC000 + (i<<12) + (range<<9) + 0x100 + 0x83;
		// Writing for the configuration
		tr.messIO(SYS.strFromCharCode(addr,1,sw>>8,sw&0xFF), 0, 0);
		// Waiting the conversion
		for(iTr = 0; iTr < 10 && (rez=tr.messIO(SYS.strFromCharCode(addr,1),0,2)).length && !(rez.charCodeAt(0)&0x80); iTr++)
			SYS.sleep(1e-3);
		// Reading for the value
		rez = tr.messIO(SYS.strFromCharCode(addr,0), 0, 2);
		if(rez.length) {
			tVl = (rez.charCodeAt(0)<<8) + rez.charCodeAt(1);
			if(tVl&0x8000) tVl -= 65536;
			range_ = (range == 0) ? 6.144 : 4.096/pow(2,range-1);
			arguments["ai"+i] = range_*tVl/32768;
		}
		else t_err = "3:"+tr("No read result.");
	}
}

if(t_err.toInt() && !f_err.toInt())
	for(i = 0; i < 4; i++)
		arguments["ai"+i] = EVAL;

f_err = t_err;


ID: MCP4725
NAME: I2C: MCP4725
DESCR:
I2C 12-bit D/A converter. Connect through a Serial output transport into the I2C mode.
Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.0

PROGRAM:
JavaLikeCalc.JavaScript
//Set transport
if(f_start) {
	f_err = "0";
	transport_ = transport;
	tr = SYS.Transport.Serial.nodeAt("out_"+transport);
	ao = 0;
	ao_ = EVAL;
}

t_err = "0";

//Check for the transport change and connect
if(!tr || transport != transport_)	{
	tr = SYS.Transport.Serial.nodeAt("out_"+transport);
	transport_ = transport;
}
if(!tr)	t_err = "1:"+tr("Output transport '%1' error.").replace("%1", transport);
else if(addr < 0 || addr > 119)	t_err = "2:"+tr("Device address '%1' out of range [0...119].").replace("%1",addr);
else {
	//Writing the changes
	if(!ao_.isEVal() && ao != ao_) {
		code = max(0,min(1,ao/vRef)) * 4096;
		tr.messIO(SYS.strFromCharCode(addr,code/256,code%256), 0, 0);
	}

	//Reading the current value	
	rez = tr.messIO(SYS.strFromCharCode(addr), 0, 3);
	if(rez.length)	ao = ao_ = vRef*(rez.charCodeAt(1)*255+rez.charCodeAt(2)>>4)/4096;
	else t_err = "3:"+tr("No read result.");
}

if(t_err.toInt() && !f_err.toInt())	ao_ = EVAL;

f_err = t_err;


ID: MAX6675
NAME: GPIO: MAX6675
DESCR:
Cold-Junction-Compensated K-Thermocouple-to-Digital Converter (0°C to +1024°C). The module designed for the sensors connect through softSPI by GPIO, mostly it's Raspberry PI BCM2835 GPIO.
Conditions: Exclusively realtime planing in the priority 199 (FIFO-99).
Author: Arcadiy Kisel <arcsin@online.ua>
Version: 0.1.0

PROGRAM:
JavaLikeCalc.JavaScript
using Special.FLibSYS;

//Set link to fast external functions
if(f_start)	f_err = "0", addr_ = "";

t_err = "0";

//Check for the address change and link
if(addr != addr_)	{
	addr_ = addr;
	function mode = addr+".fnc_mode";
	function get = addr+".fnc_get";
	function put = addr+".fnc_put";
}

if(f_start){
	mode(pin_cs, 4); put(pin_cs, true); 	//Set pin to output mode and next to true for 500us
	mode(pin_sclk, 4); put(pin_sclk, false); //Set pin to output mode and next to true for 500ms
	mode(pin_miso, 1); 
	tmSleep(1e-3);
}

//Read sensor's data function declaration
function spiread() {
	d = 0;
	for(i = 7; i >= 0; i--) {
		put(pin_sclk, false); tmSleep(1e-3);							//Set output to false for 20ms
		if(get(pin_miso))	d = d | (1 << i);
  		put(pin_sclk, true); tmSleep(1e-3);
	}
	return d;
}

//Read
put(pin_cs, false); tmSleep(1e-3);		
v = spiread();
v = v << 8;
v = v | spiread();

put(pin_cs, true); 		
if(v&0x4)	t_err = "1:"+tr("uh oh, no thermocouple attached!"); 
else	t = (v>>3)*0.25;

if(t_err.toInt() && !f_err.toInt()) t = EVAL;
f_err = t_err;


# tmplib_tests

ID: ai_simple
NAME: Simple AI
uk#NAME: Простий AI
ru#NAME: Простой AI
DESCR: Simple analog parameter.
uk#DESCR: Простий аналоговий параметр.
ru#DESCR: Простой аналоговый параметр.
PROGRAM:
JavaLikeCalc.JavaScript
val=val_cod;
if(val!=EVAL_REAL)
{
  val=bDwn+(bUp-bDwn)*(val/27648.0);
  if(bUp!=bDwn && val>bUp)		f_err="101:Upper work border violation";
  else if(bUp!=bDwn && val<bDwn)	f_err="102:Lower work border violation";
  else if(bErrUp!=bErrDwn && val>bErrUp)	f_err="103:Upper alarm border violation";
  else if(bErrUp!=bErrDwn && val<bErrDwn)	f_err="104:Lower alarm border violation";
  else if(bWUp!=bWDwn && val>bWUp)	f_err="105:Upper warning border violation";
  else if(bWUp!=bWDwn && val<bWDwn)	f_err="106:Lower warning border violation";
  else f_err="0";
}


ID: ai_man
NAME: Manual input of AI
uk#NAME: Ручний ввід AI
ru#NAME: Ручной ввод AI
DESCR: Manual input of a standard analog parameter.
uk#DESCR: Стандартний аналоговий параметр ручного вводу.
ru#DESCR: Стандартный аналоговый параметр ручного ввода.
PROGRAM:
JavaLikeCalc.JavaScript
//Check and write new value
if(val!=val_o)
{
  val_cod=(27648.0*(val-bDwn))/(bUp-bDwn);
  val_o=val;
}
else
{
  val=val_cod;
}

if(val!=EVAL_REAL)
{
  val=val_o=bDwn+(bUp-bDwn)*(val/27648.0);
  if(bUp!=bDwn && val>bUp)		f_err="101:Upper work border violation";
  else if(bUp!=bDwn && val<bDwn)	f_err="102:Lower work border violation";
  else if(bErrUp!=bErrDwn && val>bErrUp)	f_err="103:Upper alarm border violation";
  else if(bErrUp!=bErrDwn && val<bErrDwn)	f_err="104:Lower alarm border violation";
  else if(bWUp!=bWDwn && val>bWUp)	f_err="105:Upper warning border violation";
  else if(bWUp!=bWDwn && val<bWDwn)	f_err="106:Lower warning border violation";
  else f_err="0";
}


ID: gasPoint
NAME: Flow control point
PROGRAM:
JavaLikeCalc.JavaScript
if(f_start)	Q = 0;
//F=200+(rand(5)-2.5);
Q += F.isEVal() ? 0 : F/3600;
//P=9+(rand(2)-1);
//T=15+(rand(2)-1);
dP = F.isEVal() ? 0 : F/33;
DS = 1+(rand(0.2)-0.1);


# UserProtocol_uPrt

ID: SMS
NAME: SMS (replaced)
DESCR:
!!!!: Replaced by the complex template Main.ntf. Will be removed soon
Provides operations with SMS by GSM-modem connected as serial device. For now supported only sending SMS messages to a number of remote cell phone or GSM modem.
Author: Roman Savochenko <roman@oscada.org>
Version: 2.1.1

OutPROG:
JavaLikeCalc.JavaScript
//Request form:
//<{cmd} pin="1111" tel="+380XXXXXXXXX" text="{0|1}" err="1:Error">{SMSText}</cmd>
//  cmd - command, for now only "send" allowed;
//  pin - PIN code to the SIM card access;
//  tel - telephone number for receiver (remote cell phone or GSM modem);
//  text{0,1} - SMS in the text mode, only ASCII
//  SMSText - the message text;
//  err - sets for the request result.

//Transport's timings check
if(tr.timings() != "5000:100")	tr.timings("5000:100");

if(io.name() == "send") {
	tel = io.attr("tel");
	if(!tel.length || tel[0] != "+") { io.setAttr("err", "100:"+tr("Telephone number error.")); return; }

	//Disable ECHO
	rez = tr.messIO("ATE0\r");
	while(rez.length && (trez=tr.messIO("")).length) rez += trez;
	if(rez.indexOf("OK\r") < 0)	{ io.setAttr("err", "101:"+tr("Error disabling ECHO: %1").replace("%1",rez)); return; }

	//Set PIN
	if(io.attr("pin").length) {
		//Check for PIN
		rez = tr.messIO("AT+CPIN?\r");
		while(rez.length && (trez=tr.messIO("")).length) rez += trez;
		if(rez.indexOf("OK\r") < 0)	{ io.setAttr("err", "102:"+tr("Error checking for PIN-code: %1").replace("%1",rez)); return; }
		if(rez.indexOf("+CPIN: READY\r") < 0) {	//PIN is needed
			rez = tr.messIO("AT+CPIN="+io.attr("pin")+"\r");
			while(rez.length && (trez=tr.messIO("")).length) rez += trez;
			if(rez.indexOf("OK\r") < 0)	{ io.setAttr("err", "102:"+tr("Error setting PIN: %1").replace("%1",rez)); return; }
		}
	}

	if(io.attr("text").toInt()) {	//SMS in the text mode
		//Switch to Text SMS mode
		rez = tr.messIO("AT+CMGF=1\r");
		while(rez.length && (trez=tr.messIO("")).length) rez += trez;
		if(rez.indexOf("OK\r") < 0)	{ io.setAttr("err", "103:"+tr("Error setting the SMS Text mode: %1").replace("%1",rez)); return; }

		//Send the Telephone number
		rez = tr.messIO("AT+CMGS=\""+tel+"\"\r");
		while(rez.length && (trez=tr.messIO("")).length) rez += trez;
		if(rez.indexOf(">") < 0)	{ io.setAttr("err", "104:"+tr("Error sending SMS: %1").replace("%1",rez)); return; }
		rez = tr.messIO(io.text()+"\x1A");
		while(rez.length && (trez=tr.messIO("")).length) rez += trez;
		//for(var iTr = 0; iTr < 100 && rez.indexOf("OK\r") < 0; iTr++) rez += tr.messIO("");	//Up to 10 seconds wait for reply
		if(rez.indexOf("OK\r") < 0)	{ io.setAttr("err", "104:"+tr("Error sending SMS PDU: %1").replace("%1",rez)); return; }
	}
	else {	//SMS in the PDU mode
		// Switch to PDU SMS mode
		rez = tr.messIO("AT+CMGF=0\r");
		while(rez.length && (trez=tr.messIO("")).length) rez += trez;
		if(rez.indexOf("OK\r") < 0)	{ io.setAttr("err", "103:"+tr("Error setting the SMS PDU mode: %1").replace("%1",rez)); return; }

		//Prepare PDU
		pdu = "001100";	//SMS center number (default) + SMS-Submit
		//Telephone number encode
		tel = tel.slice(1);
		pdu += tel.length.toString(16,2) + "91";	//Telephone length and type
		while(tel.length < 12) tel += "F";
		for(i = 0; i < 6; i++) pdu += tel[i*2+1]+tel[i*2];
		//Message encode
		text = SYS.strCodeConv(io.text(),"","UCS2");
		if((text.length/2) > 70)	text = text.slice(0,140);
		//{ io.setAttr("err", "104:"+tr("Too long length (%1) of the message.").replace("%1",(text.length/2).toString())); return; }
		pdu += "0018C1"+(text.length).toString(16,2);
		for(i = 0; i < text.length/2; i++) pdu += text.charCodeAt(i*2+1).toString(16,2)+text.charCodeAt(i*2).toString(16,2);
		//SYS.messDebug("TEST SMS","PDU :"+pdu);

		// Send the PDU message
		rez = tr.messIO("AT+CMGS="+(pdu.length/2-1)+"\r");
		while(rez.length && (trez=tr.messIO("")).length) rez += trez;
		if(rez.indexOf(">") < 0)	{ io.setAttr("err", "104:"+tr("Error sending SMS: %1").replace("%1",rez)); return; }
		rez = tr.messIO(pdu+"\x1A");
		while(rez.length && (trez=tr.messIO("")).length) rez += trez;
		//for(var iTr = 0; iTr < 100 && rez.indexOf("OK\r") < 0; iTr++) rez += tr.messIO("");	//Up to 10 seconds wait for reply
		if(rez.indexOf("OK\r") < 0)	{ io.setAttr("err", "104:"+tr("Error sending SMS PDU: %1").replace("%1",rez)); return; }
		//SYS.messDebug("TEST SMS","PDU REZ :"+rez);
	}
	io.setAttr("err", "0");
}


ID: SCU750
NAME: EDWARDS TURBOMOLECULAR PUMPS
DESCR:
Protocol level of typical EDWARDS TURBOMOLECULAR PUMPS (http://edwardsvacuum.com) data request by SCU750 Cotrol Unit protocol.
Author: Roman Savochenko <roman@oscada.org>
Sponsored: Vasiliy Grigoriev from "Vacuum technologies laboratory (http://e-beam.ru)".
Version: 1.0.0

OutPROG:
JavaLikeCalc.JavaScript
//Request form:
//<mess addr="1" err="1:Error">{req}</mess> - message tag
//  req - request/respond data;
//  addr - remote station address (<0 - single; >=0 - multi port);
//  err - sets for the request result.
if(io.text().length > 255*255) { io.setAttr("err","1:"+tr("Message's length more 255*255")); return; }
addr = io.attr("addr").toInt();
k = ceil(io.text().length/255);	//transmission blocks
for(i_k = 1; i_k <= k; i_k++) {
	request = "\x02"+k.toString(16,3) + io.text().slice((i_k-1)*255,(i_k-1)*255+min(255,io.text().length-(i_k-1)*255)) + ((k>1&&i_k<k)?"\x17":"\x03");
	// Calc LRC
	LRC = 0xFF;
	for(i = 0; i < request.length; i++) LRC = LRC^request.charCodeAt(i);
	request += SYS.strFromCharCode(LRC);

	// Multy port
	if(addr>=0) request = "@"+addr.toString(16,2)+request;
	//SYS.messDebug("PRT","Request: "+Special.FLibSYS.strDec4Bin(request));

	//Send the request
	resp = tr.messIO(request);
	while(resp.length) {
		tresp = tr.messIO("");
		if(!tresp.length) break;
  		resp += tresp;
	}
	if(!resp.length) { io.setAttr("err","2:"+tr("No respond")); return; }
	//SYS.messDebug("PRT","Ack: "+Special.FLibSYS.strDec4Bin(resp));
	if(resp.charCodeAt(0) != 6) { io.setAttr("err","3:"+tr("No acknowledgment")); return; }
	// Pass included acknowledgement
	resp = resp.slice((addr>=0)?3:1);

	// Read data blocks
	io.setText("");
	for(i_k = 1; true; i_k++) {
		//Send application acknowledgement and wait data
		if(!resp.length) {
			request = "\x06";
			if(addr >= 0) request += addr.toString(16,2);
			resp = tr.messIO(request);
			while(resp.length) {
				tresp = tr.messIO("");
				if(!tresp.length) break;
  				resp += tresp;
			}
			if(!resp.length) { io.setAttr("err","4:"+tr("No data block get")); return; }
		}
		if(resp.length < ((addr>=0)?10:7) || resp.charCodeAt(0) != 0x40) { io.setAttr("err","5:"+tr("Data block short or error")); return; }

		//SYS.messDebug("PRT","BLK "+i_k+": "+Special.FLibSYS.strDec4Bin(resp));

		if(addr >= 0) resp = resp.slice(3);
		LRC = 0xFF;
		for(i = 0; i < (resp.length-1); i++) LRC = LRC^resp.charCodeAt(i);
		if(LRC != resp.charCodeAt(resp.length-1)) { io.setAttr("err","6:"+tr("LRC error.")); return; }
		if(i_k != resp.slice(1,4).toInt(16)) { io.setAttr("err","7:"+tr("Block sequence.")); return; }
		io.setText(io.text()+resp.slice(4,resp.length-2));
		if(resp.charCodeAt(resp.length-2) == 0x03) break;
		if(resp.charCodeAt(resp.length-2) == 0x17) { resp = ""; continue; }
		io.setAttr("err","8:"+tr("Unknown block end."));
		return;
	}
}


ID: TMH
NAME: Power supply for turbomolecular pumps
DESCR:
Protocol level of power supply for turbomolecular pumps of firm SHIMADZU (http://www.shimadzu.com), model EI-R04M.
Author: Roman Savochenko <roman@oscada.org>
Sponsored: Vasiliy Grigoriev from "Vacuum technologies laboratory (http://e-beam.ru)".
Version: 1.0.0

OutPROG:
JavaLikeCalc.JavaScript
//Request form:
//<mess addr="1" err="1:Error">{req}</mess> - message tag
//  req - request/respond data;
//  addr - remote station address (1...32);
//  err - sets for the request result.

io.setAttr("err","");
addr = io.attr("addr").toInt();
if(addr < 1 || addr > 32) { io.setAttr("err","1:"+tr("Device address out of range 1...32")); return; }
request = "MJ"+addr.toString(10,2)+io.text();
//Calc CRC
CRC = 0;
for(i = 0; i < request.length; i++) CRC += request.charCodeAt(i);
request += (CRC&0xFF).toString(16,2)+"\r";
//SYS.messDebug("PRT","Request: "+Special.FLibSYS.strDec4Bin(request));

//Send request
resp = tr.messIO(request);
while(resp.length && resp[resp.length-1] != "\r") {
	tresp = tr.messIO("");
	if(!tresp.length) break;
  	resp += tresp;
}
if(resp.length < 6 || resp[resp.length-1] != "\r" || resp.slice(0,2) != "MJ" || resp.slice(2,4).toInt() != addr)
{ io.setAttr("err","2:"+tr("No or error respond")); return; }
//SYS.messDebug("PRT","Respond: "+Special.FLibSYS.strDec4Bin(resp));
CRC = 0;
for(i = 0; i < (resp.length-3); i++) CRC += resp.charCodeAt(i);
if((CRC&0xFF) != resp.slice(resp.length-3,resp.length-1).toInt(16)) { io.setAttr("err","6:"+tr("CRC error.")); return; }
io.setText(resp.slice(4,resp.length-3));


ID: VKT7
NAME: VKT-7
DESCR:
Protocol level of firm "Teplocom" (http://www.teplocom.spb.ru) computer "VKT-7" for complex heat measurement and counting. The device complex enough to provide more parameters, more history and accessed by a nonlinear Serial-based protocol at low speed. The template implements acquisition for all significant parameters, gets for their history by hours, days and result months. Also you can to append easily enough for processing of the remained parameters.
Author: Roman Savochenko <roman@oscada.org>
Sponsored: Vladislav Chubuk
Version: 1.0.0

OutPROG:
JavaLikeCalc.JavaScript
//Request form:
//<mess addr="1" err="1:Error">{req}</mess> - message tag
//  req - request/respond data;
//  addr - remote station address (0...254);
//  err - sets for the request result.
io.setAttr("err", "");
addr = io.attr("addr").toInt();
if(addr < 0 || addr > 254) { io.setAttr("err", "1:"+tr("Device address out of range 0...254")); return; }
request = SYS.strFromCharCode(addr) + io.text();
//Calc KS
KS = 0xFFFF;
for(i = 0; i < request.length; i++) {
	KS = KS ^ request.charCodeAt(i);
	for(j = 0; j < 8; j++)
		KS = (KS&0x01) ? (KS >> 1)^0xA001 : (KS >> 1);
}
request = SYS.strFromCharCode(0xFF,0xFF) + request + SYS.strFromCharCode(KS,KS>>8);
SYS.messDebug("/VKT7/PRT","Request: "+Special.FLibSYS.strDec4Bin(request));

//Send request
resp = tr.messIO(request);
while(resp.length) {
	tresp = tr.messIO("");
	if(!tresp.length) break;
  	resp += tresp;
}
if(resp.length < 4 || resp.charCodeAt(0) != addr)	{ io.setAttr("err", "2:"+tr("No or error respond")); return; }
SYS.messDebug("/VKT7/PRT","Respond: "+Special.FLibSYS.strDec4Bin(resp));

//Calc KS
KS = 0xFFFF;
for(i = 0; i < (resp.length-2); i++) {
	KS = KS ^ resp.charCodeAt(i);
	for(j = 0; j < 8; j++)
		KS = (KS&0x01) ? (KS >> 1)^0xA001 : (KS >> 1);
}
if(KS != ((resp.charCodeAt(resp.length-1)<<8)|resp.charCodeAt(resp.length-2)))
{ io.setAttr("err","6:"+tr("KS error")+" "+KS.toString(16,4)+"=="+((resp.charCodeAt(resp.length-1)<<8)|resp.charCodeAt(resp.length-2)).toString(16,4)); return; }
if(resp.charCodeAt(1)&0x80)
{ io.setAttr("err","7:"+resp.charCodeAt(2)+":"+tr("Request error.")); return; }
io.setText(resp.slice(1,-2));


ID: TM510x
NAME: Elemer TM510x
DESCR:
Protocol level of multichannels thermometer Elemer TM5102 and TM5103 of firm Elemer (http://www.elemer.ru).
Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.0

OutPROG:
JavaLikeCalc.JavaScript
//Request form:
//<mess addr="1" err="1:Error">{req}</mess> - message tag
//  req - request/respond data;
//  addr - remote station address (1...254);
//  err - sets for the request result.
io.setAttr("err", "");
addr = io.attr("addr").toInt();
if(addr < 1 || addr > 254) { io.setAttr("err","1:"+tr("Device address out of range 1...254")); return; }
request = ":"+addr.toString(10)+";"+io.text()+";";
//Calc KS
KS = 0xFFFF;
for(i = 1; i < request.length; i++) {
	KS = KS ^ request.charCodeAt(i);
	for(j = 0; j < 8; j++)
		KS = (KS&0x01) ? (KS >> 1)^0xA001 : (KS >> 1);
}
request += KS.toString(10)+"\r";
//SYS.messDebug("PRT","Request: "+request);

//Send request
resp = tr.messIO(request);
while(resp.length && resp[resp.length-1] != "\r") {
	tresp = tr.messIO("");
	if(!tresp.length) break;
  	resp += tresp;
}
if(resp.length && resp.charCodeAt(0) == 0xFF) resp = resp.slice(1);
if(resp.length < 7 || resp[resp.length-1] != "\r" ||
	resp[0] != "!" || resp.slice(1).toInt() != addr || (KSpos=resp.lastIndexOf(";")) < 0)
{ io.setAttr("err","2:"+tr("No or error respond")); return; }
//SYS.messDebug("PRT","Respond: "+resp);

//Calc KS
KS = 0xFFFF;
for(i = 1; i < min(KSpos+1,resp.length); i++) {
	KS = KS ^ resp.charCodeAt(i);
	for(j = 0; j < 8; j++)
		KS = (KS&0x01) ? (KS >> 1)^0xA001 : (KS >> 1);
}
if(KS != resp.slice(KSpos+1).toInt(10)) { io.setAttr("err","6:"+tr("KS error.")); return; }
io.setText(resp.slice(resp.indexOf(";")+1,KSpos));


ID: SMDP
NAME: Sycon Multi Drop Protocol
DESCR:
Protocol level of STM devices for resonant frequency measurement for mass of deposited films attached to its surface by Sycon Instruments, Inc. (http://www.sycon.com)
Author: Roman Savochenko <roman@oscada.org>
Sponsored: Vasiliy Grigoriev from "Vacuum technologies laboratory (http://e-beam.ru)".
Version: 1.0.0

OutPROG:
JavaLikeCalc.JavaScript
//Request form:
//<mess addr="16" try="1" err="1:Error">{req}</mess> - message tag
//req - request/respond data;
//addr - remote device address [16...254];
//try - tries for the request;
//err - sets for the request result.

if(!io.text().length) { io.setAttr("err","1:"+tr("Message empty")); return; }
addr = io.attr("addr").toInt();
try = min(5,max(1,io.attr("try").toInt()));
request = "\x02"+SYS.strFromCharCode(addr) + io.text();
//Calc CHKSUM
CRC = 0;
for(i = 1; i < request.length; i++) CRC += request.charCodeAt(i);
request += SYS.strFromCharCode((CRC>>4&0xF)+0x30,(CRC&0xF)+0x30,0x0D);

SYS.messDebug("/UserProt/SMDP",tr("Request:")+" "+Special.FLibSYS.strDec4Bin(request));

for( ; try > 0; try--) {
	//Send the request
	resp = tr.messIO(request);
	while(resp.length && resp.charCodeAt(resp.length-1) != 0x0D)
		if((tresp=tr.messIO("")).length) resp += tresp;
		else break;

	if(!resp.length)	{ io.setAttr("err","2:"+tr("No respond")); continue; }
	if(resp.length < 6 || resp.charCodeAt(resp.length-1) != 0x0D)	{ io.setAttr("err","3:"+tr("Not full respond")); continue; }
	if(resp.charCodeAt(0) != 0x02 || resp.charCodeAt(1) != addr)	{ io.setAttr("err","4:"+tr("Wrong respond")); continue; }

	SYS.messDebug("/UserProt/SMDP",tr("Respond:")+" "+Special.FLibSYS.strDec4Bin(resp));

	CRC = 0;
	for(i = 1; i < (resp.length-3); i++) CRC += resp.charCodeAt(i);
	if((CRC&0xFF) != (((resp.charCodeAt(resp.length-3)-0x30)<<4)|(resp.charCodeAt(resp.length-2)-0x30)))
	{ io.setAttr("err","6:"+tr("CRC error.")); continue; }
	break;
}
if(try <= 0)	return;

errCd = resp.charCodeAt(2)&0x7;
if(errCd == 1) io.setAttr("err","").setText(resp.slice(2,-2));
else if(errCd == 2) io.setAttr("err",""+(10+errCd)+":Err_inv_cmd: "+tr("Illegal command (CMD code not valid)."));
else if(errCd == 3) io.setAttr("err",""+(10+errCd)+":Err_syntax: "+tr("Syntax error. (Too many bytes in data field, not enough bytes, etc)."));
else if(errCd == 4) io.setAttr("err",""+(10+errCd)+":Err_range: "+tr("Data range error."));
else if(errCd == 5) io.setAttr("err",""+(10+errCd)+":Err_inhibited: "+tr("Inhibited."));
else if(errCd == 6) io.setAttr("err",""+(10+errCd)+":Err_obso: "+tr("Obsolete command. No action taken, but not really an error."));
else io.setAttr("err",""+(10+errCd)+":"+tr("Unknown error."));


ID: IT3
NAME: Temperature measurement IT-3
DESCR:
Protocol level of temperature measurement IT-3 from OmskEtalon (http://www.omsketalon.ru).
Author: Roman Savochenko <roman@oscada.org>
Sponsored: Vasiliy Grigoriev from "Vacuum technologies laboratory (http://e-beam.ru)".
Version: 1.2.0

InPROG:
JavaLikeCalc.JavaScript
// Input part of the protocol IT-3
// for work into the background/listen mode to an other interaction session.
var prm;
if(prm.isEVal())	prm = SYS.nodeAt("/DAQ/LogicLev/cryo/prm_IT3");
if(prm.isEVal())	return false;	//Drop all request but the target parameter's object wrong
if(!request.length) {					//Empty request means it is not a data for the pointed time, 1 second
	prm.err.set("1:"+tr("No data"), 0, 0, true);
	prm.T.set(EVAL, 0, 0, true);
	prm.relSt.set(EVAL, 0, 0, true);
	prm.L.set(EVAL, 0, 0, true);
	prm.H.set(EVAL, 0, 0, true);
	return true;
}
//SYS.messDebug("/IT3/IN","TEST 00: req="+SYS.strDecode(request,"Bin"," "));

while(request.length >= 6) {	//the full header
	if(request.charCodeAt(0) != 0x16)	{ request = ""; break; }		//The package is broken, drop full

	if((6+(blkLen=request.charCodeAt(1))) > request.length)	break;	//The package is not full, wait

	//The package's header control sum check and pass broken packages
	for(KS = 0, i = 1; i < 4; i++) KS += request.charCodeAt(i);
	KS = (~(KS+1))&0xFFFF;
	if(((request.charCodeAt(5)<<8)+request.charCodeAt(4)) != KS) { request = request.slice(6+blkLen); continue; }

	//The package's body control sum check and pass broken packages
	for(KS = 0, i = 6; i < (6+blkLen-2); i++) KS += request.charCodeAt(i);
	KS = (~(KS+1))&0xFFFF;
	if(((request.charCodeAt(6+blkLen-1)<<8)+request.charCodeAt(6+blkLen-2)) != KS)	{ request = request.slice(6+blkLen); continue; }

	if(request.charCodeAt(2))	{ request = request.slice(6+blkLen); continue; }	//The package is a request, pass

	//Info respond package
	if(request.charCodeAt(6) == 0x80)
		prm.err.set((errCode=request.charCodeAt(6+1))?("5:"+errCode+":"+tr("Request error")):"0", 0, 0, true);
	//Data respond package
	if(request.charCodeAt(6) == 0x85) {
		errCode = 0;//prm.err.get().toInt();
		cntrB = request.charCodeAt(6+3);
		T = errCode ? EVAL : ((request.charCodeAt(6+2)<<8)+request.charCodeAt(6+1));
		if(!T.isEVal())	T = T/((cntrB&0x8)?100:10);
		prm.T.set(T, 0, 0, true);
		prm.relSt.set(errCode?EVAL:(cntrB&0x1), 0, 0, true);
		prm.L.set(errCode?EVAL:(cntrB&0x2), 0, 0, true);
		prm.H.set(errCode?EVAL:(cntrB&0x4), 0, 0, true);
		prm.cnt.set(prm.cnt.get()+1, 0, 0, true);
	}

	request = request.slice(6+blkLen);
}

return true;

OutPROG:
JavaLikeCalc.JavaScript
//Request form:
//<mess addr="1" err="1:Error">{req}</mess> - message tag
//  req - request/respond data;
//  addr - remote station address (1...32);
//  err - sets for the request result.
io.setAttr("err", "");
addr = io.attr("addr").toInt();
if(addr < 1 || addr > 32) { io.setAttr("err","1:"+tr("Device address out of range 1...32")); return; }
//Header
reqHdr = SYS.strFromCharCode(0x16,0x03,addr,0x00);
for(KS = 0, i = 1; i < reqHdr.length; i++)	KS += reqHdr.charCodeAt(i);
KS = ~(KS+1);
reqHdr += SYS.strFromCharCode(KS, KS>>8);
//Body
reqBody = io.text();
for(KS = 0, i = 0; i < reqBody.length; i++)	KS += reqBody.charCodeAt(i);
KS = ~(KS+1);
reqBody += SYS.strFromCharCode(KS, KS>>8);
request = reqHdr+reqBody;
SYS.messDebug("/IT3/PRT",tr("Request")+": "+SYS.strDecode(request,"Bin"," "));

//Send request
resp = tr.messIO(request);
while(resp.length) {
	if(!(tresp=tr.messIO("")).length) break;
  	resp += tresp;
}
if(resp.length == 0) { io.setAttr("err","2:"+tr("No or short respond")); return; }
SYS.messDebug("/IT3/PRT","Respond: "+SYS.strDecode(resp,"Bin"," "));
for(blkOff = 0, blkLen = 0; blkOff < resp.length; blkOff += 6+blkLen) {
	if(resp.charCodeAt(blkOff) != 0x16){ io.setAttr("err","3:"+tr("Header error")); return; }
	for(KS = 0, i = (blkOff+1); i < (blkOff+4); i++) KS += resp.charCodeAt(i);
	KS = (~(KS+1))&0xFFFF;
	if(((resp.charCodeAt(blkOff+5)<<8)+resp.charCodeAt(blkOff+4)) != KS)
	{ io.setAttr("err","3:"+tr("Header CRC error")); return; }
	blkLen = resp.charCodeAt(blkOff+1);
	if(blkOff+6+blkLen > resp.length) { io.setAttr("err","4:"+tr("Info block size error")); return; }
	for(KS = 0, i = (blkOff+6); i < (blkOff+6+blkLen-2); i++) KS += resp.charCodeAt(i);
	KS = (~(KS+1))&0xFFFF;
	if(((resp.charCodeAt(blkOff+6+blkLen-1)<<8)+resp.charCodeAt(blkOff+6+blkLen-2)) != KS)
	{ io.setAttr("err","4:"+tr("Info block CRC error.")); return; }
	if(resp.charCodeAt(blkOff+6) == 0x80 && resp.charCodeAt(blkOff+7))
	{ io.setAttr("err","5:"+resp.charCodeAt(blkOff+7)+":"+tr("Request error")); return; }
	io.setText(resp.slice(blkOff+6,blkOff+6+blkLen));
}


ID: IVE_452HS_02
NAME: IVE-452HS-02
uk#NAME: IVE-452HS-02
DESCR:
Protocol level of power supply of beam-electrons evaporator of "Plasma Tech" Ltd, from Moskov.
Author: Roman Savochenko <roman@oscada.org>
Sponsored: Vasiliy Grigoriev from "Vacuum technologies laboratory (http://e-beam.ru)".
Version: 1.0.0

OutPROG:
JavaLikeCalc.JavaScript
//Request form:
//<mess addr="255" err="1:Error">{req}</mess> - message tag
//  req - request/respond data;
//  addr - remote station address (0...255);
//  err - sets for the request result.
io.setAttr("err", "");
addr = io.attr("addr").toInt();
if(addr < 0 || addr > 255) { io.setAttr("err","1:"+tr("Device address out of range 0...255")); return; }
//Request prepare
req = SYS.strFromCharCode(addr)+io.text();
for(KS = 0, i = 0; i < req.length; i++)	KS += req.charCodeAt(i);
KS = 0x100-KS&0xFF;
req += SYS.strFromCharCode(KS);
SYS.messDebug("/IVE452HS02/PRT",tr("Request")+": "+SYS.strDecode(req,"Bin"," "));

//Send request
resp = tr.messIO(req);
while(resp.length) {
	if(!(tresp=tr.messIO("")).length) break;
  	resp += tresp;
}
if(resp.length == 0) { io.setAttr("err","2:"+tr("No a respond")); return; }
SYS.messDebug("/IVE452HS02/PRT","Respond: "+SYS.strDecode(resp,"Bin"," "));
if(resp.length <= 3 || resp.charCodeAt(0) != addr || resp.charCodeAt(1) != req.charCodeAt(1))
{ io.setAttr("err","3:"+tr("Respond too short or wrong")); return; }
for(KS = 0, i = 0; i < resp.length-1; i++)	KS += resp.charCodeAt(i);
KS = 0x100-KS&0xFF;
if(resp.charCodeAt(resp.length-1) != KS )	{ io.setAttr("err","3:"+tr("CRC error")); return; }
io.setText(resp.slice(1,resp.length-1));


ID: OPTRIS
NAME: OPTRIS CT/CTL
DESCR:
Protocol level of OPTRIS CT/CTL communication interface of "Optris" GmbH, from Berlin.
Author: Roman Savochenko <roman@oscada.org>
Sponsored: Vasiliy Grigoriev from "Vacuum technologies laboratory (http://e-beam.ru)".
Version: 1.0.0

OutPROG:
JavaLikeCalc.JavaScript
//Request form:
//<mess addr="1" cs="1" err="1:Error">{req}</mess> - message tag
//  req - request/respond data;
//  addr - remote station address (0...15);
//  cs - use control sum for SET commands [0,1]
//  err - sets for the request result.
io.setAttr("err", "");
addr = io.attr("addr").toInt();
if(addr < 0 || addr > 15) { io.setAttr("err", "1:"+tr("Device address out of range 0...15")); return; }
request = SYS.strFromCharCode(0xB0+addr) + io.text();
if(io.attr("cs").toInt()) {
	for(CS = 0, i = 0; i < request.length; i++) CS += request.charCodeAt(i);
	request += SYS.strFromCharCode(CS);
}
SYS.messDebug("/OPTRIS/PRT",tr("Request")+": "+SYS.strDecode(request,"Bin"," "));

//Send request
resp = tr.messIO(request);
while(resp.length && (tresp=tr.messIO("")).length) resp += tresp;
SYS.messDebug("/OPTRIS/PRT","Respond: "+SYS.strDecode(resp,"Bin"," "));
if(resp.length <= 0) { io.setAttr("err","3:"+tr("No a respond")); return; }
io.setText(resp);


ID: CTR
NAME: CTR 100, 101
uk#NAME: CTR 100, 101
DESCR:
Protocol part of the RS232C Serial Interface permits the communication between the digital Oerlikon Leybold Vacuum CERAVAC, from Köln.
Author: Roman Savochenko <roman@oscada.org>
Sponsored: Vasiliy Grigoriev from "Vacuum technologies laboratory (http://e-beam.ru)".
Version: 1.0.0

uk#DESCR:
Protocol part of the RS232C Serial Interface permits the communication between the digital Oerlikon Leybold Vacuum CERAVAC, from Köln.
Author: Roman Savochenko <roman@oscada.org>
Sponsored: Vasiliy Grigoriev from "Vacuum technologies laboratory (http://e-beam.ru)".

OutPROG:
JavaLikeCalc.JavaScript
//Request form:
//<mess err="1:Error">{req}</mess> - message tag
//  req - request/respond data;
//  err - sets for the request result.
io.setAttr("err", "");
//Request prepare
req = SYS.strFromCharCode(3) + io.text();
for(CS = 0, i = 1; i < req.length; i++)	CS += req.charCodeAt(i);
req += SYS.strFromCharCode(CS&0xFF);
SYS.messDebug("/CTR/PRT",tr("Request")+": "+SYS.strDecode(req,"Bin"," "));

//Send request
for(resp = tr.messIO(req); resp.length < 9 && (tresp=tr.messIO("")).length; resp += tresp) ;
if(resp.length == 0){ io.setAttr("err","2:"+tr("No a respond")); return; }
SYS.messDebug("/CTR/PRT","Respond: "+SYS.strDecode(resp,"Bin"," "));
if(resp.length < 9)	{ io.setAttr("err","3:"+tr("Respond too short")); return; }
if(resp.length > 9)	{ io.setAttr("err","4:"+tr("Respond too long, possible continuous mode")); return; }
for(CS = 0, i = 1; i < resp.length-1; i++)	CS += resp.charCodeAt(i);
if(resp.charCodeAt(resp.length-1) != (CS&0xFF))	{ io.setAttr("err","5:"+tr("CRC error")); return; }
io.setText(resp.slice(1,resp.length-1));


ID: SMTP
NAME: SMTP (replaced)
uk#NAME: SMTP
DESCR:
!!!!: Replaced by the complex template Main.ntf. Will be removed soon
Provides EMAIL sending by the protocol SMTP.
Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.0

uk#DESCR:
Provides EMAIL sending by the protocol SMTP.
Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.0

OutPROG:
JavaLikeCalc.JavaScript
//Request form:
//<send auth="{user}:{pass}" from="{FromAddr}" to="{ToAddr}" topic="{My Topic}" err="1:Error">{MessageText}</send>
//  topic - the message topic;
//  from - sender address;
//  to - destination address of the receiver;
//  auth - authenticate user and password;
//  MessageText - the message text;
//  err - sets for the request result.

//Transport's timings check
if(tr.timings() != "5:1")	tr.timings("5:1");
if(!tr.start(true))	{ io.setAttr("err", "10:"+tr("Impossible connect to the SMTP-server.")); return; }

//Wait for a greeting after the connection
rez = "";
for(iTr = 0; iTr < 5 && !rez.length; iTr++) rez += tr.messIO("");
while(rez.length && rez.slice(-2) != "\x0D\x0A" && (trez=tr.messIO("")).length) rez += trez;

auth = io.attr("auth");

//Send HELLO
rez = tr.messIO("EHLO "+SYS.system("hostname -f").parse(0,"\n")+"\x0D\x0A");
while(rez.length && rez.slice(-2) != "\x0D\x0A" && (trez=tr.messIO("")).length) rez += trez;
if(!rez.length)	{ io.setAttr("err", "100:"+tr("No response.")); return; }
for(off = 0; (sit=rez.parse(0,"\x0D\x0A",off)).length; )
	if(sit.toInt() != 250)	{ io.setAttr("err", "101:"+tr("Error HELLO: %1.").replace("%1",sit)); return; }
	else if(sit.indexOf("AUTH LOGIN") >= 0) {
		if(sit.indexOf("PLAIN") < 0)	{ io.setAttr("err", "101:"+tr("Unsupported auth methods: %1.").replace("%1",sit)); return; }
		else if(!auth.length)				{ io.setAttr("err", "101:"+tr("Auth required: %1.").replace("%1",sit)); return; }
	}

//Send AUTH
if(auth.length) {
	rez = tr.messIO("AUTH LOGIN\x0D\x0A");
	while(rez.length && rez.slice(-2) != "\x0D\x0A" && (trez=tr.messIO("")).length) rez += trez;
	if(!rez.length)	{ io.setAttr("err", "100:"+tr("No response.")); return; }
	if(rez.toInt() != 334)	{ io.setAttr("err", "102:"+tr("AUTH error: %1.").replace("%1",rez)); return; }

	rez = tr.messIO(SYS.strEncode(io.attr("auth").parse(0,":"),"Base64")+"\x0D\x0A");
	while(rez.length && rez.slice(-2) != "\x0D\x0A" && (trez=tr.messIO("")).length) rez += trez;
	if(!rez.length)	{ io.setAttr("err", "100:"+tr("No response.")); return; }
	if(rez.toInt() != 334)	{ io.setAttr("err", "102:"+tr("AUTH USER error: %1.").replace("%1",rez)); return; }

	rez = tr.messIO(SYS.strEncode(io.attr("auth").parse(1,":"),"Base64")+"\x0D\x0A");
	while(rez.length && rez.slice(-2) != "\x0D\x0A" && (trez=tr.messIO("")).length) rez += trez;
	if(!rez.length)	{ io.setAttr("err", "100:"+tr("No response.")); return; }
	if(rez.toInt() != 235)	{ io.setAttr("err", "102:"+tr("AUTH PASS error: %1.").replace("%1",rez)); return; }
}

//Send FROM
rez = tr.messIO("MAIL FROM:<"+io.attr("from")+">\x0D\x0A");
while(rez.length && rez.slice(-2) != "\x0D\x0A" && (trez=tr.messIO("")).length) rez += trez;
if(!rez.length)	{ io.setAttr("err", "100:"+tr("No response.")); return; }
if(rez.toInt() != 250)	{ io.setAttr("err", "103:"+tr("MAIL FROM error: %1.").replace("%1",rez)); return; }

//Send TO
rez = tr.messIO("RCPT TO:<"+io.attr("to")+">\x0D\x0A");
while(rez.length && rez.slice(-2) != "\x0D\x0A" && (trez=tr.messIO("")).length) rez += trez;
if(!rez.length)	{ io.setAttr("err", "100:"+tr("No response.")); return; }
if(rez.toInt() != 250)	{ io.setAttr("err", "104:"+tr("RCPT TO error: %1.").replace("%1",rez)); return; }

//Prepare and send DATA
rez = tr.messIO("DATA\x0D\x0A");
while(rez.length && rez.slice(-2) != "\x0D\x0A" && (trez=tr.messIO("")).length) rez += trez;
if(!rez.length)	{ io.setAttr("err", "100:"+tr("No response.")); return; }
if(rez.toInt() != 354)	{ io.setAttr("err", "105:"+tr("DATA error: %1.").replace("%1",rez)); return; }

// Req version
reqVer = SYS.XMLNode("get").setAttr("path","/%2fgen%2fver"); SYS.cntrReq(reqVer);

data = "Message-ID: <"+rand(999999).toString()+"."+rand(999999999).toString()+"-OpenSCADA@"+SYS.system("hostname").parse(0,"\n")+">\x0D\x0A"+
			"From: \""+io.attr("from")+"\" <"+io.attr("from")+">\x0D\x0A"+
			"To: \""+io.attr("to")+"\" <"+io.attr("to")+">\x0D\x0A"+
			"Subject: =?utf8?B?"+SYS.strEncode(io.attr("topic"),"Base64")+"?=\x0D\x0A"+
			"Date: "+SYS.system("LANG=en date \"+%a, %d %b %Y %T %z\"").parse(0,"\n")+"\x0D\x0A"+
			//"Date: "+SYS.strftime(SYS.time(),"%a, %d %b %Y %T %z")+"\x0D\x0A"+
			"X-Mailer: OpenSCADA "+reqVer.text()+"\x0D\x0A"+
			"Content-type: text/plain;charset=utf-8\x0D\x0A\x0D\x0A"+
			io.text()+"\x0D\x0A"+
			"\x0D\x0A.\x0D\x0A";	//End for code 354
rez = tr.messIO(data);
while(rez.length && rez.slice(-2) != "\x0D\x0A" && (trez=tr.messIO("")).length) rez += trez;
if(!rez.length)	{ io.setAttr("err", "100:"+tr("No response.")); return; }
if(rez.toInt() != 250)	{ io.setAttr("err", "105:"+tr("DATA send error: %1.").replace("%1",rez)); return; }

//Send QUIT
rez = tr.messIO("QUIT\x0D\x0A");
while(rez.length && rez.slice(-2) != "\x0D\x0A" && (trez=tr.messIO("")).length) rez += trez;

//SYS.messInfo("SMTP", "Response: "+rez);

//Disconnect from the SMTP-server
tr.start(false);


ID: AT24CXX
NAME: I2C: AT24C{32|64}
DESCR:
Provides operations with EEPROM memory based on I2C chips AT24C32 (4KB) and AT24C64 (8KB). Supported random reading and writing.
Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.0

OutPROG:
JavaLikeCalc.JavaScript
//Request form:
//<{cmd} addr="{ChipAddr}" off="{MemOffset}" size="{ReadSize}" err="1:Error">{ReadWriteSeq}</{cmd}>
//  cmd - command, for now there allowed: "read", "write";
//  addr - I2C device address [0...119];
//  off - memory part offset;
//  size - readed memory block size;
//  ReadWriteSeq - Read/Write bytes sequence;
//  err - sets for the request result.

//Transport's timings check
if(tr.timings() != "100:0.01")	tr.timings("100:0.01");
addr = io.attr("addr").toInt();
if(addr < 0 || addr > 119) { io.setAttr("err", "1:"+tr("Device address out of range 0...119.")); return; }
off = io.attr("off").toInt();
if(off < 0 || off >= 8192) { io.setAttr("err", "1:"+tr("Memory offset out of range 0...8191.")); return; }

io.setAttr("err", "");

if(io.name() == "read") {
	size = io.attr("size").toInt();
	if(size < 0 || size >= 8192) { io.setAttr("err", "2:"+tr("Readed memory size out of range 0...8191.")); return; }
	resp = tr.messIO(SYS.strFromCharCode(addr,off>>8,off&0xFF), 0, size);
	if(resp.length < size)	{ io.setAttr("err", "3:"+tr("Not or short respond %1 from %2.").replace("%1",resp.length.toString()).replace("%2",size.toString())); return; }
	io.setText(resp);
	return;
}
if(io.name() == "write") {
	if(io.text().length >= 8192) { io.setAttr("err", "2:"+tr("Too long write sequence.")); return; }	
	tr.messIO(SYS.strFromCharCode(addr,off>>8,off&0xFF)+io.text(), 0, 0);
	return;
}


ID: m200
NAME: Mercury 200
ru#NAME: Меркурий 200
DESCR:
Protocol level of one phase counter of electricity Mercury 200, 203.2Т, 206 from firm Incotex (http://www.incotexcom.ru).
Author: Arsen Zakojan <godzilla919@gmail.com>
Version: 1.0.0

ru#DESCR:
Протокольный уровень однофазных счетчиков электроэнергии Меркурий 200,  203.2Т,  206 компании Инкотекс (http://www.incotexcom.ru).
Автор: Арсен Закоян <godzilla919@gmail.com>
Версия 1.0

OutPROG:
JavaLikeCalc.JavaScript
//resp="";
//преобразуем сетевой адрес для запроса
net0 ="0x"+io.attr("first").toInt(10).toString(16,2);
net1 ="0x"+io.attr("second").toInt(10).toString(16,6).slice(0,2);
net2 ="0x"+io.attr("second").toInt(10).toString(16,6).slice(2,4);
net3 ="0x"+io.attr("second").toInt(10).toString(16,6).slice(4,6);
request = SYS.strFromCharCode(Special.FLibSYS.str2int(net0),Special.FLibSYS.str2int(net1),Special.FLibSYS.str2int(net2),Special.FLibSYS.str2int(net3))+io.attr("data");
//Вычисление CRC запроса
out = 0xFFFF;
for( i = 0; i < request.length; i++ )
{
out = out^request.charCodeAt(i);
for( j=0; j<8; j++ ) out = (out&1) ? ((out>>1)^0xA001) : out >> 1;
}
CRC=SYS.strFromCharCode(out&0xFF,(out&0xFF00)>>8 );
//запрос
resp1 = tr.messIO(request+CRC);
resp2 = tr.messIO("");
resp3 = tr.messIO("");
resp=(resp1+resp2+resp3);
//Что есть ответ и что есть контрольная сумма
answer=resp.slice(0,resp.length-2);
CRCans=resp.slice(resp.length-2,resp.length);
// Вычисление CRC ответа
out1 = 0xFFFF;
for( i1 = 0; i1 < answer.length; i1++ )
{
out1 = out1^answer.charCodeAt(i1);
for( j1=0; j1<8; j1++ ) out1 = (out1&1) ? ((out1>>1)^0xA001) : out1 >> 1;
}
CRC1=SYS.strFromCharCode(out1&0xFF,(out1&0xFF00)>>8 );
//Проверяем контрольную сумму  и принимаем ответ
if((CRCans==CRC1)&&(request.charCodeAt(0)==answer.charCodeAt(0))&&(request.charCodeAt(1)==answer.charCodeAt(1))&&(request.charCodeAt(2)==answer.charCodeAt(2))&&(request.charCodeAt(3)==answer.charCodeAt(3)))//break;
//k++;}
io.setText(Special.FLibSYS.strDec4Bin(resp));


ID: m230
NAME: Mercury 230
ru#NAME: Меркурий 230
DESCR:
Protocol level of three phase counter of electricity Mercury 230, 231, 232, 233, 234, 236 from firm Incotex (http://www.incotexcom.ru).
Author: Arsen Zakojan <godzilla919@gmail.com>
Version: 1.0.0

ru#DESCR:
Протокольный уровень трехфазных счетчиков электроэнергии Меркурий 230, 231, 232, 233,  234,  236  компании Инкотекс (http://www.incotexcom.ru).
Автор: Арсен Закоян <godzilla919@gmail.com>
Версия 1.0

OutPROG:
JavaLikeCalc.JavaScript
//преобрауем сетевой адрес,преобразуем из строки в целое
netaddr1 ="0x"+io.attr("netaddr").toInt(10).toString(16);
request = SYS.strFromCharCode(Special.FLibSYS.str2int(netaddr1))+io.attr("data");
//Вычисление CRC запроса
out = 0xFFFF;
for( i = 0; i < request.length; i++ )
{
out = out^request.charCodeAt(i);
for( j=0; j<8; j++ ) out = (out&1) ? ((out>>1)^0xA001) : out >> 1;
}
CRC=SYS.strFromCharCode(out&0xFF,(out&0xFF00)>>8 );
//Отправляем запрос
//resp = tr.messIO(request+CRC);
resp1 = tr.messIO(request+CRC);
resp2 = tr.messIO("");
resp3 = tr.messIO("");
resp=(resp1+resp2+resp3);
//Что есть ответ и что есть контрольная сумма
answer=resp.slice(0,resp.length-2);
CRCans=resp.slice(resp.length-2,resp.length);
// Вычисление CRC ответа
out1 = 0xFFFF;
for( i1 = 0; i1 < answer.length; i1++ )
{
out1 = out1^answer.charCodeAt(i1);
for( j1=0; j1<8; j1++ ) out1 = (out1&1) ? ((out1>>1)^0xA001) : out1 >> 1;
}
CRC1=SYS.strFromCharCode(out1&0xFF,(out1&0xFF00)>>8 );
//Последовательность байт для интерпретации ответа
b1 =Special.FLibSYS.strDec4Bin(resp.slice(1,2));
b2 =Special.FLibSYS.strDec4Bin(resp.slice(2,3));
b3 =Special.FLibSYS.strDec4Bin(resp.slice(3,4));
b4 =Special.FLibSYS.strDec4Bin(resp.slice(4,5));
b5 =Special.FLibSYS.strDec4Bin(resp.slice(5,6));
b6 =Special.FLibSYS.strDec4Bin(resp.slice(6,7));
b7 =Special.FLibSYS.strDec4Bin(resp.slice(7,8));
b8 =Special.FLibSYS.strDec4Bin(resp.slice(8,9));
b9 =Special.FLibSYS.strDec4Bin(resp.slice(9,10));
b10 =Special.FLibSYS.strDec4Bin(resp.slice(10,11));
b11 =Special.FLibSYS.strDec4Bin(resp.slice(11,12));
b12 =Special.FLibSYS.strDec4Bin(resp.slice(12,13));
b13 =Special.FLibSYS.strDec4Bin(resp.slice(13,14));
b14 =Special.FLibSYS.strDec4Bin(resp.slice(14,15));
b15 =Special.FLibSYS.strDec4Bin(resp.slice(15,16));
b16 =Special.FLibSYS.strDec4Bin(resp.slice(16,17));
//если контрольная сумма ответа совпадает и адрес ответа равен адресу запроса 
if(CRCans==CRC1&&(request.charCodeAt(0)==answer.charCodeAt(0))/*&&answer.length>1*/ )
//отклик от счетчика
{
if(request.charCodeAt(1)==0x01)
 io.setText(Special.FLibSYS.strDec4Bin(resp));
//чтение энергии
else if((request.charCodeAt(1)==0x05)&&(answer.length==17))
{R1=b2+b1+b4+b3;R2=b6+b5+b8+b7;R3=b10+b9+b12+b11;R4=b14+b13+b16+b15;
 io.setText(R1.toInt(16).toString(10,10,)+R2.toInt(16).toString(10,10,)+R3.toInt(16).toString(10,10,)+R4.toInt(16).toString(10,10,));}

//чтение напряжения U разом
else if((request.charCodeAt(1)==0x08)&&(request.charCodeAt(2)==0x16)&&(request.charCodeAt(3)==0x11)&&(answer.length==10)) 
{U1=b1+b3+b2;U2=b4+b6+b5;U3=b7+b9+b8;
io.setText(U1.toInt(16).toString(10,8,)+U2.toInt(16).toString(10,8,)+U3.toInt(16).toString(10,8,));}
//чтение Мощности P Q S разом
else if((request.charCodeAt(1)==0x08)&&(request.charCodeAt(2)==0x16)&&(request.charCodeAt(3)==0x00||request.charCodeAt(3)==0x04||request.charCodeAt(3)==0x08)&&(answer.length==13)) 
{PS=b1.slice(1,2)+b3+b2;P1=b4.slice(1,2)+b6+b5;P2=b7.slice(1,2)+b9+b8;P3=b10.slice(1,2)+b12+b11;
io.setText(PS.toInt(16).toString(10,7,)+P1.toInt(16).toString(10,7,)+P2.toInt(16).toString(10,7,)+P3.toInt(16).toString(10,7,)+b1.slice(0,1)+b4.slice(0,1)+b7.slice(0,1)+b10.slice(0,1));}

//чтение тока I разом
else if((request.charCodeAt(1)==0x08)&&(request.charCodeAt(2)==0x16)&&(request.charCodeAt(3)==0x21)&&(answer.length==10)) 
{I1=b1+b3+b2;I2=b4+b6+b5;I3=b7+b9+b8;
io.setText(I1.toInt(16).toString(10,8,)+I2.toInt(16).toString(10,8,)+I3.toInt(16).toString(10,8,));}
//чтение коэф.мощности
else if((request.charCodeAt(1)==0x08)&&(request.charCodeAt(2)==0x16)&&(request.charCodeAt(3)==0x30)&&(answer.length==13)) 
{KS=b1.slice(1,2)+b3+b2;K1=b4.slice(1,2)+b6+b5;K2=b7.slice(1,2)+b9+b8;K3=b10.slice(1,2)+b12+b11;
io.setText(KS.toInt(16).toString(10,7,)+K1.toInt(16).toString(10,7,)+K2.toInt(16).toString(10,7,)+K3.toInt(16).toString(10,7,));}
//чтение частоты F
else if((request.charCodeAt(1)==0x08)&&(request.charCodeAt(2)==0x16)&&(request.charCodeAt(3)==0x40)&&(answer.length==4))
{resp=b1+b3+b2;
 io.setText(resp.toInt(16).toString(10,,)/100);}
//чтение серийного номера
else if((request.charCodeAt(1)==0x08)&&(request.charCodeAt(2)==0x00)&&(answer.length==8))
 io.setText(b1.toInt(16).toString(10,2,)+b2.toInt(16).toString(10,2,)+b3.toInt(16).toString(10,2,)+b4.toInt(16).toString(10,2,));
;}


ID: NIK2303
NAME: NIK2303
DESCR:
Protocol level of three phase counter of electricity NIK 2303 from firm NIK LLC (http://www.nik.net.ua).
Author: Ruslan Yarmoliuk <rylio74@gmail.com>
Version: 1.0.1

OutPROG:
JavaLikeCalc.JavaScript
//Protocol NIK 2303
io.setAttr("err", "0");

SN = io.attr("SN").toInt();
cli = 0x21;
trm1 = (SN>>20)&0xFE;
trm2 = (SN>>13)&0xFE;
dnum1 = (SN>>6)&0xFE;;
dnum2 = ((SN&0x3FFF)<<1)-(dnum1<<7)+1;
//Message creation
if(!io.text().length) request = SYS.strFromCharCode(0xA0, 10, trm1, trm2, dnum1, dnum2, cli, io.attr("cntr"));
else {
	title = SYS.strFromCharCode(0xA0, io.text().length +12, trm1, trm2, dnum1, dnum2, cli, io.attr("cntr"));
	// Check for HCS
	for(HCS = 0xFFFF, i = 0; i < title.length; i++) {
		b = 0;
		for (j = 0; j<8; j++){				//byte reversion
			bit = (title.charCodeAt(i)&(1<<j))>>j;
			b = b |( bit << (8 - j - 1));
		} 
		for(j = 0; j < 8; j++, b = b << 1)		//HCS calculation
			HCS = ((b^(HCS>>8)&0x80)?(HCS<<1)^0x1021:HCS<<1) & 0xFFFF;
	}
	HCS = HCS ^ 0xFFFF; // HCS*HOR
	rev = 0;					// HCS reversion
	for (i = 0; i<16; i++){
		bit = (HCS&(1<<i))>>i;
		rev = rev |( bit << (16 - i - 1));
	} 
	HCS = rev;
	request = title + SYS.strFromCharCode(HCS&0xFF, (HCS>>8)&0xFF)+ io.text();
}
// Check for FCS
for(FCS = 0xFFFF, i = 0; i < request.length; i++) {
	b = 0;
	for (j = 0; j<8; j++){				//byte reversion
		bit = (request.charCodeAt(i)&(1<<j))>>j;
		b = b |( bit << (8 - j - 1));
	} 
	for(j = 0; j < 8; j++, b = b << 1)		//CRC calculation
		FCS = ((b^(FCS>>8)&0x80)?(FCS<<1)^0x1021:FCS<<1) & 0xFFFF;
}
FCS = FCS ^ 0xFFFF; // FCS*HOR
rev = 0;			// FCS reversion
for (i = 0; i<16; i++){
	bit = (FCS&(1<<i))>>i;
	rev = rev |( bit << (16 - i - 1));
} 
FCS = rev;
request = SYS.strFromCharCode(0x7E) + request + SYS.strFromCharCode(FCS&0xFF, (FCS>>8)&0xFF, 0x7E);
//SYS.messDebug("/NIK2303I/PRT", "Request: "+SYS.strDecode(request, "Bin","|"));


//message sending
resp = tr.messIO(request);
while(resp.length && resp.charCodeAt(resp.length-1) != 0x7E)
	if((tresp=tr.messIO("")).length) resp += tresp;
	else break;
if(resp.length == 0) {io.setText(""); io.setAttr("err","1:"+tr("No a respond")); return; }
//SYS.messDebug("/NIK2303I/PRT", "Respond: "+SYS.strDecode(resp, "Bin","|"));
if(resp.length <= 3 || resp.charCodeAt(2) != resp.length-2)
{ io.setAttr("err","3:"+tr("Respond too short or wrong")); return; }
// Remove markers
resp = resp.slice(1, resp.length-1);
// Check for FCS
for(FCS = 0xFFFF, i = 0; i < resp.length-2; i++) {
	b = 0;
	for (j = 0; j<8; j++){				//byte reversion
		bit = (resp.charCodeAt(i)&(1<<j))>>j;
		b = b |( bit << (8 - j - 1));
	} 
	for(j = 0; j < 8; j++, b = b << 1)		//FCS calculation
		FCS = ((b^(FCS>>8)&0x80)?(FCS<<1)^0x1021:FCS<<1) & 0xFFFF;
}

FCS = FCS ^ 0xFFFF; // FCS*HOR
rev = 0;			// FCS reversion
for (i = 0; i<16; i++){
	bit = (FCS&(1<<i))>>i;
	rev = rev |( bit << (16 - i - 1));
} 
FCS = rev;
if(FCS != ((resp.charCodeAt(resp.length-1)<<8)+ resp.charCodeAt(resp.length-2))) {
	io.setAttr("err", "11:"+tr("Error FCS."));
	return;
}
io.setText(resp.slice(10,resp.length-2));
io.setAttr("cntr", resp[7]);
io.setAttr("err", "0");


# tmplib_LowDevLib_io

TMPL_ID: 1602A
ID: transport
VALUE: i2c
NAME:
Transport of the I2C, Serial (i2c) or
GPIO address with function put(), mostly it's BCM2835 (DAQ.BCM2835.pi.pi)


# techApp

ID: noise
NAME: Noise: 2 harmonic + rand
uk#NAME: Шум: 2 гармоніки + випадковий.
ru#NAME: Шум: 2 гармоники + случайное
DESCR:
Noise model. Contains three parts:
- first harmonic part;
- second harmonic part;
- noise based on generator of the randomize numbers.

uk#DESCR:
Модель шуму. Містить три складові:
- перша гармоніка;
- друга гармоніка;
- шум на основі генератору випадкових чисел.

ru#DESCR:
Модель шума. Содержит три составляющие:
- первая гармоника;
- вторая гармоника;
- шум на основе генератора случайных чисел.

FORMULA:
tmp_g1 = (tmp_g1 > 6.28) ? 0 : tmp_g1+6.28/(per_g1*f_frq);
tmp_g2 = (tmp_g2 > 6.28) ? 0 : tmp_g2+6.28/(per_g2*f_frq);
out = off + a_g1*sin(tmp_g1) + a_g2*sin(tmp_g2) + a_rnd*(rand(2)-1);


ID: ballCrane
NAME: Ball crane
uk#NAME: Кульовий кран
ru#NAME: Шаровый кран
DESCR: Model of the ball crane. Includes for the going and estrangement time.
uk#DESCR: Модель кульового крану. Включає час ходу та час відриву.
ru#DESCR: Модель шарового крана. Включает время хода и время отрыва.
FORMULA:
if(!(st_close && !com) && !(st_open && com)) {
  tmp_up = (pos > 0 && pos < 100) ? 0 : (tmp_up>0&&lst_com==com)?tmp_up-1/f_frq:t_up;
  pos += (tmp_up > 0) ? 0 : (100*(com?1:-1))/(t_full*f_frq);
  pos = (pos > 100) ? 100 : (pos<0)?0:pos;
  st_open = (pos >= 100) ? true : false;
  st_close = (pos <= 0) ? true : false;
  lst_com = com;
}


ID: separator
NAME: Separator
uk#NAME: Сепаратор
ru#NAME: Сепаратор
DESCR: Separator model with two phases, liquid and gas.
uk#DESCR: Модель сепаратору з двома фазами, рідинною та газовою.
ru#DESCR: Модель сепаратора с двумя фазами, жидкой и газовой.
FORMULA:
Flq = max(0, Fi*PercLq);
DAQ.JavaLikeCalc.lib_techApp.pipeBase(Fi, Pi, 293, Si, Fo+Flq, Po, 293, So, lo, Q0, 0.95, 0.01, f_frq);
Llq = max(0, min(100,Llq+0.27*(Flq-Fo_lq)/(Vap*Qlq*f_frq)));
Po_lq = Po + Llq*Vap/Qlq;


ID: valve
NAME: Valve
uk#NAME: Клапан
ru#NAME: Клапан
DESCR:
Valve model, includes:
- two valves in the one;
- super-critical speed;
- temperature changing at the throttling;
- work in one direction, back valve;
- speed control of the position changing;
- nonlinearity of the intersection from the position.

uk#DESCR:
Модель клапану яка враховує:
- два клапани в одному;
- надкритичне витікання;
- зміна температури при дроселюванні;
- робота тільки у одному напрямку, зворотний клапан;
- керування швидкісттю зміни положення;
- нелінійність прохідного перетину від положення.

ru#DESCR:
Модель клапана, учитывающая:
- два клапана в одном;
- сверхкритическое истечение;
- изменение температуры при дросселировании;
- работа только в одном направлении, обратный клапан;
- управляемая скорость изменения положения;
- нелинейность проходного сечения от положения.

FORMULA:
Qr = Q0+Q0*Kpr*(Pi-1);
tmp_l1 += (abs(l_v1-tmp_l1) > 5) ? 100*sign(l_v1-tmp_l1)/(t_v1*f_frq) : (l_v1-tmp_l1)/(t_v1*f_frq);
tmp_l2 += (abs(l_v2-tmp_l2) > 5) ? 100*sign(l_v2-tmp_l2)/(t_v2*f_frq) : (l_v2-tmp_l2)/(t_v2*f_frq);
Sr = (S_v1*pow(tmp_l1,Kln)+S_v2*pow(tmp_l2,Kln))/pow(100,Kln);

DAQ.JavaLikeCalc.lib_techApp.pipeBase(Fi, Pi, Ti, Sr, EVAL_REAL, Po, 293, So, lo, Q0, Kpr, 0.01, f_frq);
if(noBack) Fi = max(0, Fi);
Po = max(0, min(100,Po+0.27*(Fi-Fo)/(Q0*Kpr*So*lo*f_frq)));

To = max(0, min(2e3,To+(abs(Fi)*(Ti*pow(Po/Pi,0.02)-To)+(Fwind+1)*(Twind-To)/Riz)/(Ct*So*lo*Qr*f_frq)));


ID: lagClean
NAME: Lag: clean
uk#NAME: Затримка: чиста
ru#NAME: Задержка: чистая
DESCR:
Model of clean(transportable) lag. Realizes by incorporating several links of a simple delay. Appointed for lags into long pipes.

uk#DESCR:
Модель чистої(транспортної) затримки. Реалізується шляхом включення декількох ланок простої затримки. Призначено для імітації затримок у довгих трубопроводах.

ru#DESCR:
Модель чистого(транспортного) запаздывания. Реализуется путём включения нескольких простых звеньев запаздывания. Предназначен для имитации запаздывания в длинных трубопроводах.

FORMULA:
cl1 -= (cl1-in)/(t_lg*f_frq/4);
cl2 -= (cl2-cl1)/(t_lg*f_frq/4);
cl3 -= (cl3-cl2)/(t_lg*f_frq/4);
out -= (out-cl3)/(t_lg*f_frq/4);


ID: net
NAME: Network: load
uk#NAME: Мережа: навантаження
ru#NAME: Сеть: нагрузка
DESCR: Loading with constant pressure on the network. Contains a parameter for connection the noise.
uk#DESCR: Навантаження з фіксованим тиском мережі. Містить параметр для підключення шуму.
ru#DESCR: Нагрузка с фиксированным давлением сети. Содержит параметр для подключения шума.
FORMULA:
DAQ.JavaLikeCalc.lib_techApp.pipeBase(Fi, Pi, 293, So, EVAL_REAL, Po, 293, So, 10, Q0, Kpr, 0.01, f_frq);


ID: src_press
NAME: Source: pressure
uk#NAME: Джерело: тиск
ru#NAME: Источник: давление
DESCR: Source of the constant pressure. Contains a parameter for connection the noise.
uk#DESCR: Джерело з фіксованим тиском. Містить параметр для підключення шуму.
ru#DESCR: Источник с фиксированным давлением. Содержит параметр для подключения шума.
FORMULA:
DAQ.JavaLikeCalc.lib_techApp.pipeBase(Fit, Pi*Noise, 293, So, Fo, Po, 293, So, lo, Q0, Kpr, 0.01, f_frq);


ID: cooler
NAME: Air cooler
uk#NAME: Повітряний холодильник
ru#NAME: Воздушный холодильник
DESCR: Model of the air cooler for gas flow.
uk#DESCR: Модель повітряного охолоджувача газового потоку.
ru#DESCR: Модель воздушного охладителя газового потока.
FORMULA:
DAQ.JavaLikeCalc.lib_techApp.pipeBase(Fi, Pi, 293, Si, Fo, Po, 293, So, lo, Q0, 0.95, 0.01, f_frq);
Qr = Q0 + Q0*0.95*(Pi-1);
To += (Fi*(Ti-To)+Wc*(Tair-To)/Rt)/(Ct*(Si*li+So*lo)*Qr*f_frq);


ID: compressor
NAME: Gas compressor
uk#NAME: Компресор газовий
ru#NAME: Компрессор газовый
DESCR:
Model of the gas compressor. Implements the surge effect.
The surge counts from the dynamic-gas curve, and next there counts the surge margin coefficient.

uk#DESCR:
Модель газового компресора. Враховує ефект помпажу.
Помпаж розраховується за газово-динамічною кривою, виходячи з якої розраховується коефіцієнт запасу за помпажем.

ru#DESCR:
Модель газового компрессора. Учитывает эффект помпажа. Помпаж вычисляется по газо-динамической кривой, исходя из которой получается коэффициент запаса по помпажу.

FORMULA:
Pmax = max(Pi, Po);
Pmin = min(Pi, Po);
Qr = Q0 + Q0*Kpr*(Pi-1);
Qrf = Q0 + Q0*Kpr*(Pmax-1);
Ftmp = (N > 0.1) ? (1-10*(Po-Pi)/(Qr*(pow(N,3)+0.1)*Kpmp)) : 1;
Kmrg = 1-Ftmp;  //The margin coefficient
Fi = V*N*Qr*sign(Ftmp)*pow(abs(Ftmp),Kslp)+
     0.3*(4*So*Qrf/(0.01*lo*1.7724+4*Qrf))*sign(Pi-Po)*pow(Qrf*(Pmax-max(Pmax*0.528,Pmin)),0.5);
Fit -= (Fit-Fi)/max(1,(lo*f_frq)/max(1e-4,abs(Fi/(Qrf*So))));
Po = max(0, min(100,Po+0.27*(Fi-Fo)/(Q0*Kpr*So*lo*f_frq)));

To += (abs(Fi)*(Ti*pow(Po/Pi,0.3)-To)+(Fwind+1)*(Twind-To)/Riz)/(Ct*(V+So*lo)*Qr*f_frq);


ID: pipe1_1
NAME: Pipe 1->1
uk#NAME: Труба 1->1
ru#NAME: Труба 1->1
DESCR: Model of the pipe by the scheme "1 -> 1".
uk#DESCR: Модель вузла труб за схемою "1 -> 1".
ru#DESCR: Модель узла труб по схеме "1 -> 1".
FORMULA:
DAQ.JavaLikeCalc.lib_techApp.pipeBase(Fi, Pi, 293, So, Ft1, Pti, 293, So, 0.33*lo, Q0, Kpr, 0.01, f_frq);
DAQ.JavaLikeCalc.lib_techApp.pipeBase(Ft1, Pti, 293, So, Fto, Pt1, 293, So, 0.33*lo, Q0, Kpr, 0.01, f_frq);
DAQ.JavaLikeCalc.lib_techApp.pipeBase(Fto, Pt1, 293, So, Fo, Po, 293, So, 0.33*lo, Q0, Kpr, 0.01, f_frq);


ID: pipe1_2
NAME: Pipe 1->2
uk#NAME: Труба 1->2
ru#NAME: Труба 1->2
DESCR: Model of the pipe by the scheme "1 -> 2".
uk#DESCR: Модель вузла труб за схемою "1 -> 2".
ru#DESCR: Модель узла труб по схеме "1 -> 2".
FORMULA:
DAQ.JavaLikeCalc.lib_techApp.pipeBase(F1tmp, Pi, 293, So1, Fo1, Po1, 293, So1, lo1, Q0, Kpr, 0.01, f_frq);
DAQ.JavaLikeCalc.lib_techApp.pipeBase(F2tmp, Pi, 293, So2, Fo2, Po2, 293, So2, lo2, Q0, Kpr, 0.01, f_frq);
Fi = F1tmp + F2tmp;


ID: pipe1_3
NAME: Pipe 1->3
uk#NAME: Труба 1->3
ru#NAME: Труба 1->3
DESCR: Model of the pipe by the scheme "1 -> 3".
uk#DESCR: Модель вузла труб за схемою "1 -> 3".
ru#DESCR: Модель узла труб по схеме "1 -> 3".
FORMULA:
DAQ.JavaLikeCalc.lib_techApp.pipeBase(F1tmp, Pi, 293, So1, Fo1, Po1, 293, So1, lo1, Q0, Kpr, 0.01, f_frq);
DAQ.JavaLikeCalc.lib_techApp.pipeBase(F2tmp, Pi, 293, So2, Fo2, Po2, 293, So2, lo2, Q0, Kpr, 0.01, f_frq);
DAQ.JavaLikeCalc.lib_techApp.pipeBase(F3tmp, Pi, 293, So3, Fo3, Po3, 293, So3, lo3, Q0, Kpr, 0.01, f_frq);
Fi = F1tmp + F2tmp + F3tmp;


ID: pipe1_4
NAME: Pipe 1->4
uk#NAME: Труба 1->4
ru#NAME: Труба 1->4
DESCR: Model of the pipe by the scheme "1 -> 4".
uk#DESCR: Модель вузла труб за схемою "1 -> 4".
ru#DESCR: Модель узла труб по схеме "1 -> 4".
FORMULA:
DAQ.JavaLikeCalc.lib_techApp.pipeBase(F1tmp, Pi, 293, So1, Fo1, Po1, 293, So1, lo1, Q0, Kpr, 0.01, f_frq);
DAQ.JavaLikeCalc.lib_techApp.pipeBase(F2tmp, Pi, 293, So2, Fo2, Po2, 293, So2, lo2, Q0, Kpr, 0.01, f_frq);
DAQ.JavaLikeCalc.lib_techApp.pipeBase(F3tmp, Pi, 293, So3, Fo3, Po3, 293, So3, lo3, Q0, Kpr, 0.01, f_frq);
DAQ.JavaLikeCalc.lib_techApp.pipeBase(F4tmp, Pi, 293, So4, Fo4, Po4, 293, So4, lo4, Q0, Kpr, 0.01, f_frq);
Fi = F1tmp + F2tmp + F3tmp + F4tmp;


ID: valveMech
NAME: Valve: processing mechanism
uk#NAME: Клапан: виконавчий механізм
ru#NAME: Клапан: исполнительный механизм
DESCR: Model of the valve processing mechanism. Includes the going and estrangement time.
uk#DESCR: Модель виконавчого механізму клапана. Включає час ходу та час відриву.
ru#DESCR: Модель исполнительного механизма клапана. Включает время хода и время отрыва.
FORMULA:
if((pos >= 99 && com >= 99) || (pos <= 1 && com <= 1)) {
  tmp_up = t_up;
  if( pos >= 99) { pos = 100; st_open = true; }
  else { pos = 0; st_close = true; }
}
else if(tmp_up > 0) tmp_up -= 1/f_frq;
else {
  st_open = st_close = false;
  lst_com += (com-lst_com)/(0.5*t_full*f_frq);
  pos += (lst_com-pos)/(0.5*t_full*f_frq);
}
pos_sensor += (pos-pos_sensor)/(t_sensor*f_frq);


ID: diaphragm
NAME: Diaphragm
uk#NAME: Діафрагма
ru#NAME: Диафрагма
DESCR: Diaphragm model.
uk#DESCR: Модель діафрагми.
ru#DESCR: Модель диафрагмы.
FORMULA:
DAQ.JavaLikeCalc.lib_techApp.pipeBase(Fi, Pi, 293, Sdf, Fo, Po, 293, So, lo, Q0, Kpr, 0.01, f_frq);
dP -= (dP-100*(Pi-Po))/f_frq;


ID: pipe3_1
NAME: Pipe 3->1
uk#NAME: Труба 3->1
ru#NAME: Труба 3->1
DESCR: Model of the pipe by the scheme "3 -> 1".
uk#DESCR: Модель вузла труб за схемою "3 -> 1".
ru#DESCR: Модель узла труб по схеме "3 -> 1".
FORMULA:
DAQ.JavaLikeCalc.lib_techApp.pipeBase(Fi1, Pi1, 293, Si1, EVAL_REAL, Po, 293, So, lo, Q0, Kpr, 0.01, f_frq);
DAQ.JavaLikeCalc.lib_techApp.pipeBase(Fi2, Pi2, 293, Si2, EVAL_REAL, Po, 293, So, lo, Q0, Kpr, 0.01, f_frq);
DAQ.JavaLikeCalc.lib_techApp.pipeBase(Fi3, Pi3, 293, Si3, EVAL_REAL, Po, 293, So, lo, Q0, Kpr, 0.01, f_frq);
Po = max(0, min(100,Po+0.27*(Fi1+Fi2+Fi3-Fo)/(Q0*Kpr*So*lo*f_frq)));
To = max(0, To+(Fi1*(Ti1-To)+Fi2*(Ti2-To)+Fi3*(Ti3-To)+(Fwind+1)*(Twind-To)/Riz)/(Ct*So*lo*Q0*f_frq));


ID: pipeBase
NAME: Pipe-base
uk#NAME: Труба-база
ru#NAME: Труба-база
DESCR:
Implementation of the basic foundations of the pipe model:
- flow in the pipe, taking into account: the speed, pressure difference, resistance due to friction and the critical flow;
- calculation of the pressure;
- accounting for the environment density and degree of the compressibility for both gases and liquids.

uk#DESCR:
Реалізація базових основ моделі труби:
- витрати у трубі з урахуванням: швидкості руху, різниці тиску, опору за рахунок тертя та критичної течі;
- розрахунок тиску;
- урахування щільності середовища та ступеня його стиснення як для газів, так і рідин.

ru#DESCR:
Реализация базовых основ модели трубы:
- расход в трубе с учётом: скорости движения, перепада давления, сопротивления за счёт трения и критического истечения;
- расчёт давления;
- учёт плотности среды и степени её сжимаемости как для газов, так и для жидкостей.

FORMULA:
Pmax = max(Pi,Po);
Pmin = min(Pi, Po);
Qr = Q0+Q0*Kpr*(Pmax-1);
Fit = 630*(4*Si*So*Qr/(Ktr*lo*1.7724*Si+4*So*Qr))*sign(Pi-Po)*pow(Qr*(Pmax-max(Pmax*0.528,Pmin)),0.5);
Fi -= (Fi-Fit)/max(1,(lo*f_frq)/max(1,abs(Fit/(Qr*So))));
if(!Fo.isEVal()) Po = max(0, min(100,Po+0.27*(Fi-Fo)/(Q0*Kpr*So*lo*f_frq)));


ID: heatExch
NAME: Heat exchanger
uk#NAME: Теплообмінник
ru#NAME: Теплообменник
DESCR: Model of the heat exchanger, calculating the heat exchange of the two streams.
uk#DESCR: Модель теплообміннику, що розраховує теплообмін двох потоків.
ru#DESCR: Модель теплообменника, рассчитывающая теплообмен двух потоков.
FORMULA:
DAQ.JavaLikeCalc.lib_techApp.pipeBase(Fi1, Pi1, Ti1, Si1, Fo1, Po1, 293, So1, lo1, Q0i1, Kpr1, 0.01, f_frq);
DAQ.JavaLikeCalc.lib_techApp.pipeBase(Fi2, Pi2, Ti2, Si2, Fo2, Po2, 293, So2, lo2, Q0i2, Kpr2, 0.01, f_frq);

To1 = max(0, min(1e4,(Fi1*Ti1*Ci1+ki*Fi2*Ti2*Ci2)/(Fi1*Ci1+ki*Fi2*Ci2)));
To2 = max(0, min(1e4,(ki*Fi1*Ti1*Ci1+Fi2*Ti2*Ci2)/(ki*Fi1*Ci1+Fi2*Ci2)));


ID: boilerBarrel
NAME: Boiler: barrel
uk#NAME: Котел: барабан
ru#NAME: Котёл: барабан
DESCR: The model of the boiler's barrel.
uk#DESCR: Модель барабану котлоагрегату.
ru#DESCR: Модель барабана котлоагрегата.
FORMULA:
// Water
DAQ.JavaLikeCalc.lib_techApp.pipeBase(Fi1, Pi1, 293, Si1, EVAL_REAL, Po1, 293, So1, lo1, 1e3, 0.001, 0.01, f_frq);
Fi1 = max(0, Fi1);

// Steam
Lo = max(0, min(100,Lo+(Fi1-Fstm)*100/(Vi1*1000*f_frq)));
To1 = (100*pow(Po1,0.241)+5) + 273;

if(Tv < To1) {
  Tv += (k*S*(Ti2-Tv)-Fi1*0.00418*(Tv-Ti1))/f_frq;
  Fstm = 0;
}
if(Tv >= To1) {
  Tv = To1;
  Lambda = 2750-0.00418*(Tv-273);
  Fstm = (5*S*Fi2*(Ti2-Tv)-Fi1*0.00418*(Tv-Ti1))/(Po1*Lambda);
}

To2 = Ti2-Tv/k;
Po1 = max(0, min(100,Po1+0.27*(Fstm-Fo)/(1.2*0.98*((1-Lo/100)*Vi1+So1*lo1)*f_frq)));

// Smoke gas
DAQ.JavaLikeCalc.lib_techApp.pipeBase(Fi2, Pi2, 293, Si2, Fo2, Po2, 293, Si2, 30, 1.2, 0.98, 0.01, f_frq);


ID: boilerBurner
NAME: Boiler: burner
uk#NAME: Котел: топка
ru#NAME: Котёл: топка
DESCR:
The fire-chamber model of the boiler unit, which operates on three types of fuel, initially is: blast-furnace, coke and natural gases.

uk#DESCR:
Модель топки котлоагрегату, який працює на трьох видах палива, початково це: доменний, коксовий та природний гази.

ru#DESCR:
Модель топки котлоагрегата, работающего на трех видах топлива, исходно это: доменный, коксовый и природный газы.

FORMULA:
using DAQ.JavaLikeCalc.lib_techApp;
pipeBase(Fi1, Pi1, Ti1, Si1, EVAL_REAL, Po, 293, So, lo, 1.2, 0.95, 0.01, f_frq); Fi1 = max(0, Fi1);
pipeBase(Fi2, Pi2, Ti2, Si2, EVAL_REAL, Po, 293, So, lo, 0.7, 0.95, 0.01, f_frq); Fi2 = max(0, Fi2);
pipeBase(Fi3, Pi3, Ti3, Si3, EVAL_REAL, Po, 293, So, lo, 1.33, 0.95, 0.01, f_frq); Fi3 = max(0, Fi3);
pipeBase(Fi4, Pi4, Ti4, Si4, EVAL_REAL, Po, 293, So, lo, 1.293, 0.95, 0.01, f_frq); Fi4 = max(0, Fi4);

Neobhod_vzd = Fi1 + 10*Fi2 + 4*Fi3;
F_DG = Fi1 + Fi2 + Fi3 + Fi4;
O2 = max(0, min(100,(Fi4-Neobhod_vzd)*100/F_DG));
CO = min(100, (O2<1) ? (1.2*abs(O2)) : 0);
koef = min(1, Fi4/Neobhod_vzd);
Q = koef*(8050*Fi2+3900*Fi3+930*Fi1);
delta_t = Q/(F_DG*1.047);
To = max(0, min(2000,(delta_t+(Ti4-273)+(Ti3-273)*(Fi3/Fi1)+(Ti2-273)*(Fi2/Fi1)+(Ti1-273)*(Fi1/Fi4))+273));

Po = max(0, min(10,Po+0.27*(F_DG-Fo)/(1.2*0.95*(So*lo+V)*f_frq)));


ID: pipe2_1
NAME: Pipe 2->1
uk#NAME: Труба 2->1
ru#NAME: Труба 2->1
DESCR: Model of the pipe by the scheme "2 -> 1".
uk#DESCR: Модель вузла труб за схемою "2 -> 1".
ru#DESCR: Модель узла труб по схеме "2 -> 1".
FORMULA:
DAQ.JavaLikeCalc.lib_techApp.pipeBase(Fi1, Pi1, 293, Si1, EVAL_REAL, Po, 293, So, lo, Q0, Kpr, 0.01, f_frq);
DAQ.JavaLikeCalc.lib_techApp.pipeBase(Fi2, Pi2, 293, Si2, EVAL_REAL, Po, 293, So, lo, Q0, Kpr, 0.01, f_frq);
Po = max(0, min(100,Po+0.27*(Fi1+Fi2-Fo)/(Q0*Kpr*So*lo*f_frq)));
To = max(0, To+(Fi1*(Ti1-To)+Fi2*(Ti2-To)+(Fwind+1)*(Twind-To)/Riz)/(Ct*So*lo*Q0*f_frq));


# flb_web

ID: alarms
NAME: Alarms
ru#NAME: Сигнализации
uk#NAME: Сигналізації
FORMULA:
//> Load rules
var alarmsTree = SYS.XMLNode("ALARMS");
alarmsTree.load(rules);
for(i_rl = 0; i_rl < alarmsTree.childSize(); i_rl++)
{
	if(!alarmsTree.childGet(i_rl).text().length) continue;
	var subId = alarmsTree.childGet(i_rl).attr("id").parse(0,".");
	var devId =  alarmsTree.childGet(i_rl).attr("id").parse(1,".");
	var curAttrVal = SYS.DAQ.ModBus.ZegBee[subId][devId].get();
	var curErr = "";
	if(curAttrVal.isEVal()) curErr = "Value error. Perhaps the device is missing.";
	else
	{
		var progArgs = new Object();
		progArgs.err = "";
		progArgs.x = curAttrVal;
		SYS.DAQ.funcCall("JavaLikeCalc.JavaScript",progArgs,alarmsTree.childGet(i_rl).text());
		if(progArgs.err.length) curErr = progArgs.err;
	}
	//> Create alarm
	if(curErr.length && curErr != alarmsTree.childGet(i_rl).attr("err"))
	{
		SYS.message("ALARM:House:"+alarmsTree.childGet(i_rl).attr("id"),-4,curErr);
		//>> Send SMS for new alarm messages to users
		var smsReq = SYS.XMLNode("send").setAttr("ProtIt","SMS").setText(alarmsTree.childGet(i_rl).attr("id")+": "+curErr);
		var userLst = SYS.XMLNode("get").setAttr("path","/sub_Security/grp_Web/%2fprm%2fusers");
		SYS.cntrReq(userLst);
		for(var i = 0; i < userLst.childSize(); i++)
		{
			var isSelAdmin = SYS.Security.access(userLst.childGet(i).text(),4,"root","WebRoot",0440);
			var treeEl = SYS.XMLNode("get").setAttr("path","/sub_Security/usr_"+userLst.childGet(i).text()+"/%2fprm%2fldscr");
			SYS.cntrReq(treeEl);
			var re = treeEl.text().match("SMS:\\s+([^\\n]*)");
			if(!re.length || re[1] != "true") continue;
			if(!isSelAdmin)
			{
				re=treeEl.text().match("sub_"+subId+":\\s+([^\\n]*)");
				if(!re.length || re[1] == "--") continue;
			}
			re = treeEl.text().match("TEL:\\s+([^\\n]*)");
			if(!re.length) continue;
			var telLs = re[1].split(new RegExp("\\s+"));
			for(var i_t = 0; i_t < telLs.length; i_t++)
			{
				smsReq.setAttr("tel",telLs[i_t]);
				SYS.Transport.Serial.out_GSM.messIO(smsReq,"UserProtocol");
				if(smsReq.attr("err") != "0") SYS.messWarning("SMS send",smsReq.attr("err"));
			}
		}
	}
	//> Clear alarm
	else if(!curErr.length && !alarmsTree.childGet(i_rl).attr("err").isEVal())
		SYS.message("ALARM:House:"+alarmsTree.childGet(i_rl).attr("id"),1,"Norma");
	alarmsTree.childGet(i_rl).setAttr("err",curErr);	
}
rules = alarmsTree.save();


ID: devMon
NAME: Devices monitor
ru#NAME: Монитор устройств
uk#NAME: Монітор пристроїв
FORMULA:
var treeEl = false;

//> Parse template
var pgTree = SYS.XMLNode();
var err = pgTree.load(tmpl,false,true);

//> Access
var isAdmin = SYS.Security.access(user,4,"root","WebRoot",0440);
treeEl = SYS.XMLNode("get").setAttr("path","/sub_Security/usr_"+user+"/%2fprm%2fldscr");
SYS.cntrReq(treeEl);
re=treeEl.text().match("sub_"+pgTree.attr("class")+":\\s+([^\\n]*)");
var subAccess = re.length ? re[1] : "--";

//> Devices list place to template
var cntrPrm = SYS.DAQ.ModBus.ZegBee[pgTree.attr("class")];
catAttrReq = SYS.XMLNode("get").setAttr("path","/DAQ/ModBus/ZegBee/"+pgTree.attr("class")+"/%2fprm%2fcfg%2fATTR_LS");
SYS.cntrReq(catAttrReq);
catAttrLs = catAttrReq.text();

//> Direct requests by XML process
if(!urlPrms["com"].isEVal())
{
	var xmlRespTree = SYS.XMLNode(urlPrms["com"]);
	if(urlPrms["com"] == "info")
	{
		//> Get timers
		var timersList = SYS.XMLNode("get").setAttr("path","/sub_DAQ/mod_JavaLikeCalc/cntr_timers/prm_rules/%2fval%2fvar");
		SYS.cntrReq(timersList);
		var timersTree = SYS.XMLNode("TIMERS");
		timersTree.load(timersList.text());

		//> Devices process
		for(var off = 0; (catAttrId=catAttrLs.parse(0,"\n",off)).length; )
		{
			if((catAttrId.parse(2,":") == "r" && (isAdmin || subAccess != "--")) || (catAttrId.parse(2,":") == "rw" && (isAdmin || subAccess == "rw")))
			{
				var xmlRespEl = xmlRespTree.childAdd(catAttrId.parse(5,":")).setAttr("name",catAttrId.parse(4,":")).setAttr("id",catAttrId.parse(3,":")).setAttr("acs",catAttrId.parse(2,":"));
				if(catAttrId.parse(5,":") == "bin")
					xmlRespEl.setAttr("vl1",catAttrId.parse(6,":")).setAttr("nm1",catAttrId.parse(7,":"))
								  .setAttr("vl2",catAttrId.parse(8,":")).setAttr("nm2",catAttrId.parse(9,":")).setAttr("sensorPos",catAttrId.parse(10,":"));
				else if(catAttrId.parse(5,":") == "dec") xmlRespEl.setAttr("ed",catAttrId.parse(6,":")).setAttr("sensorPos",catAttrId.parse(8,":"));;
				//> Timer get
				var timerNd = timersTree.getElementBy(pgTree.attr("class")+"."+catAttrId.parse(3,":"));
				if(typeof(timerNd)=="XMLNode" && timerNd.attr("tm").toInt(10) > 0) xmlRespEl.setAttr("tm",timerNd.attr("tm"));
			}
		}
	}
	else if(urlPrms["com"] == "vals")
		for(var off = 0; (catAttrId=catAttrLs.parse(0,"\n",off)).length; )
		{
			if((catAttrId.parse(2,":") == "r" && (isAdmin || subAccess != "--")) || (catAttrId.parse(2,":") == "rw" && (isAdmin || subAccess == "rw")))
			{
				var curAttrVal = cntrPrm[catAttrId.parse(3,":")].get();
				var xmlRespEl = xmlRespTree.childAdd(catAttrId.parse(5,":")).setAttr("id",catAttrId.parse(3,":"));
				//>>> Value error
				if(curAttrVal.isEVal()) xmlRespEl.setAttr("val","<err>").setText("Ошибка");
				else
				{
					xmlRespEl.setAttr("val",curAttrVal);
					//>>> Prepare value for different type
					if(catAttrId.parse(5,":") == "dec")
					{
						//>>>> Calc by formule add
						var calcProg = catAttrId.parse(7,":").replace("%3A",":");
						if(calcProg.length && catAttrId.parse(2,":") != "rw")
						{
							var progArgs = new Object();
							progArgs.y = curAttrVal;
							progArgs.x = curAttrVal;
							SYS.DAQ.funcCall("JavaLikeCalc.JavaScript",progArgs,calcProg);
							curAttrVal = progArgs.y;
						}
						xmlRespEl.setText(curAttrVal);
					}
					else if(catAttrId.parse(5,":") == "bin")
					{
						if(curAttrVal == catAttrId.parse(6,":").replace("%3A",":")) curAttrVal = catAttrId.parse(7,":").replace("%3A",":");
 						else if(curAttrVal == catAttrId.parse(8,":").replace("%3A",":")) curAttrVal = catAttrId.parse(9,":").replace("%3A",":");
						else curAttrVal = "Неизвестно";
						xmlRespEl.setText(curAttrVal);
					}
				}
			}
		}
	else if(urlPrms["com"] == "set")
		for(var off = 0; (catAttrId=catAttrLs.parse(0,"\n",off)).length; )
		{
			if(catAttrId.parse(2,":") != "rw" || catAttrId.parse(3,":") != urlPrms["dev"]) continue;
			var setVal = EVAL_BOOL;
			if(catAttrId.parse(5,":") == "bin")
			{
				if(urlPrms["val"] != cntrPrm[urlPrms["dev"]].get())	setVal = urlPrms["val"];
			}
			else if(catAttrId.parse(5,":") == "dec")
			{
				var calcProg = catAttrId.parse(7,":").replace("%3A",":");
				if(!calcProg.length)
				{
					if(urlPrms["val"] != cntrPrm[urlPrms["dev"]].get()) setVal = urlPrms["val"];
				}
				else
				{
					var progArgs = new Object();
					progArgs.y = urlPrms["val"];
					progArgs.x = urlPrms["val"];
					SYS.DAQ.funcCall("JavaLikeCalc.JavaScript",progArgs,calcProg);
					setVal = progArgs.y;
				}
			}
			if(!setVal.isEVal())
			{				
				if(urlPrms["tm"].toInt(10) <= 0)
				{
					cntrPrm[urlPrms["dev"]].set(setVal);
					SYS.message("SET:House:"+pgTree.attr("class")+"."+urlPrms["dev"],2,user+": "+pgTree.attr("class")+"."+urlPrms["dev"]+": установлен в: "+setVal);
				}
				else
				{
					var timersList = SYS.XMLNode("get").setAttr("path","/sub_DAQ/mod_JavaLikeCalc/cntr_timers/prm_rules/%2fval%2fvar");
					SYS.cntrReq(timersList);
					var timersTree = SYS.XMLNode("TIMERS");
					timersTree.load(timersList.text());

					var timerNd = timersTree.getElementBy(pgTree.attr("class")+"."+urlPrms["dev"]);
					if(typeof(timerNd) != "XMLNode") timerNd = timersTree.childAdd("timer").setAttr("id",pgTree.attr("class")+"."+urlPrms["dev"]);
					timerNd.setAttr("tm",urlPrms["tm"]).setAttr("user",user).setText(setVal);
					timersList = SYS.XMLNode("set").setAttr("path","/sub_DAQ/mod_JavaLikeCalc/cntr_timers/prm_rules/%2fval%2fvar").setText(timersTree.save());
					SYS.cntrReq(timersList);
					SYS.message("SET:House:"+pgTree.attr("class")+"."+urlPrms["dev"],2,user+": "+pgTree.attr("class")+"."+urlPrms["dev"]+": установка в: "+setVal+", по таймеру: "+urlPrms["tm"]+"секунд.");
				}
			}
			break;
		}
	else if(urlPrms["com"] == "timerClear")
		for(var off = 0; (catAttrId=catAttrLs.parse(0,"\n",off)).length; )
		{
			if(catAttrId.parse(2,":") != "rw" || catAttrId.parse(3,":") != urlPrms["dev"]) continue;
			var timersList = SYS.XMLNode("get").setAttr("path","/sub_DAQ/mod_JavaLikeCalc/cntr_timers/prm_rules/%2fval%2fvar");
			SYS.cntrReq(timersList);
			var timersTree = SYS.XMLNode("TIMERS");
			timersTree.load(timersList.text());

			var timerNd = timersTree.getElementBy(pgTree.attr("class")+"."+urlPrms["dev"]);
			if(typeof(timerNd) == "XMLNode")
			{
				timerNd.setAttr("tm",0);
				timersList = SYS.XMLNode("set").setAttr("path","/sub_DAQ/mod_JavaLikeCalc/cntr_timers/prm_rules/%2fval%2fvar").setText(timersTree.save());
				SYS.cntrReq(timersList);
			}
			break;
		}

	return "200 OK\nContent-Type:text/xml\n\n"+xmlRespTree.save();
}

//Save template
rez = pgTree.save();
//SYS.messDebug("TEST Dev","TEST 00: "+typeof(devLs[formEl["cat"]+"."+itId]));


ID: devices
NAME: Devices dispatcher
ru#NAME: Диспетчер устройств
uk#NAME: Диспетчер пристроїв
FORMULA:
var treeEl = false;

//> Parse template
var pgTree = SYS.XMLNode();
var err = pgTree.load(tmpl,false,true);

//> Get categories list
var catLs = SYS.XMLNode("get").setAttr("path","/DAQ/ModBus/ZegBee/%2fprm%2fprm");
SYS.cntrReq(catLs);

//> Get devices list
var devLs = new Object();
for(var i = 0; i < catLs.childSize(); i++)
{
	var curCat = catLs.childGet(i).attr("id");
	catAttrReq = SYS.XMLNode("get").setAttr("path","/DAQ/ModBus/ZegBee/"+curCat+"/%2fprm%2fcfg%2fATTR_LS");
	SYS.cntrReq(catAttrReq);
	catAttrLs = catAttrReq.text();
	for(var off = 0; (catAttrId=catAttrLs.parse(0,"\n",off)).length; )
		devLs[curCat+"."+catAttrId.parse(3,":")] = true;
}

//> Process page
//>> Place categories list
if(typeof(treeEl=pgTree.getElementBy("cat","name")) == "XMLNode")
	for(var i = 0; i < catLs.childSize(); i++)
		treeEl.childAdd("OPTION").setAttr("value",catLs.childGet(i).attr("id")).setText(catLs.childGet(i).text());

//> Check POST content for commands
if(cnts.length)
{
	var formEl = new Object();
	re = new RegExp("name=\"(.+)\"");
	//>> POST form elements process
	for(var i = 0; i < cnts.length; i++)
		formEl[re.exec(cnts[i].attr("Content-Disposition"))[1]] = cnts[i].text();
	//>> Check for apply
	if(!formEl["apply"].isEVal())
	{
		var applyOK = true;
		//>> ID: Check for correct form's values
		var itId = formEl["id"].match("[^\\s].+[^\\s]");
		if(!itId.length)
		{
			treeEl = pgTree.getElementBy("idNm","name");
			treeEl.childAdd("br");
			treeEl.childAdd("span").setAttr("style","color : red;").setText("(Пустое значение недопустимо)");
			itId = "";
			applyOK = false;
		}
		else itId = itId[0];
		pgTree.getElementBy("id","name").setAttr("value",itId);

		//>> Name: Check for correct form's values
		var itName = formEl["name"].match("[^\\s].+[^\\s]");
		if(!itName.length)
		{
			treeEl = pgTree.getElementBy("nameNm","name");
			treeEl.childAdd("br");
			treeEl.childAdd("span").setAttr("style","color : red;").setText("(Пустое значение недопустимо)");
			itName = "";
			applyOK = false;
		}
		else itName = itName[0];
		pgTree.getElementBy("name","name").setAttr("value",itName);

		//>> Address: Check for correct form's values
		var itAddr = formEl["addr"].toInt();
		if(itAddr < 0 || itAddr > 65535)
		{
			treeEl = pgTree.getElementBy("addrNm","name");
			treeEl.childAdd("br");
			treeEl.childAdd("span").setAttr("style","color : red;").setText("(Адрес ModBus должен быть в диапазоне (1-65535))");
			applyOK = false;
		}
		pgTree.getElementBy("addr","name").setAttr("value",itAddr);

		//>> Type: Check for correct form's values
		pgTree.getElementBy(formEl["type"],"value").setAttr("selected","selected");

		//>> sensorPos: Check for correct form's values
		pgTree.getElementBy("sensorPos","name").setAttr("value",formEl["sensorPos"]);

		//>> Category: Check for correct form's values
		pgTree.getElementBy(formEl["cat"],"value").setAttr("selected","selected");

		//>> Data type: 
		pgTree.getElementBy(formEl["typeData"],"value").setAttr("selected","selected");
		pgTree.getElementBy("in1").setAttr("style","display: "+((formEl["typeData"] == "bin")?"":"none"));
		pgTree.getElementBy("in2").setAttr("style","display: "+((formEl["typeData"] == "bin")?"":"none"));
		pgTree.getElementBy("ed").setAttr("style","display: "+((formEl["typeData"] == "dec")?"":"none"));
		pgTree.getElementBy("formul").setAttr("style","display: "+((formEl["typeData"] == "dec")?"":"none"));

		//>> Binary type properties
		pgTree.getElementBy("in1","name").setAttr("value",formEl["in1"]);
		pgTree.getElementBy("view1","name").setAttr("value",formEl["view1"]);
		pgTree.getElementBy("in2","name").setAttr("value",formEl["in2"]);
		pgTree.getElementBy("view2","name").setAttr("value",formEl["view2"]);

		//>> Digital type properties
		pgTree.getElementBy("ed","name").setAttr("value",formEl["ed"]);
		pgTree.getElementBy("formul","name").setAttr("value",formEl["formul"]);	

		//>> Alarm
		pgTree.getElementBy("alarm","name").childGet(0).setText(formEl["alarm"]);

		//> Add or update device
		if(applyOK)
		{
			//>> Get curent attributes list for category
			catAttrReq = SYS.XMLNode("get").setAttr("path","/DAQ/ModBus/ZegBee/"+formEl["cat"]+"/%2fprm%2fcfg%2fATTR_LS");
			SYS.cntrReq(catAttrReq);
			catAttrLs = catAttrReq.text();
			var newAttrReq = "R:"+itAddr+":"+(formEl["type"]=="out"?"rw":"r")+":"+itId+":"+itName.replace(":","%3A")+":"+formEl["typeData"];
			if(formEl["typeData"] == "bin") newAttrReq += ":"+formEl["in1"].replace(":","%3A")+":"+formEl["view1"].replace(":","%3A")+":"+formEl["in2"].replace(":","%3A")+":"+formEl["view2"].replace(":","%3A");
			else if(formEl["typeData"] == "dec") newAttrReq += ":"+formEl["ed"].replace(":","%3A")+":"+formEl["formul"].replace(":","%3A");
			newAttrReq += ":"+formEl["sensorPos"]+"\n";
			//>> Append record
			var isAppend = typeof(devLs[formEl["cat"]+"."+itId]) == "null";
			if(isAppend)
			{
				catAttrLs += newAttrReq;
				devLs[formEl["cat"]+"."+itId] = true;
			}
			//>> Replace
			else catAttrLs = catAttrLs.replace(new RegExp("R:[^:]*:[^:]*:"+itId+":[^\n]*\n"),newAttrReq);
			//>> Write new attributes list
			catAttrReq = SYS.XMLNode("CntrReqs").setAttr("path","/DAQ/ModBus/ZegBee/"+formEl["cat"]);
			catAttrReq.childAdd("set").setAttr("path","/%2fprm%2fcfg%2fATTR_LS").setText(catAttrLs);
			catAttrReq.childAdd("set").setAttr("path","/%2fprm%2fst%2fen").setText(0);
			catAttrReq.childAdd("set").setAttr("path","/%2fprm%2fst%2fen").setText(1);
			catAttrReq.childAdd("save").setAttr("path","/%2fobj");
			//XMLNode("set").setAttr("path","/DAQ/ModBus/ZegBee/"+formEl["cat"]+"/%2fprm%2fcfg%2fATTR_LS").setText(catAttrLs);
			SYS.cntrReq(catAttrReq);
			//>> Set message about correct apply
			treeEl=pgTree.getElementBy("mess","name");
			treeEl.childAdd("span").setAttr("style","color : green;").setText(
				isAppend?"Новое устройство успешно добавлено. Можно продолжать.":"Устройство успешно обновлено. Можно продолжать.");
			//>> Set alarm procedure
			var alarmsList = SYS.XMLNode("get").setAttr("path","/sub_DAQ/mod_JavaLikeCalc/cntr_alarms/prm_rules/%2fval%2fvar");
			SYS.cntrReq(alarmsList);
			var alarmsTree = SYS.XMLNode("ALARMS");
			alarmsTree.load(alarmsList.text());
			var alarmNd = alarmsTree.getElementBy(formEl["cat"]+"."+formEl["id"]);
			if(typeof(alarmNd)=="XMLNode") alarmNd.setText(formEl["alarm"]);
			else alarmsTree.childAdd("it").setAttr("id",formEl["cat"]+"."+formEl["id"]).setText(formEl["alarm"]);
			alarmsList = SYS.XMLNode("CntrReqs").setAttr("path","/sub_DAQ/mod_JavaLikeCalc/cntr_alarms/prm_rules");
			alarmsList.childAdd("set").setAttr("path","/%2fval%2fvar").setText(alarmsTree.save());
			alarmsList.childAdd("save").setAttr("path","/%2fobj");
			SYS.cntrReq(alarmsList);
		}
		//> Form error
		else
		{
			treeEl=pgTree.getElementBy("mess","name");
			treeEl.childAdd("span").setAttr("style","color : red;").setText("Ошибка в формировании формы! Исправьте отмеченные поля и повторите.");
		}
	}
	else if(!formEl["edDel"].isEVal())
	{
		if(formEl["edId"].length && typeof(devLs[formEl["edId"]]) != "null")
		{
			//>> Get curent attributes list for category
			catAttrReq = SYS.XMLNode("get").setAttr("path","/DAQ/ModBus/ZegBee/"+(formEl["edId"].parse(0))+"/%2fprm%2fcfg%2fATTR_LS");
			SYS.cntrReq(catAttrReq);
			catAttrLs = catAttrReq.text();
			catAttrLs = catAttrLs.replace(new RegExp("R:[^:]*:[^:]*:"+formEl["edId"].parse(1)+":[^\n]*\n"),"");
			//>> Write new attributes list
			catAttrReq = SYS.XMLNode("set").setAttr("path","/DAQ/ModBus/ZegBee/"+(formEl["edId"].parse(0))+"/%2fprm%2fcfg%2fATTR_LS").setText(catAttrLs);
			SYS.cntrReq(catAttrReq);
			//>> Set message about correct apply
			treeEl=pgTree.getElementBy("mess","name");
			treeEl.childAdd("span").setAttr("style","color : green;").setText("Устройство успешно удалено. Можно продолжать.");
			devLs[formEl["edId"]] = false;
		}
		else
		{
			treeEl=pgTree.getElementBy("mess","name");
			treeEl.childAdd("span").setAttr("style","color : red;").setText("Устройство для удаления отсутствует.");
		}
	}
}

//>> Devices list place to template
treeEl=pgTree.getElementBy("edId","name");
treeEl.childAdd("OPTION");
for(var i_o in devLs)
	if(devLs[i_o]) treeEl.childAdd("OPTION").setAttr("value",i_o).setText(i_o);

//>> Load variables for select device
if(!cnts.length && !urlPrms["selDev"].isEVal() && urlPrms["selDev"].length) 
{
	//> Select device
	pgTree.getElementBy(urlPrms["selDev"],"value").setAttr("selected","selected");
	//> Load values for select device
	catAttrReq = SYS.XMLNode("get").setAttr("path","/DAQ/ModBus/ZegBee/"+(urlPrms["selDev"].parse(0))+"/%2fprm%2fcfg%2fATTR_LS");
	SYS.cntrReq(catAttrReq);
	catAttrLs = catAttrReq.text().match("R:([^:]*):([^:]*):"+urlPrms["selDev"].parse(1)+":([^\n]*)");
	//>> ID: Check for correct form's values
	pgTree.getElementBy("id","name").setAttr("value",urlPrms["selDev"].parse(1));
	//>> Name: Check for correct form's values
	pgTree.getElementBy("name","name").setAttr("value",catAttrLs[3].parse(0,":").replace("%3A",":"));
	//>> Address: Check for correct form's values
	pgTree.getElementBy("addr","name").setAttr("value",catAttrLs[1]);
	//>> Type: Check for correct form's values
	pgTree.getElementBy(((catAttrLs[2]=="rw")?"out":"sensor"),"value").setAttr("selected","selected");
	//>> Category: Check for correct form's values
	pgTree.getElementBy(urlPrms["selDev"].parse(0),"value").setAttr("selected","selected");
	//>> Data type: 
	var dtType = catAttrLs[3].parse(1,":");
	pgTree.getElementBy(dtType,"value").setAttr("selected","selected");
	if(dtType == "bin")
	{
		pgTree.getElementBy("in1","name").setAttr("value",catAttrLs[3].parse(2,":").replace("%3A",":"));
		pgTree.getElementBy("view1","name").setAttr("value",catAttrLs[3].parse(3,":").replace("%3A",":"));
		pgTree.getElementBy("in2","name").setAttr("value",catAttrLs[3].parse(4,":").replace("%3A",":"));
		pgTree.getElementBy("view2","name").setAttr("value",catAttrLs[3].parse(5,":").replace("%3A",":"));
		pgTree.getElementBy("sensorPos","name").setAttr("value",catAttrLs[3].parse(6,":"));
	}
	else if(dtType == "dec")
	{
		pgTree.getElementBy("ed","name").setAttr("value",catAttrLs[3].parse(2,":").replace("%3A",":"));
		pgTree.getElementBy("formul","name").setAttr("value",catAttrLs[3].parse(3,":").replace("%3A",":"));
		pgTree.getElementBy("sensorPos","name").setAttr("value",catAttrLs[3].parse(4,":"));
	}
	pgTree.getElementBy("in1").setAttr("style","display: "+((dtType == "bin")?"":"none"));
	pgTree.getElementBy("in2").setAttr("style","display: "+((dtType == "bin")?"":"none"));
	pgTree.getElementBy("ed").setAttr("style","display: "+((dtType == "dec")?"":"none"));
	pgTree.getElementBy("formul").setAttr("style","display: "+((dtType == "dec")?"":"none"));

	//>> Get alarm procedure
	alarmsList = SYS.XMLNode("get").setAttr("path","/sub_DAQ/mod_JavaLikeCalc/cntr_alarms/prm_rules/%2fval%2fvar");
	SYS.cntrReq(alarmsList);
	var alarmsTree = SYS.XMLNode();
	alarmsTree.load(alarmsList.text());
	var alarmNd = alarmsTree.getElementBy(urlPrms["selDev"]);
	pgTree.getElementBy("alarm","name").childGet(0).setText((typeof(alarmNd)=="XMLNode")?alarmNd.text():"");
}
else pgTree.getElementBy("edDel","name").setAttr("style","visibility : hidden;");

//Save template
rez = pgTree.save();
//SYS.messDebug("TEST Dev","TEST 00: "+typeof(devLs[formEl["cat"]+"."+itId]));


ID: mess
NAME: Alarm messages
ru#NAME: Сообщения сигнализаций
uk#NAME: Повідомлення сигналізацій
FORMULA:
var treeEl = false;

//> Parse template
var pgTree = SYS.XMLNode();
var err = pgTree.load(tmpl,false,true);
treeEl = pgTree.getElementBy("mess");

var curTime = SYS.time();
var messAr = SYS.Archive.messGet(curTime,curTime,"ALARM:House:*",-1);
var trEl = treeEl.childAdd("tr");
trEl.childAdd("th").setText("Время");
trEl.childAdd("th").setText("Категория");
trEl.childAdd("th").setText("Уровень");
trEl.childAdd("th").setText("Сообщение");
for(i = 0; i < messAr.length; i++)
{
	var trEl = treeEl.childAdd("tr");
	trEl.childAdd("td").setAttr("class","mTm").setText(SYS.strftime(messAr[i].tm));
	trEl.childAdd("td").setAttr("class","mCat").setText(messAr[i].categ);
	trEl.childAdd("td").setAttr("class","mLev").setText(messAr[i].level);
	trEl.childAdd("td").setAttr("class","mMess").setText(messAr[i].mess);
}

//Save template
rez = pgTree.save();
//SYS.messDebug("TEST Dev","TEST 00: "+typeof(devLs[formEl["cat"]+"."+itId]));


ID: report
NAME: Reports
ru#NAME: Отчёты
uk#NAME: Звіти
FORMULA:
var treeEl = false;
var pgDir = "./HouseSpirit/Web/";

//> Parse template
var pgTree = SYS.XMLNode();
var err = pgTree.load(tmpl,false,true);

//> Load default values
pgTree.getElementBy("btm","name").setAttr("value",SYS.strftime(SYS.time()));
pgTree.getElementBy("etm","name").setAttr("value",SYS.strftime(SYS.time()));
var catLs = SYS.XMLNode("get").setAttr("path","/DAQ/ModBus/ZegBee/%2fprm%2fprm");
SYS.cntrReq(catLs);
treeEl = pgTree.getElementBy("subsys");
for(var i = 0; i < catLs.childSize(); i++)
{
	treeEl.childAdd("span").setText(catLs.childGet(i).text());
	treeEl.childAdd("input").setAttr("type","checkbox").setAttr("name",catLs.childGet(i).attr("id"));
	treeEl.childAdd("br");
}

//> Check POST content for commands - forming report
if(cnts.length)
{
	var formEl = new Object();
	var re = new RegExp("name=\"(.+)\"");
	//>> POST form elements process
	for(i_c = 0; i_c < cnts.length; i_c++)
		formEl[re.exec(cnts[i_c].attr("Content-Disposition"))[1]] = cnts[i_c].text();

	//>> Check for apply
	if(!formEl["show"].isEVal())
	{
		var reqRule = "";
		var applyOK = true;
		//>> Time: begin and end time check
		var btm = SYS.strptime(formEl["btm"]);
		pgTree.getElementBy("btm","name").setAttr("value",SYS.strftime(btm));
		var etm = SYS.strptime(formEl["etm"]);
		pgTree.getElementBy("etm","name").setAttr("value",SYS.strftime(etm));

		//>> System
		if(formEl["sys"].length)
		{
			pgTree.getElementBy("sys","name").setAttr("checked","checked");
			reqRule = "\\/sub_Protocol\\/mod_HTTP\\/";
		}

		//>> Subsystems
		var catLs = SYS.XMLNode("get").setAttr("path","/DAQ/ModBus/ZegBee/%2fprm%2fprm");
		SYS.cntrReq(catLs);
		for(var i = 0; i < catLs.childSize(); i++)
		{
			var subId = catLs.childGet(i).attr("id");
			if(formEl[subId].length)
			{
				pgTree.getElementBy(subId,"name").setAttr("checked","checked");
				if(reqRule.length) reqRule += "|";
				reqRule += "(ALARM|SET)\\:House\\:"+subId;
			}
		}

		//>> Report generate
		if(btm < etm && (etm-btm) < (365*24*60*60) && reqRule.length)
		{
			var messAr = SYS.Archive.messGet(btm,etm,"/("+reqRule+")/",1);
			treeEl = pgTree.getElementBy("report");
			var trEl = treeEl.childAdd("tr");
			trEl.childAdd("th").setText("Время");
			trEl.childAdd("th").setText("Категория");
			trEl.childAdd("th").setText("Уровень");
			trEl.childAdd("th").setText("Сообщение");
			for(i = 0; i < messAr.length; i++)
			{
				var trEl = treeEl.childAdd("tr");
				trEl.childAdd("td").setAttr("class","mTm").setText(SYS.strftime(messAr[i].tm));
				trEl.childAdd("td").setAttr("class","mCat").setText(messAr[i].categ);
				trEl.childAdd("td").setAttr("class","mLev").setText(messAr[i].level);
				trEl.childAdd("td").setAttr("class","mMess").setText(messAr[i].mess);
			}
			//>> Save report to file
			var saveFile = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
					"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"+
					"<html xmlns='http://www.w3.org/1999/xhtml'>\n"+
					"<head>\n"+
					"  <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/>\n"+
					"  <title>Отчёт</title>\n"+
					"  <style type='text/css'>\n"+
					"    #report { width: 100%; border: 1px solid black; border-collapse: collapse; empty-cells: show; }\n"+
					"    #report TH { border: 1px solid black; background-color: #E6E6E6; }\n"+
					"    #report TD { border: 1px solid black; }\n"+
					"    #report .mTm { white-space: nowrap; }\n"+
					"    #report .mLev { text-align: center; }\n"+
					"  </style>\n"+
					"</head>\n"+
					"<body>\n"+treeEl.save()+"\n</body>\n"+
					"</html>";
			SYS.fileWrite(pgDir+"reports/rep_"+user+".html",saveFile);
			pgTree.getElementBy("saveFile").setAttr("href","reports/rep_"+user+".html").setText("rep_"+user+".html");
		}
	}
}

//Save template
rez = pgTree.save();
//SYS.messDebug("TEST Dev","TEST 00: "+typeof(devLs[formEl["cat"]+"."+itId]));


ID: test
NAME: Test
ru#NAME: Тест
uk#NAME: Тест
FORMULA:
if(f_start)
{
	var req = SYS.XMLNode("send").setAttr("ProtIt","SMS").setAttr("tel","+380679859815").setText("Тестовое сообщение");
	SYS.Transport.Serial.out_GSM.messIO(req,"UserProtocol");
}


ID: timerWrite
NAME: Write by the timer
ru#NAME: Запись по таймеру
uk#NAME: Запис за таймером
FORMULA:
//> Load rules
var timersTree = SYS.XMLNode("TIMERS");
timersTree.load(rules);
for(i_rl = 0; i_rl < timersTree.childSize(); i_rl++)
{
	var curTm = timersTree.childGet(i_rl);
	if(!curTm.text().length) continue;
	var subId = curTm.attr("id").parse(0);
	var devId = curTm.attr("id").parse(1);
	var timer = curTm.attr("tm").toInt(10);
	if(timer && max(0,timer-1) == 0)
	{
		SYS.DAQ.ModBus.ZegBee[subId][devId].set(curTm.text());
		SYS.message("SET:House:"+curTm.attr("id"),2,curTm.attr("user")+": "+curTm.attr("id")+": установлен в: "+curTm.text());
	}
	curTm.setAttr("tm",max(0,timer-1));
}
rules = timersTree.save();


ID: user
NAME: Users dispatcher
ru#NAME: Диспетчер пользователей
uk#NAME: Диспетчер користувачів
FORMULA:
var treeEl = false;

//> Parse template
var pgTree = SYS.XMLNode();
var err = pgTree.load(tmpl,false,true);
var isAdmin = SYS.Security.access(user,4,"root","WebRoot",0440);
var curUser = user;

//> Check POST content for commands
if(cnts.length)
{
	var formEl = new Object();
	re = new RegExp("name=\"(.+)\"");
	//>> POST form elements process
	for(var i = 0; i < cnts.length; i++)
		formEl[re.exec(cnts[i].attr("Content-Disposition"))[1]] = cnts[i].text();
	//>> Check for apply
	if(!formEl["apply"].isEVal())
	{
		var applyOK = true;
		//>> Login: Check for correct form's values
		if(!(userLogin=formEl["login"].match("[^\\s].+[^\\s]")).length)
		{
			treeEl = pgTree.getElementBy("loginNm","name");
			treeEl.childAdd("br");
			treeEl.childAdd("span").setAttr("style","color : red;").setText("(Пустое значение недопустимо)");
			userLogin = "";
			applyOK = false;
		}
		else userLogin = userLogin[0];
		if(userLogin.isEVal()) userLogin = user;
		pgTree.getElementBy("login","name").setAttr("value",userLogin);
		var isUserPres = (typeof(SYS.Security.nodeAt("usr_"+userLogin)) == "TCntrNode");
		var isWebUser = isUserPres && SYS.Security.access(userLogin,4,"root","Web",0440);
		var isWebRootUser = isUserPres && SYS.Security.access(userLogin,4,"root","WebRoot",0440);
		if(isUserPres && !(isWebUser||isWebRootUser)) applyOK = false;
		if(!isAdmin && userLogin != user) applyOK = false;

		//>> Name: Check for correct form's values
		var userName = formEl["fio"].match("[^\\s].+[^\\s]");
		if(!userName.length)
		{
			treeEl = pgTree.getElementBy("fioNm","name");
			treeEl.childAdd("br");
			treeEl.childAdd("span").setAttr("style","color : red;").setText("(Пустое значение недопустимо)");
			userName = "";
			applyOK = false;
		}
		else userName = userName[0];
		pgTree.getElementBy("fio","name").setAttr("value",userName);

		//>> Password: Check for correct form's values
		if((!isUserPres && formEl["pass"].length < 8) || (isUserPres && formEl["pass"].length && formEl["pass"].length < 8))
		{
			treeEl = pgTree.getElementBy("passNm","name");
			treeEl.childAdd("br");
			treeEl.childAdd("span").setAttr("style","color : red;").setText("(Пароль должен быть более 8 символов)");
			userName = "";
			applyOK = false;
		}

		if(isAdmin)
		{
			//>> Telepfons list, SMS send and report view
			pgTree.getElementBy("tel","name").setAttr("value",formEl["tel"]);
			if(formEl["sms"].length) pgTree.getElementBy("sms","name").setAttr("checked","checked");
			if(formEl["report"].length) pgTree.getElementBy("report","name").setAttr("checked","checked");

			if(!isWebRootUser)
			{
				//>> Subsystem's permition
				var catLs = SYS.XMLNode("get").setAttr("path","/DAQ/ModBus/ZegBee/%2fprm%2fprm");
				SYS.cntrReq(catLs);
				treeEl = pgTree.getElementBy("sub_access");
				for(var i = 0; i < catLs.childSize(); i++)
				{
					var subId = "sub_"+catLs.childGet(i).attr("id");
					treeEl.childAdd("span").setText(catLs.childGet(i).text());
					treeEl.childAdd("br");
					treeEl1 = treeEl.childAdd("select").setAttr("name",subId);
					treeEl.childAdd("br");
					treeEl2 = treeEl1.childAdd("option").setAttr("value","--").setText("Нет доступа");
					if(formEl[subId] == "--") treeEl2.setAttr("selected","selected");
					treeEl2 = treeEl1.childAdd("option").setAttr("value","r-").setText("Наблюдение");
					if(formEl[subId] == "r-") treeEl2.setAttr("selected","selected");
					treeEl2 = treeEl1.childAdd("option").setAttr("value","rw").setText("Управление");
					if(formEl[subId] == "rw") treeEl2.setAttr("selected","selected");
				}
			}
			else
			{
				//> Session life time
				treeEl = pgTree.getElementBy("sub_access");
				treeEl.childAdd("span").setText("Время жизни сеанса аутентификации (минут)");
				treeEl.childAdd("br");
				treeEl.childAdd("input").setAttr("type","text").setAttr("name","sesAuth").setAttr("value",formEl["sesAuth"]);
			}
		}

		//> Add or update user
		if(applyOK)
		{
			//> Create new user if no present
			if(!isUserPres)
			{
				userReq = SYS.XMLNode("CntrReqs").setAttr("path","/sub_Security");
				userReq.childAdd("add").setAttr("path","%2fusgr%2fusers").setText(userLogin);
				userReq.childAdd("add").setAttr("path","/grp_Web/%2fprm%2fusers").setText(userLogin);
				SYS.cntrReq(userReq); 
			}
			//> Set user's properties
			userReq = SYS.XMLNode("CntrReqs").setAttr("path","/sub_Security/usr_"+userLogin);
			userReq.childAdd("set").setAttr("path","%2fprm%2fdscr").setText(userName);
			if(formEl["pass"].length)
				userReq.childAdd("set").setAttr("path","%2fprm%2fpass").setText(formEl["pass"]);
			if(isAdmin)
			{
				var descrVal = "TEL: "+formEl["tel"]+"\n"+
							"SMS: "+(formEl["sms"].length?"true":"false")+"\n"+
							"Report: "+(formEl["report"].length?"true":"false")+"\n";
				if(!isWebRootUser)
				{
					//>> Subsystem's permition
					for(var i = 0; i < catLs.childSize(); i++)
					{
						var subId = "sub_"+catLs.childGet(i).attr("id");
						descrVal += subId+": "+formEl[subId]+"\n";
					}
				}
				else
				{
					var sessLife = SYS.XMLNode("CntrReqs").setAttr("path","/sub_Protocol/mod_HTTP");
					sessLife.childAdd("set").setAttr("path","/%2fprm%2flf_tm").setText(max(1,formEl["sesAuth"].toInt()));
					sessLife.childAdd("save").setAttr("path","/%2fobj");
					SYS.cntrReq(sessLife);

				}
				userReq.childAdd("set").setAttr("path","%2fprm%2fldscr").setText(descrVal);
			}
			userReq.childAdd("save").setAttr("path","/%2fobj");
			SYS.cntrReq(userReq); 
			//>> Set message about correct apply
			treeEl=pgTree.getElementBy("mess","name");
			treeEl.childAdd("span").setAttr("style","color : green;").setText(
				(!isUserPres)?"Новый пользователь успешно добавлен. Можно продолжать.":"Пользователь успешно обновлен. Можно продолжать.");
		}
		//> Form error
		else
		{
			treeEl=pgTree.getElementBy("mess","name").childAdd("span").setAttr("style","color : red;");
			if(!isAdmin && userLogin != user) treeEl.setText("Обычный пользователь может редактировать только собственную учётную запись.");
			else if(isUserPres && !isWebUser) treeEl.setText("Пользователь присутствует, но не является Web-пользователем. Измените логин пользователя и повторите.");
			else treeEl.setText("Ошибка в формировании формы! Исправьте отмеченные поля и повторите.");
		}
	}
	else if(!formEl["edDel"].isEVal())
	{
		var isWebUser = SYS.Security.access(formEl["edLogin"],4,"root","Web",0440);
		var isWebRootUser = SYS.Security.access(formEl["edLogin"],4,"root","WebRoot",0440);
		if(isWebUser && !isWebRootUser)
		{
			userReq = SYS.XMLNode("CntrReqs").setAttr("path","/sub_Security");
			userReq.childAdd("del").setAttr("path","%2fusgr%2fusers").setText(formEl["edLogin"]);
			userReq.childAdd("del").setAttr("path","/grp_Web/%2fprm%2fusers").setText(formEl["edLogin"]);
			userReq.childAdd("save").setAttr("path","/%2fobj");
			SYS.cntrReq(userReq); 

			//>> Set message about correct remove
			treeEl=pgTree.getElementBy("mess","name");
			treeEl.childAdd("span").setAttr("style","color : green;").setText("Пользователь успешно удален. Можно продолжать.");
		}
		else
		{
			treeEl=pgTree.getElementBy("mess","name").childAdd("span").setAttr("style","color : red;");
			if(isWebRootUser) treeEl.setText("Удаление суперпользователя недопустимо!");
			else treeEl.setText("Пользователь для удаления отсутствует.");
		}
	}
}

//>> Load variables for select device
if(isAdmin)
{
	//> Load users list
	var userLst = SYS.XMLNode("get").setAttr("path","/sub_Security/grp_Web/%2fprm%2fusers");
	SYS.cntrReq(userLst);

	treeEl = pgTree.getElementBy("edLogin","name");
	treeEl.childAdd("OPTION").setAttr("value","").setText("");
	treeEl.childAdd("OPTION").setAttr("value","new").setText("Новый пользователь");
	for(var i = 0; i < userLst.childSize(); i++)
	{
		treeEl1 = SYS.XMLNode("get").setAttr("path","/sub_Security/usr_"+userLst.childGet(i).text()+"/%2fprm%2fdscr");
		SYS.cntrReq(treeEl1);
		treeEl.childAdd("OPTION").setAttr("value",userLst.childGet(i).text()).setText(treeEl1.text());
	}
}
else
{
	//> Hide select input and block login edit
	pgTree.getElementBy("edLogin","name").setAttr("style","display : none;");
	pgTree.getElementBy("login","name").setAttr("disabled","disabled");
	pgTree.getElementBy("tel").setAttr("style","visibility : hidden;");
	pgTree.getElementBy("sms").setAttr("style","visibility : hidden;");
	pgTree.getElementBy("report").setAttr("style","visibility : hidden;");
}

if(!cnts.length || !formEl["edDel"].isEVal())
{
	if(!urlPrms["selUser"].isEVal() && urlPrms["selUser"].length)
	{
		curUser = urlPrms["selUser"];
		pgTree.getElementBy(curUser,"value").setAttr("selected","selected");
	}
	var isSelAdmin = SYS.Security.access(curUser,4,"root","WebRoot",0440);

	//> Connect to user object
	//>> User login
	pgTree.getElementBy("login","name").setAttr("value",curUser);
	if(isAdmin && SYS.Security.access(curUser,4,"root","WebRoot",0440))
	{
		pgTree.getElementBy("login","name").setAttr("disabled","disabled");
		pgTree.getElementBy("edDel","name").setAttr("style","display : none;");
	}
	//>> User FN
	treeEl = SYS.XMLNode("get").setAttr("path","/sub_Security/usr_"+curUser+"/%2fprm%2fdscr");
	SYS.cntrReq(treeEl);
	pgTree.getElementBy("fio","name").setAttr("value",treeEl.text());
	if(isAdmin)
	{
		//>> Description
		treeEl = SYS.XMLNode("get").setAttr("path","/sub_Security/usr_"+curUser+"/%2fprm%2fldscr");
		SYS.cntrReq(treeEl);
		//>> Phones, SMS and repor
		re=treeEl.text().match("TEL:\\s+([^\\n]*)");
		pgTree.getElementBy("tel","name").setAttr("value",re.length ? re[1] : "");
		re=treeEl.text().match("SMS:\\s+([^\\n]*)");
		if(re.length && re[1] == "true") pgTree.getElementBy("sms","name").setAttr("checked","checked");
		re=treeEl.text().match("Report:\\s+([^\\n]*)");
		if(re.length && re[1] == "true") pgTree.getElementBy("report","name").setAttr("checked","checked");
		//>> Subsystem's permition
		if(!isSelAdmin)
		{
			var catLs = SYS.XMLNode("get").setAttr("path","/DAQ/ModBus/ZegBee/%2fprm%2fprm");
			SYS.cntrReq(catLs);
			treeEl1 = pgTree.getElementBy("sub_access");
			for(var i = 0; i < catLs.childSize(); i++)
			{
				var subId = "sub_"+catLs.childGet(i).attr("id");
				re=treeEl.text().match(subId+":\\s+([^\\n]*)");
				treeEl1.childAdd("span").setText(catLs.childGet(i).text());
				treeEl1.childAdd("br");
				treeEl2 = treeEl1.childAdd("select").setAttr("name",subId);
				treeEl1.childAdd("br");
				treeEl3 = treeEl2.childAdd("option").setAttr("value","--").setText("Нет доступа");
				if(re.length && re[1] == "--") treeEl3.setAttr("selected","selected");
				treeEl3 = treeEl2.childAdd("option").setAttr("value","r-").setText("Наблюдение");
				if(re.length && re[1] == "r-") treeEl3.setAttr("selected","selected");
				treeEl3 = treeEl3.childAdd("option").setAttr("value","rw").setText("Управление");
				if(re.length && re[1] == "rw") treeEl3.setAttr("selected","selected");
			}
		}
		else
		{
			//> Session life time
			treeEl1 = pgTree.getElementBy("sub_access");
			treeEl1.childAdd("span").setText("Время жизни сеанса аутентификации (минут)");
			treeEl1.childAdd("br");
			var sessLife = SYS.XMLNode("get").setAttr("path","/sub_Protocol/mod_HTTP/%2fprm%2flf_tm");
			SYS.cntrReq(sessLife);
			treeEl1.childAdd("input").setAttr("type","text").setAttr("name","sesAuth").setAttr("value",sessLife.text());
		}
	}
}

//> Delete button hide
if(!isAdmin || cnts.length || urlPrms["selUser"].isEVal() || !urlPrms["selUser"].length)
	pgTree.getElementBy("edDel","name").setAttr("style","display : none;");

//Save template
rez = pgTree.save();
//SYS.messDebug("TEST Dev","TEST 00: "+typeof(devLs[formEl["cat"]+"."+itId]));


