# lib_Controllers

ID: prescr
NAME: Prescriptions manager (moved)
DESCR:
  !!!!: 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

FORMULA:
  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 by user session of the program")+" \""+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 by error session of the program")+" \""+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 by user session of the program")+" \""+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 by error session of the program")+" \""+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;


ID: test
NAME: test
ru#NAME: test
DESCR:
  Different tests of the JavaLikeCalc language for execution into the controller mode.

  Author: Roman Savochenko
  Version: 1.0.0

FORMULA:
  using Special.FLibSYS;

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

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

  //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);
  }*/


ID: test1
NAME: test1
uk#NAME: test1
DESCR:
  Different tests of the JavaLikeCalc language for execution into the controller mode.

  Author: Roman Savochenko
  Version: 1.0.0

FORMULA:
  //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;


ID: ntfDispatch
NAME: Notifications dispatcher (replaced)
DESCR:
  !!!!: 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

FORMULA:
  //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);
  }


# lib_servProc

ID: procArh
NAME: Archives recalculation
uk#NAME: Перерахунок архівів
ru#NAME: Пересчёт архивов
DESCR:
  Value archives recalculation.

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

FORMULA:
  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;
  }


ID: crc16
NAME: CRC 16
DESCR:
  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

FORMULA:
  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;
  }


ID: releaseTests
NAME: TEST: Release
uk#NAME: Тести випуску
ru#NAME: Тесты выпуска
DESCR:
  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

FORMULA:
  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>");


ID: archPackFStests
NAME: TEST: Packing of the archiver on FS
DESCR:
  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

FORMULA:
  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);
  }


ID: wacko2media
NAME: EXT: WackoWiki to MediaWiki
DESCR:
  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

FORMULA:
  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;


ID: docOffLine
NAME: EXT: Off-line documentation
DESCR:
  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.0.0

FORMULA:
  trNm = "offLine";
  docHost = "oscada.org:80";
  docHost_ = "http://" + docHost.parse(0, ":");
  defLang = "en";

  //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
  for(var ip in pgsOprc) {
  	pLang = ip.lastIndexOf("/"); pWiki = ip.slice(0, pLang); pLang = ip.slice(pLang+1);
  	pOffLn = pgsO[ip];

  	// 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
  	for(iTr = 0; iTr < reqTr; iTr++)
  		if(!(rez=tr.messIO(req,"HTTP")).length && req.text().length) break;
  	//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"; return; }

  	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);

  	// Images processing
  	imgs = body.getElementsBy("img");
  	for(iEl = 0; iEl < imgs.length; 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"));
  				for(iTr = 0; iTr < reqTr; iTr++)
  					if(!(rez=tr.messIO(req,"HTTP")).length && req.text().length) break;
  				//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"; return; }
  				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]);
  		}

  	// Links processing
  	anchs = body.getElementsBy("a");
  	for(iEl = 0; iEl < anchs.length; iEl++) {
  		href = anchs[iEl].attr("href");
  		if(href[0] == "/") {
  			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);
  			else if(dynLngPg.length && !(tDst=pgsO[dynLngPg+"/"+defLang]).isEVal())
  					anchs[iEl].setAttr("href", topDir+"../"+defLang+"/"+tDst);
  			else {
  				anchs[iEl].setAttr("href", docHost_+href);
  				if(!anchs[iEl].attr("class").length)	anchs[iEl].setAttr("class", "external");
  			}
  			lnkCnt++;
  		}
  	}

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

  res = "0: Fetched and processed pages="+pCnt+"; images="+imgCnt+"; links="+lnkCnt+"; languages="+lngCnt;


# flb_regEl

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

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

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

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

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

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

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

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

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

  if(!auto) manIn = 50;

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

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


ID: pidUnifD
NAME: PID dynamic
uk#NAME: ПІД динамічний
ru#NAME: ПИД динамический
DESCR:
  Completely identical to the unified PID regulator 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

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

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

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

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

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

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

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

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

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

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

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

  if(analog) return outA;

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  if(!auto) manIn = 50;

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

  if(impQup || impQdwn) integ = 0;

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


# flb_doc

ID: getVal
NAME: Getting value from archive
uk#NAME: Запит значення архіву
ru#NAME: Запрос знач. архива
DESCR:
  Query the value for a specified time from the assigned archive and issuing the result with the specified number of decimal points.
  Author: Roman Savochenko <roman@oscada.org>
  Version: 1.1.1

uk#DESCR:
  Запит значення на визначений час з вказаного архіву та видача результату з вказаною кількістю знаків після точки.
  Автор: Роман Савоченко <roman@oscada.org>
  Версія: 1.1.1

ru#DESCR:
  Запрос значения на указанное время из установленного архива и выдача результата с указанным количеством знаков после точки.
  Автор: Роман Савоченко <roman@oscada.org>
  Версия: 1.1.1

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


ID: sumVal
NAME: Sum values of archive
uk#NAME: Сума значень архіву
ru#NAME: Сумма знач. архива
DESCR:
  Calculating the sum of values ​​for a specified period of time an archive and issuing results with the specified number of decimal points.
  Author: Roman Savochenko <roman@oscada.org>
  Version: 1.1.0

uk#DESCR:
  Обчислення суми значень за проміжок часу визначеного архіву та видача результату з вказаною кількістю знаків після точки.
  Автор: Роман Савоченко <roman@oscada.org>
  Версія: 1.1.0

ru#DESCR:
  Вычисление суммы значений за промежуток времени указанного архива и выдача результата с указанным количеством знаков после точки.
  Автор: Роман Савоченко <roman@oscada.org>
  Версия: 1.1.0

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


ID: averVal
NAME: Average value of archive
uk#NAME: Середнє значення архіву
ru#NAME: Среднее знач. архива
DESCR:
  Calculate the average value over a specified period of time an archive and issuing results with the specified number of decimal points.
  Author: Roman Savochenko <roman@oscada.org>
  Version: 1.1.0

uk#DESCR:
  Обчислення середнього значення за проміжок часу визначеного архіву та видача результату з вказаною кількістю знаків після точки.
  Автор: Роман Савоченко <roman@oscada.org>
  Версія: 1.1.0

ru#DESCR:
  Вычисление среднего значения за промежуток времени указанного архива и выдача результата с указанным количеством знаков после точки.
  Автор: Роман Савоченко <roman@oscada.org>
  Версия: 1.1.0

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


# tmplib_LowDevLib

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

PROGRAM:
  JavaLikeCalc.JavaScript
  using Special.FLibSYS;

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

  t_err = "0";

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

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

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

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

  f_err = t_err;


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

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

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

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

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

  	return rez;
  }

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

  t_err = "0";

  //Check for the transport change and connect
  if(!tr || transport != transport_)	{
  	tr = SYS.Transport.Serial.nodeAt("out_"+transport);
  	transport_ = transport;
  }
  if(!tr)	t_err = "1:"+tr("Output transport '%1' error.").replace("%1",transport);
  else {
  	//Generic information update.
  	if(f_start || (tmResc && (tmResc_=tmResc_-1/f_frq) <= 0)) {
  		tmResc_ = tmResc;
  		tmSc = SYS.time();
  		// Check for power
  		if(!reset(tr) ||
  		 	io(tr,(req=SYS.strFromCharCode(0xCC))) != req ||
  			io(tr,(req=SYS.strFromCharCode(0xB4))) != req ||
  			!(resp=io(tr,SYS.strFromCharCode(1),1)).length) power = EVAL;
  		else power = resp.charCodeAt(0);
  		// Scan for allowed devices on the bus.
  		sn = new Array(0, 0, 0, 0, 0, 0, 0, 0);
  		for(devID = "start", lstDiscr = -1; devID.length && lstDiscr; ) {
  			if(lstDiscr < 0) lstDiscr = 0;
  			if((devID=scan(tr,sn,lstDiscr)).length)	{
  				//SYS.messInfo("OneWire","devID="+SYS.strDecode(devID,"Bin"," "));
  				if((dO=devLs[devID]).isEVal()) {
  					devLs[devID] = dO = new Object();
  					devFamily = devID.charCodeAt(0); 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);
  			if(dO.isT) {	//Temperature
  				dP = dO.dP;
  				//SYS.messInfo("OneWire","devID="+SYS.strDecode(devID,"Bin"," "));
  				//Check for a modifications
  				isMdf = false;
  				m_ = dP.m.get(), res_ = max(9,min(12,dP.res.get()));
  				if(!dO.m.isEVal() && !dO.res.isEVal() && (m_ != dO.m || res_ != dO.res)) {
  					//Set scratchpad
  					if(!reset(tr)) continue;
  					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
  					if(io(tr,(req=SYS.strFromCharCode(0x4E,m_&0xFF,(m_>>8)&0xFF,((res_-9)<<5)|0x1F))) != req) continue;
  					//Copy scratchpad part to the EEPROM
  					if(!reset(tr)) continue;
  					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
  					if(io(tr,(req=SYS.strFromCharCode(0x48))) != req) continue;
  					isMdf = true;
  				}

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

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

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

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

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

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

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

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

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

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

  f_err = t_err;


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

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

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

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

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

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

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

  	return rez;
  }

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

  t_err = "0";

  //Check for the transport change and connect
  if(!tr || transport != transport_)	{
  	tr = SYS.Transport.Serial.nodeAt("out_"+transport);
  	transport_ = transport;
  	DS2480 = false;
  }
  //Try DS2480
  if(tr && !DS2480)	{
  	tr.addr(tr.addr().parse(0,":")+":9600:8N1");	tr.timings("500:20");
  	if(tr.start(true)) {
  		tr.sendbreak(); SYS.sleep(2e-3);
  		tr.messIO(SYS.strFromCharCode(0xC1));	SYS.sleep(4e-3);	//Send timing byte
  		//PDSRC=1.37Vus; W1LT=10us; DSO/WORT=8us;
  		//read the baud rate (to test command block); do 1 bit operation (to test 1-Wire block)
  		req = SYS.strFromCharCode(0x17, 0x45, 0x5B, 0x0F, 0x91);
  		for(resp = tr.messIO(req); resp.length && resp.length < 5 && (tresp=tr.messIO("")).length; resp += tresp) ;
  		if(resp.length == 5 && (resp.charCodeAt(3)&0xF1) == 0x00 && (resp.charCodeAt(3)&0x0E) == 0x00/*9600*/ &&
  				(resp.charCodeAt(4)&0xF0) == 0x90 && (resp.charCodeAt(4)&0x0C) == 0x00/*9600*/)
  			DS2480 = true;
  	}
  	isData = false;
  }
  if(!tr)	t_err = "1:"+tr("Output transport '%1' error.").replace("%1", transport);
  else if(!DS2480)	t_err = "2:"+tr("DS2480 is not detected.");
  else {
  	//Generic information update.
  	if(f_start || (tmResc && (tmResc_=tmResc_-1/f_frq) <= 0)) {
  		tmResc_ = tmResc;
  		tmSc = SYS.time();
  		// Check for power
  		if(!reset(tr) ||
  		 	io(tr,(req=SYS.strFromCharCode(0xCC))) != req ||
  			io(tr,(req=SYS.strFromCharCode(0xB4))) != req ||
  			!(resp=io(tr,SYS.strFromCharCode(1),1)).length) power = EVAL;
  		else power = resp.charCodeAt(0);
  		// Scan for allowed devices on the bus.
  		sn = new Array(0, 0, 0, 0, 0, 0, 0, 0);
  		for(devID = "start", lstDiscr = 0; devID.length && lstDiscr >= 0; ) {
  			if((devID=scan(tr,sn,lstDiscr)).length)	{
  				//SYS.messInfo("OneWire","lstDiscr="+lstDiscr+"; devID="+SYS.strDecode(devID,"Bin"," "));
  				if((dO=devLs[devID]).isEVal()) {
  					devLs[devID] = dO = new Object();
  					devFamily = devID.charCodeAt(0); 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);
  			if(dO.isT) {	//Temperature
  				dP = dO.dP;
  				//SYS.messInfo("OneWire","devID="+SYS.strDecode(devID,"Bin"," "));
  				//Check for a modifications
  				isMdf = false;
  				m_ = dP.m.get(), res_ = max(9,min(12,dP.res.get()));
  				if(!dO.m.isEVal() && !dO.res.isEVal() && (m_ != dO.m || res_ != dO.res)) {
  					//Set scratchpad
  					if(!reset(tr)) continue;
  					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
  					if(io(tr,(req=SYS.strFromCharCode(0x4E,m_&0xFF,(m_>>8)&0xFF,((res_-9)<<5)|0x1F))) != req) continue;
  					//Copy scratchpad part to the EEPROM
  					if(!reset(tr)) continue;
  					if(io(tr,(req=SYS.strFromCharCode(0x55)+devID)) != req) continue;
  					if(io(tr,(req=SYS.strFromCharCode(0x48))) != req) continue;
  					isMdf = true;
  				}

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

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

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

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

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

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

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

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

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

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

  f_err = t_err;


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

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

  t_err = "0";

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

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

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


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

PROGRAM:
  JavaLikeCalc.JavaScript
  using Special.FLibSYS;

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

  t_err = "0";

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

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

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

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

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

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

  	return vl;
  }

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

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


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

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

  t_err = "0";

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

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

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

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

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

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

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


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

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

  t_err = "0";

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

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

  f_err = t_err;


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

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

  t_err = "0";

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

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

  f_err = t_err;


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

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

  t_err = "0";

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

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

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

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

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

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

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

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

  f_err = t_err;


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

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

  t_err = "0";

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

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

  f_err = t_err;


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

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

  t_err = "0";

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

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

  f_err = t_err;


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

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

  t_err = "0";

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

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

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

  f_err = t_err;


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

PROGRAM:
  JavaLikeCalc.JavaScript
  using Special.FLibSYS;

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

  t_err = "0";

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

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

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

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

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

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


# tmplib_tests

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


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

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


# UserProtocol_uPrt

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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


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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

  return true;

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

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


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

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

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


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

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

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


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

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

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

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


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

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

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

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

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

  auth = io.attr("auth");

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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


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

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

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


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

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

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

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

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


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

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

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


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

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


