CREATE TABLE IF NOT EXISTS 'lib_Controllers' ("ID" TEXT DEFAULT '' ,"NAME" TEXT DEFAULT '' ,"ru#NAME" TEXT DEFAULT '' ,"uk#NAME" TEXT DEFAULT '' ,"DESCR" TEXT DEFAULT '' ,"ru#DESCR" TEXT DEFAULT '' ,"uk#DESCR" TEXT DEFAULT '' ,"START" INTEGER DEFAULT '1' ,"MAXCALCTM" INTEGER DEFAULT '10' ,"PR_TR" INTEGER DEFAULT '1' ,"FORMULA" TEXT DEFAULT '' ,"ru#FORMULA" TEXT DEFAULT '' ,"uk#FORMULA" TEXT DEFAULT '' ,"TIMESTAMP" INTEGER DEFAULT '' , PRIMARY KEY ("ID"));
INSERT INTO lib_Controllers VALUES('prescr','Prescriptions manager (moved)','','','!!!!: Moved and replaced by the template PrescrTempl.manager. Will be removed soon
Prescriptions manager and controller. Used in addition with user interface''s cadre "Prescription: editing" and "Prescription: runtime" for which into a parameter of the controller you must pass that parameters: "mode", "prog", "startTm", "curCom", "comLs", "work".

Author: Roman Savochenko <roman@oscada.org>
Sponsor: Vasiliy Grigoriev from "Vacuum technologies laboratory (http://e-beam.ru)".
Version: 1.1.1','','',1,10,0,'clcCnt++;

if(f_start)	work = SYS.XMLNode("prg");

//Check to commands controller present
comCntrO = false;
if(comsCntr.length)	comCntrO = SYS.DAQ.nodeAt(comsCntr,".");

//Commands list update
if(f_start || !(clcCnt%(60*f_frq))) {
	if(!comCntrO) {
		rez = SYS.BD.nodeAt(dbDB,".").SQLReq("SELECT * FROM "+dbComs+";");
		comLs = new Object();
		for(i_t = 1; i_t < rez.length; i_t++) {
			comNm = rez[i_t]["name"];
			comLs[comNm] = new Object();
			comLs[comNm]["descr"] = rez[i_t]["descr"];
			comLs[comNm]["proc"] = rez[i_t]["proc"];
			for(i_a = 1; i_a <= 5; i_a++)
				comLs[comNm]["arg"+i_a] = rez[i_t]["arg"+i_a];
		}
	}
	else {
		var nL = comCntrO.nodeList("prm_");
		comLs = new Object();
		for(i_n = 0; i_n < nL.length; i_n++) {
			comO = comCntrO[nL[i_n]];
			comNm = comO.cfg("NAME");
			comLs[comNm] = new Object();
			comLs[comNm]["prmID"] = nL[i_n].slice(4);
			comLs[comNm]["descr"] = comO.cfg("DESCR");
			for(i_a = 1; i_a <= 5; i_a++)
				if(!(comA=comO["arg"+i_a]).isEVal())
					comLs[comNm]["arg"+i_a] = comA.descr();
		}
	}
}

//Programm selection change
if(curMode <= 0 && prog.length && (prog != work.attr("name") || mode == 1)) {
	work = SYS.XMLNode("prg");
	rez = SYS.BD.nodeAt(dbDB,".").SQLReq("SELECT prgTxt FROM "+dbProgs+" WHERE name=''"+prog+"'';");
	if(rez.length > 1) {
		//Parse program and insert procedure text and argument''s labels
		work.load(rez[1][0]);
		work.setAttr("name",prog);
		for(comElN = work, comPos = 0; true; comPos++) {
			if(comPos >= comElN.childSize()) {
				if((comElN=comElN.parent())) { comPos = comElN.attr("seekPos").toInt(); continue; }
				break;
			}
			comElI = comElN.childGet(comPos);
			comId = comElI.attr("id");
			if(comLs[comId].isEVal())	continue;
			comElI.setAttr("proc",comLs[comId].proc);
			for(i_a = 1; i_a <= 5; i_a++) comElI.setAttr("labArg"+i_a,comLs[comId]["arg"+i_a]);
			if(comElI.childSize()) { comElN.setAttr("seekPos",comPos); comElN = comElI; comPos = -1; }
		}
	}
	else prog = "";
}

//Start process
if(curMode <= 0 && mode == 1 && prog.length) {
	startTm = SYS.time();
	curCom = 0;
	curMode = mode;	

	if(comCntrO) {
		var cL = comCntrO.nodeList("prm_");
		for(i_c = 0; i_c < cL.length; i_c++) {
			var cLi = comCntrO[cL[i_c]];
			cLi.run.set(false);
			cLi.stop.set(false);
			cLi.error.set(false);
			cLi.abort.set(false);
			cLi.start.set(true);
		}
	}
}
//Pause and other process
else if((mode == 0 && curMode < 0) || (curMode == 1 && mode == 2) || (curMode == 2 && (mode == 0 || mode == 1))) {
	//curMode = mode;
	if((mode == 0 || mode == 1 || mode == 2) && comCntrO) {
		var cL = comCntrO.nodeList("prm_");
		for(i_c = 0; i_c < cL.length; i_c++)
			comCntrO[cL[i_c]].pause.set(mode==2);
	}
}

//Call program
if(curMode == 1 || curMode == 2) {
	prog = work.attr("name");

	//Curent node at levels obtain
	curComNd = work;
	for(curComLev = 0, curComPos = 0, off = 0; curComNd && (tVl=curCom.parse(0,":",off)).length; curComLev++, curComPos = tVl.toInt())
		curComNd = curComNd.childGet(tVl.toInt());
	if(!curComNd) {
		SYS.messInfo("uprg"+prog,tr("No current node present")+" \""+prog+"\" : "+SYS.strftime(startTm)+" : "+SYS.strftime(SYS.time()));
		curMode = mode = -3;
	}
	curComNd = curComNd.parent();

	//Internal commands call
	if(!comCntrO) {
		//????
		if(mode == 3 && curCom >= 0 && curCom < work.childSize()) {
			work.childGet(curCom).setAttr("rez", "-10:"+tr("Step missed"));
			curCom++;
		}
		if(curCom >= 0 && curCom < work.childSize()) {
			comEl = work.childGet(curCom);
			//Stop process
			if(mode == 0) {
				comEl.setAttr("rez","-10:"+tr("Program terminated"));
				// Call stop command
				if(!comLs["Stop"].isEVal()) {
					off = 0;
					prLang = comLs["Stop"].proc.parse(0,"\n",off);
					SYS.DAQ.funcCall(prLang,new Object(),comLs["Stop"].proc.slice(off));
				}
				SYS.messInfo("uprg"+prog,tr("Terminated program session by the user")+" \""+prog+"\" : "+SYS.strftime(startTm)+" : "+SYS.strftime(SYS.time()));
				curMode = mode = -3;
			}
			//Pass empty command		
			else if(!comEl.attr("proc").length) curCom++;
			//Execute command
			else if(curMode == 1) {
				args = new Object();
				args.rez = "";
				args.f_start = !comEl.attr("tm").length;
				if(args.f_start) comEl.setAttr("tm",SYS.time());
				args.f_frq = f_frq;
				for(i_a = 1; i_a <= 5; i_a++) args["arg"+i_a] = comEl.attr("arg"+i_a);
				for(i_a = 1; i_a <= 10; i_a++) args["tmp"+i_a] = comEl.attr("tmp"+i_a);
				off = 0;
				prLang = comEl.attr("proc").parse(0,"\n",off);
				rez = SYS.DAQ.funcCall(prLang,args,comEl.attr("proc").slice(off));
				if(rez && args.rez.toInt() > 0) curCom++;
				else if(!rez || args.rez.toInt() < 0) {
					// Call error command
					if(!comLs["Error"].isEVal()) {
						off = 0;
						prLang = comLs["Error"].proc.parse(0,"\n",off);
						SYS.DAQ.funcCall(prLang,new Object(),comLs["Error"].proc.slice(off));
					}
					SYS.messInfo("uprg"+prog,tr("Terminated program session by the error")+" \""+prog+"\" : "+SYS.strftime(startTm)+" : "+SYS.strftime(SYS.time()));
					curMode = mode = -1;
				}
				comEl.setAttr("rez",args.rez);
				for(i_a = 1; i_a <= 5; i_a++) comEl.setAttr("arg"+i_a, args["arg"+i_a]);
				for(i_a = 1; i_a <= 10; i_a++) comEl.setAttr("tmp"+i_a, args["tmp"+i_a]);
				//SYS.messDebug("TEST Calc","TEST Calc rezult: "+args.y);
			}
		}

		//End call
		if(curCom < 0 || curCom >= work.childSize()) {
			// Call stop command
			if(!comLs["Stop"].isEVal()) {
				off = 0;
				prLang = comLs["Stop"].proc.parse(0,"\n",off);
				SYS.DAQ.funcCall(prLang,new Object(),comLs["Stop"].proc.slice(off));
			}

			curMode = mode = -2;
			SYS.messInfo("uprg"+prog,tr("Successful session of the program")+" \""+prog+"\" : "+SYS.strftime(startTm)+" : "+SYS.strftime(SYS.time()));

			//Place to program last execution time
			rez = SYS.BD.nodeAt(dbDB,".").SQLReq("SELECT prgTxt FROM "+dbProgs+" WHERE name=''"+prog+"'';");
			if(rez.length > 1) {
				//Parse programm and insert procedure text and argument''s labels to here
				comTree = SYS.XMLNode("prg");
				comTree.load(rez[1][0]);
				comTree.setAttr("wtm",SYS.time()-startTm);
				SYS.BD.nodeAt(dbDB,".").SQLReq("UPDATE "+dbProgs+" SET prgTxt=''"+comTree.save().replace("''","''''")+"'' WHERE name=''"+prog+"'';");
			}
		}
	}
	//External commands call
	else {
		// Get current command parameter-object		
		toNext = toStop = toPass = false;
		isRun = actBackGrnd = false;

		curComPrm = EVAL_BOOL;
		if(curComPos >= 0 && curComPos < curComNd.childSize()) {
			if(!(comId=comLs[curComNd.childGet(curComPos).attr("id")]).isEVal()) curComPrm = comCntrO["prm_"+comId.prmID];
			if(curComPrm.isEVal()) {
				curMode = mode = -1;
				curComNd.childGet(curComPos).setAttr("rez","-11:"+tr("Command miss: ")+work.childGet(curCom).attr("id"));
				return;
			}
		}
		if(mode == 3 && curComPos >= 0 && curComPos < curComNd.childSize()) {
			//curComNd.childGet(curComPos).setAttr("rez","-10:"+tr("Step missed"));
			//curComPrm.run.set(false);
			mode = curMode;
			toNext = toPass = true;
		}
		else if(curComPos >= 0 && curComPos < curComNd.childSize()) {
			comEl = curComNd.childGet(curComPos);

			//Stop process
			if(mode == 0) {
				toStop = true;
				//comEl.setAttr("rez","-12:"+tr("Program terminated"));
				// Stop all typical and set "abort" flag
				var cL = comCntrO.nodeList("prm_");
				for(i_c = 0; i_c < cL.length; i_c++) {
					var cLi = comCntrO[cL[i_c]];
					cLi.run.set(false);
					cLi.start.set(false);
					cLi.abort.set(true);
				}
				SYS.messInfo("uprg"+prog, tr("Terminated program session by the user")+" \""+prog+"\" : "+SYS.strftime(startTm)+" : "+SYS.strftime(SYS.time()));
				curMode = mode = -3;
			}
			//Commands process
			else {
				// Start command
				if(!comEl.attr("tm").length) {
					curComPrm.run.set(false);	//Stop for possible background call
					comEl.setAttr("tm", SYS.time());
					for(i_a = 1; i_a <= 5; i_a++)
						if(!(comA=curComPrm["arg"+i_a]).isEVal())
							comA.set(comEl.attr("arg"+i_a));
					curComPrm.rez.set(0);
					curComPrm.run.set(true);
					if(mode == 2) curComPrm.pause.set(true);
				}
				isRun = true;
			}
		}
		//Update steps status, up to the current comand
		isCurCmd = false;
		for(comElN = work, comPos = 0, comLev = 0, comCur = true; isRun || toPass || toStop; comPos++) {
			if(comPos >= comElN.childSize()) {
				if((comElN=comElN.parent())) {
					comLev--;
					comPos = comElN.attr("seekPos").toInt();
					comCur = comElN.attr("comCur").toInt();
					continue;
				}
				if(actBackGrnd && toNext) toNext = false;
				break;
			}
			comElI = comElN.childGet(comPos);
			isBackgrnd = comElI.attr("backgrnd").toInt();
			if(!isBackgrnd && actBackGrnd)	toNext = false;
			if(isCurCmd) break;
			isCurCmd = comCur && comLev == (curComLev-1) && curCom.parse(comLev,":").toInt() == comPos;
			curComPI = comCntrO["prm_"+comLs[comElI.attr("id")].prmID];
			rez = curComPI.rez.get();
			if(isCurCmd || ((toStop || curComPI.run.get()) && comElI.attr("rez").toInt() != 1 && comElI.attr("rez").toInt() > -10)) {
				if(toStop)	rez = "-12:" + tr("Program terminated");
				else if(toPass) {
					rez = "-10:" + tr("Step missed");
					curComPI.run.set(false);
				}
				else if(rez.toInt() < 0) {
					// Stop all typical and call "error" command
					var cL = comCntrO.nodeList("prm_");
					for(i_c = 0; i_c < cL.length; i_c++) {
						var cLi = comCntrO[cL[i_c]];
						cLi.run.set(false);
						cLi.start.set(false);
						cLi.error.set(true);
					}
					SYS.messInfo("uprg"+prog,tr("Terminated program session by the error")+" \""+prog+"\" : "+SYS.strftime(startTm)+" : "+SYS.strftime(SYS.time()));
					curMode = mode = -1;
				}
				if(!actBackGrnd && isBackgrnd && rez.toInt() == 0) actBackGrnd = true;
				if(isCurCmd && (rez.toInt() > 0 || isBackgrnd) && !toStop)	toNext = true;
				comElI.setAttr("rez", rez);
				for(i_a = 1; i_a <= 5; i_a++)
					if(!(comA=curComPI["arg"+i_a]).isEVal())
						comElI.setAttr("arg"+i_a, comA.get());
			}
			if(isCurCmd) continue;
			//Force backgrounded rezult update for running
			if(rez.toInt() != 0 && comElI.attr("rez").toInt() == 0)	comElI.setAttr("rez", rez);

			if(comElI.childSize()) {
				comElN.setAttr("seekPos", comPos);
				comElN.setAttr("comCur", comCur);
				comElN = comElI;
				comCur = comCur && comLev < curComLev && curCom.parse(comLev,":").toInt() == comPos;
				comPos = -1; comLev++;
			}
		}

		// Go to next step
		if(toNext) {
			if(curComNd.childGet(curComPos).childSize())	{ curComLev++; curComPos = 0; }
			else if((++curComPos) >= curComNd.childSize())
			{ curComLev--; curComPos = curComLev ? curCom.parse(curComLev-1,":").toInt()+1 : curComNd.childSize(); }
			for(i_c = 0, curComN = ""; i_c < (curComLev-1); i_c++) curComN += curCom.parse(i_c,":");
			curCom = curComN.length ? curComN+":"+curComPos : curComPos;
		}
		
		//End call
		if(curComLev <= 0) {
			// Stop all typical and call "stop" command
			var cL = comCntrO.nodeList("prm_");
			for(i_c = 0; i_c < cL.length; i_c++) {
				var cLi = comCntrO[cL[i_c]];
				cLi.run.set(false);
				cLi.start.set(false);
				cLi.stop.set(true);
			}

			curMode = mode = -2;
			SYS.messInfo("uprg"+prog,tr("Successful session of the program")+" \""+prog+"\" : "+SYS.strftime(startTm)+" : "+SYS.strftime(SYS.time()));

			//Place to program last execution time
			rez = SYS.BD.nodeAt(dbDB,".").SQLReq("SELECT prgTxt FROM "+dbProgs+" WHERE name=''"+prog+"'';");
			if(rez.length > 1) {
				//Parse program and insert procedure text and argument''s labels to here
				comTree = SYS.XMLNode("prg");
				comTree.load(rez[1][0]);
				comTree.setAttr("wtm",SYS.time()-startTm);
				SYS.BD.nodeAt(dbDB,".").SQLReq("UPDATE "+dbProgs+" SET prgTxt=''"+comTree.save().replace("''","''''")+"'' WHERE name=''"+prog+"'';");
			}
		}
	}
}
curMode = mode;
//mode = curMode;','','',1559660641);
INSERT INTO lib_Controllers VALUES('test','test','test','test','Different tests of the JavaLikeCalc language for execution into the controller mode.

Author: Roman Savochenko
Version: 1.1.1
','','',1,10,0,'/*using Special.FLibSYS;

out+=10;
if(out>100) out=0;

test = (10).toString(16,2,true);
test = (-10.34).toFixed(4,8);*/


//==================================================
//Testing common operation of the internal functions
//--------------------------------------------------
function f1( ) {
	aOut = 3.14;
	a = 20*2;
	b = 20*3;
	c = a + b;
	SYS.messInfo("InF","F1: a="+a+"; b="+b+"; c="+c+"; inFarg="+inFarg);
	return c;
}

function f2( ) {
	a = 30*2;
	b = 30*3;
	c = a + b + f1();
	SYS.messInfo("InF","F2: a="+a+"; b="+b+"; c="+c);
	return c;
}

a = 10*2;
b = 10*3;
res = c = a + b + f2();

function fR( a1, a2 ) {
	SYS.messInfo("InF","a2="+a2);
	d = 10;
	if(a1 < 5) fR(a1+1, a2);
	d++; a2++;
	SYS.messInfo("InF","d="+d+"; a1="+a1+"; a2="+a2);
}

//aOut = 0;
fR(0, aOut=0);

SYS.messInfo("InF","a="+a+"; b="+b+"; c="+c+"; f2="+f2()+"; aOut="+aOut);

//===========================================
//Testing recursion of the internal functions
//-------------------------------------------
/*function ASN1_Data( inSeq, offIn ) {
	val = EVAL;
	offIn += 1;
	if((vTp=inSeq.charCodeAt(offIn-1)) == 0)	//NULL
		val = 0;
	else if(vTp == 1 || vTp == 2) {		//Array, Structure
		itSz = inSeq.charCodeAt(offIn);
		val = new Array(itSz);
		offIn += 1;
		for(itI = 0; itI < itSz; itI++)
			val[itI] = ASN1_Data(inSeq, offIn);
	}
	else if(vTp == 4 || vTp == 18) {		//BIT STRING, Unsigned16
		val = inSeq.charCodeAt(offIn, "UTF-16BE");
		offIn += 2;
	}
	else if(vTp == 5) {							//Integer32
		val = inSeq.charCodeAt(offIn, "UTF-32BE");
		if(val > 2147483647) val -= 4294967296;
		offIn += 4;
	}
	else if(vTp == 6) {							//Unsigned32
		val = inSeq.charCodeAt(offIn, "UTF-32BE");
		offIn += 4;
	}
	else if(vTp == 9 || vTp == 10) {		//OCTET STRING, VisibleString
		itSz = inSeq.charCodeAt(offIn);
		val = inSeq.slice(offIn+1, offIn+1+itSz);
		offIn += 1+itSz;
	}
	else if(vTp == 13 || vTp == 15) {		//Integer8
		val = inSeq.charCodeAt(offIn);
		if(val > 127) val -= 256;
		offIn += 1;
	}
	else if(vTp == 16) {						//Integer16
		val = inSeq.charCodeAt(offIn, "UTF-16BE");
		if(val > 32767) val -= 65536;
		offIn += 2;
	}
	else if(vTp == 17 || vTp == 22) {		//Unsigned8, ENUMERATED
		val = inSeq.charCodeAt(offIn);
		offIn += 1;
	}
	else if(vTp == 18) {						//Unsigned16
		val = inSeq.charCodeAt(offIn, "UTF-16BE");
		offIn += 2;
	}
	else {
		SYS.messInfo("/IEC62056", tr("Unknown data type %1, termination").replace("%1",vTp.toString()));
		offIn = inSeq.length;
	}
	//SYS.messInfo("/IEC62056", tr("Unknown data type %1, termination").replace("%1",vTp.toString()));

	return val;
}

inAMess = SYS.strFromCharCode(0x02, 0x02, 0x11, 0x07, 0x11, 0x1F);
val = ASN1_Data(inAMess, 0);
SYS.messInfo("/IEC62056", "Data value "+typeof(val)+": "+val);*/


//Request prepare
//req = SYS.XMLNode("#").setAttr("ProtIt","DCON").setAttr("addr",10);
//Send request
//SYS.Transport["Serial"]["out_TestDCON"].messIO(req,"UserProtocol");
//if(!req.attr("err").length) SYS.messDebug("TEST REQ","RES: "+req.text());

//test = "Text \n value[\041abc\x21].";

//test = arguments[4];
//arguments[4] = 10;

//test="Java123Script".search(new RegExp("script","i"));
//text = "Javascript3\"sdf\"3javaScript __ javascript";
//test = text.replace(4,3,"67");
//test = text.replace("3","55");
//test = text.replace(new RegExp("javascript","ig"),"JavaScript");//
//test = text.replace(new RegExp("\"([^\"]*)\"","g"),"``$1''''");
//text = "1 плюс 2 плюс 3";
//test = text.match("\\d+","g");
//text = "Посетите мою домашнюю страницу http://www.isp.com/~david";
//test = text.match("(\\w+):\\/\\/([\\w.]+)\\/(\\S*)");
//test = "1,2, 3 , 4 ,5".split(new RegExp("\\s*,\\s*"));
//test = "hello <b>world</b>".split(new RegExp("(<[^>]*>)"));
//var p = new RegExp("(\\d\\d)[-/](\\d\\d)[-/](\\d\\d(?:\\d\\d)?)","");
//test = p.test("abc12/30/1969def");

/*CRC = new Array(0,1,2,3,4,5);vSz = 3
hi=CRC[out%6];
test = hi;*/

//test = SYS.Transport.Sockets.out_testModBus.messIO("123456");

/*obj = new Object();
obj["test1"]=1;l
obj["test2"]=1;
for( var i in obj ) SYS.messDebug("test","Prop: "+i);
SYS.messDebug("test","Props test");*/

//vl = SYS.strFromCharCode(100,200,50,60);
//test = vl.charCodeAt(1);

//test = SYS.system("ps -Ao pid,comm");

//ModBus.Unidrive.pos.pos=out;
//ModBus.testRTU.test.pos=out;
//test = rand(offset*0.4);
//val="Test";
//if( val=="Test1" || val=="Test2" ) test="TestN"; else test="Test";

//test1=new Array(1,"a",2,"b",3,"c",4);
//test1[7]=new Array(5,6,7);
//test=test1[7][1];

//test1="ab;cde;fgh;jk;lm";
//test = test1.split(";").join();

//req = xmlNode("get").setAttr("path","/%2fgen%2fid");
//test = xmlCntrReq(req);
//test = req.text();

/*test1 = vArh("DAQ.System.AutoDA.CPULoad.load");
test2 = test1.FFT(0,60);
test = "";
for( i = 0; i < test2.length; i++ ) test += ""+test2[i]+";";*/

//messPut("cron",0,"Time: "+tmFStr(tmTime()));

/*req = SYS.XMLNode();
test = req.load("test1.xml",true);*/
//for( i = 0; i < req.childSize(); i++ )
//  test += req.childGet(i).attr("id")+"; ";
/*sutm = 0; stm = SYS.time(sutm);
req.save(0,"test2.xml");
eutm = 0; etm = SYS.time(eutm);
test = 1e6*(etm-stm)+(eutm-sutm);*/

//test = strDec4Bin(Transport.Sockets.out_testModBus.messIO(strEnc2Bin("15 01 00 00 00 06 01 03 00 00 00 05")));

/*req = SYS.XMLNode("GET");
req.setAttr("URI","/");
SYS.Transport.Sockets.out_testHTTP.messIO(req,"HTTP");
test = req.text();*/

/*req = SYS.XMLNode("POST");
req.setAttr("URI","/WebUser/FlowTec.txt");
cntNode = req.childAdd("cnt").setAttr("name","pole0").setAttr("filename","Object2-k001-100309-17.txt");
cntNode.childAdd("prm").setAttr("id","Content-Type").setText("text/plain");
cntText = "Object2-k001\r\n";
cntText += "\r\n";
cntText += "v002\r\n";
cntText += " n1\r\n";
cntText += "  09.03.10 16   Polnyj    7155.25    216.0  32.000  17.5\r\n";
cntText += "v005\r\n";
cntText += " n1\r\n";
cntText += "  09.03.10 16   Polnyj     188.81    350.0   4.000  40.0\r\n";
cntText += "\r\n";
cntNode.setText(cntText);
SYS.Transport.Sockets.out_testHTTP.messIO(req,"HTTP");*/

//test = SYS.UI.VCAEngine["ses_AGLKS"].alrmSndPlay();
//test = "1;2;5;7;9";
//test = "1;2;5;7;9".type();

//SYS.messDebug("test",strEnc2Bin("68 74 74 70 3a 2f 2f 77 77 77 2e 77 33 2e 6f 72 67 2f 32 30 30 30 2f 30 39 2f 78 6d 6c 64 73 69 67 23 72 73 61 2d 73 68 61 31"));

//test = SYS.DAQ.ModBus.testTCP.test.a_8_1_1_ПС_ОПД_стенд_температура__ОПД_.get();

/*req = SYS.XMLNode("get").setAttr("path","/%2fgen%2fid");
rez = SYS.cntrReq(req,"loop");
test = req.text();*/

/*DBTbl=SYS.BD.MySQL.GenDB.SQLReq("SELECT * from DB;");
for( var i_rw = 0; i_rw < DBTbl.length; i_rw++ )
{
  var rec = "";
  for( var i_fld = 0; i_fld < DBTbl[i_rw].length; i_fld++ )
    rec += DBTbl[i_rw][i_fld]+"\t";
  SYS.messDebug("TEST DB","Row "+i_rw+": "+rec);
}*/','','',1591789744);
INSERT INTO lib_Controllers VALUES('test1','test1','','test1','Different tests of the JavaLikeCalc language for execution into the controller mode.

Author: Roman Savochenko
Version: 1.0.0','','',1,10,0,'//clc=0;
//First getting previous time
//if(!p_tm){ p_tm = Special.FLibSYS.tmTime(); break; }

//Get curent time
//c_tm = Special.FLibSYS.tmTime()-2;

//Open value archive
//a_id = Special.FLibSYS.avalOpen("Archive.va_test");
//if(a_id==-1) break;

//Calc average archive
//for(i_tm=p_tm;i_tm<c_tm;i_tm++)
//{ 
//  val = Special.FLibSYS.avalGetR(a_id,i_tm,0,"");
//  rez = (rez*(i_tm-p_tm)+val)/(i_tm-p_tm+1);
//  clc++;
//}

//Close value archive
//Special.FLibSYS.avalClose(a_id);
//p_tm=c_tm;','','',1560101785);
INSERT INTO lib_Controllers VALUES('ntfDispatch','Notifications dispatcher (replaced)','','','!!!!: Replaced by the complex template Main.ntf. Will be removed soon
Notifications dispatcher by EMail and SMS for pointed messages of OpenSCADA messages buffer.

Author: Roman Savochenko <roman@oscada.org>
Sponsor: Oleksandr Knestyapin <olexanderrr@gmail.com>
Version: 1.3.1','','',1,30,0,'//Initial
if(f_start) {
	queue = new Object();
	queueSMS = new Object();
	prTm = SYS.time();
	prTmU = 0;
	emailSentN = SMSSentN = 0;
	emailServer_ = emailServer;
	SMTPTr = emailServer.length ? SYS.Transport.nodeAt(emailServer,".") : false;
	SMSTrId_ = SMSTrId;
	SMSTr = SMSTrId.length ? SYS.Transport.Serial.nodeAt("out_"+SMSTrId) : false;
	return;	//Don''t real process at start
}
if(f_stop)	{ emailState = SMSState = ""; return; }
if(emailServer_ != emailServer) {
	SMTPTr = emailServer.length ? SYS.Transport.nodeAt(emailServer,".") : false;
	emailServer_ = emailServer;
}
if(SMSTrId_ != SMSTrId)	{
	SMSTr = SMSTrId.length ? SYS.Transport.Serial.nodeAt("out_"+SMSTrId) : false;
	SMSTrId_ = SMSTrId;
}

//Current alarms reading, and the queue of dispatching update
mess = SYS.Archive.messGet(prTm, SYS.time(), messCat, abs(messLev), "<buffer>");
for(iM = 0; iM < mess.length; iM++) {
	oM = mess[iM];
	if((oM.tm == prTm && oM.utm <= prTmU) || (messLev < 0 && oM.level >= 0))	continue;

	if(emailServer.length)	queue[oM.level.toString()+oM.tm.toString()+oM.categ] = oM;
	if(SMSTr)	queueSMS[oM.level.toString()+oM.tm.toString()+oM.categ] = oM;

	if((oM.tm*1e6+oM.utm) > (prTm*1e6+prTmU))	prTm = oM.tm, prTmU = oM.utm;
	//SYS.messInfo("NTF","Queue mess: "+oM.mess);
}

//The notifications mail queue process in a cycle limited by overall time
stTm = SYS.time();
emailQueueN = 0;
emailErr = "";
for(var nM in queue) {
	if(!SMTPTr)	break;
	if((SYS.time()-stTm) > tmOut)	{ emailQueueN++; continue; }
	oM = queue[nM];
	//SYS.messInfo("NTF","Send mess \""+nM+"\": "+oM.mess);

	topic = emailTopic + " (" + ((oM.level < 0)?tr("Alarm"):tr("Norm")) + ")";
	mess = emailMess + SYS.strftime(oM.tm) + ": " + oM.mess;

	req = SYS.XMLNode("send");
	req.setAttr("ProtIt", "SMTP").setAttr("auth", emailAuth).setAttr("from", emailSender).setAttr("to", emailReceiver).setAttr("topic", topic).setText(mess);
	SMTPTr.messIO(req, "UserProtocol");
	//SYS.messInfo("NTF", "Send error: "+req.attr("err"));
	if(!req.attr("err").toInt()) { delete queue[nM]; emailSentN++; }
	else {
		emailQueueN++;
		if(!emailErr.length)	emailErr = req.attr("err");
	}

	/*req = "sendemail -q -f " + emailSender + " -t " + emailReceiver +
			" -u \"=?utf8?B?" + SYS.strEncode(topic,"Base64") + "?=\" -m \"" + mess + "\" -s " + emailServer;
	if(emailAuth.length)	req += " -xu " + emailAuth.parse(0,":") + " -xp " + emailAuth.parse(1,":");
	req += " -o tls=no -o message-charset=utf8";
	//SYS.messInfo("NTF","Send mess: "+req);
	rez = SYS.system(req, true);
	if(!rez) { delete queue[nM]; emailSentN++; }
	else {
		emailQueueN++;
		if(!emailErr.length)	emailErr = req.attr("err");
	}*/
}
if(!SMTPTr)	emailState = "Disabled!";
else {
	emailState = tr("Sent %1. In queue %2.").replace("%1",emailSentN.toString()).replace("%2",emailQueueN.toString());
	if(emailErr.length) emailState += " "+tr("Error: %1.").replace("%1",emailErr);
}

//The notifications SMS queue process in a cycle limited by overall time
stTm = SYS.time();
SMSQueueN = 0;
SMSErr = "";
for(var nM in queueSMS) {
	if(!SMSTr)	break;
	if((SYS.time()-stTm) > tmOut)	{ SMSQueueN++; continue; }
	oM = queueSMS[nM];
	//SYS.messInfo("NTF","Send SMS \""+nM+"\": "+oM.mess);

	mess = oM.mess;
	req = SYS.XMLNode("send"); req.setAttr("ProtIt", "SMS").setAttr("pin", SMSPin).setAttr("text",SMSTextMd).setAttr("tel", SMSTel).setText(mess);
	SMSTr.messIO(req, "UserProtocol");
	if(!req.attr("err").toInt()) { delete queueSMS[nM]; SMSSentN++; }
	else {
		SMSQueueN++;
		if(!SMSErr.length)	SMSErr = req.attr("err");
	}
}
if(!SMSTr)	SMSState = "Disabled!";
else {
	SMSState = tr("Sent %1. In queue %2.").replace("%1",SMSSentN.toString()).replace("%2",SMSQueueN.toString());
	if(SMSErr.length) SMSState += " "+tr("Error: %1.").replace("%1",SMSErr);
}','','',1560101769);
CREATE TABLE IF NOT EXISTS 'lib_servProc' ("ID" TEXT DEFAULT '' ,"NAME" TEXT DEFAULT '' ,"uk#NAME" TEXT DEFAULT '' ,"ru#NAME" TEXT DEFAULT '' ,"DESCR" TEXT DEFAULT '' ,"uk#DESCR" TEXT DEFAULT '' ,"ru#DESCR" TEXT DEFAULT '' ,"START" INTEGER DEFAULT '1' ,"MAXCALCTM" INTEGER DEFAULT '10' ,"PR_TR" INTEGER DEFAULT '0' ,"FORMULA" TEXT DEFAULT '' ,"uk#FORMULA" TEXT DEFAULT '' ,"ru#FORMULA" TEXT DEFAULT '' ,"TIMESTAMP" INTEGER DEFAULT '' , PRIMARY KEY ("ID"));
INSERT INTO lib_servProc VALUES('procArh','Archives recalculation','Перерахунок архівів','Пересчёт архивов','Value archives recalculation.

Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.0','','',1,600,0,'using Special.FLibSYS;

//messPut("UserProc/ProcessArch",1,"Archive open");
a_src = vArh(fromarch);
a_dst = vArh(toarch);

//messPut("UserProc/ProcessArch",1,"Archive process");

bf_per = 100;	//Buffers period microseconds
bf_size = 100000;	//Buffers size

//Open value buffers
bf_src = vArhBuf(1,bf_size,bf_per,true,true);
bf_dst = vArhBuf(4,bf_size,bf_per,true,true);

//Parse time string
begtm = tmStrPTime(begs);
endtm = tmStrPTime(ends);

begwtm = begtm;
while( begwtm<endtm )
{
    //Get value
    endwtm = begwtm+bf_size*bf_per/1000000;
    if(endwtm>endtm) endwtm = endtm;
    messPut("UserProc/ProcessArch",1,"Values from: "+begwtm+", to: "+endwtm);
    bf_src.copy(a_src,begwtm,0,endwtm,0);

    buf_beg = begwtm; buf_beg_u = 0;
    buf_end = endwtm; buf_end_u = 0;
    while( !(buf_beg>=buf_end && buf_beg_u>buf_end_u) )
    {
        r_val = bf_src.get(buf_beg,buf_beg_u,true);
        if(r_val == EVAL_INT) bf_dst.set(EVAL_REAL,buf_beg,buf_beg_u);
        else bf_dst.set(200.*((10.*r_val/32768.)-1.)/4.,buf_beg,buf_beg_u);

        buf_beg_u++;
    }

    a_dst.copy(bf_dst,begwtm,0,endwtm,0);
    begwtm = endwtm;
}','','',1560097197);
INSERT INTO lib_servProc VALUES('crc16','CRC 16','','','Standard Cyclic Redundancy Check (CRC) with free setting the polynomial, by default 0x8005. It isn''t actual more by the common CRC implementing.

Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.0','','',1,10,0,'out = 0xFFFF;
for(i = 0; i < in.length; i++) {
  out = out^in.charCodeAt(i);
  for(j = 0; j < 8; j++) out = (out&1) ? ((out>>1)^poly) : out >> 1;
}','','',1560097197);
INSERT INTO lib_servProc VALUES('releaseTests','TEST: Release','Тести випуску','Тесты выпуска','Set of formal tests of the OpenSCADA releases:
  - 0:System
  - 1:Security User/Group tests
  - 2:Transport subsystem tests
	- Data transferring through output and input transports by the test "TrOut":
		means presence of the input transports with the name "testRelease[{N}]" and
			the connected output transport adress into description or address field (if equal one).
  - 3:Protocol subsystem tests
	- Execution of the test controller object DAQ.ModBUS.testTCP:
		means presence of the controller object DAQ.ModBUS.testTCP.
	- Execution of the test controller object DAQ.OPC_UA.test:
		means presence of the  controller object DAQ.OPC_UA.test.
	- Send requests to a test-echo protocol from a transport:
		means presence of the user protocol object Protocol.UserProtocol.up_test.
  - 4:Archive subsystem tests
	- Using the test "Mess" of the module "SystemTests" for archiving messages:
		means presence the message archivers "test";
	- Assign/Clear archiving from the parameter attribute:
		means presence "/DAQ/LogicLev/experiment/F3" with the attribute "ed", and
		the value archivers "1s"
	- The tests "Archive" of the value archiver to verify the correctness of the functioning
			of the sequential mechanism for packaging by "SystemTests":
		means presence the archive "testArch_cntr" with the data period 1s and
		processed by the archiver FSArch.1s.
  - 5:DB subsystem tests
	- The tests "BD" of the module "SystemTests":
		means presence of the test DB with the name "testRelease".
  - 6:DAQ subsystem tests
	- Test "Param" from the module "SystemTests":
		presence of the DAQ parameter LogicLev.experiment.F3.
	- Test "Val" of the module "SystemTests" for the parameter attribute:
		presence of the DAQ parameter LogicLev.experiment.F3.var.
	- Blocks create/remove, change, load, save and load from config and copy, set link:
		presence the DAQ controller object "KM102cntr" for copy the block scheme.
		presence the DAQ parameter LogicLev.experiment.F3.var.
  - 7:UI subsystem tests
  - 8:Special subsystem tests

Author: Roman Savochenko <roman@oscada.org>
Version: 2.0.0','','',1,600,0,'nNm = "testNode";
node  = false;		//Process node
testREZ = "";		//Test rezult
rez = "";
rezF = "";

function result(name, val) {
	rez += name + "\n> " + val + "\n\n";
	rezF += "<tr><td>"+name+"</td><td>"+val+"</td></tr>\n";
}

//**********************************
//Get config file name
SYS.cntrReq(req=SYS.XMLNode("get").setAttr("path","/%2fgen%2fconfig"));
configPath = req.text();
//Save original config file
SYS.fileWrite(configPath+".orig",SYS.fileRead(configPath));
//Load config file tree
cfgTree=SYS.XMLNode();
cfgTree.load(configPath,true,true);

//**********************************
//System
if(sub <= 0) {
	stTm = SYS.utime();
	testREZ = SYS.Special.SystemTests.XML(configPath);
	result("Control interface: Test parsing the XML file", testREZ);
	testREZ = SYS.Special.SystemTests.SysContrLang("/Archive/FSArch/mess_StatErrors/%2fprm%2fst");
	result("Control interface: Test of the control interface", testREZ);
	testREZ = SYS.Special.SystemTests.Base64Code();
	result("SYS: Tests for encoding of the Mime Base64 algorithm", testREZ);
	result("SYS: Time of the execution, seconds", (SYS.utime()-stTm)*1e-6);
}

//**********************************
//Security User/Group tests
if(sub < 0 || sub == 1) {
	stTm = SYS.utime();
	testREZ = "PASSED";
	// User
	nAddr = "/Security/usr_"+nNm;
	if(SYS.cntrReq(SYS.XMLNode("add").setAttr("path","/Security/%2fbr%2fusr_").setText(nNm)) != 0 || !(node=SYS.nodeAt(nAddr))) testREZ = "FAILED: user add";
	else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2fprm%2fDESCR").setText("Test")) != 0 || node.cfg("DESCR") != "Test") testREZ = "FAILED: change user";
	else if(SYS.cntrReq(SYS.XMLNode("save").setAttr("path",nAddr+"/%2fobj")) != 0) testREZ = "FAILED: save user";
	else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2fprm%2fDESCR").setText("Test 1")) != 0 || 
		SYS.cntrReq(SYS.XMLNode("load").setAttr("path",nAddr+"/%2fobj")) != 0 || node.cfg("DESCR") != "Test") testREZ = "FAILED: load user";
	else {
		node = false;
		if(SYS.cntrReq(SYS.XMLNode("del").setAttr("path","/Security/%2fbr%2fusr_").setText(nNm)) != 0 || SYS.nodeAt(nAddr)) testREZ = "FAILED: user delete";
		else {
			if(!cfgTree.getElementBy("Security_user"))	cfgTree.getElementBy("sub_Security").childAdd("tbl").setAttr("id","Security_user");
			cfgTree.getElementBy("Security_user").childAdd("fld").setAttr("NAME",nNm+"_cfg");
			cfgTree.save(0x20, configPath);
			SYS.cntrReq(SYS.XMLNode("scan").setAttr("path","/%2fgen%2fconfig"));		//Reload config command
			if(!SYS.nodeAt(nAddr+"_cfg")) testREZ = "FAILED: load user from config";
		}
	}
	//Group
	nAddr = "/Security/grp_"+nNm;
	if(testREZ == "PASSED") {
		if((SYS.cntrReq(SYS.XMLNode("add").setAttr("path","/Security/%2fbr%2fgrp_").setText(nNm)) != 0 || !(node=SYS.nodeAt(nAddr)))) testREZ = "FAILED: group add";
		else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2fprm%2fDESCR").setText("Test")) != 0 || node.cfg("DESCR") != "Test") testREZ = "FAILED: change group";
		else if(SYS.cntrReq(SYS.XMLNode("save").setAttr("path",nAddr+"/%2fobj")) != 0) testREZ = "FAILED: save group";
		else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2fprm%2fDESCR").setText("Test 1")) != 0 || 
			SYS.cntrReq(SYS.XMLNode("load").setAttr("path",nAddr+"/%2fobj")) != 0 || node.cfg("DESCR") != "Test") testREZ = "FAILED: load group";
		else {
			node = false;
			if(SYS.cntrReq(SYS.XMLNode("del").setAttr("path","/Security/%2fbr%2fgrp_").setText(nNm)) != 0 || SYS.nodeAt(nAddr)) testREZ = "FAILED: group delete";
			else {
				if(!cfgTree.getElementBy("Security_grp"))	cfgTree.getElementBy("sub_Security").childAdd("tbl").setAttr("id","Security_grp");
				cfgTree.getElementBy("Security_grp").childAdd("fld").setAttr("NAME",nNm+"_cfg").setAttr("USERS",nNm+"_cfg");
				cfgTree.save(0x20, configPath);
				SYS.cntrReq(SYS.XMLNode("scan").setAttr("path","/%2fgen%2fconfig"));		//Reload config command
				if(!SYS.nodeAt(nAddr+"_cfg")) testREZ = "FAILED: load group from config";
			}
		}
	}
	result("Security: User/Group create/remove, change, load, save and load from the config", testREZ);
	result("Security: Time of the execution, seconds", (SYS.utime()-stTm)*1e-6);
}

//**********************************
//Transport subsystems tests
if(sub < 0 || sub == 2) {
	stTm = SYS.utime();
	nList = SYS.Transport.nodeList("mod_");
	if(!cfgTree.getElementBy("Transport_in"))	cfgTree.getElementBy("sub_Transport").childAdd("tbl").setAttr("id","Transport_in");
	if(!cfgTree.getElementBy("Transport_out"))	cfgTree.getElementBy("sub_Transport").childAdd("tbl").setAttr("id","Transport_out");
	for(iN = 0; iN < nList.length; iN++) {
		//Disconnect/Connect module
		req = SYS.XMLNode("get").setAttr("path","/Transport/"+nList[iN]+"/%2fmodule%2fm_inf%2fSource");
		SYS.cntrReq(req);
		if(req.text()[0] != "*") {
			testREZ = SYS.Special.SystemTests.SOAttach(req.text(),-1);
			testREZ += "/"+SYS.Special.SystemTests.SOAttach(req.text(),1);
			result("Transport."+nList[iN].slice(4)+": Disconnect/Connect", testREZ);
		} else result("Transport."+nList[iN].slice(4)+": Disconnect/Connect", "Built-in");

		//Input transport
		testREZ = "PASSED";
		nAddr = "/Transport/"+nList[iN]+"/in_"+nNm;
		if(SYS.cntrReq(SYS.XMLNode("add").setAttr("path","/Transport/"+nList[iN]+"/%2fbr%2fin_").setAttr("id",nNm)) != 0 || !(node=SYS.nodeAt(nAddr))) testREZ = "FAILED: input add";
		else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2fprm%2fcfg%2fDESCRIPT").setText("Test")) != 0 || node.cfg("DESCRIPT") != "Test") testREZ = "FAILED: change input";
		else if(SYS.cntrReq(SYS.XMLNode("save").setAttr("path",nAddr+"/%2fobj")) != 0) testREZ = "FAILED: save input";
		else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2fprm%2fcfg%2fDESCRIPT").setText("Test 1")) != 0 || 
			SYS.cntrReq(SYS.XMLNode("load").setAttr("path",nAddr+"/%2fobj")) != 0 || node.cfg("DESCRIPT") != "Test") testREZ = "FAILED: load input";
		else {
			node = false;
			if(SYS.cntrReq(SYS.XMLNode("del").setAttr("path","/Transport/"+nList[iN]+"/%2fbr%2fin_").setAttr("id",nNm)) != 0 || SYS.nodeAt(nAddr)) testREZ = "FAILED: input delete";
			else {
				cfgTree.getElementBy("Transport_in").childAdd("fld").setAttr("ID",nNm+"_cfg").setAttr("MODULE",nList[iN].slice(4));
				cfgTree.save(0x20, configPath);
				SYS.cntrReq(SYS.XMLNode("scan").setAttr("path","/%2fgen%2fconfig"));		//Reload config command
				if(!SYS.nodeAt(nAddr+"_cfg")) testREZ = "FAILED: load input from config";
			}
		}
		//Output transport
		nAddr = "/Transport/"+nList[iN]+"/out_"+nNm;
		if(testREZ == "PASSED") { 
			if((SYS.cntrReq(SYS.XMLNode("add").setAttr("path","/Transport/"+nList[iN]+"/%2fbr%2fout_").setAttr("id",nNm)) != 0 || !(node=SYS.nodeAt(nAddr)))) testREZ = "FAILED: output add";
			else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2fprm%2fcfg%2fDESCRIPT").setText("Test")) != 0 || node.cfg("DESCRIPT") != "Test") testREZ = "FAILED: change output";
			else if(SYS.cntrReq(SYS.XMLNode("save").setAttr("path",nAddr+"/%2fobj")) != 0) testREZ = "FAILED: save output";
			else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2fprm%2fcfg%2fDESCRIPT").setText("Test 1")) != 0 || 
				SYS.cntrReq(SYS.XMLNode("load").setAttr("path",nAddr+"/%2fobj")) != 0 || node.cfg("DESCRIPT") != "Test") testREZ = "FAILED: load output";
			else {
				node = false;
				if(SYS.cntrReq(SYS.XMLNode("del").setAttr("path","/Transport/"+nList[iN]+"/%2fbr%2fout_").setAttr("id",nNm)) != 0 || SYS.nodeAt(nAddr)) testREZ = "FAILED: output delete";
				else {
					cfgTree.getElementBy("Transport_out").childAdd("fld").setAttr("ID",nNm+"_cfg").setAttr("MODULE",nList[iN].slice(4));
					cfgTree.save(0x20, configPath);
					SYS.cntrReq(SYS.XMLNode("scan").setAttr("path","/%2fgen%2fconfig"));		//Reload config command
					if(!SYS.nodeAt(nAddr+"_cfg")) testREZ = "FAILED: load output from config";
				}
			}
		}
		result("Transport."+nList[iN].slice(4)+": Input/Output create/remove, change, load, save and load from config", testREZ);

		//Transmission
		iTrList = SYS.Transport[nList[iN]].nodeList("in_");
		for(testREZ = "", iTr = 0; iTr < iTrList.length; iTr++) {
			iTrO = SYS.Transport[nList[iN]][iTrList[iTr]];
			if(!(iTrOutAddr=iTrO.cfg("DESCRIPT")).length) iTrOutAddr = iTrO.cfg("ADDR");
			if(iTrList[iTr].slice(0,14) != "in_testRelease")	continue;
			testREZ += (testREZ.length?"; ":"") + iTrOutAddr + ": " +
				SYS.Special.SystemTests.TrOut(iTrOutAddr,nList[iN].slice(4),"1234567890");
		}
		iTrO = 0;
		result("Transport."+nList[iN].slice(4)+": Data transferring through output and input transports by the test \"TrOut\"", testREZ.length?testREZ:"No test input transports");
	}
	result("Transport: Time of the execution, seconds", (SYS.utime()-stTm)*1e-6);
}

//**********************************
//Protocol subsystems tests
if(sub < 0 || sub == 3) {
	stTm = SYS.utime();
	nList = SYS.Protocol.nodeList("mod_");
	for(iN = 0; iN < nList.length; iN++) {
		if(nList[iN].slice(4) == "ModBus")
			result("Protocol."+nList[iN].slice(4)+": Execution of the test controller object DAQ.ModBUS.testTCP",
				(prmO=SYS.nodeAt("/DAQ/ModBus/testTCP"))?prmO.status():"missed");
		else if(nList[iN].slice(4) == "OPC_UA")
			result("Protocol."+nList[iN].slice(4)+": Execution of the test controller object DAQ.OPC_UA.test",
				(prmO=SYS.nodeAt("/DAQ/OPC_UA/test"))?prmO.status():"missed");
		else if(nList[iN].slice(4) == "UserProtocol") {
			SYS.cntrReq((req=SYS.XMLNode("get").setAttr("path","/Protocol/UserProtocol/up_test/%2fup%2fst%2fstatus")));
			result("Protocol."+nList[iN].slice(4)+": Send requests to a test-echo protocol from a transport", req.text());
		}

		//Disconnect/Connect module
		req = SYS.XMLNode("get").setAttr("path","/Protocol/"+nList[iN]+"/%2fmodule%2fm_inf%2fSource");
		SYS.cntrReq(req);
		if(req.text()[0] != "*") {
			testREZ = SYS.Special.SystemTests.SOAttach(req.text(),-1);
			testREZ += "/"+SYS.Special.SystemTests.SOAttach(req.text(),1);
			result("Protocol."+nList[iN].slice(4)+": Disconnect/Connect", testREZ);
		} else result("Protocol."+nList[iN].slice(4)+": Disconnect/Connect", "Built-in");
	}
	result("Protocol: Time of the execution, seconds", (SYS.utime()-stTm)*1e-6);
}

//**********************************
//Archive subsystem tests
if(sub < 0 || sub == 4) {
	stTm = SYS.utime();
	testREZ = SYS.Special.SystemTests.ValBuf();
	result("Archive: Tests for the value buffer.", testREZ);

	// Value archive
	testREZ = "PASSED";
	nAddr = "/Archive/va_"+nNm;
	if(SYS.cntrReq(SYS.XMLNode("add").setAttr("path","/Archive/%2fbr%2fva_").setAttr("id",nNm)) != 0 || !(node=SYS.nodeAt(nAddr))) testREZ = "FAILED: add";
	else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2fprm%2fcfg%2fDESCR").setText("Test")) != 0 || node.cfg("DESCR") != "Test") testREZ = "FAILED: change";
	else if(SYS.cntrReq(SYS.XMLNode("save").setAttr("path",nAddr+"/%2fobj")) != 0) testREZ = "FAILED: save";
	else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2fprm%2fcfg%2fDESCR").setText("Test 1")) != 0 || 
		SYS.cntrReq(SYS.XMLNode("load").setAttr("path",nAddr+"/%2fobj")) != 0 || node.cfg("DESCR") != "Test") testREZ = "FAILED: load";
	else {
		node = false;
		if(SYS.cntrReq(SYS.XMLNode("del").setAttr("path","/Archive/%2fbr%2fva_").setAttr("id",nNm)) != 0 || SYS.nodeAt(nAddr)) testREZ = "FAILED: delete";
		else {
			cfgTree.getElementBy("sub_Archive").childAdd("tbl").setAttr("id","Archive_val").childAdd("fld").setAttr("ID",nNm+"_cfg");
			cfgTree.save(0x20, configPath);
			SYS.cntrReq(SYS.XMLNode("scan").setAttr("path","/%2fgen%2fconfig"));		//Reload config command
			if(!SYS.nodeAt(nAddr+"_cfg")) testREZ = "FAILED: load from config";
		}
	}
	result("Archive: Value archive create/remove, change, load, save and load from config", testREZ);

	// Archivers
	nList = SYS.Archive.nodeList("mod_");
	cfgTree.getElementBy("sub_Archive").childAdd("tbl").setAttr("id","Archive_mess_proc");
	cfgTree.getElementBy("sub_Archive").childAdd("tbl").setAttr("id","Archive_val_proc");
	for(iN = 0; iN < nList.length; iN++) {
		testREZ = "PASSED";
		//Messages archiver
		nAddr = "/Archive/"+nList[iN]+"/mess_"+nNm;
		if(SYS.cntrReq(SYS.XMLNode("add").setAttr("path","/Archive/"+nList[iN]+"/%2fbr%2fmess_").setAttr("id",nNm)) != 0 || !(node=SYS.nodeAt(nAddr))) testREZ = "FAILED: messages add";
		else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2fprm%2fcfg%2fDESCR").setText("Test")) != 0 || node.cfg("DESCR") != "Test") testREZ = "FAILED: change messages";
		else if(SYS.cntrReq(SYS.XMLNode("save").setAttr("path",nAddr+"/%2fobj")) != 0) testREZ = "FAILED: save messages";
		else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2fprm%2fcfg%2fDESCR").setText("Test 1")) != 0 || 
			SYS.cntrReq(SYS.XMLNode("load").setAttr("path",nAddr+"/%2fobj")) != 0 || node.cfg("DESCR") != "Test") testREZ = "FAILED: load messages";
		else {
			node = false;
			if(SYS.cntrReq(SYS.XMLNode("del").setAttr("path","/Archive/"+nList[iN]+"/%2fbr%2fmess_").setAttr("id",nNm)) != 0 || SYS.nodeAt(nAddr)) testREZ = "FAILED: messages delete";
			else {
				cfgTree.getElementBy("Archive_mess_proc").childAdd("fld").setAttr("ID",nNm+"_cfg").setAttr("MODUL",nList[iN].slice(4));
				cfgTree.save(0x20, configPath);
				SYS.cntrReq(SYS.XMLNode("scan").setAttr("path","/%2fgen%2fconfig"));		//Reload config command
				if(!SYS.nodeAt(nAddr+"_cfg")) testREZ = "FAILED: load messages from config";
			}
		}
		//Value archiver
		nAddr = "/Archive/"+nList[iN]+"/val_"+nNm;
		if(testREZ == "PASSED") {
			if((SYS.cntrReq(SYS.XMLNode("add").setAttr("path","/Archive/"+nList[iN]+"/%2fbr%2fval_").setAttr("id",nNm)) != 0 || !(node=SYS.nodeAt(nAddr)))) testREZ = "FAILED: values add";
			else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2fprm%2fcfg%2fDESCR").setText("Test")) != 0 || node.cfg("DESCR") != "Test") testREZ = "FAILED: change values";
			else if(SYS.cntrReq(SYS.XMLNode("save").setAttr("path",nAddr+"/%2fobj")) != 0) testREZ = "FAILED: save values";
			else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2fprm%2fcfg%2fDESCR").setText("Test 1")) != 0 || 
				SYS.cntrReq(SYS.XMLNode("load").setAttr("path",nAddr+"/%2fobj")) != 0 || node.cfg("DESCR") != "Test") testREZ = "FAILED: load values";
			else {
				node = false;
				if(SYS.cntrReq(SYS.XMLNode("del").setAttr("path","/Archive/"+nList[iN]+"/%2fbr%2fval_").setAttr("id",nNm)) != 0 || SYS.nodeAt(nAddr)) testREZ = "FAILED: values delete";
				else {
					cfgTree.getElementBy("Archive_val_proc").childAdd("fld").setAttr("ID",nNm+"_cfg").setAttr("MODUL",nList[iN].slice(4));
					cfgTree.save(0x20, configPath);
					SYS.cntrReq(SYS.XMLNode("scan").setAttr("path","/%2fgen%2fconfig"));		//Reload config command
					if(!SYS.nodeAt(nAddr+"_cfg")) testREZ = "FAILED: load values from config";
				}
			}
		}
		result("Archive."+nList[iN].slice(4)+": Message/Value archiver create/remove, change, load, save and load from config", testREZ);

		//Using the test "Mess" of the module "SystemTests" for archiving messages
		nNm1 = "test";
		result("Archive."+nList[iN].slice(4)+": The test \"Mess\" of the module \"SystemTests\" for archiving messages",
			SYS.Archive[nList[iN]]["mess_"+nNm1] ? SYS.Special.SystemTests.Mess(nList[iN].slice(4)+"."+nNm1,"",600) : "missed message archiver ''"+nNm1+"''");

		//Assign/Clear archiving from the parameter attribute
		nNm1 = "/DAQ/LogicLev/experiment/F3";
		testREZ = "missed DAQ parameter ''"+nNm1+"''";
		if(SYS.nodeAt(nNm1)) {
			testREZ = "PASSED";
			SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nNm1+"/%2farch%2farch").setAttr("key_atr","ed").setAttr("col",nList[iN].slice(4)+".1s").setText("1"));
			if(!SYS.nodeAt("/Archive/va_F3_ed"))	testREZ = "error assigning";
			else {
				SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nNm1+"/%2farch%2farch").setAttr("key_atr","ed").setAttr("col","prc").setText("0"));
				if(SYS.nodeAt("/Archive/va_F3_ed"))	testREZ = "error clearing";
			}
		}
		result("Archive."+nList[iN].slice(4)+": Assign/Clear archiving from the parameter attribute", testREZ);

		//The tests "Archive" of the value archiver to verify the correctness of the functioning of the sequential mechanism for packaging by "SystemTests".
		if(nList[iN].slice(4) == "FSArch") {
			nNm1 = "testArch_cntr";
			result("Archive."+nList[iN].slice(4)+": The tests \"Archive\" of the module \"SystemTests\" for the value archiver to verify the correctness of the functioning of the sequential packaging mechanism",
				SYS.nodeAt("/Archive/va_"+nNm1) ? SYS.Special.SystemTests.Archive(nNm1,1e6,nList[iN].slice(4)+".1s") : "missed archive "+nNm1);
		}

		//Disconnect/Connect module
		req = SYS.XMLNode("get").setAttr("path","/Archive/"+nList[iN]+"/%2fmodule%2fm_inf%2fSource");
		SYS.cntrReq(req);
		if(req.text()[0] != "*") {
			testREZ = SYS.Special.SystemTests.SOAttach(req.text(),-1);
			testREZ += "/"+SYS.Special.SystemTests.SOAttach(req.text(),1);
			result("Archive."+nList[iN].slice(4)+": Disconnect/Connect", testREZ);
		} else result("Archive."+nList[iN].slice(4)+": Disconnect/Connect", "Built-in");
	}
	result("Archive: Time of the execution, seconds", (SYS.utime()-stTm)*1e-6);
}

//**********************************
//DB subsystem tests
if(sub < 0 || sub == 5) {
	stTm = SYS.utime();
	nList = SYS.BD.nodeList("mod_");
	for(iN = 0; iN < nList.length; iN++) {
		//Disconnect/Connect module
		req = SYS.XMLNode("get").setAttr("path","/BD/"+nList[iN]+"/%2fmodule%2fm_inf%2fSource");
		SYS.cntrReq(req);
		if(req.text()[0] != "*") {
			testREZ = SYS.Special.SystemTests.SOAttach(req.text(),-1);
			testREZ += "/"+SYS.Special.SystemTests.SOAttach(req.text(),1);
			result("DB."+nList[iN].slice(4)+": Disconnect/Connect", testREZ);
		} else result("DB."+nList[iN].slice(4)+": Disconnect/Connect", "Built-in");

		//DB object
		testREZ = "PASSED";
		nAddr = "/BD/"+nList[iN]+"/db_"+nNm;
		if(SYS.cntrReq(SYS.XMLNode("add").setAttr("path","/BD/"+nList[iN]+"/%2fbr%2fdb_").setAttr("id",nNm)) != 0 || !(node=SYS.nodeAt(nAddr))) testREZ = "FAILED: add";
		else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2fprm%2fcfg%2fDESCR").setText("Test")) != 0 || node.cfg("DESCR") != "Test") testREZ = "FAILED: change";
		else if(SYS.cntrReq(SYS.XMLNode("save").setAttr("path",nAddr+"/%2fobj")) != 0) testREZ = "FAILED: save";
		else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2fprm%2fcfg%2fDESCR").setText("Test 1")) != 0 || 
			SYS.cntrReq(SYS.XMLNode("load").setAttr("path",nAddr+"/%2fobj")) != 0 || node.cfg("DESCR") != "Test") testREZ = "FAILED: load";
		else {
			node = false;
			if(SYS.cntrReq(SYS.XMLNode("del").setAttr("path","/BD/"+nList[iN]+"/%2fbr%2fdb_").setAttr("id",nNm)) != 0 || SYS.nodeAt(nAddr)) testREZ = "FAILED: delete";
			else {
				cfgTree.getElementBy("DB").childAdd("fld").setAttr("ID",nNm+"_cfg").setAttr("TYPE",nList[iN].slice(4)).setAttr("EN",0);
				cfgTree.save(0x20, configPath);
				SYS.cntrReq(SYS.XMLNode("scan").setAttr("path","/%2fgen%2fconfig"));		//Reload config command
				if(!SYS.nodeAt(nAddr+"_cfg")) testREZ = "FAILED: load from config";
			}
		}
		result("DB."+nList[iN].slice(4)+": create/remove, change, load, save and load from config", testREZ);

		//The tests "BD" of the module "SystemTests"
		nNm1 = "/BD/"+nList[iN]+"/testRelease";
		testREZ = "missed DB ''"+nNm1+"''";
		if((dbO=SYS.nodeAt(nNm1))) {
			testREZ = "inaccessible DB ''"+nNm1+"''";
			if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nNm1+"/%2fprm%2fst%2fst").setText("1")) == 0) {
				SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nNm1+"/%2fprm%2fst%2fst").setText("0"));
				testREZ = SYS.Special.SystemTests.DB(nList[iN].slice(4), SYS.nodeAt(nNm1).cfg("ADDR"), "test", 1000);
			}
		}
		result("DB."+nList[iN].slice(4)+": The tests \"BD\" of the module \"SystemTests\"", testREZ);
	}
	result("DB: Time of the execution, seconds", (SYS.utime()-stTm)*1e-6);
}

//**********************************
//DAQ subsystem tests
if(sub < 0 || sub == 6) {
	stTm = SYS.utime();
	// Templates
	testREZ = "PASSED";
	nAddr = "/DAQ/tmplb_"+nNm;
	if(SYS.cntrReq(SYS.XMLNode("add").setAttr("path","/DAQ/%2fbr%2ftmplb_").setAttr("id",nNm)) != 0 || !(node=SYS.nodeAt(nAddr))) testREZ = "FAILED: template library add";
	else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2flib%2fcfg%2fDESCR").setText("Test")) != 0 || node.cfg("DESCR") != "Test") testREZ = "FAILED: change template library";
	else if(SYS.cntrReq(SYS.XMLNode("save").setAttr("path",nAddr+"/%2fobj")) != 0) testREZ = "FAILED: save template library";
	else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2flib%2fcfg%2fDESCR").setText("Test 1")) != 0 || 
		SYS.cntrReq(SYS.XMLNode("load").setAttr("path",nAddr+"/%2fobj")) != 0 || node.cfg("DESCR") != "Test") testREZ = "FAILED: load template library";
	else {
		node = false;

		// Template
		nAddrLb = nAddr+"/tmpl_"+nNm;
		if(SYS.cntrReq(SYS.XMLNode("add").setAttr("path",nAddr+"/%2fbr%2ftmpl_").setAttr("id",nNm)) != 0 || !(node=SYS.nodeAt(nAddrLb))) testREZ = "FAILED: template add";
		else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddrLb+"/%2ftmpl%2fcfg%2fDESCR").setText("Test")) != 0 || node.cfg("DESCR") != "Test") testREZ = "FAILED: change template";
		else if(SYS.cntrReq(SYS.XMLNode("save").setAttr("path",nAddrLb+"/%2fobj")) != 0) testREZ = "FAILED: save template";
		else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddrLb+"/%2ftmpl%2fcfg%2fDESCR").setText("Test 1")) != 0 || 
			SYS.cntrReq(SYS.XMLNode("load").setAttr("path",nAddrLb+"/%2fobj")) != 0 || node.cfg("DESCR") != "Test") testREZ = "FAILED: load template";
		node = false;

		if(SYS.cntrReq(SYS.XMLNode("del").setAttr("path","/DAQ/%2fbr%2ftmplb_").setAttr("id",nNm)) != 0 || SYS.nodeAt(nAddr)) testREZ = "FAILED: template library delete";
		else {
			cfgTree.getElementBy("sub_DAQ").childAdd("tbl").setAttr("id","tmplib").childAdd("fld").setAttr("ID",nNm+"_cfg").setAttr("DB","tmplib_"+nNm+"_cfg");
			cfgTree.getElementBy("sub_DAQ").childAdd("tbl").setAttr("id","tmplib_"+nNm+"_cfg").childAdd("fld").setAttr("ID",nNm+"_cfg");
			cfgTree.save(0x20, configPath);
			SYS.cntrReq(SYS.XMLNode("scan").setAttr("path","/%2fgen%2fconfig"));		//Reload config command
			if(!SYS.nodeAt(nAddr+"_cfg") || !SYS.nodeAt(nAddr+"_cfg/tmpl_"+nNm+"_cfg")) testREZ = "FAILED: load template library from config";
		}
	}
	result("DAQ: Templates library create/remove, change, load, save and load from config", testREZ);

	// Modules
	nList = SYS.DAQ.nodeList("mod_");
	for(iN = 0; iN < nList.length; iN++) {
		testREZ = "PASSED";
		testREZ_ = "PASSED";
		//  Controller
		nAddr = "/DAQ/"+nList[iN]+"/cntr_"+nNm;
		if(SYS.cntrReq(SYS.XMLNode("add").setAttr("path","/DAQ/"+nList[iN]+"/%2fbr%2fcntr_").setAttr("id",nNm)) != 0 || !(node=SYS.nodeAt(nAddr))) testREZ = "FAILED: controller add";
		else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2fcntr%2fcfg%2fDESCR").setText("Test")) != 0 || node.cfg("DESCR") != "Test") testREZ = "FAILED: controller change";
		else if(SYS.cntrReq(SYS.XMLNode("save").setAttr("path",nAddr+"/%2fobj")) != 0) testREZ = "FAILED: controller save";
		else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2fcntr%2fcfg%2fDESCR").setText("Test 1")) != 0 || 
			SYS.cntrReq(SYS.XMLNode("load").setAttr("path",nAddr+"/%2fobj")) != 0 || node.cfg("DESCR") != "Test") testREZ = "FAILED: controller load";
		else {
			node = false;

			// Parameter
			if(nList[iN] != "mod_DAQGate") {
				nAddr1 = nAddr+"/prm_"+nNm;
				if(SYS.cntrReq(SYS.XMLNode("add").setAttr("path",nAddr+"/%2fbr%2fprm_").setAttr("id",nNm)) != 0 || !(node=SYS.nodeAt(nAddr1))) testREZ = "FAILED: parameter add";
				else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr1+"/%2fprm%2fcfg%2fDESCR").setText("Test")) != 0 || node.cfg("DESCR") != "Test") testREZ = "FAILED: parameter change";
				else if(SYS.cntrReq(SYS.XMLNode("save").setAttr("path",nAddr1+"/%2fobj")) != 0) testREZ = "FAILED: parameter save";
				else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr1+"/%2fprm%2fcfg%2fDESCR").setText("Test 1")) != 0 || 
					SYS.cntrReq(SYS.XMLNode("load").setAttr("path",nAddr1+"/%2fobj")) != 0 || node.cfg("DESCR") != "Test") testREZ = "FAILED: parameter load";
				node = false;
			}
			if(nList[iN] == "mod_BlockCalc") {
				nAddr1 = nAddr+"/blk_"+nNm;
				if(SYS.cntrReq(SYS.XMLNode("add").setAttr("path",nAddr+"/%2fbr%2fblk_").setAttr("id",nNm)) != 0 || !(node=SYS.nodeAt(nAddr1))) testREZ_ = "FAILED: block add";
				else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr1+"/%2fblck%2fcfg%2fDESCR").setText("Test")) != 0 || node.cfg("DESCR") != "Test") testREZ_ = "FAILED: block change";
				else if(SYS.cntrReq(SYS.XMLNode("save").setAttr("path",nAddr1+"/%2fobj")) != 0) testREZ_ = "FAILED: block save";
				else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr1+"/%2fblck%2fcfg%2fDESCR").setText("Test 1")) != 0 || 
					SYS.cntrReq(SYS.XMLNode("load").setAttr("path",nAddr1+"/%2fobj")) != 0 || node.cfg("DESCR") != "Test") testREZ_ = "FAILED: block load";
				else if(SYS.cntrReq((req=SYS.XMLNode("copy").setAttr("path","/%2fobj").setAttr("src","/DAQ/"+nList[iN]+"/cntr_KM102cntr").setAttr("dst","/DAQ/"+nList[iN]+"/cntr_KM102cntr_copy"))) != 0 || req.attr("rez").toInt())
					testREZ_ = "FAILED: block scheme copy: "+req.text();
				else if(SYS.cntrReq((req=SYS.XMLNode("set").setAttr("path","/DAQ/BlockCalc/KM102cntr_copy/blk_КШ111/%2flnk%2fio%2f1|t_full").setText("3"))) != 0 || req.attr("rez").toInt())
					testREZ_ = "FAILED: block scheme link type: "+req.text();
				else if(SYS.cntrReq((req=SYS.XMLNode("set").setAttr("path","/DAQ/BlockCalc/KM102cntr_copy/blk_КШ111/%2flnk%2fio%2f2|t_full").setText("LogicLev.experiment.F3.var"))) != 0 || req.attr("rez").toInt())
					testREZ_ = "FAILED: block scheme link value: "+req.text();
				node = false;
			}
			else if(nList[iN] == "mod_JavaLikeCalc") {
				nAddr_ = "/DAQ/"+nList[iN]+"/lib_"+nNm;
				if(SYS.cntrReq(SYS.XMLNode("add").setAttr("path","/DAQ/"+nList[iN]+"/%2fbr%2flib_").setAttr("id",nNm)) != 0 || !(node=SYS.nodeAt(nAddr_))) testREZ_ = "FAILED: library add";
				else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr_+"/%2flib%2fcfg%2fDESCR").setText("Test")) != 0 || node.cfg("DESCR") != "Test") testREZ_ = "FAILED: library change";
				else if(SYS.cntrReq(SYS.XMLNode("save").setAttr("path",nAddr_+"/%2fobj")) != 0) testREZ_ = "FAILED: library save";
				else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr_+"/%2flib%2fcfg%2fDESCR").setText("Test 1")) != 0 || 
					SYS.cntrReq(SYS.XMLNode("load").setAttr("path",nAddr_+"/%2fobj")) != 0 || node.cfg("DESCR") != "Test") testREZ_ = "FAILED: library load";
				else {
					node = false;

					// Function
					nAddr_1 = nAddr_+"/fnc_"+nNm;
					if(SYS.cntrReq(SYS.XMLNode("add").setAttr("path",nAddr_+"/%2fbr%2ffnc_").setAttr("id",nNm)) != 0 || !(node=SYS.nodeAt(nAddr_1))) testREZ_ = "FAILED: function add";
					else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr_1+"/%2ffunc%2fcfg%2fDESCR").setText("Test")) != 0 || node.cfg("DESCR") != "Test") testREZ_ = "FAILED: function change";
					else if(SYS.cntrReq(SYS.XMLNode("save").setAttr("path",nAddr_1+"/%2fobj")) != 0) testREZ_ = "FAILED: function save";
					else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr_1+"/%2ffunc%2fcfg%2fDESCR").setText("Test 1")) != 0 || 
						SYS.cntrReq(SYS.XMLNode("load").setAttr("path",nAddr_1+"/%2fobj")) != 0 || node.cfg("DESCR") != "Test") testREZ_ = "FAILED: function load";
					else if(SYS.cntrReq((req=SYS.XMLNode("copy").setAttr("path","/%2fobj").setAttr("src",nAddr_1).setAttr("dst",nAddr_1+"_copy"))) != 0 || req.attr("rez").toInt())
						testREZ_ = "FAILED: function copy: "+req.text();
					node = false;
					if(SYS.cntrReq(SYS.XMLNode("del").setAttr("path","/DAQ/"+nList[iN]+"/%2fbr%2flib_").setAttr("id",nNm)) != 0 || SYS.nodeAt(nAddr_)) testREZ_ = "FAILED: function delete";
				}			
			}

			if(SYS.cntrReq(SYS.XMLNode("del").setAttr("path","/DAQ/"+nList[iN]+"/%2fbr%2fcntr_").setAttr("id",nNm)) != 0 || SYS.nodeAt(nAddr)) testREZ = "FAILED: controller delete";
			else {
				treeNd = cfgTree.getElementBy("sub_DAQ");
				if(!treeNd.getElementBy(nList[iN])) treeNd.childAdd("node").setAttr("id",nList[iN]);
				treeNd = treeNd.getElementBy(nList[iN]);

				treeNd_t = treeNd.childAdd("tbl").setAttr("id","DAQ").childAdd("fld").setAttr("ID",nNm+"_cfg").setAttr("PRM_BD",nNm+"_cfg_prm").setAttr("ENABLE",1);
				if(nList[iN] == "mod_JavaLikeCalc") treeNd_t.setAttr("FUNC","servProc.fnc_crc16");
				treeNd.childAdd("tbl").setAttr("id",nNm+"_cfg_prm").childAdd("fld").setAttr("SHIFR",nNm+"_cfg");
				if(nList[iN] == "mod_BlockCalc") {
					treeNd_t.setAttr("BLOCK_SH",nNm+"_cfg_blcks");
					treeNd.childAdd("tbl").setAttr("id",nNm+"_cfg_blcks").childAdd("fld").setAttr("ID",nNm+"_cfg");
				}
				if(nList[iN] == "mod_JavaLikeCalc") {
					treeNd.childAdd("tbl").setAttr("id","lib").childAdd("fld").setAttr("ID",nNm+"_cfg").setAttr("DB","lib_"+nNm+"_cfg");
					treeNd.childAdd("tbl").setAttr("id","lib_"+nNm+"_cfg").childAdd("fld").setAttr("ID",nNm+"_cfg");
				}

				cfgTree.save(0x20, configPath);
				SYS.cntrReq(SYS.XMLNode("scan").setAttr("path","/%2fgen%2fconfig"));		//Reload config command
				SYS.nodeAt(nAddr+"_cfg").enable(true);
				if(!SYS.nodeAt(nAddr+"_cfg")) testREZ = "FAILED: load controller from config";
				else {
					if(!SYS.nodeAt(nAddr+"_cfg/prm_"+nNm+"_cfg")) testREZ = "FAILED: load parameter from config. The field of the parameters table seems is not PRM_BD.";
					if(nList[iN] == "mod_BlockCalc" && !SYS.nodeAt(nAddr+"_cfg/blk_"+nNm+"_cfg")) testREZ_ = "FAILED: load block from config";
					if(nList[iN] == "mod_JavaLikeCalc") {
						if(!SYS.nodeAt(nAddr_+"_cfg")) testREZ_ = "FAILED: load library from config";
						else if(!SYS.nodeAt(nAddr_+"_cfg/fnc_"+nNm+"_cfg")) testREZ_ = "FAILED: load function from config";
					}
				}
			}
		}
		result("DAQ."+nList[iN].slice(4)+": Controller object, parameter and other main objects create/remove, change, load, save and load from config", testREZ);
		if(nList[iN] == "mod_BlockCalc")
			result("DAQ."+nList[iN].slice(4)+": Blocks create/remove, change, load, save and load from config and copy, set link", testREZ_);
		else if(nList[iN] == "mod_JavaLikeCalc")
			result("DAQ."+nList[iN].slice(4)+": Functions and its libraries create/remove, change, load, save and load from config and function copy", testREZ_);
		else if(nList[iN] == "mod_LogicLev") {
			result("DAQ."+nList[iN].slice(4)+": Test \"Param\" from the module \"SystemTests\"",
				SYS.Special.SystemTests.Param("LogicLev.experiment.F3"));
			result("DAQ."+nList[iN].slice(4)+": Test \"Val\" of the module \"SystemTests\" for the parameter attribute",
				SYS.Special.SystemTests.Val("LogicLev.experiment.F3.var",100,1e6));
		}

		//Disconnect/Connect module
		if(nList[iN] != "mod_JavaLikeCalc") {
			req = SYS.XMLNode("get").setAttr("path","/DAQ/"+nList[iN]+"/%2fmodule%2fm_inf%2fSource");
			SYS.cntrReq(req);
			if(req.text()[0] != "*") {
				testREZ = SYS.Special.SystemTests.SOAttach(req.text(),-1);
				testREZ += "/"+SYS.Special.SystemTests.SOAttach(req.text(),1);
				result("DAQ."+nList[iN].slice(4)+": Disconnect/Connect", testREZ);
			} else result("DAQ."+nList[iN].slice(4)+": Disconnect/Connect", "Built-in");
		}
	}
	result("DAQ: Time of the execution, seconds", (SYS.utime()-stTm)*1e-6);
}

//**********************************
//UI subsystem tests
if(sub < 0 || sub == 7) {
	stTm = SYS.utime();
	nList = SYS.UI.nodeList("mod_");
	for(iN = 0; iN < nList.length; iN++) {
		if(nList[iN] == "mod_QTStarter" || nList[iN] == "mod_QTCfg") continue;

		//UI.VCAEngine tests
		if(nList[iN] == "mod_VCAEngine") {
			// Widgets library
			testREZ = "PASSED";
			nAddr = "/UI/"+nList[iN]+"/wlb_"+nNm;
			if(SYS.cntrReq(SYS.XMLNode("add").setAttr("path","/UI/"+nList[iN]+"/%2fbr%2fwlb_").setAttr("id",nNm)) || !(node=SYS.nodeAt(nAddr))) testREZ = "FAILED: widgets library add";
			else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2fobj%2fcfg%2fdescr").setText("Test")) || 
				SYS.cntrReq(req=SYS.XMLNode("get").setAttr("path",nAddr+"/%2fobj%2fcfg%2fdescr")) || req.text() != "Test") testREZ = "FAILED: change widgets library";
			else if(SYS.cntrReq(SYS.XMLNode("save").setAttr("path",nAddr+"/%2fobj"))) testREZ = "FAILED: save widgets library";
			else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2fobj%2fcfg%2fdescr").setText("Test 1")) || 
				SYS.cntrReq(SYS.XMLNode("load").setAttr("path",nAddr+"/%2fobj")) || SYS.cntrReq(req=SYS.XMLNode("get").setAttr("path",nAddr+"/%2fobj%2fcfg%2fdescr")) ||
				req.text() != "Test") testREZ = "FAILED: load widgets library";
			else {
				node = false;

				// Widget
				nAddr1 = nAddr+"/wdg_"+nNm;
				if(SYS.cntrReq(SYS.XMLNode("add").setAttr("path",nAddr+"/%2fbr%2fwdg_").setAttr("id",nNm)) || !(node=SYS.nodeAt(nAddr1))) testREZ = "FAILED: widget add";
				else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr1+"/%2fwdg%2fcfg%2fdescr").setText("Test")) || 
					SYS.cntrReq(req=SYS.XMLNode("get").setAttr("path",nAddr1+"/%2fwdg%2fcfg%2fdescr")) || req.text() != "Test") testREZ = "FAILED: change widget";
				else if(SYS.cntrReq(SYS.XMLNode("save").setAttr("path",nAddr1+"/%2fobj"))) testREZ = "FAILED: save widget";
				else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr1+"/%2fwdg%2fcfg%2fdescr").setText("Test 1")) || 
					SYS.cntrReq(SYS.XMLNode("load").setAttr("path",nAddr1+"/%2fobj")) || SYS.cntrReq(req=SYS.XMLNode("get").setAttr("path",nAddr1+"/%2fwdg%2fcfg%2fdescr")) ||
						req.text() != "Test") testREZ = "FAILED: load widget";
				node = false;

				if(SYS.cntrReq(SYS.XMLNode("del").setAttr("path","/UI/"+nList[iN]+"/%2fbr%2fwlb_").setAttr("id",nNm)) != 0 || SYS.nodeAt(nAddr)) testREZ = "FAILED: widgets library delete";
				else {
					cfgTree.getElementBy("mod_VCAEngine").childAdd("tbl").setAttr("id","LIB").childAdd("fld").setAttr("ID",nNm+"_cfg").setAttr("DB_TBL","wlib_"+nNm+"_cfg");
					cfgTree.getElementBy("mod_VCAEngine").childAdd("tbl").setAttr("id","wlib_"+nNm+"_cfg").childAdd("fld").setAttr("ID",nNm+"_cfg").setAttr("PARENT","/wlb_originals/wdg_Box");
					cfgTree.save(0x20, configPath);
					SYS.cntrReq(SYS.XMLNode("scan").setAttr("path","/%2fgen%2fconfig"));		//Reload config command
					if(!SYS.nodeAt(nAddr+"_cfg") || !SYS.nodeAt(nAddr+"_cfg/wdg_"+nNm+"_cfg")) testREZ = "FAILED: load widgets library from config";
				}
			}
			result("UI.VCAEngine: Widgets and widgets'' library create/remove, change, load, save and load from config", testREZ);

			// Project
			testREZ = "PASSED";
			nAddr = "/UI/"+nList[iN]+"/prj_"+nNm;
			if(SYS.cntrReq(SYS.XMLNode("add").setAttr("path","/UI/"+nList[iN]+"/%2fbr%2fprj_").setAttr("id",nNm)) || !(node=SYS.nodeAt(nAddr))) testREZ = "FAILED: project add";
			else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2fobj%2fcfg%2fdescr").setText("Test"))  || 
				SYS.cntrReq(req=SYS.XMLNode("get").setAttr("path",nAddr+"/%2fobj%2fcfg%2fdescr")) || req.text() != "Test") testREZ = "FAILED: change project";
			else if(SYS.cntrReq(SYS.XMLNode("save").setAttr("path",nAddr+"/%2fobj")) != 0) testREZ = "FAILED: save project";
			else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr+"/%2fobj%2fcfg%2fdescr").setText("Test 1")) != 0 || 
				SYS.cntrReq(SYS.XMLNode("load").setAttr("path",nAddr+"/%2fobj")) != 0 || SYS.cntrReq(req=SYS.XMLNode("get").setAttr("path",nAddr+"/%2fobj%2fcfg%2fdescr")) ||
				req.text() != "Test") testREZ = "FAILED: load project";
			else {
				node = false;

				// Page
				nAddr1 = nAddr+"/pg_"+nNm;
				if(SYS.cntrReq(SYS.XMLNode("add").setAttr("path",nAddr+"/%2fbr%2fpg_").setAttr("id",nNm)) != 0 || !(node=SYS.nodeAt(nAddr1))) testREZ = "FAILED: page add";
				else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr1+"/%2fwdg%2fst%2fparent").setText("/wlb_originals/wdg_Box")) != 0) testREZ = "FAILED: change page";
				else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr1+"/%2fwdg%2fst%2fen").setText(1)) != 0) testREZ = "FAILED: change page";
				else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr1+"/%2fwdg%2fcfg%2fdescr").setText("Test")) != 0 || 
						SYS.cntrReq(req=SYS.XMLNode("get").setAttr("path",nAddr1+"/%2fwdg%2fcfg%2fdescr")) || req.text() != "Test") testREZ = "FAILED: change page";
				else if(SYS.cntrReq(SYS.XMLNode("save").setAttr("path",nAddr1+"/%2fobj")) != 0) testREZ = "FAILED: save page";
				else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr1+"/%2fwdg%2fcfg%2fdescr").setText("Test 1")) != 0 || 
						SYS.cntrReq(SYS.XMLNode("load").setAttr("path",nAddr1+"/%2fobj")) != 0 || SYS.cntrReq(req=SYS.XMLNode("get").setAttr("path",nAddr1+"/%2fwdg%2fcfg%2fdescr")) ||
					req.text() != "Test") testREZ = "FAILED: load page";

				nAddr2 = nAddr1+"/wdg_"+nNm;
				if(SYS.cntrReq(SYS.XMLNode("add").setAttr("path",nAddr1+"/%2fbr%2fwdg_").setAttr("id",nNm)) != 0 || !(node=SYS.nodeAt(nAddr2))) testREZ = "FAILED: widget add";
				else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr2+"/%2fwdg%2fcfg%2fdescr").setText("Test")) != 0 || 
						SYS.cntrReq(req=SYS.XMLNode("get").setAttr("path",nAddr2+"/%2fwdg%2fcfg%2fdescr")) || req.text() != "Test") testREZ = "FAILED: change widget";
				else if(SYS.cntrReq(SYS.XMLNode("save").setAttr("path",nAddr2+"/%2fobj")) != 0) testREZ = "FAILED: save widget";
				else if(SYS.cntrReq(SYS.XMLNode("set").setAttr("path",nAddr2+"/%2fwdg%2fcfg%2fdescr").setText("Test 1")) != 0 || 
						SYS.cntrReq(SYS.XMLNode("load").setAttr("path",nAddr2+"/%2fobj")) != 0 || SYS.cntrReq(req=SYS.XMLNode("get").setAttr("path",nAddr2+"/%2fwdg%2fcfg%2fdescr")) ||
					req.text() != "Test") testREZ = "FAILED: load widget";
				node = false;

				if(SYS.cntrReq(SYS.XMLNode("del").setAttr("path","/UI/"+nList[iN]+"/%2fbr%2fprj_").setAttr("id",nNm)) != 0 || SYS.nodeAt(nAddr)) testREZ = "FAILED: project delete";
				else {
					cfgTree.getElementBy("mod_VCAEngine").childAdd("tbl").setAttr("id","PRJ").childAdd("fld").setAttr("ID",nNm+"_cfg").setAttr("DB_TBL","prj_"+nNm+"_cfg");
					cfgTree.getElementBy("mod_VCAEngine").childAdd("tbl").setAttr("id","prj_"+nNm+"_cfg").childAdd("fld").setAttr("OWNER","/"+nNm+"_cfg").setAttr("ID",nNm+"_cfg").setAttr("PARENT","/wlb_originals/wdg_Box");//.setAttr("FLGS",1);
					cfgTree.getElementBy("mod_VCAEngine").childAdd("tbl").setAttr("id","prj_"+nNm+"_cfg_incl").childAdd("fld").setAttr("IDW","/prj_"+nNm+"_cfg/pg_"+nNm+"_cfg").setAttr("ID",nNm+"_cfg").setAttr("PARENT","/wlb_originals/wdg_Box");
					cfgTree.save(0x20, configPath);
					SYS.cntrReq(SYS.XMLNode("scan").setAttr("path","/%2fgen%2fconfig"));		//Reload config command
					if(!SYS.nodeAt(nAddr+"_cfg"))	testREZ = "FAILED: load project from config";
					else if(!SYS.nodeAt(nAddr+"_cfg/pg_"+nNm+"_cfg"))	testREZ = "FAILED: load page from config";
					else if(!SYS.nodeAt(nAddr+"_cfg/pg_"+nNm+"_cfg/wdg_"+nNm+"_cfg"))	testREZ = "FAILED: load widget from config";
				}
			}
			result("UI.VCAEngine: Project, pages and widgets create/remove, change, load, save and load from config", testREZ);

			//Session
			testREZ = "PASSED";
			nAddr = "/UI/"+nList[iN]+"/ses_"+nNm;
			req = SYS.XMLNode("CntrReqs").setAttr("path","/UI/"+nList[iN]);
			req.childAdd("add").setAttr("path","/%2fbr%2fses_").setText(nNm);
			req.childAdd("set").setAttr("path","/ses_"+nNm+"/%2fobj%2fst%2fprj").setText("tmplSO");
			req.childAdd("set").setAttr("path","/ses_"+nNm+"/%2fobj%2fst%2fstart").setText(1);
			if(SYS.cntrReq(req) != 0 || !SYS.nodeAt(nAddr)) testREZ = "FAILED: session add/start";
			else if(SYS.cntrReq(req=SYS.XMLNode("del").setAttr("path","/UI/"+nList[iN]+"/%2fbr%2fses_").setText(nNm)) || SYS.nodeAt(nAddr)) testREZ = "FAILED: session delete";
			result("UI.VCAEngine: Session create, start and remove", testREZ);
		}

		//Disconnect/Connect module
		req = SYS.XMLNode("get").setAttr("path","/UI/"+nList[iN]+"/%2fmodule%2fm_inf%2fSource");
		SYS.cntrReq(req);
		if(req.text()[0] != "*") {
			testREZ = SYS.Special.SystemTests.SOAttach(req.text(),-1);
			testREZ += "/"+SYS.Special.SystemTests.SOAttach(req.text(),1);
			result("UI."+nList[iN].slice(4)+": Disconnect/Connect", testREZ);
		} else result("UI."+nList[iN].slice(4)+": Disconnect/Connect", "Built-in");
	}
	result("UI: Time of the execution, seconds", (SYS.utime()-stTm)*1e-6);
}

//**********************************
//Special subsystem tests
if(sub < 0 || sub == 8) {
	stTm = SYS.utime();
	nList = SYS.Special.nodeList("mod_");
	for(iN = 0; iN < nList.length; iN++) {
		if(nList[iN] == "mod_SystemTests") continue;
		//Disconnect/Connect module
		req = SYS.XMLNode("get").setAttr("path","/Special/"+nList[iN]+"/%2fmodule%2fm_inf%2fSource");
		SYS.cntrReq(req);
		if(req.text()[0] != "*") {
			testREZ = SYS.Special.SystemTests.SOAttach(req.text(),-1);
			testREZ += "/"+SYS.Special.SystemTests.SOAttach(req.text(),1);
			result("Special."+nList[iN].slice(4)+": Disconnect/Connect", testREZ);
		} else result("Special."+nList[iN].slice(4)+": Disconnect/Connect", "Built-in");
	}
	result("Special: Time of the execution, seconds", (SYS.utime()-stTm)*1e-6);
}

//Restore original config file
SYS.fileWrite(configPath,SYS.fileRead(configPath+".orig"));

//Save result to file ReleaseTests.html
SYS.fileWrite("ReleaseTests.html", "<?xml version=''1.0'' ?>\n"
	"<!DOCTYPE html PUBLIC ''-//W3C//DTD XHTML 1.0 Transitional//EN''\n"
	"''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"
	"</head>\n"
	"<body>\n"
	"<h1>Release tests report table</h1>\n"
	"<TABLE border=\"1\" cellpadding=\"2\" cellspacing=\"0\" width=\"100%\">\n"
	"  <TR align=\"center\"><TH>Test</TH><TH>Result</TH></TR>\n"+
	rezF+
	"</TABLE>\n"
	"</body>"
	"</html>");','','',1560097197);
INSERT INTO lib_servProc VALUES('archPackFStests','TEST: Packing of the archiver on FS','','','Tests set for packing of the value archives to File System, to check the optimization algorithm. The stages are provided:
  - Stage 0: Filling the main values: "1,1,10,10,100,100,1000,1000,10000,10000,EVAL"
  - Stage 1: Changing to different value 20: "1,1,20,10,100,100,1000,1000,10000,10000,EVAL"
  - Stage 2: Setting a value for merging (equal) to the up (10): "1,1,20,10,10,100,1000,1000,10000,10000,EVAL"
  - Stage 3: Setting a value for merging (equal) to the down (1000): "1,1,20,10,10,1000,1000,1000,10000,10000,EVAL"
  - Stage 4: Setting a different value to the end 20000: "1,1,20,10,10,1000,1000,1000,10000,10000,20000,EVAL"
  - Stage 5: Changing the end value for merging to the up (10000): "1,1,20,10,10,1000,1000,1000,10000,10000,10000,EVAL"
  - Stage 6: Setting a value to the end for merging to the up (10000): "1,1,20,10,10,1000,1000,1000,10000,10000,10000,10000,EVAL"
  - Stage 7: Setting the end value to EVAL: "1,1,20,10,10,1000,1000,1000,10000,10000,10000,EVAL"
  - Stage 8: Setting an equal value for inserting 10000: "1,1,20,10,10,1000,1000,1000,10000,10000,10000,EVAL"

Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.0','','',1,10,0,'using Special.FLibSYS;
arh = vArh("Archive.va_"+addr);

//Stage 0: Main values fill: "1,1,10,10,100,100,1000,1000,10000,10000,EVAL"
if(step < 0 || step == 0) {
	buf = vArhBuf(1, 10, per*1000000, true, true);
	for(iV = 0; iV < 10; iV++) buf.set(pow(10,floor(iV/2)), tm+iV*per, 0);
	arh.copy(buf, buf.begin(), 0, buf.end(), 0, archiver);
}

//Stage 1: Change to different value 20: "1,1,20,10,100,100,1000,1000,10000,10000,EVAL"
if(step < 0 || step == 1) {
	buf = vArhBuf(1, 10, per*1000000, true, true);
	buf.set(20, tm+2*per, 0); arh.copy(buf, buf.begin(), 0, buf.end(), 0, archiver);
}

//Stage 2: Set value for merge, equal, to up 10: "1,1,20,10,10,100,1000,1000,10000,10000,EVAL"
if(step < 0 || step == 2) {
	buf = vArhBuf(1, 10, per*1000000, true, true);
	buf.set(10, tm+4*per, 0); arh.copy(buf, buf.begin(), 0, buf.end(), 0, archiver);
}

//Stage 3: Set value for merge, equal, to down 1000: "1,1,20,10,10,1000,1000,1000,10000,10000,EVAL"
if(step < 0 || step == 3) {
	buf = vArhBuf(1, 10, per*1000000, true, true);
	buf.set(1000, tm+5*per, 0); arh.copy(buf, buf.begin(), 0, buf.end(), 0, archiver);
}

//Stage 4: Set different value to end 20000: "1,1,20,10,10,1000,1000,1000,10000,10000,20000,EVAL"
if(step < 0 || step == 4) {
	buf = vArhBuf(1, 10, per*1000000, true, true);
	buf.set(20000, tm+10*per, 0); arh.copy(buf, buf.begin(), 0, buf.end(), 0, archiver);
}

//Stage 5: Change end value for merge to up 10000: "1,1,20,10,10,1000,1000,1000,10000,10000,10000,EVAL"
if(step < 0 || step == 5) {
	buf = vArhBuf(1, 10, per*1000000, true, true);
	buf.set(10000, tm+10*per, 0); arh.copy(buf, buf.begin(), 0, buf.end(), 0, archiver);
}

//Stage 6: Set value to end for merge to up 10000: "1,1,20,10,10,1000,1000,1000,10000,10000,10000,10000,EVAL"
if(step < 0 || step == 6) {
	buf = vArhBuf(1, 10, per*1000000, true, true);
	buf.set(10000, tm+11*per, 0); arh.copy(buf, buf.begin(), 0, buf.end(), 0, archiver);
}

//Stage 7: Set to end value EVAL: "1,1,20,10,10,1000,1000,1000,10000,10000,10000,EVAL"
if(step < 0 || step == 7) {
	buf = vArhBuf(1, 10, per*1000000, true, true);
	buf.set(EVAL_INT, tm+11*per, 0); arh.copy(buf, buf.begin(), 0, buf.end(), 0, archiver);
}

//Stage 8: Set equal value to insert 10000: "1,1,20,10,10,1000,1000,1000,10000,10000,10000,EVAL"
if(step < 0 || step == 8) {
	buf = vArhBuf(1, 10, per*1000000, true, true);
	buf.set(10000, tm+9*per, 0); arh.copy(buf, buf.begin(), 0, buf.end(), 0, archiver);
}','','',1560097170);
INSERT INTO lib_servProc VALUES('wacko2media','EXT: WackoWiki to MediaWiki','','','Procedure of conversion Wiki-dialect from WackoWiki to MediaWiki. Used to move OpenSCADA Wiki to MediaWiki.

Author: Roman Savochenko <roman@oscada.org>
Version: 1.1.0','','',1,10,0,'dbO = SYS.BD.MySQL[ndb];
if(!dbO)	return "Error: DB ''"+ndb+"'' missed!";
DBTbl = dbO.SQLReq("SELECT body FROM `wk_pages` WHERE `tag`=\""+wpg+"\";");
if(DBTbl.err.length) return "Error: "+DBTbl.err;
else if(DBTbl.length < 2 || !DBTbl[1].length)	return "Error: Page ''"+wpg+"'' is not found!"; 
ibuf = DBTbl[1][0];
obuf = "";
tblIn = tblInRow = formIt = formBold = formAlert = formSup = formUndrLn = false;
for(pos = 0; pos < ibuf.length; ) {
	//if(ln.search(new RegExp("script","i"))
	stLine = (pos == 0 || ibuf[pos-1] == "\n");
	pos1 = pos;
	ln = ibuf.parseLine(0, pos1);
	twos = ibuf.slice(pos, pos+2);
	// Blocks: %% {Cnt} %%
	if(twos == "%%" && (pos1=ibuf.indexOf("%%",pos+2)) >= 0) {
		cntr = ibuf.slice(pos+2, pos1);
		cntrProps = cntr.match("^\\s*\\([^)]*\\)\\s*\\n*","gm");
		if(cntrProps.length && cntrProps[0].indexOf("wrapper_align=center",0) >= 0)
			obuf += (cntrProps.length?cntr.slice(cntrProps[0].length):cntr);
		else obuf += "<pre style=\"white-space: pre-wrap;\">\n" + (cntrProps.length?cntr.slice(cntrProps[0].length):cntr) + "</pre>";
		pos = pos1+2;
		continue;
	}
	//Templates
	if((mr=ln.match("^{{(TableOfContents|toc)[^}]*}}\\s*$")).length)	{ obuf += "__TOC__"; pos += mr[0].length; continue; }
	// Generic shild simbol ~
	if(ibuf[pos] == "~")	{ obuf += ibuf[pos+1]; pos += 2; }
	// Tables
	if((mr=ln.match("^#\\|\\s*$","gm")).length)	{ tblIn = true; obuf += "{| class=\"wikitable\""; pos += mr[0].length; continue; }
	if(tblIn && twos == "||") {
		if(!tblInRow)	{ obuf += "|-\n|"; tblInRow = true; }
		else {
			tblInRow = false;
			//  Checking for the table header
			if((stLineOut=obuf.lastIndexOf("\n")) < 0)	stLineOut = 0;
			obuf = obuf.slice(0, stLineOut) + obuf.slice(stLineOut).replace(new RegExp("\\|\\s*''''''(.+?)''''''\\s*\\|","g"),"! $1 !").replace(new RegExp("\\|\\s*''''''(.+?)''''''\\s*$","m"),"! $1");
		}
		pos += 2; continue;
	}
	if(tblIn && tblInRow && ibuf[pos] == "|")	{ obuf += "||"; pos++; continue; }
	if(tblIn && (mr=ln.match("^\\|#\\s*$","gm")).length)	{ tblIn = tblInRow = false; obuf += "|}"; pos += mr[0].length; continue; }
	// Links processing
	if((twos == "((" && (pos1=ln.indexOf("))",0)) > 0) || (twos == "[[" && (pos1=ln.indexOf("]]",0)) > 0)) {
		pos2 = ln.slice(2,pos1).indexOf(" ", 0);
		//obuf += "["+ln.slice(2,pos1).parse(0," ",pos2)+"|"+ln.slice(2,pos1).slice(pos2)+"]"; 
		if(pos2 >= 0) obuf += "["+((ln[2]=="/")?"http://wiki.oscada.org":"")+ln.slice(2,pos1)+"]";
		else obuf += ((ln[2]=="/")?"http://wiki.oscada.org":"[[") + ln.slice(2,pos1) + ((ln[2]=="/")?"":"|"+ln.slice(2,pos1)+"]]");
		pos += pos1+2; continue;
	}
	if((mr=ln.match("^file:[^\\s\\n]+")).length) {
		obuf += "[[File:"+mr[0].slice(5)+"]]";
		pos += mr[0].length; continue;
	}
	// Generic format
	if(twos == "//" && ibuf.slice(pos-5,pos+2) != "http://") {
		if(!formIt && ln.match("//","g").length > 1) { obuf += "''''"; formIt = true; pos += 2; continue; }
		if(formIt)	{ obuf += "''''"; formIt = false; pos += 2; continue; }
	}
	if(twos == "**") {
		if(!formBold && ln.match("\\*\\*","g").length > 1) { obuf += "''''''"; formBold = true; pos += 2; continue; }
		if(formBold)	{ obuf += "''''''"; formBold = false; pos += 2; continue; }
	}
	if(twos == "__") {
		if(!formUndrLn && ln.match("__","g").length > 1) { obuf += "<u>"; formUndrLn = true; pos += 2; continue; }
		if(formUndrLn)	{ obuf += "</u>"; formUndrLn = false; pos += 2; continue; }
	}
	if(twos == "!!") {
		if(!formAlert && ln.match("!!","g").length > 1) {
			mr = ln.match("^!!\\s*\\(([^\\)]+)\\)");
			obuf += "<span style=\"color: "+(mr.length?mr[1]:"red")+"\">";
			formAlert = true;
			pos += mr.length ? mr[0].length : 2;
			continue;
		}
		if(formAlert)	{ obuf += "</span>"; formAlert = false; pos += 2; continue; }
	}
	if(twos == "^^") {
		if(!formSup && ln.match("\\^\\^","g").length > 1) { obuf += "<sup>"; formSup = true; pos += 2; continue; }
		if(formSup)	{ obuf += "</sup>"; formSup = false; pos += 2; continue; }
	}
	// Lists
	if(stLine && (mr=ln.match("^ {2,}+[\\*-]")).length && ((mr[0].length-1)%2) == 0) {
		for(iS = 0; iS < (mr[0].length-1); iS += 2)	obuf += "*";
		pos += mr[0].length; continue;
	}
	// Indents
	if(stLine && (mr=ln.match("^ {2,}+")).length) {
		for(iS = 0; iS < (mr[0].length); iS += 2)	obuf += ":";
		pos += mr[0].length; continue;
	}
	if(ibuf.slice(pos-1,pos+3).match("^[^\n=%#]\n[^\n=|#{ %]").length)	obuf += "<br/>";
	obuf += ibuf[pos]; pos++;
}

return obuf;','','',1560098890);
INSERT INTO lib_servProc VALUES('docOffLine','EXT: Off-line documentation','','','Procedure of forming the offline documentation of OpenSCADA from Wiki by reading and processing the specified Wiki pages on different languages, currently it is: English, Ukrainian, Russian.

Author: Roman Savochenko <roman@oscada.org>
Version: 1.2.0','','',1,360,0,'trNm = "offLine";
docHost = "oscada.org:80";
docHost_ = "http://" + docHost.parse(0,":");
defLang = "en";

SYS.cntrReq((req=SYS.XMLNode("get").setAttr("path","/%2fgen%2fprog")));
prgNm = req.text();
if(!prgVer.length) {
	SYS.cntrReq((req=SYS.XMLNode("get").setAttr("path","/%2fgen%2fver")));
	prgVer = req.text();
}
docData = SYS.strftime(SYS.time(), "%B %Y");

//Check and connect to self transport
tr = SYS.Transport.Sockets.nodeAt("out_"+trNm);
if(!tr) {
	//Create self transport if it is missed
	SYS.cntrReq((req=SYS.XMLNode("add").setAttr("path","/sub_Transport/mod_Sockets/%2fbr%2fout_").setAttr("id",trNm)));
	tr = SYS.Transport.Sockets.nodeAt("out_"+trNm);
}
if(!tr)	return;

//Preparation
pCnt = imgCnt = lnkCnt = lngCnt = 0;
dirsO = new Object();
imgsO = new Object();

// Pages list moving to an object for rapid links checking
pgsO = new Object();
for(off = 0; (tVl=pages.parseLine(0,off)).length; ) {
	off1 = 0; pWiki = tVl.parse(0, ":", off1); pLngs = tVl.parse(0, ":", off1); pOffLn = tVl.parse(0, ":", off1);
	for(off1 = 0; (tVl2=pLngs.parse(0,",",off1)).length; ) {
		if(dirsO[tVl2] != true) {
			if(tVl2 != defLang)
				SYS.system("if test ! -d "+resDir+tVl2+"; then install -d "+resDir+tVl2+"/files; fi", true);
				//SYS.system("if test ! -d "+resDir+tVl2+"; then install -d "+resDir+tVl2+"/files; cp "+resDir+"en/files/doc.css "+resDir+tVl2+"/files; fi", true);
			dirsO[tVl2] = true; lngCnt++;
		}
		pgsO[pWiki+"/"+tVl2] = pOffLn;
	}
}

if(pagesCur.length) {
	pgsOprc = new Object();
	for(off = 0; (tVl=pagesCur.parseLine(0,off)).length; ) {
		off1 = 0; pWiki = tVl.parse(0, ":", off1); pLngs = tVl.parse(0, ":", off1); pOffLn = tVl.parse(0, ":", off1);
		for(off1 = 0; (tVl2=pLngs.parse(0,",",off1)).length; ) {
			if(dirsO[tVl2] != true) {
				if(tVl2 != defLang)
					SYS.system("if test ! -d "+resDir+tVl2+"; then install -d "+resDir+tVl2+"/files; fi", true);
					//SYS.system("if test ! -d "+resDir+tVl2+"; then install -d "+resDir+tVl2+"/files; cp "+resDir+"en/files/doc.css "+resDir+tVl2+"/files; fi", true);
				dirsO[tVl2] = true; lngCnt++;
			}
			pgsOprc[pWiki+"/"+tVl2] = pOffLn;
		}
	}
} else pgsOprc = pgsO;

//Pages fetching and processing
isError = false;
for(var ip in pgsOprc) {
	pLang = ip.lastIndexOf("/"); pWiki = ip.slice(0, pLang); pLang = ip.slice(pLang+1);
	pOffLn = pgsO[ip];
	if(isError)	{ pagesCur += pWiki+":"+pLang+":"+pOffLn+"\n"; continue; }

	// Take folders configuration
	dstDir = topDir = "";
	for(off = 0; (tVl=pOffLn.parsePath(0,off)).length && off < pOffLn.length; ) {
		dstDir += tVl+"/";
		topDir += "../";
	}
	// Create intermediate folders
	if(dstDir.length && dirsO[pLang+"/"+dstDir] != true) {
		SYS.system("if test ! -d "+resDir+pLang+"/"+dstDir+"; then install -d "+resDir+pLang+"/"+dstDir+"; fi", true);
		dirsO[pLang+"/"+dstDir] = true;
	}

	// Request the documentation page
	req = SYS.XMLNode("GET").setAttr("Host",docHost).setAttr("URI","/wiki/index.php?title="+ip.replace("/"+defLang,"")+"&printable=yes");
	// Load the documentation page to a XML tree
	stTm = SYS.mtime();
	for(iTr = 0; iTr < reqTr; iTr++)
		if(!(rez=tr.messIO(req,"HTTP")).length && req.text().length) break;
		else tr.start(false);
	//if(req.text().length < 10)
	//	SYS.messInfo("OffLine", "TEST 00: rez="+rez+"; length="+req.text().length);
	if(iTr == reqTr)	{
		res = "1: Page "+ip+" load error in "+reqTr+" tries and "+1e-3*(SYS.mtime()-stTm)+" seconds";
		isError = true; pagesCur = pWiki+":"+pLang+":"+pOffLn+"\n";
		continue;
	}

	docTree = SYS.XMLNode();
	rez1 = docTree.load(req.text(), false, 0x01);
	if(rez1.toInt()) {
		//SYS.messInfo("OffLine", "TEST 00a: page="+ip+"; rez="+rez+"; length="+req.text().length+"; rez1="+rez1);
		res = "1: Page ''"+ip+"'' parsing for XML is an error: "+rez1+". The original one saved!";
		SYS.fileWrite(resDir+pLang+"/"+pOffLn, req.text());
		return;
	}

	// HTML header processing
	head = docTree.childGet("head");
	for(iEl = 0, passPrev = true; iEl < head.childSize(); iEl++) {
		chO = head.childGet(iEl);
		if(chO.name() == "<*>") {
			if(!passPrev)	{ head.childDel(iEl); iEl--; }
			continue;
		}
		passPrev = false;
		if(chO.name() == "script" || chO.name() == "style" ||
				(chO.name() == "meta" && chO.attr("name") != "generator" && !chO.attr("charset").length) ||
				(chO.name() == "link" && chO.attr("rel") != "copyright"))	{
			head.childDel(iEl);
			iEl--;
		}
		else passPrev = true;
	}
	head.childAdd("link").setAttr("rel","stylesheet").setAttr("href",topDir+((pLang!=defLang)?"../"+defLang+"/":"")+"files/doc.css");

	// BODY processing
	body = docTree.childGet("body");
	header = body.getElementBy("mw_header");
	cnt = body.getElementBy("mw-content-text");
	body.clear(true);
	body.childAdd(header); body.childAdd(cnt);

	// Languages area processing
	if((lngSelAr=body.getElementBy("mw-pt-languages noprint","class"))) {
		lngSelAr.childDel(0);
		lngSelAr.setAttr("class", "mw-pt-languages");
		anchs = lngSelAr.getElementsBy("a");
		for(iEl = 0; iEl < anchs.length; iEl++) {
			href = anchs[iEl].attr("href");
			tVl = href.slice(href.lastIndexOf("/")+1);
			anchs[iEl].setAttr("href", topDir+"../"+(dirsO[tVl] == true?tVl:defLang)+"/"+pOffLn);
		}
	}

	// Images processing
	imgs = body.getElementsBy("img");
	for(iEl = 0; iEl < imgs.length && !isError; iEl++)
		if((tVl=imgs[iEl].attr("src").match("^/.*/(.*)$")).length) {
			isDef = false;
			if(imgsO[pLang+":"+tVl[1]] != true && (pLang == defLang || (isDef=imgsO[defLang+":"+tVl[1]]) != true)) {
				req = SYS.XMLNode("GET").setAttr("Host",docHost).setAttr("URI",imgs[iEl].attr("src"));

				stTm = SYS.mtime();
				for(iTr = 0; iTr < reqTr; iTr++)
					if(!(rez=tr.messIO(req,"HTTP")).length && req.text().length) break;
					else tr.start(false);
				//if(req.text().length < 10)
				//	SYS.messInfo("OffLine", "TEST 01: rez="+rez+"; length="+req.text().length);
				if(iTr == reqTr)	{
					res = "1: Image ''"+req.attr("URI")+"'' load error in "+reqTr+" tries and "+1e-3*(SYS.mtime()-stTm)+" seconds";
					isError = true; pagesCur = pWiki+":"+pLang+":"+pOffLn+"\n";
					break;
				}
				SYS.fileWrite(resDir+pLang+"/files/"+tVl[1], req.text());
				imgsO[pLang+":"+tVl[1]] = true;
				imgCnt++;
			}
			//SYS.messInfo("DOC", "pLang="+pLang+"; file="+tVl[1]+"; isP="+isP);
			imgs[iEl].setAttr("src", topDir+((isDef==true)?"../"+defLang+"/":"")+"files/"+tVl[1]);
		}
	if(isError)	continue;

	// Links processing
	anchs = body.getElementsBy("a");
	for(iEl = 0; iEl < anchs.length; iEl++) {
		href = anchs[iEl].attr("href");
		if(href[0] == "/") {
			if((tVl=href.lastIndexOf("#")) >= 0)	hTag = href.slice(tVl), href = href.slice(0, tVl);
			else hTag = "";

			dynLngPg = (href.indexOf("/wiki/Special:MyLanguage/") == 0) ? href.slice(25) : "";
			stLngPg = (!dynLngPg.length && href.indexOf("/wiki/") == 0) ? href.slice(6) : "";
			if(dynLngPg.length && (!(tDst=pgsO[dynLngPg+"/"+pLang]).isEVal() || !(tDst=pgsO[stLngPg]).isEVal()))
					anchs[iEl].setAttr("href", topDir+tDst+hTag);
			else if(dynLngPg.length && !(tDst=pgsO[dynLngPg+"/"+defLang]).isEVal())
					anchs[iEl].setAttr("href", topDir+"../"+defLang+"/"+tDst+hTag);
			else {
				anchs[iEl].setAttr("href", docHost_+href+hTag);
				if(!anchs[iEl].attr("class").length)	anchs[iEl].setAttr("class", "external");
			}
			lnkCnt++;
		}
	}

	// Appending the custom header and footer
	body.childIns(0, "div").load("<div class=''floatright''><a href=''"+docHost_+"''><img alt=''"+prgNm+"'' src=''"+topDir+"../en/files/Logo-big.png'' /></a></div>");
	body.childAdd("table").load("<table style=''border-top: dotted 2px #999999; margin-top: 20pt; color: gray;'' width=''100%''><tr>"
		"<td style=''text-align: left;'' width=''40%''><a href=''"+docHost_+"/wiki/"+pWiki+"/"+pLang+"''>"+pWiki+"/"+pLang+"</a> - <a href=''"+docHost_+"/en/main/about-the-project/licenses/''>GFDL</a></td>"
		"<td style=''text-align: center;''>"+docData+"</td>"
		"<td style=''text-align: right;'' width=''40%''>"+prgNm+" "+prgVer+"</td></tr></table>", false, 0x01);

	docTree.save(0x3C0, resDir+pLang+"/"+pOffLn);
	pCnt++;
	//SYS.messInfo("OffLine", "TEST 00: pLang="+pLang);
}

if(!isError)	pagesCur = "", res = "0: Fetched and processed pages="+pCnt+"; images="+imgCnt+"; links="+lnkCnt+"; languages="+lngCnt;','','',1579619800);
CREATE TABLE IF NOT EXISTS 'flb_regEl' ("ID" TEXT DEFAULT '' ,"NAME" TEXT DEFAULT '' ,"uk#NAME" TEXT DEFAULT '' ,"ru#NAME" TEXT DEFAULT '' ,"DESCR" TEXT DEFAULT '' ,"uk#DESCR" TEXT DEFAULT '' ,"ru#DESCR" TEXT DEFAULT '' ,"START" INTEGER DEFAULT '1' ,"MAXCALCTM" INTEGER DEFAULT '10' ,"PR_TR" INTEGER DEFAULT '0' ,"FORMULA" TEXT DEFAULT '' ,"uk#FORMULA" TEXT DEFAULT '' ,"ru#FORMULA" TEXT DEFAULT '' ,"TIMESTAMP" INTEGER DEFAULT '' , PRIMARY KEY ("ID"));
INSERT INTO flb_regEl VALUES('pidUnif','PID unified','ПІД уніфікований','ПИД унифицированный','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','Суміщений-уніфікований аналоговий та імпульсний ПІД-регулятор. У основі регулятора лежить ядро стандартного аналогового ПІД-регулятора з бібліотеки "FLibComplex1" та реалізація ШІМ для імпульсної частини.

Автор: Роман Савоченко <roman@oscada.org>
Версія: 1.0.0
Ліцензія: GPLv2','Совмещённый-унифицированный аналоговый и импульсный ПИД-регулятор. В основе регулятора лежит ядро стандартного аналогового ПИД-регулятора из библиотеки "FLibComplex1" и реализация ШИМ для импульсной части.

Автор: Роман Савоченко <roman@oscada.org>
Версия: 1.0.0
Лицензия: GPLv2',1,10,0,'//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;','','',1584883408);
INSERT INTO flb_regEl VALUES('pidUnifD','PID dynamic','ПІД динамічний','ПИД динамический','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','Цілковито ідентичний уніфікованому ПІД-регулятору, але реалізований динамічно на JavaLikeCalc. Динамічна реалізація дозволяє легко адаптувати регулятор під потрібні вимоги, просто відредагувавши його.

Автор: Роман Савоченко <roman@oscada.org>
Версія: 1.0.0
Ліцензія: GPLv2','Полностью идентичный унифицированному ПИД-регулятору, но реализованный динамически на JavaLikeCalc. Динамическая реализация позволяет легко адаптировать регулятор под нужные требования, просто отредактировав его.

Автор: Роман Савоченко <roman@oscada.org>
Версия: 1.0.0
Лицензия: GPLv2',1,10,0,'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));','','',1584883408);
INSERT INTO flb_regEl VALUES('pidImp','PID pulse','ПІД імпульсний','ПИД импульсный','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','Спеціалізований імпульсний ПІД-регулятор реалізований за особливим алгоритмом із компенсацією подвійного інтегрування.

Автор: Роман Савоченко <roman@oscada.org>
Версія: 1.0.0
Ліцензія: GPLv2','Специализированный импульсный ПИД регулятор реализованный по особому алгоритму с компенсацией двойного интегрирования.

Автор: Роман Савоченко <roman@oscada.org>
Версия: 1.0.0
Лицензия: GPLv2',1,10,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(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;','','',1584883408);
CREATE TABLE IF NOT EXISTS 'flb_doc' ("ID" TEXT DEFAULT '' ,"NAME" TEXT DEFAULT '' ,"uk#NAME" TEXT DEFAULT '' ,"ru#NAME" TEXT DEFAULT '' ,"DESCR" TEXT DEFAULT '' ,"uk#DESCR" TEXT DEFAULT '' ,"ru#DESCR" TEXT DEFAULT '' ,"START" INTEGER DEFAULT '1' ,"MAXCALCTM" INTEGER DEFAULT '10' ,"PR_TR" INTEGER DEFAULT '0' ,"FORMULA" TEXT DEFAULT '' ,"uk#FORMULA" TEXT DEFAULT '' ,"ru#FORMULA" TEXT DEFAULT '' ,"TIMESTAMP" INTEGER DEFAULT '0' , PRIMARY KEY ("ID"));
INSERT INTO flb_doc VALUES('getVal','Getting value from the value archive','Отримання значення з архіву значень','Получение значения из архива значений','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','Запит значення за визначений час (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','Запрос значения за определённое время (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',1,10,0,'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;','','',1579609648);
INSERT INTO flb_doc VALUES('sumVal','Sum values of the value archive','Підсумок значень архіву значень','Сумма значений архива значений','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','Обчислення суми значень за вказаний період часу [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','Вычисление суммы значений за указанный период времени [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',1,10,0,'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;','','',1579609896);
INSERT INTO flb_doc VALUES('averVal','Average value of the value archive','Середнє значення архіву значень','Среднее значение архива значений','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','Обчислення середнього значення за вказаний період часу [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','Вычисление среднего значения за указанный период времени [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',1,10,0,'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;','','',1579609632);
INSERT INTO flb_doc VALUES('diagram','Diagram','Діаграма','Диаграмма','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','Побудова діаграми трендів у 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','Построение диаграммы трендов в 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',1,10,0,'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();','','',1581943347);
CREATE TABLE IF NOT EXISTS 'tmplib_LowDevLib' ("ID" TEXT DEFAULT '' ,"NAME" TEXT DEFAULT '' ,"DESCR" TEXT DEFAULT '' ,"MAXCALCTM" INTEGER DEFAULT '10' ,"PR_TR" INTEGER DEFAULT '0' ,"PROGRAM" TEXT DEFAULT '' ,"TIMESTAMP" INTEGER DEFAULT '0' , PRIMARY KEY ("ID"));
INSERT INTO tmplib_LowDevLib VALUES('1602A','GPIO|I2C: 1602A(HD44780)','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',10,0,'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;',1509990639);
INSERT INTO tmplib_LowDevLib VALUES('1W_DS9097','One Wire by DS9097','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',30,0,'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;',1509990639);
INSERT INTO tmplib_LowDevLib VALUES('1W_DS9097U','One Wire by DS9097U','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',30,0,'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;',1509990639);
INSERT INTO tmplib_LowDevLib VALUES('BMP180','I2C: BMP180','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',10,0,'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;',1509990639);
INSERT INTO tmplib_LowDevLib VALUES('DHT','GPIO: DHT11,22 (AM23XX)','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',10,0,'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;',1509373346);
INSERT INTO tmplib_LowDevLib VALUES('DS3231','I2C: DS1307,DS3231','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',10,0,'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;',1541249292);
INSERT INTO tmplib_LowDevLib VALUES('PCF8574','I2C: PCF8574','I2C 8-bit 8DIO. Connect through a Serial output transport into the I2C mode.
Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.1',10,0,'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;',1509990639);
INSERT INTO tmplib_LowDevLib VALUES('PCF8591','I2C: PCF8591','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',10,0,'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;',1509908745);
INSERT INTO tmplib_LowDevLib VALUES('BME280','I2C: BME280','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',10,0,'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;',1509989860);
INSERT INTO tmplib_LowDevLib VALUES('SHT3x','I2C: SHT3x','Digital Temperature and Humidity Sensor for the models: SHT30
Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.0',10,0,'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;',1542469153);
INSERT INTO tmplib_LowDevLib VALUES('ADS111x','I2C: ADS101x, ADS111x','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',10,0,'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;',1549528595);
INSERT INTO tmplib_LowDevLib VALUES('MCP4725','I2C: MCP4725','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',10,0,'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;',1549488136);
INSERT INTO tmplib_LowDevLib VALUES('MAX6675','GPIO: MAX6675','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',10,0,'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;',1550995458);
CREATE TABLE IF NOT EXISTS 'tmplib_tests' ("ID" TEXT DEFAULT '' ,"NAME" TEXT DEFAULT '' ,"uk#NAME" TEXT DEFAULT '' ,"ru#NAME" TEXT DEFAULT '' ,"DESCR" TEXT DEFAULT '' ,"uk#DESCR" TEXT DEFAULT '' ,"ru#DESCR" TEXT DEFAULT '' ,"MAXCALCTM" INTEGER DEFAULT '10' ,"PR_TR" INTEGER DEFAULT '0' ,"PROGRAM" TEXT DEFAULT '' ,"TIMESTAMP" INTEGER DEFAULT '0' , PRIMARY KEY ("ID"));
INSERT INTO tmplib_tests VALUES('ai_simple','Simple AI','Простий AI','Простой AI','Simple analog parameter.','Простий аналоговий параметр.','Простой аналоговый параметр.',10,0,'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";
}',0);
INSERT INTO tmplib_tests VALUES('ai_man','Manual input of AI','Ручний ввід AI','Ручной ввод AI','Manual input of a standard analog parameter.','Стандартний аналоговий параметр ручного вводу.','Стандартный аналоговый параметр ручного ввода.',10,0,'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";
}',1572510316);
INSERT INTO tmplib_tests VALUES('test','Test','Тест','Тест','S7 controller testing','Тестування контролера S7','Тестирование контроллера S7',10,0,'
',1539451468);
INSERT INTO tmplib_tests VALUES('gasPoint','Flow control point','','','','','',10,0,'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);
',1561831602);
CREATE TABLE IF NOT EXISTS 'UserProtocol_uPrt' ("ID" TEXT DEFAULT '' ,"NAME" TEXT DEFAULT '' ,"uk#NAME" TEXT DEFAULT '' ,"ru#NAME" TEXT DEFAULT '' ,"DESCR" TEXT DEFAULT '' ,"uk#DESCR" TEXT DEFAULT '' ,"ru#DESCR" TEXT DEFAULT '' ,"EN" INTEGER DEFAULT '0' ,"DAQTmpl" TEXT DEFAULT '' ,"WaitReqTm" INTEGER DEFAULT '0' ,"InPROG" TEXT DEFAULT '' ,"OutPROG" TEXT DEFAULT '' ,"PR_TR" INTEGER DEFAULT '0' ,"TIMESTAMP" INTEGER DEFAULT '0' , PRIMARY KEY ("ID"));
INSERT INTO UserProtocol_uPrt VALUES('SMS','SMS (replaced)','','','!!!!: 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','','',1,'',0,'','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");
}',0,1550073193);
INSERT INTO UserProtocol_uPrt VALUES('SCU750','EDWARDS TURBOMOLECULAR PUMPS','','','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','','',1,'',0,'','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;
	}
}',0,1509283349);
INSERT INTO UserProtocol_uPrt VALUES('TMH','Power supply for turbomolecular pumps','','','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','','',1,'',0,'','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));',0,1509283419);
INSERT INTO UserProtocol_uPrt VALUES('VKT7','VKT-7','','','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','','',1,'',0,'','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));',0,1509283495);
INSERT INTO UserProtocol_uPrt VALUES('TM510x','Elemer TM510x','','','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','','',1,'',0,'','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));',0,1509283413);
INSERT INTO UserProtocol_uPrt VALUES('SMDP','Sycon Multi Drop Protocol','','','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','','',1,'',0,'','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."));',0,1509283368);
INSERT INTO UserProtocol_uPrt VALUES('IT3','Temperature measurement IT-3','','','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','','',1,'',0,'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;','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));
}',0,1472721683);
INSERT INTO UserProtocol_uPrt VALUES('IVE_452HS_02','IVE-452HS-02','IVE-452HS-02','','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','','',1,'',0,'','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));',0,1509285266);
INSERT INTO UserProtocol_uPrt VALUES('OPTRIS','OPTRIS CT/CTL','','','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','','',1,'',0,'','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);',0,1509286719);
INSERT INTO UserProtocol_uPrt VALUES('CTR','CTR 100, 101','CTR 100, 101','','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','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)".','',1,'',0,'','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));',0,1509287419);
INSERT INTO UserProtocol_uPrt VALUES('SMTP','SMTP (replaced)','SMTP','','!!!!: 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','Provides EMAIL sending by the protocol SMTP.
Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.0','',1,'',0,'','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);',0,1550073190);
INSERT INTO UserProtocol_uPrt VALUES('AT24CXX','I2C: AT24C{32|64}','','','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','','',1,'',0,'','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;
}',0,1479654824);
INSERT INTO UserProtocol_uPrt VALUES('m200','Mercury 200','','Меркурий 200','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','','Протокольный уровень однофазных счетчиков электроэнергии Меркурий 200,  203.2Т,  206 компании Инкотекс (http://www.incotexcom.ru).
Автор: Арсен Закоян <godzilla919@gmail.com>
Версия 1.0',1,'',0,'','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));
',1,1509290174);
INSERT INTO UserProtocol_uPrt VALUES('m230','Mercury 230','','Меркурий 230','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','','Протокольный уровень трехфазных счетчиков электроэнергии Меркурий 230, 231, 232, 233,  234,  236  компании Инкотекс (http://www.incotexcom.ru).
Автор: Арсен Закоян <godzilla919@gmail.com>
Версия 1.0',1,'',0,'','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,));
;}',1,1509290179);
INSERT INTO UserProtocol_uPrt VALUES('NIK2303','NIK2303','','','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','','',1,'',0,'','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");',1,1512240522);
CREATE TABLE IF NOT EXISTS 'tmplib_LowDevLib_io' ("TMPL_ID" TEXT DEFAULT '' ,"ID" TEXT DEFAULT '' ,"NAME" TEXT DEFAULT '' ,"TYPE" INTEGER DEFAULT '0' ,"FLAGS" INTEGER DEFAULT '0' ,"VALUE" TEXT DEFAULT '' ,"POS" INTEGER DEFAULT '0' ,"uk#NAME" TEXT DEFAULT '' ,"ru#NAME" TEXT DEFAULT '' , PRIMARY KEY ("TMPL_ID","ID"));
INSERT INTO tmplib_LowDevLib_io VALUES('1602A','transport','Transport of the I2C, Serial (i2c) or
GPIO address with function put(), mostly it''s BCM2835 (DAQ.BCM2835.pi.pi)',0,64,'i2c',0,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('1602A','addr','I2C device address [0...119]',1,64,'39',1,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('1602A','RS','GPIO Pin: Reset',1,64,'7',2,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('1602A','E','GPIO Pin: Enable',1,64,'8',3,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('1602A','D4','GPIO Pin: Data4',1,64,'25',4,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('1602A','D5','GPIO Pin: Data5',1,64,'24',5,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('1602A','D6','GPIO Pin: Data6',1,64,'23',6,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('1602A','D7','GPIO Pin: Data7',1,64,'18',7,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('1602A','ln1','Line 1',0,32,'',8,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('1602A','ln2','Line 2',0,32,'',9,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('1W_DS9097','transport','Transport of the One Wire bus, Serial',0,64,'oneWire',0,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('1W_DS9097','tmResc','Rescan period, s',2,64,'60',1,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('1W_DS9097','tryEVAL','Tries after which set value to EVAL',1,64,'3',2,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('1W_DS9097','power','Power, for temperature',3,16,'',3,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('1W_DS9097','this','Object',4,0,'',4,'Об''єкт','Объект');
INSERT INTO tmplib_LowDevLib_io VALUES('1W_DS9097U','transport','Transport of the One Wire bus, Serial',0,64,'oneWire',0,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('1W_DS9097U','tmResc','Rescan period, s',2,64,'60',1,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('1W_DS9097U','tryEVAL','Tries after which set value to EVAL',1,64,'3',2,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('1W_DS9097U','power','Power, for temperature',3,16,'',3,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('1W_DS9097U','isData','In data mode',3,0,'0',4,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('1W_DS9097U','this','Object',4,0,'',5,'Об''єкт','Объект');
INSERT INTO tmplib_LowDevLib_io VALUES('BMP180','transport','Transport of the I2C, Serial',0,64,'i2c',0,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('BMP180','addr','Device address [0...119]',1,64,'119',1,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('BMP180','oss','Oversampling setting (0...3)',1,64,'0',2,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('BMP180','t','T, °С',2,16,'',3,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('BMP180','p','P, Pa',2,16,'',4,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('DHT','addr','GPIO address with functions mode(), get() and put(), mostly it''s BCM2835',0,64,'DAQ.GPIO.io.pi',0,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('DHT','pin','IO pin number of the GPIO',1,64,'17',1,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('DHT','tries','Tries [1...5]',1,64,'2',2,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('DHT','dev','Device (0-DHT11, 1-DHT22)',1,64,'1',3,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('DHT','t','T, °С',2,16,'',4,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('DHT','h','H, %',2,16,'',5,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('DS3231','transport','Transport of the I2C, Serial',0,64,'i2c',0,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('DS3231','addr','Device address [0...119]',1,64,'104',1,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('DS3231','tm','Date and time, YYYY-MM-DDTHH:mm:SS',0,32,'',3,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('DS3231','pSQW','Enable SQUARE-WAVE OUTPUT',3,32,'',4,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8574','transport','Transport of the I2C, Serial',0,64,'i2c',0,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8574','addr','Device address [0...119]',1,64,'39',1,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8574','di0','DI0',3,16,'',2,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8574','di1','DI1',3,16,'',3,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8574','di2','DI2',3,16,'',4,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8574','di3','DI3',3,16,'',5,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8574','di4','DI4',3,16,'',6,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8574','di5','DI5',3,16,'',7,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8574','di6','DI6',3,16,'',8,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8574','di7','DI7',3,16,'',9,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8574','do0','DO0',3,33,'',10,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8574','do1','DO1',3,33,'',11,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8574','do2','DO2',3,33,'',12,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8574','do3','DO3',3,33,'',13,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8574','do4','DO4',3,33,'',14,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8574','do5','DO5',3,33,'',15,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8574','do6','DO6',3,33,'',16,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8574','do7','DO7',3,33,'',17,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8591','transport','Transport of the I2C, Serial',0,64,'i2c',0,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8591','addr','Device address [0...119]',1,64,'72',1,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8591','vRef','Reference voltage, V',2,64,'3.2',2,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8591','ai0','AI0',2,16,'',3,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8591','ai1','AI1',2,16,'',4,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8591','ai2','AI2',2,16,'',5,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8591','ai3','AI3',2,16,'',6,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('PCF8591','ao','AO',2,32,'',7,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('BME280','transport','Transport of the I2C, Serial',0,64,'i2c',0,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('BME280','addr','Device address [0...119]',1,64,'118',1,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('BME280','oss','Oversampling setting (0...7)',1,64,'3',2,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('BME280','t','T, °С',2,16,'',3,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('BME280','p','P, Pa',2,16,'',4,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('BME280','h','H, %',2,16,'',5,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('DS3231','mode','Mode',1,40,'0
0;1
DS3231;DS1307',2,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('DS3231','this','Parameter',4,0,'',6,'Параметр','Параметр');
INSERT INTO tmplib_LowDevLib_io VALUES('DS3231','pSQWf','SQUARE-WAVE OUTPUT frequency',1,32,'',5,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('SHT3x','transport','Transport of the I2C, Serial',0,64,'i2c',0,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('SHT3x','addr','Device address [0...119]',1,64,'68',1,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('SHT3x','H','Humidity',2,16,'',2,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('SHT3x','T','Temperature',2,16,'',3,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('ADS111x','transport','Transport of the I2C, Serial',0,64,'i2c',0,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('ADS111x','addr','Device address [0...119]',1,64,'72',1,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('ADS111x','range','Range, ±V',1,40,'2
0;1;2;3;4;5
6.144;4.096;2.048;1.024;0.512;0.256',2,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('ADS111x','ai0','AI0',2,16,'',3,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('ADS111x','ai1','AI1',2,16,'',4,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('ADS111x','ai2','AI2',2,16,'',5,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('ADS111x','ai3','AI3',2,16,'',6,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('MCP4725','transport','Transport of the I2C, Serial',0,64,'i2c',0,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('MCP4725','addr','Device address [0...119]',1,64,'96',1,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('MCP4725','vRef','Reference voltage, V',2,64,'3.2',2,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('MCP4725','ao','AO',2,32,'',3,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('MAX6675','addr','GPIO address with functions mode(), get() and put(), mostly it''s BCM2835',0,64,'DAQ.GPIO.io.pi',0,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('MAX6675','pin_cs','CS pin number of the GPIO',1,64,'8',1,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('MAX6675','pin_sclk','SCLK pin number of the GPIO',1,64,'11',2,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('MAX6675','pin_miso','MISO pin number of the GPIO',1,64,'9',3,'','');
INSERT INTO tmplib_LowDevLib_io VALUES('MAX6675','t','T, °С',2,17,'',4,'','');
CREATE TABLE IF NOT EXISTS 'techApp' ("ID" TEXT DEFAULT '' ,"NAME" TEXT DEFAULT '' ,"uk#NAME" TEXT DEFAULT '' ,"ru#NAME" TEXT DEFAULT '' ,"DESCR" TEXT DEFAULT '' ,"uk#DESCR" TEXT DEFAULT '' ,"ru#DESCR" TEXT DEFAULT '' ,"START" INTEGER DEFAULT '1' ,"MAXCALCTM" INTEGER DEFAULT '10' ,"PR_TR" INTEGER DEFAULT '0' ,"FORMULA" TEXT DEFAULT '' ,"TIMESTAMP" INTEGER DEFAULT '0' , PRIMARY KEY ("ID"));
INSERT INTO techApp VALUES('lag','Lag','Затримка','Запаздывание','Lag model. Can be used for lag imitation of the sensor variables.','Модель затримки. Може використовуватися для імітації запізнення значень давачів.','Модель задержки. Может использоваться для имитации запаздывания значений датчиков.',1,10,0,'out -= (out-in)/(t_lg*f_frq);',1556609878);
INSERT INTO techApp VALUES('noise','Noise: 2 harmonic + rand','Шум: 2 гармоніки + випадковий.','Шум: 2 гармоники + случайное','Noise model. Contains three parts:
- first harmonic part;
- second harmonic part;
- noise based on generator of the randomize numbers.','Модель шуму. Містить три складові:
- перша гармоніка;
- друга гармоніка;
- шум на основі генератору випадкових чисел.','Модель шума. Содержит три составляющие:
- первая гармоника;
- вторая гармоника;
- шум на основе генератора случайных чисел.',1,10,0,'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);',1556609878);
INSERT INTO techApp VALUES('ballCrane','Ball crane','Кульовий кран','Шаровый кран','Model of the ball crane. Includes for the going and estrangement time.','Модель кульового крану. Включає час ходу та час відриву.','Модель шарового крана. Включает время хода и время отрыва.',1,10,0,'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;
}',1556609878);
INSERT INTO techApp VALUES('separator','Separator','Сепаратор','Сепаратор','Separator model with two phases, liquid and gas.','Модель сепаратору з двома фазами, рідинною та газовою.','Модель сепаратора с двумя фазами, жидкой и газовой.',1,10,0,'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;',1556609878);
INSERT INTO techApp VALUES('valve','Valve','Клапан','Клапан','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.','Модель клапану яка враховує:
- два клапани в одному;
- надкритичне витікання;
- зміна температури при дроселюванні;
- робота тільки у одному напрямку, зворотний клапан;
- керування швидкісттю зміни положення;
- нелінійність прохідного перетину від положення.','Модель клапана, учитывающая:
- два клапана в одном;
- сверхкритическое истечение;
- изменение температуры при дросселировании;
- работа только в одном направлении, обратный клапан;
- управляемая скорость изменения положения;
- нелинейность проходного сечения от положения.',1,10,0,'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)));',1556609878);
INSERT INTO techApp VALUES('lagClean','Lag: clean','Затримка: чиста','Задержка: чистая','Model of clean(transportable) lag. Realizes by incorporating several links of a simple delay. Appointed for lags into long pipes.','Модель чистої(транспортної) затримки. Реалізується шляхом включення декількох ланок простої затримки. Призначено для імітації затримок у довгих трубопроводах.','Модель чистого(транспортного) запаздывания. Реализуется путём включения нескольких простых звеньев запаздывания. Предназначен для имитации запаздывания в длинных трубопроводах.',1,10,0,'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);',1556609878);
INSERT INTO techApp VALUES('net','Network: load','Мережа: навантаження','Сеть: нагрузка','Loading with constant pressure on the network. Contains a parameter for connection the noise.','Навантаження з фіксованим тиском мережі. Містить параметр для підключення шуму.','Нагрузка с фиксированным давлением сети. Содержит параметр для подключения шума.',1,10,0,'DAQ.JavaLikeCalc.lib_techApp.pipeBase(Fi, Pi, 293, So, EVAL_REAL, Po, 293, So, 10, Q0, Kpr, 0.01, f_frq);',1556609878);
INSERT INTO techApp VALUES('src_press','Source: pressure','Джерело: тиск','Источник: давление','Source of the constant pressure. Contains a parameter for connection the noise.','Джерело з фіксованим тиском. Містить параметр для підключення шуму.','Источник с фиксированным давлением. Содержит параметр для подключения шума.',1,10,0,'DAQ.JavaLikeCalc.lib_techApp.pipeBase(Fit, Pi*Noise, 293, So, Fo, Po, 293, So, lo, Q0, Kpr, 0.01, f_frq);',1556609878);
INSERT INTO techApp VALUES('cooler','Air cooler','Повітряний холодильник','Воздушный холодильник','Model of the air cooler for gas flow.','Модель повітряного охолоджувача газового потоку.','Модель воздушного охладителя газового потока.',1,10,0,'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);',1556609878);
INSERT INTO techApp VALUES('compressor','Gas compressor','Компресор газовий','Компрессор газовый','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.','Модель газового компресора. Враховує ефект помпажу.
Помпаж розраховується за газово-динамічною кривою, виходячи з якої розраховується коефіцієнт запасу за помпажем.','Модель газового компрессора. Учитывает эффект помпажа. Помпаж вычисляется по газо-динамической кривой, исходя из которой получается коэффициент запаса по помпажу.',1,10,0,'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);',1556609878);
INSERT INTO techApp VALUES('src_flow','Source: flow','Джерело: витрати','Источник: расход','Source of the constant flow. Contains a parameter for connection the noise.','Джерело з фіксованими витратами. Містить параметр для підключення шуму.','Источник с фиксированным расходом. Содержит параметр для подключения шума.',1,10,0,'Po = max(0, min(100,Po+0.27*(Noise*Fi-Fo)/(Q0*Kpr*So*lo*f_frq)));',1556609878);
INSERT INTO techApp VALUES('pipe1_1','Pipe 1->1','Труба 1->1','Труба 1->1','Model of the pipe by the scheme "1 -> 1".','Модель вузла труб за схемою "1 -> 1".','Модель узла труб по схеме "1 -> 1".',1,10,0,'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);',1556609878);
INSERT INTO techApp VALUES('pipe1_2','Pipe 1->2','Труба 1->2','Труба 1->2','Model of the pipe by the scheme "1 -> 2".','Модель вузла труб за схемою "1 -> 2".','Модель узла труб по схеме "1 -> 2".',1,10,0,'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;',1556609878);
INSERT INTO techApp VALUES('pipe1_3','Pipe 1->3','Труба 1->3','Труба 1->3','Model of the pipe by the scheme "1 -> 3".','Модель вузла труб за схемою "1 -> 3".','Модель узла труб по схеме "1 -> 3".',1,10,0,'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;',1556609878);
INSERT INTO techApp VALUES('pipe1_4','Pipe 1->4','Труба 1->4','Труба 1->4','Model of the pipe by the scheme "1 -> 4".','Модель вузла труб за схемою "1 -> 4".','Модель узла труб по схеме "1 -> 4".',1,10,0,'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;',1556609878);
INSERT INTO techApp VALUES('valveMech','Valve: processing mechanism','Клапан: виконавчий механізм','Клапан: исполнительный механизм','Model of the valve processing mechanism. Includes the going and estrangement time.','Модель виконавчого механізму клапана. Включає час ходу та час відриву.','Модель исполнительного механизма клапана. Включает время хода и время отрыва.',1,10,0,'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);',1556609878);
INSERT INTO techApp VALUES('diaphragm','Diaphragm','Діафрагма','Диафрагма','Diaphragm model.','Модель діафрагми.','Модель диафрагмы.',1,10,0,'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;',1556609878);
INSERT INTO techApp VALUES('pipe3_1','Pipe 3->1','Труба 3->1','Труба 3->1','Model of the pipe by the scheme "3 -> 1".','Модель вузла труб за схемою "3 -> 1".','Модель узла труб по схеме "3 -> 1".',1,10,0,'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));',1556609878);
INSERT INTO techApp VALUES('pipeBase','Pipe-base','Труба-база','Труба-база','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.','Реалізація базових основ моделі труби:
- витрати у трубі з урахуванням: швидкості руху, різниці тиску, опору за рахунок тертя та критичної течі;
- розрахунок тиску;
- урахування щільності середовища та ступеня його стиснення як для газів, так і рідин.','Реализация базовых основ модели трубы:
- расход в трубе с учётом: скорости движения, перепада давления, сопротивления за счёт трения и критического истечения;
- расчёт давления;
- учёт плотности среды и степени её сжимаемости как для газов, так и для жидкостей.',1,10,0,'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)));',1556609878);
INSERT INTO techApp VALUES('heatExch','Heat exchanger','Теплообмінник','Теплообменник','Model of the heat exchanger, calculating the heat exchange of the two streams.','Модель теплообміннику, що розраховує теплообмін двох потоків.','Модель теплообменника, рассчитывающая теплообмен двух потоков.',1,10,0,'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)));',1556609878);
INSERT INTO techApp VALUES('boilerBarrel','Boiler: barrel','Котел: барабан','Котёл: барабан','The model of the boiler''s barrel. ','Модель барабану котлоагрегату.','Модель барабана котлоагрегата.',1,10,0,'// 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);',1556609878);
INSERT INTO techApp VALUES('boilerBurner','Boiler: burner','Котел: топка','Котёл: топка','The fire-chamber model of the boiler unit, which operates on three types of fuel, initially is: blast-furnace, coke and natural gases.','Модель топки котлоагрегату, який працює на трьох видах палива, початково це: доменний, коксовий та природний гази.','Модель топки котлоагрегата, работающего на трех видах топлива, исходно это: доменный, коксовый и природный газы.',1,10,0,'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)));',1556609878);
INSERT INTO techApp VALUES('pipe2_1','Pipe 2->1','Труба 2->1','Труба 2->1','Model of the pipe by the scheme "2 -> 1".','Модель вузла труб за схемою "2 -> 1".','Модель узла труб по схеме "2 -> 1".',1,10,0,'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));',1556609878);
CREATE TABLE IF NOT EXISTS 'flb_web' ("ID" TEXT DEFAULT '' ,"NAME" TEXT DEFAULT '' ,"ru#NAME" TEXT DEFAULT '' ,"uk#NAME" TEXT DEFAULT '' ,"DESCR" TEXT DEFAULT '' ,"START" INTEGER DEFAULT '1' ,"MAXCALCTM" INTEGER DEFAULT '10' ,"PR_TR" INTEGER DEFAULT '0' ,"FORMULA" TEXT DEFAULT '' ,"TIMESTAMP" INTEGER DEFAULT '0' , PRIMARY KEY ("ID"));
INSERT INTO flb_web VALUES('alarms','Alarms','Сигнализации','Сигналізації','',1,10,0,'//> 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();',1377261539);
INSERT INTO flb_web VALUES('devMon','Devices monitor','Монитор устройств','Монітор пристроїв','',1,10,0,'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]));',1377260842);
INSERT INTO flb_web VALUES('devices','Devices dispatcher','Диспетчер устройств','Диспетчер пристроїв','',1,10,0,'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]));',1377260944);
INSERT INTO flb_web VALUES('mess','Alarm messages','Сообщения сигнализаций','Повідомлення сигналізацій','',1,10,0,'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]));',1377261138);
INSERT INTO flb_web VALUES('report','Reports','Отчёты','Звіти','',1,10,0,'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]));',1377261196);
INSERT INTO flb_web VALUES('test','Test','Тест','Тест','',1,10,0,'if(f_start)
{
	var req = SYS.XMLNode("send").setAttr("ProtIt","SMS").setAttr("tel","+380679859815").setText("Тестовое сообщение");
	SYS.Transport.Serial.out_GSM.messIO(req,"UserProtocol");
}',1377261398);
INSERT INTO flb_web VALUES('timerWrite','Write by the timer','Запись по таймеру','Запис за таймером','',1,10,0,'//> 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();',1377261462);
INSERT INTO flb_web VALUES('user','Users dispatcher','Диспетчер пользователей','Диспетчер користувачів','',1,10,0,'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]));',1377261537);
COMMIT;
