CREATE TABLE IF NOT EXISTS 'tmplib_DevLib' ("ID" TEXT DEFAULT '' ,"NAME" TEXT DEFAULT '' ,"uk#NAME" TEXT DEFAULT '' ,"ru#NAME" TEXT DEFAULT '' ,"DESCR" TEXT DEFAULT '' ,"uk#DESCR" TEXT DEFAULT '' ,"ru#DESCR" TEXT DEFAULT '' ,"MAXCALCTM" INTEGER DEFAULT '10' ,"PR_TR" INTEGER DEFAULT '1' ,"PROGRAM" TEXT DEFAULT '' ,"uk#PROGRAM" TEXT DEFAULT '' ,"ru#PROGRAM" TEXT DEFAULT '' ,"TIMESTAMP" INTEGER DEFAULT '' , PRIMARY KEY ("ID"));
INSERT INTO tmplib_DevLib VALUES('SCU750','EDWARDS TURBOMOLECULAR PUMPS','','','Typical EDWARDS TURBOMOLECULAR PUMPS (http://edwardsvacuum.com) data acquisition 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
License: GPLv2','','',10,0,'JavaLikeCalc.JavaScript
if(f_start) {
	version = "";
	operModes = new Object();
	operModes[1] = tr("Levitation");
	operModes[2] = tr("No Levitation");
	operModes[3] = tr("Acceleration");
	operModes[4] = tr("Normal");
	operModes[5] = tr("Deceleration (Brake)");
	operModes[6] = tr("Autotest");
	operModes[7] = tr("Tuning");
	operModes[8] = tr("Tuning Complete");
}

tr = SYS.Transport.Serial.nodeAt("out_"+transport);
req = SYS.XMLNode("mess").setAttr("ProtIt","SCU750").setAttr("addr",addr);

//======= Commands =========
//START, STOP and RESET process
if(comReset == true) req.setText(" E04");
else if(comStop == true) req.setText(" E02");
else if(comStart == true) req.setText(" E01");
else req.setText("");
if(req.text().length) { tr.messIO(req,"UserProtocol"); comReset=comStop=comStart=false; }

//Speed setpoint set
if(spSpeed != spSpeedPrev) {
	req.setText(" h"+spSpeed.toString(16,4));
	tr.messIO(req,"UserProtocol");
	spSpeedPrev = spSpeed;
}

//======= Data getting (by self bigger period) ========
if((cnt++)%max(1,perGet*f_frq))	return;
t_err = "";

//Read version, single
if(!version.length || version.isEVal()) {
	req.setText("?V");
	tr.messIO(req,"UserProtocol");
	if(req.attr("err").length) { t_err = req.attr("err"); version = EVAL_STR; }
	else {
		if(req.text().length != 42) version = EVAL_STR;
		else {
			CUSv = "";
			lstNoSp = 0;
			for(i_s = 2; i_s < 32; i_s+=2) {
				cod = req.text().slice(i_s,i_s+2).toInt(16);
				CUSv += SYS.strFromCharCode(cod);
				if(cod != 0x20) lstNoSp = i_s/2;
			}
			version = "Contr. Unit Soft V.="+CUSv.slice(0,lstNoSp)+"; "
					    "Motor Drive Soft V.="+req.text().charAt(35)+"."+req.text().charAt(36)+"; "
					    "DSP Soft V.="+req.text().charAt(39)+"."+req.text().charAt(40);
		}
	}
}

//Read rotational speed.
if(t_err.length) tTMS = tMotor = rotSpeed = EVAL_INT;
else {
	req.setText("?[");
	tr.messIO(req,"UserProtocol");
	if(req.attr("err").length) { t_err = req.attr("err"); tTMS = tMotor = rotSpeed = EVAL_INT; }
	else {
		tTMS = req.text().slice(32,36).toInt(16);
		tMotor = req.text().slice(36,40).toInt(16);
		rotSpeed = req.text().slice(50,54).toInt(16);
	}
}

//Pump operation mode and errors read
if(t_err.length) { operMode = EVAL_INT; errors = EVAL_STR; }
else {
	req.setText("?M");
	tr.messIO(req,"UserProtocol");
	if(req.attr("err").length) { t_err = req.attr("err"); operMode = EVAL_INT; errors = EVAL_STR; }
	else {
		oMode = req.text().slice(2,4).toInt(16);
		operMode = oMode.toString()+":"+(operModes[oMode].isEVal()?tr("Unknown"):operModes[oMode]);
		//SYS.messDebug("CNTR","pumpOperMode: "+pumpOperMode);
		
		errCnt = req.text().slice(4,6).toInt(16);
		errors = "";
		for(i_e = 0; i_e < errCnt; i_e++)
			errors += req.text().slice(6+i_e*2,8+i_e*2)+"; ";
	}
}

//Events read
if(t_err.length)	events = EVAL_STR;
else {
	req.setText("?g");
	tr.messIO(req,"UserProtocol");
	if(req.attr("err").length) { t_err = req.attr("err"); events = EVAL_STR; }
	else {
		errCnt = req.text().slice(2,4).toInt(16);
		events = "";
		for(i_e = 0; i_e < errCnt; i_e++)
			events += req.text().slice(4+i_e*2,6+i_e*2)+"; ";
	}
}

//Counters
if(t_err.length) { snCntrUnit = snPump = EVAL_STR; cntPumpHour = cntCntrUnitHour = cntStart = EVAL_REAL; }
else {
	req.setText("?c");
	tr.messIO(req,"UserProtocol");
	if(req.attr("err").length) { t_err = req.attr("err"); snCntrUnit = snPump = EVAL_STR; cntPumpHour = cntCntrUnitHour = cntStart = EVAL_REAL; }
	else {
		snCntrUnit = req.text().slice(2,12);
		snPump = req.text().slice(12,22);
		cntPumpHour = req.text().slice(22,30).toInt(16);
		cntCntrUnitHour = req.text().slice(30,38).toInt(16);
		cntStart = req.text().slice(38,46).toInt(16);
	}
}

//Read setpoints
if(t_err.length)	spSpeed = spSpeedPrev = spTMSt = EVAL_INT;
else {
	req.setText("?d");
	tr.messIO(req,"UserProtocol");
	if(req.attr("err").length) { t_err = req.attr("err"); spSpeed = spSpeedPrev = spTMSt = EVAL_INT; }
	else {
		spSpeed = spSpeedPrev = req.text().slice(2,6).toInt(16);
		spTMSt = req.text().slice(6,10).toInt(16);
	}
}

f_err = t_err;','','',1580047532);
INSERT INTO tmplib_DevLib VALUES('TMH','Power supply for turbomolecular pumps','','','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
License: GPLv2','','',10,0,'JavaLikeCalc.JavaScript
tr = SYS.Transport.Serial.nodeAt("out_"+transport);
req = SYS.XMLNode("mess").setAttr("ProtIt","TMH").setAttr("addr",addr);

if(f_start) {
	modelID = "";
	operModes = new Object();
	//Modes
	operModes["NS"] = "1:"+tr("Levitation");
	operModes["NA"] = "3:"+tr("Acceleration");
	operModes["NN"] = "4:"+tr("Normal");
	operModes["NB"] = "5:"+tr("Deceleration");
	operModes["FS"] = "6:"+tr("Stop operation failure");
	operModes["FF"] = "7:"+tr("Idling failure");
	operModes["FR"] = "8:"+tr("Regeneration breaking failure");
	operModes["FB"] = "9:"+tr("Deceleration failure");
	//Conrols
	operModes["LL"] = tr("Local");
	operModes["LR"] = tr("Remote");
	operModes["LC"] = "RS-232C";
	operModes["LD"] = "RS-485";

	//Set to Interactive
	req.setText("LN");
	tr.messIO(req,"UserProtocol");
}

//======= Commands =========
//START, STOP and RESET process
if(comReset == true)			req.setText("RR");
else if(comStop == true)		req.setText("RP");
else if(comStart == true)	req.setText("RT");
else req.setText("");
if(req.text().length)	{ tr.messIO(req,"UserProtocol"); comReset = comStop = comStart = false; }

//Interactive, Autonomous
if(comInteract == true)		req.setText("LN");
else if(comAutonom == true)	req.setText("LF");
else req.setText("");
if(req.text().length)	{ tr.messIO(req,"UserProtocol"); comInteract = comAutonom = false; }

//======= Data getting (by self bigger period) ========
if((cnt++)%max(1,perGet*f_frq))	return;
t_err = "0";

//Oper mode
req.setText("CS");
tr.messIO(req,"UserProtocol");
if(req.attr("err").length) { t_err = req.attr("err"); operMode = EVAL_STR; }
else operMode = /*req.text().slice(2)+":"+*/operModes[req.text().slice(0,2)];

//Oper control
if(t_err.toInt())	operCntr = EVAL_STR;
else {
	req.setText("LS");
	tr.messIO(req,"UserProtocol");
	if(req.attr("err").length) { t_err = req.attr("err"); operCntr = EVAL_STR; }
	else operCntr = operModes[req.text().slice(0,2)];
}

//Alarms list obtain
if(t_err.toInt())	errors = EVAL_STR;
else {
	errors = "";
	for(i = 0; true; i++) {
		req.setText("CF"+i.toString(10,2));
		tr.messIO(req,"UserProtocol");
		if(req.attr("err").length) { t_err = req.attr("err"); errors = EVAL_STR; break; }
		//SYS.messDebug("TMH","Result: "+req.text());
		if(req.text().slice(0,2) != "CA")	break;
		errors += req.text().slice(2)+";";
		continue;
	}
}

//Get parameters
// Model ID
if(t_err.toInt())	modelID = EVAL_STR;
else if(!modelID.length || modelID == EVAL_STR) {
	req.setText("PR01");
	tr.messIO(req,"UserProtocol");
	if(req.attr("err").length) { t_err = req.attr("err"); modelID = EVAL_STR;}
	else if(req.text().slice(0,2) != "PA") modelID = EVAL_STR;
	else modelID = "TMP-"+req.text().slice(4);
}

// Rotation speed
if(t_err.toInt())	rotSpeed = EVAL_INT;
else {
	req.setText("PR03");
	tr.messIO(req,"UserProtocol");
	if(req.attr("err").length) { t_err = req.attr("err"); rotSpeed = EVAL_INT;}
	else if(req.text().slice(0,2) != "PA") rotSpeed = EVAL_INT;
	else rotSpeed = req.text().slice(4).toInt()*10;
}

// Current
if(t_err.toInt())	I = EVAL_REAL;
else {
	req.setText("PR04");
	tr.messIO(req,"UserProtocol");
	if(req.attr("err").length) { t_err = req.attr("err"); I = EVAL_REAL;}
	else if(req.text().slice(0,2) != "PA") I = EVAL_REAL;
	else I = req.text().slice(4).toInt()/10;
}

// Axle 1 disbalance
if(t_err.toInt())	axle1disb = EVAL_INT;
else {
	req.setText("PR21");
	tr.messIO(req,"UserProtocol");
	if(req.attr("err").length) { t_err = req.attr("err"); axle1disb = EVAL_INT;}
	else if(req.text().slice(0,2) != "PA") axle1disb = EVAL_INT;
	else axle1disb = req.text().slice(4).toInt();
}

// Axle 2 disbalance
if(t_err.toInt())	axle2disb = EVAL_INT;
else {
	req.setText("PR22");
	tr.messIO(req,"UserProtocol");
	if(req.attr("err").length) { t_err = req.attr("err"); axle2disb = EVAL_INT;}
	else if(req.text().slice(0,2) != "PA") axle2disb = EVAL_INT;
	else axle2disb = req.text().slice(4).toInt();
}

// MP X1
if(t_err.toInt())	MP_X1 = EVAL_INT;
else {
	req.setText("PR26");
	tr.messIO(req,"UserProtocol");
	if(req.attr("err").length) { t_err = req.attr("err"); MP_X1 = EVAL_INT;}
	else if(req.text().slice(0,2) != "PA") MP_X1 = EVAL_INT;
	else MP_X1 = req.text().slice(4).toInt();
}

// MP Y1
if(t_err.toInt())	MP_Y1 = EVAL_INT;
else {
	req.setText("PR27");
	tr.messIO(req,"UserProtocol");
	if(req.attr("err").length) { t_err = req.attr("err"); MP_Y1 = EVAL_INT;}
	else if(req.text().slice(0,2) != "PA") MP_Y1 = EVAL_INT;
	else MP_Y1 = req.text().slice(4).toInt();
}

// MP X2
if(t_err.toInt())	MP_X2 = EVAL_INT;
else {
	req.setText("PR28");
	tr.messIO(req,"UserProtocol");
	if(req.attr("err").length) { t_err = req.attr("err"); MP_X2 = EVAL_INT;}
	else if(req.text().slice(0,2) != "PA") MP_X2 = EVAL_INT;
	else MP_X2 = req.text().slice(4).toInt();
}

// MP Y2
if(t_err.toInt())	MP_Y2 = EVAL_INT;
else {
	req.setText("PR29");
	tr.messIO(req,"UserProtocol");
	if(req.attr("err").length) { t_err = req.attr("err"); MP_Y2 = EVAL_INT;}
	else if(req.text().slice(0,2) != "PA") MP_Y2 = EVAL_INT;
	else MP_Y2 = req.text().slice(4).toInt();
}

// MP Z
if(t_err.toInt())	MP_Z = EVAL_INT;
else {
	req.setText("PR30");
	tr.messIO(req,"UserProtocol");
	if(req.attr("err").length) { t_err = req.attr("err"); MP_Z = EVAL_INT;}
	else if(req.text().slice(0,2) != "PA") MP_Z = EVAL_INT;
	else MP_Z = req.text().slice(4).toInt();
}

f_err = t_err;','','',1580047532);
INSERT INTO tmplib_DevLib VALUES('TM510x','Elemer TM510x','','','Multi-channels thermometer Elemer TM5102 and TM5103 of the firm Elemer (http://www.elemer.ru).

Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.0
License: GPLv2','','',10,0,'JavaLikeCalc.JavaScript
if(f_start) devTp = EVAL_INT;

tr = SYS.Transport.Serial.nodeAt("out_"+transport);
req = SYS.XMLNode("mess").setAttr("ProtIt", "TM510x").setAttr("addr", addr);

t_err = "";

//Device type get
if(devTp.isEVal() || !devTp) {
	req.setText("0");
	tr.messIO(req, "UserProtocol");
	if(req.attr("err").length)	t_err = req.attr("err");
	else devTp = req.text().toInt();
}

//Inputs read
errors = "";
if(t_err.length) in1 = in2 = in3 = in4 = in5 = in6 = in7 = in8 = EVAL_REAL;
else {
	req.setText("6;0;8");
	tr.messIO(req,"UserProtocol");
	if(req.attr("err").length)
	{ t_err = req.attr("err"); in1 = in2 = in3 = in4 = in5 = in6 = in7 = in8 = EVAL_REAL; }
	else {
		off = 0;
		tvl = "";
		in1 = ((tvl=req.text().parse(0, ";", off))[0] == "$") ? EVAL_INT : tvl.toReal();
		errors += ((tvl[0] == "$") ? tvl.slice(1) : "0")+";";
		in2 = ((tvl=req.text().parse(0, ";", off))[0] == "$") ? EVAL_INT : tvl.toReal();
		errors += ((tvl[0] == "$") ? tvl.slice(1) : "0")+";";
		in3 = ((tvl=req.text().parse(0, ";", off))[0] == "$") ? EVAL_INT : tvl.toReal();
		errors += ((tvl[0] == "$") ? tvl.slice(1) : "0")+";";
		in4 = ((tvl=req.text().parse(0, ";", off))[0] == "$") ? EVAL_INT : tvl.toReal();
		errors += ((tvl[0] == "$") ? tvl.slice(1) : "0")+";";
		in5 = ((tvl=req.text().parse(0, ";", off))[0] == "$") ? EVAL_INT : tvl.toReal();
		errors += ((tvl[0] == "$") ? tvl.slice(1) : "0")+";";
		in6 = ((tvl=req.text().parse(0, ";", off))[0] == "$") ? EVAL_INT : tvl.toReal();
		errors += ((tvl[0] == "$") ? tvl.slice(1) : "0")+";";
		in7 = ((tvl=req.text().parse(0, ";", off))[0] == "$") ? EVAL_INT : tvl.toReal();
		errors += ((tvl[0] == "$") ? tvl.slice(1) : "0")+";";
		in8 = ((tvl=req.text().parse(0, ";", off))[0] == "$") ? EVAL_INT : tvl.toReal();
		errors += ((tvl[0] == "$") ? tvl.slice(1) : "0")+";";
	}
}

f_err = t_err;','','',1580047532);
INSERT INTO tmplib_DevLib VALUES('UPS','UPS (moved)','','','!!!!: Moved to the template library Main. Will be removed soon
Uninterruptible power supply unifying data for provide all the data into single attribute of object type "All items" for next the control as the object with the data provide as table, alarming and allowing set writable attributes. The template aimed for using with module "System" data source as "UPS" and widget "Main.objProps" as the data presenter. The template also you can use as example for create like other data unification as complex object with properties, alarming and writing.

Author: Roman Savochenko <roman@oscada.org>
Version: 1.2.1','','',10,0,'JavaLikeCalc.JavaScript
if(f_start)	{ srcPrm = false; items = new Object(); conDelay_ = 0; }

alLev = 0;
tErr = "";

//Connect to source
if(typeof(srcPrm) != "TCntrNode:TValue:TParamContr") srcPrm = SYS.DAQ.nodeAt(srcAddr,".");
if(!srcPrm) { tErr = tr("No connection to source object"); alLev = 3; }
else if(srcPrm.err.get() != 0)	 { tErr = tr("Source error")+": "+srcPrm.err.get().parse(1,":"); alLev = 3; }
else {
	//Attributes list get and "items" update
	nLst = srcPrm.nodeList("a_");
	for(i_n = 0; i_n < nLst.length; i_n++) {
		aId = nLst[i_n].slice(2);
		aNd = srcPrm[nLst[i_n]];
		if(items[aId].isEVal()) {
			items[aId] = itW = new Object();
			itW.descr = aNd.descr();

			// Writeable check
			//SYS.messInfo("UPS test", aId+": aNd.flg()="+aNd.flg());
			if((itW.wr=!(aNd.flg()&0x04)) && aNd.flg()&0x01) {
				itW.wr = "";
				for(off = 0, pos = 0; (selId=aNd.values().parse(0,";",off)).length; pos++)
					itW.wr += ((selId==(selNm=aNd.selNames().parse(pos,";")))?selId:(selNm+" ("+selId+")"))+";";
			}
			itW.alarm = 0;
		}
		items[aId].val = aNd.get();
	}

	//Alarms process and mark
	varS = "ups_status";
	if(!(tP=srcPrm[varS]).isEVal() && !(tVl=tP.get()).isEVal()) {
		if(tVl == "OB")	{ items[varS].alarm = 1; tErr += tr("Status")+" \""+tr("On battery")+"\"; "; }
		else if(tVl == "LB")	{ items[varS].alarm = 2; tErr += tr("Status")+" \""+tr("Low battery")+"\"; "; }
		else if(tVl == "SD")	{ items[varS].alarm = 2; tErr += tr("Status")+" \""+tr("Shutdown load")+"\"; "; }
		else if(tVl.indexOf("ALARM") != -1)	{ items[varS].alarm = 2; tErr += tr("Status")+" \""+tr("ALARM")+"\"; "; }
		else items[varS].alarm = 0;
	}
	varS = "battery_packs";
	if(!(tP=srcPrm[varS]).isEVal() && !(tVl=tP.get()).isEVal()) {
		if(tVl.toInt() == 0)	{ items[varS].alarm = 2; tErr += tr("None of good battery present")+"; "; }
		else items[varS].alarm = 0;
	}
	varS = "battery_charge";
	if(!(tP=srcPrm[varS]).isEVal() && !(tVl=tP.get()).isEVal()) {
		if(tVl.toReal() < bChL) { items[varS].alarm = 1; tErr += tr("Battery charge low")+"; "; }
		else if(tVl.toReal() < bChLL) { items[varS].alarm = 2; tErr += tr("Battery charge critical")+"; "; }
		else items[varS].alarm = 0;
	}
	varS = "battery_packs_bad";
	if(!(tP=srcPrm[varS]).isEVal() && !(tVl=tP.get()).isEVal()) {
		if(tVl.toInt())	{ items[varS].alarm = 1; tErr += tr("Bad %1 batteries present").replace("%1",tVl); }
		else items[varS].alarm = 0;
	}
	varS = "input_voltage";
	if(!(tP=srcPrm[varS]).isEVal() && !(tVl=tP.get()).isEVal()) {
		if(tVl.toReal() > 10 && tVl.toReal() < inVL) { items[varS].alarm = 1; tErr += tr("Input voltage low")+"; "; }
		else if(tVl.toReal() > inVH) { items[varS].alarm = 1; tErr += tr("Input voltage high")+"; "; }
		else items[varS].alarm = 0;
	}
	varS = "input_frequency";
	if(!(tP=srcPrm[varS]).isEVal() && !(tVl=tP.get()).isEVal()) {
		if(tVl.toReal() < inFLL) { items[varS].alarm = 2; tErr += tr("Input frequency too low")+"; "; }
		else if(tVl.toReal() > inFHH)	{ items[varS].alarm = 2; tErr += tr("Input frequency too high")+"; "; }
		else if(tVl.toReal() < inFL) { items[varS].alarm = 1; tErr += tr("Input frequency low")+"; "; }
		else if(tVl.toReal() > inFH) { items[varS].alarm = 1; tErr += tr("Input frequency high")+"; "; }
		else items[varS].alarm = 0;
	}
	varS = "ups_load";
	if(!(tP=srcPrm[varS]).isEVal() && !(tVl=tP.get()).isEVal()) {
		if(tVl.toReal() > loadHH)		{ items[varS].alarm = 2; tErr += tr("UPS overloaded")+"; "; }
		else if(tVl.toReal() > loadH)	{ items[varS].alarm = 1; tErr += tr("UPS load high")+"; "; }
		else items[varS].alarm = 0;
	}
	varS = "ups_temperature";
	if(!(tP=srcPrm[varS]).isEVal() && !(tVl=tP.get()).isEVal()) {
		if(tVl.toReal() > tHH) 		{ items[varS].alarm = 2; tErr += tr("UPS overheated")+"; "; }
		else if(tVl.toReal() > tH)	{ items[varS].alarm = 1; tErr += tr("Temperature high")+"; "; }
		else items[varS].alarm = 0;
	}

	//Set variables process
	for(var aIt in items) {
		it = items[aIt];
		if(!it.set.isEVal()) {
			aNd = srcPrm["a_"+it.id];
			if(aNd.flg()&0x01 && (selV=it.set.match(".+\\((.+)\\)$")).length) it.set = selV[1];
			aNd.set(it.set);
			it.set = EVAL_REAL;
		}
		alLev = max(alLev, it.alarm);
	}
}

//SYS.messInfo("UPS test", "tErr="+tErr+"; alLev="+alLev);
tErr = tErr.length ? ""+alLev+":"+tErr : "0";

//Alarms forming
if(alDelay > 0 && conDelay_ <= 0 && tErr.toInt() != f_err.toInt())	tErr1 = tErr.toInt();
if(alDelay > 0 && conDelay_ > 0 && tErr.toInt() != tErr1)	{ conDelay_ = 0; tErr1 = tErr.toInt(); }
if(alDelay > 0 && conDelay_ < alDelay){ conDelay_ += 1/f_frq; return; }
if(tErr.toInt() && tErr.toInt() != f_err.toInt())	this.alarmSet(DESCR+": "+tErr.parse(1,":"), -(2+alLev));
else if(f_err.toInt() && !tErr.toInt())			this.alarmSet(DESCR+": "+tr("NORM"), 1);
f_err = tErr;
conDelay_ = 0;','','',1561317269);
INSERT INTO tmplib_DevLib VALUES('VKT7','VKT-7','','','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
License: GPLv2','','',60,0,'JavaLikeCalc.JavaScript
using Special.FLibSYS;

if(f_start) {
	//Variables list prepare
	varsLs = new Object();
	//Тв1
	varsLs["t1_1"] = 0; varsLs["t2_1"] = 1; varsLs["t3_1"] = 2;		//t
	varsLs["V1_1"] = 3; varsLs["V2_1"] = 4;	varsLs["V3_1"] = 5;		//V
	varsLs["M1_1"] = 6;	varsLs["M2_1"] = 7;	varsLs["M3_1"] = 8;		//M
	varsLs["P1_1"] = 9;	varsLs["P2_1"] = 10;	//P
	varsLs["Mg_1"] = 11;	//Mg
	varsLs["Qo_1"] = 12;	//Qo
	varsLs["Qg_1"] = 13;	//Qg
	varsLs["dt_1"] = 14;	//dt
	varsLs["BNR_1"] = 17; varsLs["BOC_1"] = 18;//BNR and BOC
	varsLs["G1_1"] = 19; varsLs["G2_1"] = 20;	//G
	//Тв2
	varsLs["t1_2"] = 22; varsLs["t2_2"] = 23; varsLs["t3_2"] = 24;	//t
	varsLs["V1_2"] = 25; varsLs["V2_2"] = 26; varsLs["V3_2"] = 27;	//V
	varsLs["M1_2"] = 28; varsLs["M2_2"] = 29; varsLs["M3_2"] = 30;	//M
	varsLs["P1_2"] = 31; varsLs["P2_2"] = 32;	//P
	varsLs["Mg_2"] = 33;	//Mg
	varsLs["Qo_2"] = 34;	//Qo
	varsLs["Qg_2"] = 35;	//Qg
	varsLs["dt_2"] = 36;	//dt
	varsLs["BNR_2"] = 39; varsLs["BOC_2"] = 40;//BNR and BOC
	varsLs["G1_2"] = 41; varsLs["G2_2"] = 42;	//G

	//Buffers create
	bfH = vArhBuf(4/*Real*/, 10, 3600*1000000, true, false);
	bfD = vArhBuf(4/*Real*/, 10, 24*3600*1000000, true, false);
	bfM = vArhBuf(4/*Real*/, 10, 24*3600*1000000, true, false);
}

t_err = "";
//Imitation for values
if(imit > 0.1) {
	//SYS.messDebug("/VKT7/TMPL","imit="+t1_1+"; dt="+(imit*(rand(t1_1)-t1_1/2)/100));
	for(var cA in varsLs)
		if(cA.slice(0,2) == "dt") arguments[cA] = arguments["t1_"+cA.slice(3)]-arguments["t2_"+cA.slice(3)];
		else arguments[cA] += imit*(rand(arguments[cA])-arguments[cA]/2)/100;
	f_err = "0";
	return;
}

if(f_stop) {
	f_err = "";
	for(var cA in varsLs) arguments[cA] = EVAL_REAL;
	return;
}

//Set transport and RTS
if(f_start){ tr = SYS.nodeAt(trAddr,"."); tr.TS(true); valsPrec = false; valsSz = false; }
if(!tr)	t_err = "1:"+tr("Transport ''%1'' error.").replace("%1",trAddr);

req = SYS.XMLNode("mess").setAttr("ProtIt","VKT7").setAttr("addr",0);

//Session start
if(!t_err.length) {
	SYS.messDebug("/VKT7/TMPL",tr("Start session."));
	req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x10,0x3F,0xFF,0x00,0x00,0xCC,0x80,0x00,0x00,0x00));
	tr.messIO(req,"UserProtocol");
	t_err = req.attr("err");
}

//Request for service information
if(!t_err.length && (f_start || firmVer.isEVal())) {
	SYS.messDebug("/VKT7/TMPL",tr("Request for service info."));
	req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x03,0x3f,0xf9,0x00,0x00));
	tr.messIO(req,"UserProtocol");
	t_err = req.attr("err");
	if(!t_err.length) {
		rez = req.text();
		firmVer = (rez.charCodeAt(2)>>4)+(rez.charCodeAt(2)&0x0F)/10;
		abonId = rez.slice(7,7+8);
		netNum = rez.charCodeAt(15);
		repDay = rez.charCodeAt(16);
		modelImpl = rez.charCodeAt(17);
	}
}

//Request for counter time and hour archive begin
if(!t_err.length && (f_start || !lastHTm || !lastDTm || !lastResTm)) {
	SYS.messDebug("/VKT7/TMPL",tr("Request for counter time and hour archive begin."));
	req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x03,0x3f,0xf6,0x00,0x00));
	tr.messIO(req,"UserProtocol");
	t_err = req.attr("err");
	if(!t_err.length) {
		rez = req.text();
		lastHTm = max(lastHTm, SYS.mktime(0,0,rez.charCodeAt(5),rez.charCodeAt(2),rez.charCodeAt(3)-1,2000+rez.charCodeAt(4)));
		diffTm = floor((SYS.time()-SYS.mktime(0,0,rez.charCodeAt(9),rez.charCodeAt(6),rez.charCodeAt(7)-1,2000+rez.charCodeAt(8)))/3600);
		leftHTm = (SYS.time()-lastHTm)/3600 - diffTm;
	}
	lastDTm = max(lastDTm, SYS.time()-maxDayDepth*24*3600);
	leftDTm = (SYS.time()-lastDTm)/(24*3600);
	lastResTm = max(lastResTm, SYS.time()-maxDayDepth*24*3600);
	leftResTm = (SYS.time()-lastResTm)/(31*24*3600);
}

//Request for dimensions and precisions
if(!t_err.length && (f_start || !valsPrec)) {
	SYS.messDebug("/VKT7/TMPL",tr("Request for dimensions and precisions."));
	req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x10, 0x3F, 0xFF, 0x00, 0x00, 0x72,
		0x2C, 0x00, 0x00, 0x40, 0x07, 0x00,	//tTypeM
		0x2D, 0x00, 0x00, 0x40, 0x07, 0x00,	//GTypeM
		0x2E, 0x00, 0x00, 0x40, 0x07, 0x00,	//VTypeM
		0x2F, 0x00, 0x00, 0x40, 0x07, 0x00,	//MTypeM
		0x30, 0x00, 0x00, 0x40, 0x07, 0x00,	//PTypeM
		0x35, 0x00, 0x00, 0x40, 0x07, 0x00,	//QoTypeM
		0x37, 0x00, 0x00, 0x40, 0x07, 0x00,	//QntTypeHIM
		0x38, 0x00, 0x00, 0x40, 0x07, 0x00,	//QntTypeM
		0x39, 0x00, 0x00, 0x40, 0x01, 0x00,	//tTypeFractDiNum
		0x3B, 0x00, 0x00, 0x40, 0x01, 0x00,	//VTypeFractDigNum1
		0x3C, 0x00, 0x00, 0x40, 0x01, 0x00,	//MTypeFractDigNum1
		0x3D, 0x00, 0x00, 0x40, 0x01, 0x00,	//PTypeFractDigNum1
		0x3E, 0x00, 0x00, 0x40, 0x01, 0x00,	//dtTypeFractDigNum1
		0x42, 0x00, 0x00, 0x40, 0x01, 0x00,	//QoTypeFractDigNum1
		0x45, 0x00, 0x00, 0x40, 0x01, 0x00,	//VTypeFractDigNum2
		0x46, 0x00, 0x00, 0x40, 0x01, 0x00,	//MTypeFractDigNum2
		0x47, 0x00, 0x00, 0x40, 0x01, 0x00,	//PTypeFractDigNum2
		0x48, 0x00, 0x00, 0x40, 0x01, 0x00,	//dtTypeFractDigNum2
		0x4C, 0x00, 0x00, 0x40, 0x01, 0x00));	//QoTypeFractDigNum2
	tr.messIO(req,"UserProtocol");
	t_err = req.attr("err");
	if(!t_err.length) {
		req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x03,0x3F,0xFE,0x00,0x00));
		tr.messIO(req,"UserProtocol");
		t_err = req.attr("err");
		if(!t_err.length) {
			rez = req.text();	off = 2;
			vLen = (rez.charCodeAt(off+1)<<8)|rez.charCodeAt(off);
			tTypeM = SYS.strCodeConv(rez.slice(off+2,off+2+vLen),"CP866","");	off += vLen+4;
			vLen = (rez.charCodeAt(off+1)<<8)|rez.charCodeAt(off);
			GTypeM = SYS.strCodeConv(rez.slice(off+2,off+2+vLen),"CP866","");	off += vLen+4;
			vLen = (rez.charCodeAt(off+1)<<8)|rez.charCodeAt(off);
			VTypeM = SYS.strCodeConv(rez.slice(off+2,off+2+vLen),"CP866","");	off += vLen+4;
			vLen = (rez.charCodeAt(off+1)<<8)|rez.charCodeAt(off);
			MTypeM = SYS.strCodeConv(rez.slice(off+2,off+2+vLen),"CP866","");	off += vLen+4;
			vLen = (rez.charCodeAt(off+1)<<8)|rez.charCodeAt(off);
			PTypeM = SYS.strCodeConv(rez.slice(off+2,off+2+vLen),"CP866","");	off += vLen+4;
			vLen = (rez.charCodeAt(off+1)<<8)|rez.charCodeAt(off);
			QoTypeM = SYS.strCodeConv(rez.slice(off+2,off+2+vLen),"CP866","");	off += vLen+4;
			vLen = (rez.charCodeAt(off+1)<<8)|rez.charCodeAt(off);
			QntTypeHIM = SYS.strCodeConv(rez.slice(off+2,off+2+vLen),"CP866","");	off += vLen+4;
			vLen = (rez.charCodeAt(off+1)<<8)|rez.charCodeAt(off);
			QntTypeM = SYS.strCodeConv(rez.slice(off+2,off+2+vLen),"CP866","");	off += vLen+4;
			valsPrec = new Object();
			valsPrec["tTypeFractDiNum"] = rez.charCodeAt(off);	off += 3;
			valsPrec["VTypeFractDigNum1"] = rez.charCodeAt(off);	off += 3;
			valsPrec["MTypeFractDigNum1"] = rez.charCodeAt(off);	off += 3;
			valsPrec["PTypeFractDigNum1"] = rez.charCodeAt(off);	off += 3;
			valsPrec["dtTypeFractDigNum1"] = rez.charCodeAt(off);	off += 3;
			valsPrec["QoTypeFractDigNum1"] = rez.charCodeAt(off);	off += 3;
			valsPrec["VTypeFractDigNum2"] = rez.charCodeAt(off);	off += 3;
			valsPrec["MTypeFractDigNum2"] = rez.charCodeAt(off);	off += 3;
			valsPrec["PTypeFractDigNum2"] = rez.charCodeAt(off);	off += 3;
			valsPrec["dtTypeFractDigNum2"] = rez.charCodeAt(off);	off += 3;
			valsPrec["QoTypeFractDigNum2"] = rez.charCodeAt(off);	off += 3;
		}
	}
}

//Request for values present list and size.
if(!t_err.length && (f_start || !valsSz || !valsSz.length)) {
	SYS.messDebug("/VKT7/TMPL",tr("Request for values present list and size."));
	req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x03,0x3f,0xfc,0x00,0x00));
	tr.messIO(req,"UserProtocol");
	t_err = req.attr("err");
	if(!t_err.length) {
		rez = req.text();
		valsSz = new Array();
		for(off = 2; off < rez.length; off += 6)
			valsSz[rez.charCodeAt(off)] = rez.charCodeAt(off+4);
	}
}

//Readed items list write
if(!t_err.length) {
	SYS.messDebug("/VKT7/TMPL",tr("Readed items list write."));
	data = "";
	for(var cA in varsLs) {
		itId = varsLs[cA];
		if(!valsSz[itId].isEVal())	data += SYS.strFromCharCode(itId, 0x00, 0x00, 0x40, valsSz[itId], 0x00);
	}

	if(!data.length) {
		for(var cA in varsLs) arguments[cA] = EVAL_REAL;
		f_err = "10:"+tr("Data list for request empty.");
		return;
	}
	req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x10,0x3f,0xff,0x00,0x00,data.length)+data);
	//SYS.messDebug("/VKT7/TMPL","SetDataList: "+Special.FLibSYS.strDec4Bin(req.text()));
	tr.messIO(req,"UserProtocol");
	t_err = req.attr("err");
}

//Read 0-current value(4), 1-hour archive(0) and 2-result month archive(3)
errAttrs = "";
for(i_dt = 0; !t_err.length && i_dt < 4; i_dt++) {
	tpReq = 4;																						//current
	if(i_dt == 1)			{ tpReq = 0; if(!arhH.length) continue; }		//hour archive
	else if(i_dt == 2)	{ tpReq = 1; if(!arhD.length) continue; }		//day archive
	else if(i_dt == 3)	{ tpReq = 3; if(!arhRes.length) continue; }	//result archive

	//Request for curent values (4).
	if(tpReq == 4 && !t_err.length) {
		SYS.messDebug("/VKT7/TMPL",tr("Request for curent values."));
		req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x10,0x3f,0xfd,0x00,0x00,0x02,0x04,0x00));
		tr.messIO(req,"UserProtocol");
		t_err = req.attr("err");
	}

	//Request for archive values hour
	if(tpReq == 0) {
		if(!t_err.length) {
			if((SYS.time()-lastHTm-diffTm*3600)/3600 < 2) continue;	//No more ready hours present
			SYS.messDebug("/VKT7/TMPL",tr("Request for hour archive values."));
			req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x10,0x3f,0xfd,0x00,0x00,0x02,tpReq,0x00));
			tr.messIO(req,"UserProtocol");
			t_err = req.attr("err");
		}

		// Set for hour archive value data
		if(!t_err.length) {
			SYS.messDebug("/VKT7/TMPL",tr("Request for set archive data."));
			stHour = stDay = stMonth = stYear = 0;
			SYS.localtime(lastHTm, 0, 0, stHour, stDay, stMonth, stYear);
			stHour++;
			rdTime = SYS.mktime(0, 0, stHour, stDay, stMonth, stYear);

			req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x10,0x3f,0xfb,0x00,0x00,0x04,stDay,stMonth+1,stYear-2000,stHour));
			tr.messIO(req,"UserProtocol");
			t_err = req.attr("err");
			if(t_err.parse(1,":").toInt() == 3) { lastHTm = rdTime; leftHTm = (SYS.time()-lastHTm)/3600 - diffTm; t_err = ""; continue; }
		}
	}

	//Request for archive values day
	if(tpReq == 1) {
		if(!t_err.length) {
			if((SYS.time()-lastDTm-diffTm*3600)/(24*3600) < 1) continue;	//No more ready days present
			SYS.messDebug("/VKT7/TMPL",tr("Request for day archive values."));
			req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x10,0x3f,0xfd,0x00,0x00,0x02,tpReq,0x00));
			tr.messIO(req,"UserProtocol");
			t_err = req.attr("err");
		}

		// Set for day archive value data
		if(!t_err.length) {
			SYS.messDebug("/VKT7/TMPL",tr("Request for set archive data."));
			stDay = stMonth = stYear = 0;
			SYS.gmtime(lastDTm, 0, 0, 0, stDay, stMonth, stYear);
			stDay++;
			rdDTime = SYS.timegm(0, 0, 0, stDay, stMonth, stYear);

			req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x10,0x3f,0xfb,0x00,0x00,0x04,stDay,stMonth+1,stYear-2000,stHour));
			tr.messIO(req,"UserProtocol");
			t_err = req.attr("err");
			if(t_err.parse(1,":").toInt() == 3) { lastDTm = rdDTime; leftDTm = (SYS.time()-lastDTm)/(24*3600) - diffTm; t_err = ""; continue; }
		}
	}

	//Request for results month archive
	if(tpReq == 3) {
		if(!t_err.length) {
			if((SYS.time()-lastResTm-diffTm*3600)/(24*3600) < 31) continue;	//No more ready month present
			SYS.messDebug("/VKT7/TMPL",tr("Request for results month archive values."));
			req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x10,0x3f,0xfd,0x00,0x00,0x02,tpReq,0x00));
			tr.messIO(req,"UserProtocol");
			t_err = req.attr("err");
		}
		// Set for month results archive value data
		if(!t_err.length) {
			SYS.messDebug("/VKT7/TMPL",tr("Request for set archive data."));
			stMonth = stYear = 0;
			SYS.gmtime(lastResTm, 0, 0, 0, 0, stMonth, stYear);
			stMonth++;
			rdResTime = SYS.timegm(0, 0, 0, repDay, stMonth, stYear);

			req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x10,0x3f,0xfb,0x00,0x00,0x04,repDay,stMonth+1,stYear-2000,0));
			tr.messIO(req,"UserProtocol");
			t_err = req.attr("err");
			if(t_err.parse(1,":").toInt() == 3) { lastResTm = rdResTime; leftResTm = (SYS.time()-lastResTm)/(31*24*3600); t_err = ""; continue; }
		}
	}

	//Request for data read
	if(!t_err.length) {
		SYS.messDebug("/VKT7/TMPL",tr("Request for data read."));
		req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x03,0x3F,0xFE,0x00,0x00));
		tr.messIO(req,"UserProtocol");
		t_err = req.attr("err");
		if(!t_err.length) {
			rez = req.text(); off = 2; vSz = 0;
			for(var cA in varsLs) {
				itId = varsLs[cA]; vSz = valsSz[itId];
				if(vSz.isEVal()) {
					if(tpReq == 4) arguments[cA] = EVAL_REAL;
					continue;
				}
				//SYS.messDebug("/VKT7/TMPL","off: "+off+"; itId: "+itId+"; vSz: "+vSz);
				if(rez.charCodeAt(off+vSz) == 0xC0) {
					for(pVal = 0, i_b = 0; i_b < vSz; i_b++) pVal = pVal | rez.charCodeAt(off+i_b)<<(i_b*8);
					//Float type values
					if(cA[0] == "G") pVal = floatMergeWord(pVal&0xFFFF, (pVal>>16)&0xFFFF);
					//Integer type values, negative process
					else if(pVal&(0x80<<(8*(vSz-1))))	pVal -= (1<<(8*vSz));
					// Type specific
					if(cA[0] == "t")						tmpVl = pVal/pow(10,valsPrec["tTypeFractDiNum"]);
					else if(cA[0] == "V")				tmpVl = pVal/pow(10,valsPrec["VTypeFractDigNum"+cA[3]]);
					else if(cA[0] == "M")				tmpVl = pVal/pow(10,valsPrec["MTypeFractDigNum"+cA[3]]);
					else if(cA[0] == "P")				tmpVl = pVal/pow(10,valsPrec["PTypeFractDigNum"+cA[3]]);
					else if(cA[0] == "Q")				tmpVl = pVal/pow(10,valsPrec["QoTypeFractDigNum"+cA[3]]);
					else if(cA.slice(0,2) == "dt")tmpVl = pVal/pow(10,valsPrec["dtTypeFractDigNum"+cA[3]]);
					else tmpVl = pVal;

					// Write current or archive
					if(tpReq == 4) {
						arguments[cA] = tmpVl;
						SYS.messDebug("/VKT7/TMPL",tr("Set current")+" "+cA+": "+tmpVl);
					}
					else if(!(aObj=vArh(this.nodePath(".")+"."+cA)).isNull()) {
						if(tpReq == 0) {
							bfH.set(tmpVl, rdTime, 0); aObj.copy(bfH, rdTime, 0, rdTime, 0, arhH);
							SYS.messDebug("/VKT7/TMPL",tr("Write to hour archive for time")+": "+rdTime+"; "+cA+": "+tmpVl);
						}
						if(tpReq == 1) {
							bfD.set(tmpVl, rdDTime, 0); aObj.copy(bfD, rdDTime, 0, rdDTime, 0, arhD);
							SYS.messDebug("/VKT7/TMPL",tr("Write to day archive for time")+": "+rdDTime+"; "+cA+": "+tmpVl);
						}
						else if(tpReq == 3) {
							bfM.set(tmpVl, rdResTime, 0); aObj.copy(bfM, rdResTime, 0, rdResTime, 0, arhRes);
							SYS.messDebug("/VKT7/TMPL",tr("Write to result month archive for time")+": "+rdResTime+"; "+cA+": "+tmpVl);
						}
					}
				}
				else if(tpReq == 4) { arguments[cA] = EVAL_REAL; errAttrs += ""+itId+"="+rez.charCodeAt(off+vSz).toString(16)+"; "; }
				off += vSz+2;
			}
			if(tpReq == 0) { lastHTm = rdTime; leftHTm = (SYS.time()-lastHTm)/3600 - diffTm; }
			else if(tpReq == 1) { lastDTm = rdDTime; leftDTm = (SYS.time()-lastDTm)/(24*3600) - diffTm; }
			else if(tpReq == 3) { lastResTm = rdResTime; leftResTm = (SYS.time()-lastResTm)/(31*24*3600); }
		}
	}
}

if(t_err.length) {
	SYS.messDebug("/VKT7/TMPL",tr("Error response")+": "+t_err);
	f_err = t_err;
	for(var cA in varsLs) arguments[cA] = EVAL_REAL;
}
else f_err = errAttrs.length ? "11:"+tr("Quality errors")+": "+errAttrs : "0";','','',1580047532);
INSERT INTO tmplib_DevLib VALUES('SMDP','Sycon Multi Drop Protocol','','','STM devices for resonant frequency measurement for mass of deposited films attached to their 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
License: GPLv2','','',10,0,'JavaLikeCalc.JavaScript
if(f_stop) return;
if(f_start) {
	cnt = 0;
	version = "";
	SessId_ = SessId = QlvlTrip_ = QlvlTrip = SlvlTrip_ = SlvlTrip = EVAL_INT;
	Fq_ = Fq = Fm_ = Fm = Density_ = Density = Zratio_ = Zratio = Tooling_ = Tooling = RateReq_ = RateReq = EVAL_REAL;
}

tr = SYS.Transport.Serial.nodeAt("out_"+transport);
req = SYS.XMLNode("mess").setAttr("ProtIt","SMDP").setAttr("addr",addr).setAttr("try",3);

t_err = "";

//Configurations first write
// Session ID.
if(SessId != SessId_) {
	SYS.messDebug("/SMDP/tmpl",""+SessId+"!="+SessId_);	
	req.setAttr("err","1:"+tr("No connect")).setText("\xD0\x41"+SessId); tr.messIO(req,"UserProtocol");
	if(!(t_err=req.attr("err")).length) SessId_ = SessId; else SessId = SessId_;
}
// Xtal start freq. (Hz)
if(Fq != Fq_) {
	req.setAttr("err","1:"+tr("No connect")).setText("\xD0\x42"+Fq); tr.messIO(req,"UserProtocol");
	if(!(t_err=req.attr("err")).length) Fq_ = Fq; else Fq = Fq_;
}
// Xtal min freq. (Hz)
if(Fm != Fm_) {
	req.setAttr("err","1:"+tr("No connect")).setText("\xD0\x43"+Fm); tr.messIO(req,"UserProtocol");
	if(!(t_err=req.attr("err")).length) Fm_ = Fm; else Fm = Fm_;
}
// Material density (gm/cc).
if(Density != Density_) {
	req.setAttr("err","1:"+tr("No connect")).setText("\xD0\x44"+Density); tr.messIO(req,"UserProtocol");
	if(!(t_err=req.attr("err")).length) Density_ = Density; else Density = Density_;
}
// Material Z ratio. Not scaled or unitized.
if(Zratio != Zratio_) {
	req.setAttr("err","1:"+tr("No connect")).setText("\xD0\x45"+Zratio); tr.messIO(req,"UserProtocol");
	if(!(t_err=req.attr("err")).length) Zratio_ = Zratio; else Zratio = Zratio_;
}
// 1.000 is 100 % tooling (unity).
if(Tooling != Tooling_) {
	req.setAttr("err","1:"+tr("No connect")).setText("\xD0\x46"+Tooling); tr.messIO(req,"UserProtocol");
	if(!(t_err=req.attr("err")).length) Tooling_ = Tooling; else Tooling = Tooling_;
}
// Requested rate (A/S).
if(RateReq != RateReq_) {
	req.setAttr("err","1:"+tr("No connect")).setText("\xD0\x47"+RateReq); tr.messIO(req,"UserProtocol");
	if(!(t_err=req.attr("err")).length) RateReq_ = RateReq; else RateReq = RateReq_;
}
// Quality threshold, if non 0 and exceeded, xtal fail occurs.
if(QlvlTrip != QlvlTrip_) {
	req.setAttr("err","1:"+tr("No connect")).setText("\xD0\x48"+QlvlTrip); tr.messIO(req,"UserProtocol");
	if(!(t_err=req.attr("err")).length) QlvlTrip_ = QlvlTrip; else QlvlTrip = QlvlTrip_;
}
// Stability threshold, if non 0 and exceeded, xtal fail occurs.
if(SlvlTrip != SlvlTrip_) {
	req.setAttr("err","1:"+tr("No connect")).setText("\xD0\x49"+SlvlTrip); tr.messIO(req,"UserProtocol");
	if(!(t_err=req.attr("err")).length) SlvlTrip_ = SlvlTrip; else SlvlTrip = SlvlTrip_;
}

//======= Data getting (by self bigger period) ========
if((cnt++)%max(1,perGet*f_frq))	return;

//Configurations first read
// Session ID.
if(!t_err.length && SessId.isEVal()) {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x41"); tr.messIO(req,"UserProtocol");
	SessId = SessId_ = (t_err=req.attr("err")).length ? EVAL_INT : req.text().slice(2).toInt(10);
}
// Xtal start freq. (Hz)
if(!t_err.length && Fq.isEVal()) {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x42"); tr.messIO(req,"UserProtocol");
	Fq = Fq_ = (t_err=req.attr("err")).length ? EVAL_REAL : req.text().slice(2).toReal();
}
// Xtal min freq. (Hz)
if(!t_err.length && Fm.isEVal()) {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x43"); tr.messIO(req,"UserProtocol");
	Fm = Fm_ = (t_err=req.attr("err")).length ? EVAL_REAL : req.text().slice(2).toReal();
}
// Material density (gm/cc).
if(!t_err.length && Density.isEVal()) {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x44"); tr.messIO(req,"UserProtocol");
	Density = Density_ = (t_err=req.attr("err")).length ? EVAL_REAL : req.text().slice(2).toReal();
}
// Material Z ratio. Not scaled or unitized.
if(!t_err.length && Zratio.isEVal()) {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x45"); tr.messIO(req,"UserProtocol");
	Zratio = Zratio_ = (t_err=req.attr("err")).length ? EVAL_REAL : req.text().slice(2).toReal();
}
// 1.000 is 100 % tooling (unity).
if(!t_err.length && Tooling.isEVal()) {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x46"); tr.messIO(req,"UserProtocol");
	Tooling = Tooling_ = (t_err=req.attr("err")).length ? EVAL_REAL : req.text().slice(2).toReal();
}
// Requested rate (A/S).
if(!t_err.length && RateReq.isEVal()) {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x47"); tr.messIO(req,"UserProtocol");
	RateReq = RateReq_ = (t_err=req.attr("err")).length ? EVAL_REAL : req.text().slice(2).toReal();
}
// Quality threshold, if non 0 and exceeded, xtal fail occurs.
if(!t_err.length && QlvlTrip.isEVal()) {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x48"); tr.messIO(req,"UserProtocol");
	QlvlTrip = QlvlTrip_ = (t_err=req.attr("err")).length ? EVAL_REAL : req.text().slice(2).toInt(10);
}
// Stability threshold, if non 0 and exceeded, xtal fail occurs.
if(!t_err.length && SlvlTrip.isEVal()) {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x49"); tr.messIO(req,"UserProtocol");
	SlvlTrip = SlvlTrip_ = (t_err=req.attr("err")).length ? EVAL_REAL : req.text().slice(2).toInt(10);
}

//Runtime variables
// Read version, single
if(!t_err.length && (!version.length || version.isEVal())) {
	req.setAttr("err","1:"+tr("No connect")).setText("\x40"); tr.messIO(req,"UserProtocol");
	version = (t_err=req.attr("err")).length ? EVAL_STR : req.text().slice(1);
}

// Read Configuration parameter session ID
if(t_err.length) CfgPrmSSID = EVAL_INT;
else {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x61"); tr.messIO(req,"UserProtocol");
	CfgPrmSSID = (t_err=req.attr("err")).length ? EVAL_INT : req.text().slice(2).toInt(10);
	//SYS.messDebug("/SMDP/tmpl","CfgPrmSSID: "+req.text());
}

// Read Measurement serial number
if(t_err.length) Srlno = EVAL_INT;
else {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x62"); tr.messIO(req,"UserProtocol");
	Srlno = (t_err=req.attr("err")).length ? EVAL_INT : req.text().slice(2).toInt(10);
}

// Read Channel freq.
if(t_err.length) RawFreq = EVAL_REAL;
else {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x63"); tr.messIO(req,"UserProtocol");
	RawFreq = (t_err=req.attr("err")).length ? EVAL_REAL : req.text().slice(2).toReal();
}

// Read Last used, good channel freq. (Hz)
if(t_err.length) GoodFreq = EVAL_REAL;
else {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x64"); tr.messIO(req,"UserProtocol");
	GoodFreq = (t_err=req.attr("err")).length ? EVAL_REAL : req.text().slice(2).toReal();
}

// Read Computed raw thickness, from frequency. (A)
if(t_err.length) RawThick = EVAL_REAL;
else {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x65"); tr.messIO(req,"UserProtocol");
	RawThick = (t_err=req.attr("err")).length ? EVAL_REAL : req.text().slice(2).toReal();
}

// Read Computed material related thickness, can be zeroed. (A)
if(t_err.length) XtalThick = EVAL_REAL;
else {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x66"); tr.messIO(req,"UserProtocol");
	XtalThick = (t_err=req.attr("err")).length ? EVAL_REAL : req.text().slice(2).toReal();
}

// Read Filtered computed material related thickness, can be zeroed. (A)
if(t_err.length) XtalThick_F = EVAL_REAL;
else {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x67"); tr.messIO(req,"UserProtocol");
	XtalThick_F = (t_err=req.attr("err")).length ? EVAL_REAL : req.text().slice(2).toReal();
}

// Read Rate, angstroms per second.
if(t_err.length) XtalRate = EVAL_REAL;
else {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x68"); tr.messIO(req,"UserProtocol");
	XtalRate = (t_err=req.attr("err")).length ? EVAL_REAL : req.text().slice(2).toReal();
}

// Read Rate, angstroms per second, filtered.
if(t_err.length) XtalRate_F = EVAL_REAL;
else {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x69"); tr.messIO(req,"UserProtocol");
	XtalRate_F = (t_err=req.attr("err")).length ? EVAL_REAL : req.text().slice(2).toReal();
}

// Read XtalLife (%)
if(t_err.length) XtalLife = EVAL_REAL;
else {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x6A"); tr.messIO(req,"UserProtocol");
	XtalLife = (t_err=req.attr("err")).length ? EVAL_REAL : req.text().slice(2).toReal();
}

// Read Quality level (0-9).
if(t_err.length) XtalQual = EVAL_INT;
else {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x6B"); tr.messIO(req,"UserProtocol");
	XtalQual = (t_err=req.attr("err")).length ? EVAL_INT : req.text().slice(2).toInt(10);
}

// Read Highest quality level seen (0-9).
if(t_err.length) XtalQualPeak = EVAL_INT;
else {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x6C"); tr.messIO(req,"UserProtocol");
	XtalQualPeak = (t_err=req.attr("err")).length ? EVAL_INT : req.text().slice(2).toInt(10);
}

// Read Stability level (0-9).
if(t_err.length) XtalStab = EVAL_INT;
else {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x6D"); tr.messIO(req,"UserProtocol");
	XtalStab = (t_err=req.attr("err")).length ? EVAL_INT : req.text().slice(2).toInt(10);
}

// Read Highest stability level seen (0-9).
if(t_err.length) XtalStabPeak = EVAL_INT;
else {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x6E"); tr.messIO(req,"UserProtocol");
	XtalStabPeak = (t_err=req.attr("err")).length ? EVAL_INT : req.text().slice(2).toInt(10);
}

// Read Channel status.
if(t_err.length) XtalStat = EVAL_STR;
else {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x6F"); tr.messIO(req,"UserProtocol");
	if((t_err=req.attr("err")).length) XtalStat = EVAL_STR;
	else {
		stCd = req.text().slice(2).toInt(10);
		stLab = tr("Unknown");
		if(stCd == 0)			stLab = "XPROB_NONE: "+tr("No Problems. The rest below are prioritized.");
		else if(stCd == 1)	stLab = "XPROB_FREQ: "+tr("Frequency of xtal is > Fq or < Fm. Halts all other calculations.");
		else if(stCd == 2)	stLab = "XPROB_LOWLIFE: "+tr("Frequency was bad (previous XtalStat was XPROB_FREQ) and frequency is now in range, but life is less than 3%.");
		else if(stCd == 3)	stLab = "XPROB_S_FAIL: "+tr("Stability level XtalStab of xtal >= SlvlTrip.");
		else if(stCd == 4)	stLab = "XPROB_MATH: "+tr("Unable to determine rate because of a computation error, most likely caused by a parameter (density, z-ratio, etc) having a zero or otherwise invalid (non numeric or infinity) value.");
		else if(stCd == 5)	stLab = "XPROB_Q_FAIL: "+tr("XtalQual of xtal >= QlvlTrip.");
		XtalStat = ""+stCd+":"+stLab;
	}
}

// Read XtalLife (%).
if(t_err.length) XtalLife_C = EVAL_INT;
else {
	req.setAttr("err","1:"+tr("No connect")).setText("\xC0\x70"); tr.messIO(req,"UserProtocol");
	XtalLife_C = (t_err=req.attr("err")).length ? EVAL_INT : req.text().slice(2).toInt(10);
}

f_err = t_err.length ? t_err : "0";','','',1580047251);
INSERT INTO tmplib_DevLib VALUES('IT3','Temperature measurement IT-3','','','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
License: GPLv2','','',10,0,'JavaLikeCalc.JavaScript
if(mdPass)	{
	if(f_start) { f_err = EVAL; this.cnt.set(0, 0, 0, true); cnt_ = 0; tDl = 5; }
	if((cnt=this.cnt.get()) != cnt_)	{ tDl = 5; cnt_ = cnt; }
	else if(tDl > 0 && (tDl=tDl-1/f_frq) <= 0) {
		this.err.set("1:"+tr("No data"), 0, 0, true);
		this.T.set(EVAL, 0, 0, true);
		this.H.set(EVAL, 0, 0, true);
		this.L.set(EVAL, 0, 0, true);
		this.relSt.set(EVAL, 0, 0, true);
	}
	return;
}

//Set transport
if(f_start)	tr = SYS.Transport.Serial.nodeAt("out_"+transport);
if(!tr)	t_err = "1:"+tr("Serial output transport ''%1'' error.").replace("%1",transport);

req = SYS.XMLNode("mess").setAttr("ProtIt","IT3").setAttr("addr",addr);

SYS.messDebug("/IT3/TMPL",tr("Request: TRANSL_TEMP."));
req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0xC3));
tr.messIO(req, "UserProtocol");
if(!(t_err=req.attr("err")).length && req.text().charCodeAt(0) == 0x85) {
	cntrB = req.text().charCodeAt(3);
	T = (req.text().charCodeAt(2)<<8)+req.text().charCodeAt(1);
	T = T/((cntrB&0x8)?100:10);
	relSt = (cntrB&0x1);
	L = (cntrB&0x2);
	H = (cntrB&0x4);
	//SYS.messDebug("/IT3/TMPL",tr("Request: TRANSL_TEMP: "+req.text().length));
}
else {
	T = H = L = EVAL_STR;
	if(t_err.parse(1,":").toInt() == 8) {
		req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x00));
		tr.messIO(req, "UserProtocol");
	}
}

if(t_err.length) { SYS.messDebug("/IT3/TMPL",tr("Error response")+": "+t_err); f_err = t_err; }
else f_err = "0";','','',1580046256);
INSERT INTO tmplib_DevLib VALUES('IVE_452HS_02','IVE-452HS-02','','','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
License: GPLv2','','',10,0,'JavaLikeCalc.JavaScript
//Set transport
if(f_start) {
	addr_ = addr;
	transport_ = transport;
	COIA = COIA_ = COUA = COUA_ = COPA = COPA_ = DIA = DUA = DPA = DFA = EVAL_INT;
	COM_DEW = COM_DEW_ = COM_OUT = COM_OUT_ = COM_DEP	= COM_DEP_ = COM_DEL = COM_DEL_ = COM_UF = COM_UF_ =
					DKW = DKZ = DK = DE = EVAL_BOOL;
	tr = SYS.Transport.nodeAt(transport,".");
}

//Check for the transport change and connect
if(!tr || transport != transport_)	{
	tr = SYS.Transport.nodeAt(transport,".");
	transport_ = transport;
}
if(!tr)	t_err = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
else {
	req = SYS.XMLNode("mess").setAttr("ProtIt","IVE_452HS_02").setAttr("addr",addr);

	//Check for the address need to change
	if(addr != addr_) {
		hwReg = 0x00;
		req.setAttr("err","2:"+tr("No connection")).setAttr("addr",addr_)
			.setText(SYS.strFromCharCode(0x57,0x04,0x00,hwReg,hwReg,0x06,addr));
		tr.messIO(req, "UserProtocol");
		req.setText(SYS.strFromCharCode(0x57,0x04,0x00,hwReg,hwReg,0x08,0x00));
		tr.messIO(req, "UserProtocol");
		req.setAttr("addr", addr);
		addr_ = addr;
	}

	//Check and write commands
	if(COIA != COIA_ && !(t_err=req.attr("err")).length)	{
		hwReg = 0x01;
		req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x57,0x04,0x00,hwReg,hwReg,COIA&0xFF,(COIA>>8)&0xFF));
		tr.messIO(req, "UserProtocol");
	}
	if(COUA != COUA_ && !(t_err=req.attr("err")).length) {
		hwReg = 0x02;
		req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x57,0x04,0x00,hwReg,hwReg,COUA&0xFF,(COUA>>8)&0xFF));
		tr.messIO(req, "UserProtocol");
	}
	if(COPA != COPA_ && !(t_err=req.attr("err")).length) {
		hwReg = 0x03;
		req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x57,0x04,0x00,hwReg,hwReg,COPA&0xFF,(COPA>>8)&0xFF));
		tr.messIO(req, "UserProtocol");
	}
	if((COM_DEW != COM_DEW_ || COM_OUT != COM_OUT_ || COM_DEP != COM_DEP_ || COM_DEL != COM_DEL_ || COM_UF != COM_UF_) && !(t_err=req.attr("err")).length) {
		tmp = 0;
		if(COM_DEW)	tmp = tmp | 0x8000;
		if(COM_OUT)	tmp = tmp | 0x4000;
		if(COM_DEP)	tmp = tmp | 0x1000;
		if(COM_DEL)	tmp = tmp | 0x0800;
		if(COM_UF)	tmp = tmp | 0x0004;
		hwReg = 0x15;
		req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x57,0x04,0x00,hwReg,hwReg,tmp&0xFF,(tmp>>8)&0xFF));
		tr.messIO(req, "UserProtocol");
	}

	//Read data
	if(!(t_err=req.attr("err")).length) {
		SYS.messDebug("/IVE452HS02/TMPL",tr("Read all data."));
		req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x52,0x02,0x00,0x01,0x16));
		tr.messIO(req, "UserProtocol");
		if(!(t_err=req.attr("err")).length) {
			if(req.text().length < (0x16*2+5))	req.setAttr("err", "10:"+tr("Reply is not full"));
			else {
				hwReg = 0x01; COIA = COIA_ = (req.text().charCodeAt((hwReg-1)*2+6)<<8)+req.text().charCodeAt((hwReg-1)*2+5);
				hwReg = 0x02; COUA = COUA_ = (req.text().charCodeAt((hwReg-1)*2+6)<<8)+req.text().charCodeAt((hwReg-1)*2+5);
				hwReg = 0x03; COPA = COPA_ = (req.text().charCodeAt((hwReg-1)*2+6)<<8)+req.text().charCodeAt((hwReg-1)*2+5);
				hwReg = 0x07; DIA = (req.text().charCodeAt((hwReg-1)*2+6)<<8)+req.text().charCodeAt((hwReg-1)*2+5);
				hwReg = 0x08; DUA = (req.text().charCodeAt((hwReg-1)*2+6)<<8)+req.text().charCodeAt((hwReg-1)*2+5);
				hwReg = 0x0E; DAC = (req.text().charCodeAt((hwReg-1)*2+6)<<8)+req.text().charCodeAt((hwReg-1)*2+5);
				hwReg = 0x10; DPA = (req.text().charCodeAt((hwReg-1)*2+6)<<8)+req.text().charCodeAt((hwReg-1)*2+5);
				hwReg = 0x11; DFA = (req.text().charCodeAt((hwReg-1)*2+6)<<8)+req.text().charCodeAt((hwReg-1)*2+5);
				hwReg = 0x15; tmp = (req.text().charCodeAt((hwReg-1)*2+6)<<8)+req.text().charCodeAt((hwReg-1)*2+5);
				COM_DEW = COM_DEW_ = tmp&0x8000;
				COM_OUT = COM_OUT_ = tmp&0x4000;
				COM_DEP = COM_DEP_ = tmp&0x1000;
				COM_DEL = COM_DEL_ = tmp&0x0800;
				COM_UF = COM_UF_ = tmp&0x0004;
				hwReg = 0x16; tmp = (req.text().charCodeAt((hwReg-1)*2+6)<<8)+req.text().charCodeAt((hwReg-1)*2+5);
				DKW = !(tmp&0x8); DKZ = !(tmp&0x4); DK = !(tmp&0x2); DE = tmp&0x1;
			}
		}
	}
}

if(t_err.length) {
	COIA = COIA_ = COUA = COUA_ = COPA = COPA_ = DIA = DUA = DAC = DPA = DFA = EVAL_INT;
	COM_DEW = COM_DEW_ = COM_OUT = COM_OUT_ = COM_DEP	= COM_DEP_ = COM_DEL = COM_DEL_ = COM_UF = COM_UF_ = 
					DKW = DKZ = DK = DE = EVAL_BOOL;
	SYS.messDebug("/IVE452HS02/TMPL",tr("Error response")+": "+t_err);
	f_err = t_err;
}
else f_err = "0";','','',1580046355);
INSERT INTO tmplib_DevLib VALUES('OPTRIS','OPTRIS CT/CTL','','','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
License: GPLv2','','',10,0,'JavaLikeCalc.JavaScript
//Set transport
if(f_start) {
	transport_ = transport;
	Tproc = Thead = Tbox = Tact = eps = eps_ = trans = trans_ = EVAL_REAL;
	spIll = spIll_ = EVAL_BOOL;
	tr = SYS.Transport.nodeAt(transport, ".");
}

//Check for the transport change and connect
if(!tr || transport != transport_)	{
	tr = SYS.Transport.nodeAt(transport, ".");
	transport_ = transport;
}
if(!tr)	t_err = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
else {
	req = SYS.XMLNode("mess").setAttr("ProtIt", "OPTRIS").setAttr("addr", addr).setAttr("cs", "1");

	//Check and write
	// Epsilon
	if(eps != eps_ && !(t_err=req.attr("err")).length) {
		epsH = eps*1000;
		req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x84,epsH/256,epsH%256));
		tr.messIO(req, "UserProtocol");
	}
	// Transmission
	if(trans != trans_ && !(t_err=req.attr("err")).length) {
		transH = trans*1000;
		req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x85,transH/256,transH%256));
		tr.messIO(req, "UserProtocol");
	}
	// Spot illumination
	if(spIll != spIll_ && !(t_err=req.attr("err")).length) {
		req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0xA5,spIll));
		tr.messIO(req, "UserProtocol");
	}

	//Read data
	req.setAttr("cs", "0");
	// Tproc
	if(!(t_err=req.attr("err")).length) {
		req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x01));
		tr.messIO(req, "UserProtocol");
		Tproc = (!(t_err=req.attr("err")).length && req.text().length >= 2) ? (req.text().charCodeAt(0)*256+req.text().charCodeAt(1)-1000)/10 : EVAL_REAL;
	}
	// Thead
	if(!(t_err=req.attr("err")).length) {
		req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x02));
		tr.messIO(req, "UserProtocol");
		Thead = (!(t_err=req.attr("err")).length && req.text().length >= 2) ? (req.text().charCodeAt(0)*256+req.text().charCodeAt(1)-1000)/10 : EVAL_REAL;
	}
	// Tbox
	if(!(t_err=req.attr("err")).length) {
		req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x03));
		tr.messIO(req, "UserProtocol");
		Tbox = (!(t_err=req.attr("err")).length && req.text().length >= 2) ? (req.text().charCodeAt(0)*256+req.text().charCodeAt(1)-1000)/10 : EVAL_REAL;
	}
	// Tact
	if(!(t_err=req.attr("err")).length) {
		req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x81));
		tr.messIO(req, "UserProtocol");
		Tact = (!(t_err=req.attr("err")).length && req.text().length >= 2) ? (req.text().charCodeAt(0)*256+req.text().charCodeAt(1)-1000)/10 : EVAL_REAL;
	}
	// IR epsilon
	if(!(t_err=req.attr("err")).length) {
		req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x04));
		tr.messIO(req, "UserProtocol");
		eps = eps_ = (!(t_err=req.attr("err")).length && req.text().length >= 2) ? (req.text().charCodeAt(0)*256+req.text().charCodeAt(1))/1000 : EVAL_REAL;
	}
	// IR transmission
	if(!(t_err=req.attr("err")).length) {
		req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x05));
		tr.messIO(req, "UserProtocol");
		trans = trans_ = (!(t_err=req.attr("err")).length && req.text().length >= 2) ? (req.text().charCodeAt(0)*256+req.text().charCodeAt(1))/1000 : EVAL_REAL;
	}
	// Spot illumination
	if(!(t_err=req.attr("err")).length) {
		req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x25));
		tr.messIO(req, "UserProtocol");
		spIll = spIll_ = (!(t_err=req.attr("err")).length && req.text().length >= 1) ? req.text().charCodeAt(0) : EVAL_BOOL;
	}
}

//Error set
if(t_err.length) {
	Tproc = Thead = Tbox = Tact = eps = eps_ = trans = trans_ = spIll = spIll_ = EVAL_REAL;
	spIll = spIll_ = EVAL_BOOL;
	SYS.messDebug("/OPTRIS/TMPL",tr("Error response")+": "+t_err);
	f_err = t_err;
}
else f_err = "0";','','',1580047532);
INSERT INTO tmplib_DevLib VALUES('CTR','CTR 100, 101','','','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
License: GPLv2','','',10,0,'JavaLikeCalc.JavaScript
//Set transport
if(f_start) {
	transport_ = transport;
	press = FSR_Mantissa = FSR_Exp = EVAL_REAL;
	tr = SYS.Transport.nodeAt(transport, ".");
}

u_err = "";

//Check for the transport change and connect
if(!tr || transport != transport_)	{
	tr = SYS.Transport.nodeAt(transport, ".");
	transport_ = transport;
}
if(!tr)	t_err = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
else {
	req = SYS.XMLNode("mess").setAttr("ProtIt","CTR");

	//Read data
	// F.S.R Mantisa
	if(!(t_err=req.attr("err")).length && FSR_Mantissa.isEVal()) {
		req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x00,0x39,0x00));
		tr.messIO(req, "UserProtocol");
		if(!(t_err=req.attr("err")).length) {
			if((cV=req.text().charCodeAt(5)) == 0) FSR_Mantissa = 1.0;
			else if(cV == 1) FSR_Mantissa = 1.1;
			else if(cV == 2) FSR_Mantissa = 2.0;
			else if(cV == 3) FSR_Mantissa = 2.5;
			else if(cV == 4) FSR_Mantissa = 5.0;
		}
	}
	// F.S.R Exp
	if(!(t_err=req.attr("err")).length && FSR_Exp.isEVal()) {
		req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x00,0x38,0x00));
		tr.messIO(req, "UserProtocol");
		if(!(t_err=req.attr("err")).length) {
			if((cV=req.text().charCodeAt(5)) == 2) FSR_Exp = -1;
			else if(cV == 3) FSR_Exp = 0;
			else if(cV == 4) FSR_Exp = 1;
			else if(cV == 5) FSR_Exp = 2;
			else if(cV == 6) FSR_Exp = 3;
		}
	}
	// Main value
	if(!(t_err=req.attr("err")).length && !FSR_Mantissa.isEVal() && !FSR_Exp.isEVal()) {
		req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x00,0x00,0x00));
		tr.messIO(req, "UserProtocol");
		if(!(t_err=req.attr("err")).length) {
			press = ((req.text().charCodeAt(3)*256+req.text().charCodeAt(4))/32000)*FSR_Mantissa*pow(10,FSR_Exp);
			//  Error process
			cV = req.text().charCodeAt(2);
			if(cV&0x7F) {
				if(cV&0x01)	u_err += tr("RS232 synchronization error. ");
				if(cV&0x02)	u_err += tr("Incorrect command, e.g. inadmissible address (syntax error). ");
				if(cV&0x04)	u_err += tr("Inadmissible read command. ");
				if(cV&0x08)	u_err += tr("SP1 status. ");
				if(cV&0x10)	u_err += tr("SP2 status. ");
			}
			else {
				//   Extended Error L-Byte
				req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x00,0x36,0x00));
				tr.messIO(req, "UserProtocol");
				if(!(t_err=req.attr("err")).length) {
					cV = req.text().charCodeAt(5);
					if(cV&0x01)	u_err += tr("Atm. pressure out of range. ");
					if(cV&0x02)	u_err += tr("Temperature out of range. ");
					if(cV&0x10)	u_err += tr("Cal. mode wrong. ");
					if(cV&0x20)	u_err += tr("Pressure underflow. ");
					if(cV&0x40)	u_err += tr("Pressure overflow. ");
					if(cV&0x80)	u_err += tr("Zero adjust warning. ");
				}
				//   Extended Error H-Byte
				req.setAttr("err","2:"+tr("No connection")).setText(SYS.strFromCharCode(0x00,0x37,0x00));
				tr.messIO(req, "UserProtocol");
				cV = req.text().charCodeAt(5);
				if(cV&0x01)	u_err += tr("PT1000 fault (CTR 101 only). ");
				if(cV&0x02)	u_err += tr("Heaterblock overtemp. ");
				if(cV&0x04)	u_err += tr("Electronic overtemp. ");
				if(cV&0x08)	u_err += tr("Zero adjust error. ");
			}
		}
	}

	//Check and write commands
	// Zero-adjust
	if(!(t_err=req.attr("err")).length && zeroSet) {
		req.setAttr("err", "2:"+tr("No connection")).setText(SYS.strFromCharCode(0x40,0x02,0x00));
		tr.messIO(req, "UserProtocol");
		if(!(t_err=req.attr("err")).length) zeroSet = false;
	}

	//Check and set poll mode
	if((t_err=req.attr("err")).length && t_err.toInt() == 4) {
		req.setAttr("err", "2:"+tr("No connection")).setText(SYS.strFromCharCode(0x10,0x00,0x01));
		tr.messIO(req, "UserProtocol");
	}
}

if(t_err.length) {
	press = EVAL_REAL;
	f_err = t_err + " " + u_err;
}
else f_err = "0: " + u_err;','','',1580045150);
INSERT INTO tmplib_DevLib VALUES('IEC60870','IEC-60870','IEC-60870','','IEC 60870 part 5 is one from the IEC 60870 set of standards which defines systems used for telecontrol (supervisory control and data acquisition) in electrical engineering and power system automation applications. Part 5 provides a communication profile for sending basic telecontrol messages between two systems, which uses permanent directly connected data circuits between the systems.

The template currently implements the part 104 (Ethernet transport) for the client and followed services: STARTDT, STOPDT, TESTFR, Ack, C_IC_NA_1 (100), C_CI_NA_1 (101), C_SC_NA_1 (45), M_SP_NA_1 (1), M_SP_TA_1 (2), M_DP_NA_1 (3), M_DP_TA_1 (4), M_ME_NB_1 (11), M_ME_NC_1 (13), M_ME_TF_1 (36), C_CS_NA_1 (103). For information ones, the sequential (SQ) mode enabling and disabling are supported.

For the direct definition of the requested data, the "itemsSet" attribute is provided, where they can be defined individually, with a semantic identifier and name, or in a range named by a template. Often, remote systems may not need to determine the data to transmit and send existing or generic ones on their own initiative, and if they do not, have and need to ask for something additional ones, then you need to get a map of the available parameters with the addresses these you enter in this attribute in the format ai|di|do:{IOA}[-{EndIOA}][:a[:{NameBase}]], where:
- ai|di|do — data type, where:
  - "ai" — analog inputs, transmitted by the message M_ME_NB_1, M_ME_NC_1, M_ME_TF_1 and requested by C_SC_NA_1;
  - "di" — discrete inputs, transmitted by the message M_SP_NA_1, M_SP_TA_1, M_DP_NA_1, M_DP_TA_1 and requested by C_SC_NA_1;
  - "do" — discrete outputs, transmitted by C_SC_NA_1.
- {IOA} — parameter address of the range begin;
- {EndIOA} — the range end (the last item address);
- a — sign of creating the representative attribute for the parameter or the parameters group;
- {NameBase} — naming base of the attributes.

Examples of the parameters definition, rows of the attribute "itemsSet":
- di:100-108:a — discrete inputs in the address range [100...108] with forming the attributes;
- ai:873-880:a — analog inputs in the address range [873...880] with forming the attributes;
- do:1-10:a — discrete outputs in the address range [1...10] with forming the attributes.

An object into the attribute "items" is used to accumulate the gathered and controlling data, to next control as an object with the data represent in a table of the widget "Main.objProps", alarming and allowing to set the writable attributes.

The module provides integrated support for time synchronization of PLC by sending the C_CS_NA_1 service command.

In the template for the first time used the not requesting mode of an output transport and the attributes creation in free while performing.

Author: Roman Savochenko <roman@oscada.org>
Total complexity: 3.8 HD
Sponsored by: Ustijancev Michael for 3.0 HD, SVItoVYR Ltd for 0.8 HD
Version: 1.4.0
License: GPLv2','IEC 60870 у частині 5 є одним з набору стандартів IEC 60870 який визначає системи, що використовуються у віддаленому контролі (телемеханіці — диспетчерському контролі та зборі даних) у інженерній електриці та у застосунках автоматизації енергетичних систем. Частина 5 надає комунікаційний профіль для надсилання базових повідомлень віддаленого контролю між двома системами, який використовує постійні прямі підключення даних між системами.

Шаблон наразі реалізує частину 104 (Ethernet транспорт) для клієнту та наступних сервісів: STARTDT, STOPDT, TESTFR, Ack, C_IC_NA_1 (100), C_CI_NA_1 (101), C_SC_NA_1 (45), M_SP_NA_1 (1), M_ME_NB_1 (11), M_ME_NC_1 (13), M_ME_TF_1 (36), C_CS_NA_1 (103). Для інформаційних сервісів підтримується ввімкнення та вимкнення послідовного (SQ) режиму.

Для прямого визначення запитуваних даних передбачено атрибут "itemsSet", де вони можуть визначатися як окремо, зі смисловим ідентифікатором та назвою, або за діапазоном із найменуванням по шаблону. Часто віддалені системи можуть не потребувати визначення даних для передачі та надсилають наявні або основні зі своєї ініціативи та якщо вони цього не роблять, мають та потрібно запитати щось додаткове то вам треба отримати мапу наявних параметрів з адресами які й ввести у цьому атрибуті записами у форматі ai|di|do:{IOA}[-{EndIOA}][:a[:{NameBase}]], де:
- ai|di|do — тип даних, де:
  - "ai" — аналогові входи, передані повідомленням M_ME_NB_1, M_ME_NC_1, M_ME_TF_1 та запитані C_SC_NA_1;
  - "di" — дискретні входи, передані повідомленням M_SP_NA_1 та запитані C_SC_NA_1;
  - "do" — дискретні виходи, передавані командою C_SC_NA_1.
- {IOA} — адреса параметру або початок діапазону;
- {EndIOA} — кінець (адреса останнього елементу) діапазону;
- a — ознака створення репрезентативних атрибутів для параметру або групи параметрів;
- {NameBase} — база назви атрибуту(ів).

Приклади визначення параметрів, рядки атрибуту "itemsSet":
- di:100-108:a — дискретні входи за діапазоном адрес [100...108] із формуванням атрибутів;
- ai:873-880:a — аналогові входи за діапазоном адрес [873...880] із формуванням атрибутів;
- do:1-10:a — дискретні виходи за діапазоном адрес [1...10] із формуванням атрибутів.

Об''єкт у атрибуті "items" використовується для акумулювання зібраних та контрольованих даних задля подальшого контрою як об''єкт із представленням даних у таблиці віджету "Main.objProps", сигналізуючи та дозволяючи встановлювати записувані атрибути.

Модулем надається вбудована підтримка синхронізації часу ПЛК, надсиланням сервісної команди C_CS_NA_1.

У шаблоні вперше використано не запитувальний режим вихідного транспорту та вільне створення атрибутів під час виконання.

Автор: Роман Савоченко <roman@oscada.org>
Загальна працемісткість: 3.8 ЛД
Спонсорування: Устьянцев Михайло на 3.0 ЛД, ТОВ "СВІТоВИР" на 0.8 ЛД
Версія: 1.3.1
Ліцензія: GPLv2','',10,0,'JavaLikeCalc.JavaScript
function getTm(iSq) {
	ttm_ms = iSq.charCodeAt(1)*256 + iSq.charCodeAt(0);
	ttm = SYS.mktime(ttm_ms/1000, iSq.charCodeAt(2)&0x3F, iSq.charCodeAt(3)&0x1F, iSq.charCodeAt(4)&0x1F, (iSq.charCodeAt(5)&0x0F)-1, 2000+(iSq.charCodeAt(6)&0x7F));
	return ttm + ttm_ms*1e-3;
}

function inVal(aId, aVal, SIQ, wr, itm) {
	if(!SIQ.isEVal() && (SIQ&0xF0)) aVal = EVAL;	else SIQ = 0;
	if(wr.isEVal())	wr = false;
	if(itm.isEVal())	itm = 0;
	if(items[aId].isEVal()) {
		items[aId] = itW = new Object();
		itW.descr = aId;
		itW.wr = wr; itW.alarm = 0;
	} else itW = items[aId];
	itW.val = aVal;
	itW.SIQ = SIQ;	//For a specific quality processing
	if((aO=this[aId])) {
		aO.set(aVal, 0, 0, true);
		if(itm && aO.arch())	aO.arch().setVal(1e6*itm, aVal);
		if(itW.wr) oAVals[aId] = aVal;
	}
}

if(f_start)	{
	transport_ = "", tr = false;
	itemsSet_ = "";
	tmRetr_ = tmRetr;
	t1_ = t1__ = t2_ = t3_ = w_ = 0;
	TESTFR_Act = false;
	STARTDT_act = STARTDT_con = false;
	bufIn = bufOut = "";
	cntTx = cntRx = 0;
	destAddr = addr.parse(0,".").toInt();
	destOA = addr.parse(1,".").toInt();
	//C_IC_NA_1_req = 0;
	C_IC_NA_1 = C_SC_NA_1 = 0;
	C_IC_NA_1con = C_CS_NA_1 = C_CS_NA_1con = C_CI_NA_1 = C_CI_NA_1con = false;
	items = new Object();
	oAVals = new Object();
	return;
}
if(f_stop && !STARTDT_con)	return;	//But at stop we must yet send STOPDT

//Items set changing process
if(itemsSet != itemsSet_) {
	for(off = 0; (iIt=itemsSet.parse(0,"\n",off)).length; ) {
		iIt_tp = iIt.parse(0, ":");
		iIt_IOA = iIt.parse(1, ":");
		iIt_flgs = iIt.parse(2, ":");
		iIt_nmBase = iIt.parse(3, ":");
		iIt_sDscr = false;
		if((iIt_EndIOA=iIt_IOA.indexOf("-")) >= 0) {
			iIt_EndIOA = iIt_IOA.slice(iIt_EndIOA+1).toInt(0);
			iIt_IOA = iIt_IOA.slice(0,iIt_EndIOA).toInt(0);
		}
		else { iIt_IOA = iIt_EndIOA = iIt_IOA.toInt(0); iIt_sDscr = iIt_nmBase.length; }
		if(iIt_tp == "ai")			{ iIt_nmBase = iIt_nmBase.length ? iIt_nmBase : "AI"; iIt_vtp = "real,ro"; }
		else if(iIt_tp == "di")	{ iIt_nmBase = iIt_nmBase.length ? iIt_nmBase : "DI"; iIt_vtp = "boolean,ro"; }
		else if(iIt_tp == "do")	{ iIt_nmBase = iIt_nmBase.length ? iIt_nmBase : "DO"; iIt_vtp = "boolean"; }
		else continue;
		while(iIt_IOA <= iIt_EndIOA) {
			aId = iIt_tp+iIt_IOA; aDscr = iIt_nmBase+(iIt_sDscr?"":"["+iIt_IOA+"]"); aWr = (iIt_tp == "do");
			if(items[aId].isEVal()) { items[aId] = itW = new Object(); itW.descr = aDscr; itW.wr = aWr; itW.alarm = 0; }
			if(iIt_flgs.indexOf("a") >= 0) {
				this.attrAdd(aId, aDscr, iIt_vtp);
				if(aWr)	oAVals[aId] = EVAL_INT;
				this[aId].set(EVAL_INT, 0, 0, true);
			}
			iIt_IOA++;
		}
	}
	itemsSet_ = itemsSet;
}

//Check for the transport change and connect
t_err = "";
if(!tr || transport != transport_)	{
	tr = SYS.Transport.nodeAt(transport, ".");
	transport_ = transport;
	if(tr) tr.start(false);
	itemsSet_ = "";
	tmRetr_ = tmRetr;
	STARTDT_act = false;
	items = new Object();
	oAVals = new Object();
}
if(!tr)	t_err = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
else if(!tr.start()) {
	if(tmRetr_ >= tmRetr) {
		tr.start(true);
		bufIn = bufOut = itemsSet_ = "";
		tmRetr_ = t1_ = t1__ = t2_ = t3_ = w_ = 0;
	}
	else tmRetr_ += 1/f_frq;
	if(tr.start()) STARTDT_act = false;
	else t_err = "1:"+tr("Output transport ''%1'' stoped.").replace("%1",transport);
}
else {
	//Check and finish, U (STOPDT, Act)
	if(f_stop && STARTDT_con) bufOut += SYS.strFromCharCode(0x68, 0x04, 0x10|0x03, 0, 0, 0);

	//Send output buffer and read the transport for an input data
	if(bufOut.length) SYS.messDebug("/IEC60870",tr("Good output sequence")+": "+SYS.strDecode(bufOut,"Bin"," "));
	bufIn += tr.messIO(bufOut, -0.001);
	bufOut = "";
	//Input requests processing
	while(bufIn.length) {
		if(bufIn.length >= 2 && (bufIn.charCodeAt(0) != 0x68 || (seqSz=bufIn.charCodeAt(1)) > 253)) {
			SYS.messDebug("/IEC60870",tr("Wrong sequence, rejected")+": "+SYS.strDecode(bufIn,"Bin"," "));
			bufIn = "";
			break;
		}
		if(bufIn.length < 6 || (bufIn.length-2) < seqSz)	break;		//Not full, wait

		SYS.messDebug("/IEC60870",tr("Good input sequence")+": "+SYS.strDecode(bufIn.slice(0,seqSz+2),"Bin"," "));

		cntRx_ = -1;
		sB1 = bufIn.charCodeAt(2);
		if((sB1&0x3) == 0x3 && (sB1&0x40))			TESTFR_Act = true;	//U (TESTFR, Act)
		else if((sB1&0x3) == 0x3 && (sB1&0x80))	t1__ = 0;				//U (TESTFR, Con)
		else if((sB1&0x3) == 0x3 && (sB1&0x08)) {								//U (STARTDT, Con)
			STARTDT_con = true;
			t1_ = t1__ = t2_ = t3_ = 0;
			cntTx = cntRx = 0;
			destAddr = addr.parse(0,".").toInt();
			destOA = addr.parse(1,".").toInt();
			C_IC_NA_1 = C_SC_NA_1 = 0;
			C_CS_NA_1 = C_CI_NA_1 = false;
		}
		else if(STARTDT_con && (sB1&0x3) == 1)	cntRx_ = (bufIn.charCodeAt(5)>>1)*128 + (bufIn.charCodeAt(4)>>1);		//S (Ack)
		else if(STARTDT_con && (sB1&0x1) == 0) {	//I
			cntTx_ = bufIn.charCodeAt(3)*128 + (sB1>>1);
			if(cntTx_ != cntRx)
				t_err = "2:"+tr("Connection terminated by the input sequence broken, lost input packages, S(R)=%1, S(T)=%2").replace("%1",cntRx.toString()).replace("%2",cntTx_.toString());
			cntRx_ = bufIn.charCodeAt(5)*128 + (bufIn.charCodeAt(4)>>1);
			ASDU_id = bufIn.charCodeAt(6);
			ASDU_els = bufIn.charCodeAt(7);			//7=SQ, 0...6=number
			ASDU_reas = bufIn.charCodeAt(8);			//7=TEST, 6=P/N, 0...5=number
			ASDU_addr = bufIn.charCodeAt(9);
			ASDU_OA = bufIn.charCodeAt(11)*256 + bufIn.charCodeAt(10);
			//destAddr = ASDU_addr, destOA = ASDU_OA;
			isSQ = ASDU_els&0x80;
			ASDU_els = ASDU_els&0x7F;

			cntRx = (cntRx+1)&0x7FFF;
			t2_ = 1/f_frq;	
			w_++;

			if(ASDU_id == 100) {
				if(ASDU_reas == 7)			C_IC_NA_1con = true;	//ActCon
				else if(ASDU_reas == 10)	C_IC_NA_1 = tmPoolAll;//-1;// 1;	//ActTerm, repeate after {tmPoolAll} second
			}
			else if(ASDU_id == 101) {
				if(ASDU_reas == 7)			C_CI_NA_1con = true;	//ActCon
				//else if(ASDU_reas == 10)	C_CI_NA_1 = false;	//ActTerm, repeate after some timeout
			}
			else if(ASDU_id == 103 && ASDU_reas == 7)	C_CS_NA_1con = true;
			else if(ASDU_id == 1) {	//M_SP_NA_1, Single-point information
				if((10+4+(ASDU_els-1)*(isSQ?1:4)) != seqSz)	SYS.messDebug("/IEC60870","M_SP_NA_1: "+tr("Items number is discrepancy to the package size"));
				else for(iEl = 0, iOff = 12; iEl < ASDU_els; iEl++, iOff += 1) {
					if(!isSQ || !iEl)	{ IOA = (bufIn.charCodeAt(iOff+2)<<16) + (bufIn.charCodeAt(iOff+1)<<8) + bufIn.charCodeAt(iOff); iOff += 3; }
					else IOA++;
					inVal("di"+IOA, (SIQ=bufIn.charCodeAt(iOff))&0x01, SIQ);
				}
			}
			else if(ASDU_id == 2) {	//M_SP_TA_1, Single-point information with timestamp
				if((10+4+(ASDU_els-1)*(isSQ?8:11)) != seqSz)	SYS.messDebug("/IEC60870","M_SP_TA_1: "+tr("Items number is discrepancy to the package size"));
				else for(iEl = 0, iOff = 12; iEl < ASDU_els; iEl++, iOff += 8) {
					if(!isSQ || !iEl)	{ IOA = (bufIn.charCodeAt(iOff+2)<<16) + (bufIn.charCodeAt(iOff+1)<<8) + bufIn.charCodeAt(iOff); iOff += 3; }
					else IOA++;
					inVal("di"+IOA, (SIQ=bufIn.charCodeAt(iOff))&0x01, SIQ, false, getTm(bufIn.slice(iOff+1,iOff+8)));
				}
			}
			else if(ASDU_id == 3) {	//M_DP_NA_1, Two-element information
				if((10+4+(ASDU_els-1)*(isSQ?1:4)) != seqSz)	SYS.messDebug("/IEC60870","M_DP_NA_1: "+tr("Items number is discrepancy to the package size"));
				else for(iEl = 0, iOff = 12; iEl < ASDU_els; iEl++, iOff += 1) {
					if(!isSQ || !iEl)	{ IOA = (bufIn.charCodeAt(iOff+2)<<16) + (bufIn.charCodeAt(iOff+1)<<8) + bufIn.charCodeAt(iOff); iOff += 3; }
					else IOA++;
					inVal("di"+IOA, (((SIQ=bufIn.charCodeAt(iOff))&0x3)==0x3||(SIQ&0x3)==0)?EVAL:(SIQ&0x2)?true:false, SIQ);
				}
			}
			else if(ASDU_id == 4) {	//M_DP_TA_1, Two-element information with timestamp
				if((10+4+(ASDU_els-1)*(isSQ?8:11)) != seqSz)	SYS.messDebug("/IEC60870","M_DP_TA_1: "+tr("Items number is discrepancy to the package size"));
				else for(iEl = 0, iOff = 12; iEl < ASDU_els; iEl++, iOff += 8) {
					if(!isSQ || !iEl)	{ IOA = (bufIn.charCodeAt(iOff+2)<<16) + (bufIn.charCodeAt(iOff+1)<<8) + bufIn.charCodeAt(iOff); iOff += 3; }
					else IOA++;
					inVal("di"+IOA, (((SIQ=bufIn.charCodeAt(iOff))&0x3)==0x3||(SIQ&0x3)==0)?EVAL:(SIQ&0x2)?true:false, SIQ, false, getTm(bufIn.slice(iOff+1,iOff+8)));
				}
			}
			else if(ASDU_id == 11) {	//M_ME_NB_1, Measured value, scaled value
				if((10+6+(ASDU_els-1)*(isSQ?3:6)) != seqSz)	SYS.messDebug("/IEC60870","M_ME_NB_1: "+tr("Items number is discrepancy to the package size"));
				else for(iEl = 0, iOff = 12; iEl < ASDU_els; iEl++, iOff += 3) {
					if(!isSQ || !iEl)	{ IOA = (bufIn.charCodeAt(iOff+2)<<16) + (bufIn.charCodeAt(iOff+1)<<8) + bufIn.charCodeAt(iOff); iOff += 3; }
					else IOA++;
					val = (bufIn.charCodeAt(iOff+1)<<8) + bufIn.charCodeAt(iOff);
					if(val > 32767) val -= 65536;
					inVal("ai"+IOA, val, bufIn.charCodeAt(iOff+2));
				}
			}
			else if(ASDU_id == 13) {	//M_ME_NC_1, Measured value, short real value
				if((10+8+(ASDU_els-1)*(isSQ?5:8)) != seqSz)	SYS.messDebug("/IEC60870","M_ME_NC_1: "+tr("Items number is discrepancy to the package size"));
				else for(iEl = 0, iOff = 12; iEl < ASDU_els; iEl++, iOff += 5) {
					if(!isSQ || !iEl)	{ IOA = (bufIn.charCodeAt(iOff+2)<<16) + (bufIn.charCodeAt(iOff+1)<<8) + bufIn.charCodeAt(iOff); iOff += 3; }
					else IOA++;
					inVal("ai"+IOA, Special.FLibSYS.floatMergeWord(bufIn.charCodeAt(iOff+1)*256+bufIn.charCodeAt(iOff),bufIn.charCodeAt(iOff+3)*256+bufIn.charCodeAt(iOff+2)), bufIn.charCodeAt(iOff+4));
				}
			}
			else if(ASDU_id == 36) {	//M_ME_TF_1, Measured value, short real value with timestamp
				if((10+15+(ASDU_els-1)*(isSQ?12:15)) != seqSz)	SYS.messDebug("/IEC60870","M_ME_TF_1: "+tr("Items number is discrepancy to the package size"));
				else for(iEl = 0, iOff = 12; iEl < ASDU_els; iEl++, iOff += 12) {
					if(!isSQ || !iEl)	{ IOA = (bufIn.charCodeAt(iOff+2)<<16) + (bufIn.charCodeAt(iOff+1)<<8) + bufIn.charCodeAt(iOff); iOff += 3; }
					else IOA++;
					inVal("ai"+IOA, Special.FLibSYS.floatMergeWord(bufIn.charCodeAt(iOff+1)*256+bufIn.charCodeAt(iOff),bufIn.charCodeAt(iOff+3)*256+bufIn.charCodeAt(iOff+2)), bufIn.charCodeAt(iOff+4),
						false, getTm(bufIn.slice(iOff+5,iOff+12)));
				}
			}
			else if(ASDU_id == 45) {	//C_SC_NA_1, Single-point confirm
				if((10+4+(ASDU_els-1)*(isSQ?1:4)) != seqSz)	SYS.messDebug("/IEC60870", "C_SC_NA_1: "+tr("Items number is discrepancy to the package size"));
				else for(iEl = 0, iOff = 12; iEl < ASDU_els; iEl++, iOff += 1) {
					if(!isSQ || !iEl)	{ IOA = (bufIn.charCodeAt(iOff+2)<<16) + (bufIn.charCodeAt(iOff+1)<<8) + bufIn.charCodeAt(iOff); iOff += 3; }
					else IOA++;
					inVal("do"+IOA, (SIQ=bufIn.charCodeAt(iOff))&0x01, SIQ);
				}
				C_SC_NA_1 = 0;
			}
			//else ...
		}
		if(cntRx_ >= 0 && cntRx_ == cntTx) t1_ = 0;

		t3_ = 0;
		bufIn = bufIn.slice(seqSz+2);
	}
	//Check and the service negotiation perform.
	while(!t_err.length) {
		if(TESTFR_Act) {
			bufOut += SYS.strFromCharCode(0x68, 0x04, 0x80|0x03, 0, 0, 0);
			TESTFR_Act = false;
		}
		else if(!STARTDT_act)	{	//U (STARTDT, Act);
			bufOut += SYS.strFromCharCode(0x68, 0x04, 0x04|0x03, 0, 0, 0);
			STARTDT_act = true; STARTDT_con = false;
		}
		else if(t1_ > t1)
			t_err = "3:" + tr("Connection terminated by not acknowledge transmited package ''%1'' by timeout t1 (%2).").replace("%1",cntTx.toString()).replace("%2",t1.toFixed(2));
		else if(t1__ > t1)
			t_err = "4:" + tr("Connection terminated by not confirmed TEST package by timeout t1 (%1).").replace("%1",t1.toFixed(2));
		else if(t3_ > t3) {						//U (TESTFR, Act);
			bufOut += SYS.strFromCharCode(0x68, 0x04, 0x40|0x03, 0, 0, 0);
			t3_ = 0; t1__ = 1/f_frq;
		}
		else if(t2_ > t2 || w_ >= w) {	//S (Ack)
			bufOut += SYS.strFromCharCode(0x68, 0x04, 0x01, 0, (cntRx<<1)&0xFF, cntRx>>7);
			t2_ = w_ = 0;
		}
		else if(STARTDT_con /*&& destAddr >= 0*/) {	//I
			ASDU_ = "";
			// Services process
			if(C_IC_NA_1 > 0)	C_IC_NA_1 = max(0, C_IC_NA_1-1/f_frq);
			else if(C_IC_NA_1 == 0) {
				//Generic acquisition request (20)
				ASDU_ = SYS.strFromCharCode(100, 0x01, 0x06, destAddr, destOA&0xFF, destOA>>8, 0x00, 0x00, 0x00, 0x14);
				C_IC_NA_1 = -1; C_IC_NA_1con = false;
			}
			else if(!C_CI_NA_1) {
				//Generic counters request (5)
				ASDU_ = SYS.strFromCharCode(101, 0x01, 0x06, destAddr, destOA&0xFF, destOA>>8, 0x00, 0x00, 0x00, 0x05);
				C_CI_NA_1 = true; C_CI_NA_1con = false;
			}
			else if(syncTimePLC && !C_CS_NA_1) {
				//Time sync
				tmMs = 0; tm = SYS.time(tmMs); tmMs = (tm%60)*1000+tmMs/1000;
				tmMin = tmHour = tmDay = tmMonth = tmYear = tmWDay = tmIsDst = 0;
				SYS.localtime(tm, 0, tmMin, tmHour, tmDay, tmMonth, tmYear, tmWDay, 0, tmIsDst);
				ASDU_ = SYS.strFromCharCode(103, 0x01, 0x06, destAddr, destOA&0xFF, destOA>>8, 0, 0, 0, tmMs&0xFF, tmMs>>8, tmMin, tmHour|(tmIsDst?0x80:0), tmDay|((tmWDay?tmWDay:7)<<5), tmMonth+1, tmYear-2000);
				C_CS_NA_1 = true; C_CS_NA_1con = false;
			}
			else if((C_SC_NA_1=max(0,C_SC_NA_1-1/f_frq)) == 0) {
				//Check for commands set, C_SC_NA_1 (45)
				cItN = 0; cItSeq = "";
				// By attributes
				for(iIt in oAVals)
					if((aO=this[iIt]) && !(aOval=aO.get()).isEVal() && aOval != oAVals[iIt] && !cItN) {
						IOA = iIt.slice(2).toInt();
						cItSeq += SYS.strFromCharCode(IOA&0xFF, (IOA>>8)&0xFF, IOA>>16, aOval?0x01:0);
						//oAVals[iIt] = aOval;
						cItN++;
					}
				// By items object
				for(iIt in items)
					if((aO=items[iIt]) && !(aOval=aO.set).isEVal() && !cItN) {
						IOA = iIt.slice(2).toInt();
						cItSeq += SYS.strFromCharCode(IOA&0xFF, (IOA>>8)&0xFF, IOA>>16, aOval.toInt()?0x01:0);
						aO.set = EVAL_BOOL;
						cItN++;
					}
				// Finish the request
				if(cItN) {
					ASDU_ = SYS.strFromCharCode(45, cItN, 0x06, destAddr, destOA&0xFF, destOA>>8) + cItSeq;
					C_SC_NA_1 = 1;	//After one second repeat
				}
			}

			// Append header and place to output
			if(ASDU_.length) {
				bufOut += SYS.strFromCharCode(0x68, 4+ASDU_.length, (cntTx<<1)&0xFF, cntTx>>7, (cntRx<<1)&0xFF, cntRx>>7) + ASDU_;
				cntTx = (cntTx+1)&0x7FFF;
				t1_ = 1/f_frq;
				//t2_ = w_ = 0;
			} else break;
		}
		else break;
	}

	if(t1_)	t1_ += 1/f_frq;
	if(t1__)	t1__ += 1/f_frq;
	if(t2_)	t2_ += 1/f_frq;
	t3_ += 1/f_frq;
}

//Error set
if(t_err.length) {
	if(tr && tr.start()) tr.start(false);
	if(f_err != t_err) {
		items = new Object();
		oAVals = new Object();
		aLs = this.nodeList("a_");
		for(iA = 0; iA < aLs.length; iA++)
			if((aPref=aLs[iA].slice(2,4)) == "di" || aPref == "do" || aPref == "ai")
				this[aLs[iA].slice(2)].set(EVAL_INT, 0, 0, true);
		SYS.messDebug("/IEC60870", tr("Error")+": "+t_err);
	}
	f_err = t_err;
}
else f_err = "0";','','',1592158535);
INSERT INTO tmplib_DevLib VALUES('SSCP','Shark Slave Communication Protocol','','','Shark Slave Communication Protocol from EnergoCentrum PLUS, s.r.o.

Author: Roman Savochenko <roman@oscada.org>
Sponsored: Costumer Faster CZ (http://faster.cz)
Version: 0.6.2
License: GPLv2','','',30,0,'JavaLikeCalc.JavaScript
//Same request to the device
function req(tr, addr, func, data) {
	req = SYS.strFromCharCode(addr, (func>>8)&0xFF, func&0xFF, (data.length>>8)&0xFF, data.length&0xFF) + data;
	//SYS.messInfo("/SSCP",tr("Request")+": "+SYS.strDecode(req,"Bin"," "));
	resp = tr.messIO(req);
	while(resp.length && (resp.length < 5 || (resp.length-5) < ((resp.charCodeAt(3)<<8)|resp.charCodeAt(4))) &&
		(tresp=tr.messIO("")).length) resp += tresp;
	//SYS.messInfo("/SSCP",tr("Response")+": "+SYS.strDecode(resp,"Bin"," "));
	data = resp;
	if(resp.length < 5)
		return tr("3:No a response or the response short.");
	if((resp.length-5) < ((resp.charCodeAt(3)<<8)|resp.charCodeAt(4)))
		return tr("3:Incomplete response.");
	if(resp.charCodeAt(0) != addr)	return tr("4:Invalid host''s address received.");
	func_ = ((resp.charCodeAt(1)&0xFF)<<8) | resp.charCodeAt(2);
	if(func_ == 0xfffe)						return tr("4:Unknown function.");
	if(func_ == 0xffff)						return tr("4:Insufficient rights.");
	if((func_&0x3FFF) != func)			return tr("4:Invalid function received.");
	if((func_&0xC000) != 0x8000) {
		opErr = tr("5:Operation error.");
		if(resp.length >= 9) {
			errCod = (resp.charCodeAt(5)<<24) | (resp.charCodeAt(6)<<16) | (resp.charCodeAt(7)<<8) | resp.charCodeAt(8);
			if(errCod == 0x0101)		opErr += " "+tr("Wrong login.");
			else if(errCod == 0x103)	opErr += " "+tr("No such variable.");
			else if(errCod == 0x112)	opErr += " "+tr("Size mismatch.");
			//... Append here for needs
			else opErr += " "+tr("Unknown error %1.").replace("%1",errCod.toString());
		}
		if(resp.length >= 17)	opErr += " "+tr("Destination:")+" "+SYS.strDecode(resp.slice(9),"Bin","")+"h";
		return opErr;
	}
	data = resp.slice(5);
	return "0";
}

//Set transport and init
if(f_start) {
	transport_ = transport;
	tr = SYS.Transport.Sockets.nodeAt("out_"+transport);
	vlist = new Object();
	prcVList = new Array();
	isLogin = false;
	toLoadVarLsts = true;
	aSelList = false;
	list_ = "";
}
if(f_stop) return;

t_err = "0";

//Check for the transport change and connect
if(!tr || transport != transport_)	{
	tr = SYS.Transport.Sockets.nodeAt("out_"+transport);
	transport_ = transport;
}
if(!tr)	t_err = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
else if(addr < 0 || addr > 255)
	t_err = "2:"+tr("Address ''%1'' out of range [0...255].").replace("%1",addr.toString());
else {
	//Generic information update.
	if(toLoadVarLsts) {
		toLoadVarLsts = false;
		fLs = SYS.system("ls -1 "+listsDir+"/*.vlist");
		for(off = 0; (fln=fLs.parse(0,"\n",off)).length; ) {
			//stTm = SYS.time();
			fl = SYS.fileRead(fln);
			//fl = fl.split("\x0D\x0A");
			//for(iL = 0; iL < fl.length; iL++) {
				//ln = fl[iL];
			for(off1 = 0; (ln=fl.parse(0,"\x0D\x0A",off1)).length; ) {
			//for(off1 = 0; (ln=Special.FLibSYS.strParse(fl,0,"\x0D\x0A",off1)).length; ) {
				off2 = 0;
				vid = ln.parse(0, ";", off2);
				if(vid == "Project" || !(tVl=ln.parse(0,";",off2)).length) continue;
				vid += ":"+tVl;
				//SYS.messInfo("SSCP","vid="+vid);
				vlist[vid] = vO = new Object();
				vO.Type = ln.parse(0, ";", off2);
				vO.CommUid = ln.parse(0, ";", off2).toInt();
				vO.Offset = ln.parse(0, ";", off2).toInt();
				vO.Length = ln.parse(0, ";", off2).toInt();
				vO.ParentTypeFamily = ln.parse(0, ";", off2);
				vO.HistoryId = ln.parse(0, ";", off2);
			}
			//SYS.messInfo("SSCP", "Time="+(SYS.time()-stTm)+"s; length="+fl.length);
		}
		fl = selLst = "";
		//for(tVl in vlist) selLst += tVl+";";
		//this.attrAdd("selList", tr("Appending variable"), "string|sel", selLst+"\n"+selLst);
		//aSelList = this.selList;
		//aSelList.set("");
		for(tVl in vlist) selLst += tVl+"\n";
		this.attrAdd("selList", tr("Allowed variables"), "text|ro");
		aSelList = this.selList;
		aSelList.set(selLst,0,0,true);
	}
	//Check for select a varable and append it to the processing list
	/*if(aSelList && (tVl=aSelList.get()).length) {
		if(list.indexOf(tVl) == -1)
			list += ((list.length && list[list.length-1] != "\n")?"\n":"")+ tVl+"\n";
		aSelList.set("");
	}*/
	//The processing variables list update/form
	if(list != list_) {
		list_ = list;
		prcVList = new Array();
		for(off = 0; (vid=list.parse(0,"\n",off)).length; ) {
			if((vO=vlist[vid]).isEVal()) continue;
			prcVList.push(vO);
			vO.aId = vid.replace(new RegExp("[\\.\\:]+","g"),"_").replace(new RegExp("[\\$\\-]+","g"),"");
			vTp = "string";
			if(vO.Type.indexOf("bool") != -1)			vTp = "boolean";
			else if(vO.Type.indexOf("real") != -1)	vTp = "real";
			else if(vO.Type.indexOf("int") != -1)		vTp = "integer";
			this.attrAdd(vO.aId, vid, vTp+"|ro");
			vO.aO = this[vO.aId];
			//SYS.messInfo("/SSCP","aId="+vO.aId);
		}
	}

	//Same requests
	if(!isLogin) {
		io = SYS.strFromCharCode(0x01, (maxDtFrm>>8)&0xFF, maxDtFrm&0xFF, user.length) +
				user + SYS.strFromCharCode(0x10) + Special.FLibSYS.MD5(pass);
		if(!(t_err=req(tr,addr,0x0100,io)).toInt()) {
			verPrt = io.charCodeAt(0);
			maxDtFrmServ = (io.charCodeAt(1)<<8) | io.charCodeAt(2);
			isLogin = true;
		}
	}
	if(isLogin && !t_err.toInt()) {
		for(iV0 = iV1 = 0; iV0 < prcVList.length; iV0 = iV1) {
			frmSz = frmSzServ = 0;
			io = Special.FLibSYS.IO("", "", "b");
			io.wr(0x80);
			for(iV1 = iV0; iV1 < prcVList.length; iV1++) {
				vO = prcVList[iV1];
				frmSz += vO.Length; frmSzServ += 12;
				if(frmSz >= maxDtFrm || frmSzServ >= maxDtFrmServ) break;
				io.wr(vO.CommUid, "uint32").wr(vO.Offset, "uint32").wr(vO.Length, "uint32");
			}
			//SYS.messInfo("/SSCP","req="+SYS.strDecode(io.string,"Bin"," "));
			t_err = req(tr, addr, 0x0500, io.string); io.pos = 0;
			//SYS.messInfo("/SSCP","resp="+SYS.strDecode(io.string,"Bin"," "));
			for(iV2 = iV0; iV2 < iV1; iV2++) {
				vO = prcVList[iV2];
				//SYS.messInfo("/SSCP","aId="+vO.aId+"; pos="+io.pos);
				if(t_err.toInt()) tVl = EVAL;
				else {
					if(vO.Type.indexOf("bool") != -1 && vO.Length == 1)
						tVl = io.read("int8", 1);
					else if(vO.Type.indexOf("real") != -1 && (vO.Length == 4 || vO.Length == 8))
						tVl = io.read((vO.Length==8)?"double":"float", 1);
					else if(vO.Type.indexOf("int") != -1 && (vO.Length == 2 || vO.Length == 4))
						tVl = io.read((vO.Length==4)?"int32":"int16", 1);
					else if(vO.Type.indexOf("dt") != -1 && vO.Length == 8) {
						tVl = io.read("int64", 1);
						tVl = SYS.strftime(tVl*1e-7-62135596800) + "." + (tVl%10000000).toString(10,7);
					}
					else if(vO.Type.indexOf("time") != -1 && vO.Length == 8) {
						tm = io.read("int64",1)*1e-7;
						lev = 0;
						days = floor(tm/(24*60*60));
						hours = floor(tm/(60*60))%24;
						mins = floor(tm/(60))%60;
						usec = 1e6 * (tm - days*24*60*60 - hours*60*60 - mins*60);
						tVl = "";
						if(days)	{ tVl += days+tr("day"); lev = max(lev,6); }
						if(hours)	{ tVl += (tVl.length?" ":"")+hours+tr("hour"); lev = max(lev,5); }
						if(mins && lev < 6)	{ tVl += (tVl.length?" ":"")+mins+tr("min"); lev = max(lev,4); }
						if((1e-6*usec) > 0.5 && lev < 5)		{ tVl += (tVl.length?" ":"")+(1e-6*usec).toPrecision(3)+tr("s"); lev = max(lev,3); }
						else if((1e-3*usec) > 0.5 && !lev)	{ tVl += (tVl.length?" ":"")+(1e-3*usec).toPrecision(4)+tr("ms"); lev = max(lev,2); }
						else if(usec > 0.5 && !lev)				{ tVl += (tVl.length?" ":"")+usec.toPrecision(4)+tr("us"); lev = max(lev,1); }
						else if(!lev)	tVl += (tVl.length?" ":"")+(1e3*usec).toPrecision(4)+tr("ns");
					}
					else tVl = SYS.strDecode(io.read("char", vO.Length), "Bin", " ");;
				}
				vO.aO.set(tVl, 0, 0, true);
			}
		}
	}
}

f_err = t_err;','','',1580047300);
INSERT INTO tmplib_DevLib VALUES('m200','Mercury 200','Меркурій 200','Меркурий 200','One phase counter of electricity Mercury 200, 203.2Т, 206 from the firm "Incotex" (http://www.incotexcom.ru)

Author: Arsen Zakojan <godzilla919@gmail.com>
Version: 1.0.0
License: GPLv2','Однофазні лічильники електроенергії Меркурій 200,  203.2Т,  206 компанії Інкотекс (http://www.incotexcom.ru).
Автор: Арсен Закоян <godzilla919@gmail.com>
Версія 1.0.0','Однофазные счетчики электроэнергии Меркурий 200,  203.2Т,  206 компании Инкотекс (http://www.incotexcom.ru).
Автор: Арсен Закоян <godzilla919@gmail.com>
Версия 1.0.0',600,0,'JavaLikeCalc.JavaScript
if(naladchik)//если счетчик закондирован программой Наладчик+
{first=250;
second=3+(8*netaddr);}
else//если обычный сетевой адрес(с завода 6 последних цифр)
{first= floor(netaddr/16777216);//результат деления с округлением в меньшую сторону
second=netaddr-(first*16777216);}//результат вычисления остатка от деления
if(f_start){;t1= EVAL_STR;t2= EVAL_STR;t3= EVAL_STR;t4= EVAL_STR;s= EVAL_STR;U= EVAL_STR;I= EVAL_STR;P= EVAL_STR;}//обнуляем значения
if(tarif)//если выбрано считывание показаний тарифов
{req = SYS.XMLNode("mess").setAttr("ProtIt","m200").setAttr("first",first).setAttr("second",second).setAttr("data",SYS.strFromCharCode(0x27));}
SYS.Transport["Serial"]["out_"+transport].messIO(req,"UserProtocol");
if(req.text().length <23){ answer = EVAL_STR;t1= EVAL_STR;t2= EVAL_STR;t3= EVAL_STR;t4= EVAL_STR;s= EVAL_STR;}//если нет ответа
else //если ответ получен
{t1 =Special.FLibSYS.str2real( req.text().slice(15,17)+req.text().slice(18,20)+req.text().slice(21,23)+req.text().slice(24,26))/100;
t2 =Special.FLibSYS.str2real( req.text().slice(27,29)+req.text().slice(30,32)+req.text().slice(33,35)+req.text().slice(36,38))/100;
t3 =Special.FLibSYS.str2real( req.text().slice(39,41)+req.text().slice(42,44)+req.text().slice(45,47)+req.text().slice(48,50))/100;
t4 =Special.FLibSYS.str2real( req.text().slice(51,53)+req.text().slice(54,56)+req.text().slice(57,59)+req.text().slice(60,62))/100;
s=t1+t2+t3+t4;}
if(UIP)//{//если выбрано считывание мгновенных показаний
{req = SYS.XMLNode().setAttr("ProtIt","m200").setAttr("first",first).setAttr("second",second).setAttr("data",SYS.strFromCharCode(0x63));
SYS.Transport["Serial"]["out_"+transport].messIO(req,"UserProtocol");}
if(req.text().length <14){U= EVAL_STR;I= EVAL_STR;P= EVAL_STR;}//если нет ответа
else//если ответ получен
{U =Special.FLibSYS.str2real( req.text().slice(15,17)+req.text().slice(18,20))/10;
I =Special.FLibSYS.str2real( req.text().slice(21,23)+req.text().slice(24,26))/100;
P =Special.FLibSYS.str2real( req.text().slice(27,29)+req.text().slice(30,32)+req.text().slice(33,35));}','','',1580047532);
INSERT INTO tmplib_DevLib VALUES('m230','Mercury 230','Меркурій 230','Меркурий 230','Three phase counter of electricity Mercury 230, 231, 232, 233, 234, 236 from the firm "Incotex" (http://www.incotexcom.ru).

Author: Arsen Zakojan <godzilla919@gmail.com>
Version: 1.0.0
License: GPLv2','Трифазні лічильники електроенергії Меркурій 230, 231, 232, 233,  234,  236  компанії Інкотекс (http://www.incotexcom.ru).
Автор: Арсен Закоян <godzilla919@gmail.com>
Версія 1.0','Трехфазные счетчики электроэнергии Меркурий 230, 231, 232, 233,  234,  236  компании Инкотекс (http://www.incotexcom.ru).
Автор: Арсен Закоян <godzilla919@gmail.com>
Версия 1.0',300,0,'JavaLikeCalc.JavaScript
if(f_start)
//очищаем все данные
{answer = EVAL_STR;TSAp  = EVAL_STR;TSAm = EVAL_STR;TSRp = EVAL_STR;TSRm = EVAL_STR;
T1Ap  = EVAL_STR;T1Am = EVAL_STR;T1Rp = EVAL_STR;T1Rm = EVAL_STR;T2Ap  = EVAL_STR;T2Am = EVAL_STR;
T2Rp = EVAL_STR;T2Rm = EVAL_STR; T3Ap  = EVAL_STR;T3Am = EVAL_STR;T3Rp = EVAL_STR;T3Rm = EVAL_STR;
T4Ap  = EVAL_STR;T4Am = EVAL_STR;T4Rp = EVAL_STR;T4Rm = EVAL_STR;
PS = EVAL_STR;P1= EVAL_STR;P2= EVAL_STR;P3= EVAL_STR;U1= EVAL_STR;U2= EVAL_STR;U3= EVAL_STR;
I1= EVAL_STR;I2= EVAL_STR;I3= EVAL_STR;QS= EVAL_STR;Q1= EVAL_STR;Q2= EVAL_STR;Q3= EVAL_STR;
SS= EVAL_STR;S1= EVAL_STR;S2= EVAL_STR;S3= EVAL_STR;KS= EVAL_STR;K1= EVAL_STR;K2= EVAL_STR;
K3= EVAL_STR;F1= EVAL_STR;N1= EVAL_STR;}
//отправка пароля
req = SYS.XMLNode().setAttr("ProtIt","m230").setAttr("netaddr",netaddr).setAttr("data",SYS.strFromCharCode(0x01,0x01,Special.FLibSYS.str2int(password.slice(0,1)),Special.FLibSYS.str2int(password.slice(1,2)),Special.FLibSYS.str2int(password.slice(2,3)),Special.FLibSYS.str2int(password.slice(3,4)),Special.FLibSYS.str2int(password.slice(4,5)),Special.FLibSYS.str2int(password.slice(5,6))));
SYS.Transport["Serial"]["out_"+transport].messIO(req,"UserProtocol");
if(!req.text()) {answer = EVAL_STR;TSAp  = EVAL_STR;TSAm = EVAL_STR;TSRp = EVAL_STR;TSRm = EVAL_STR;
T1Ap  = EVAL_STR;T1Am = EVAL_STR;T1Rp = EVAL_STR;T1Rm = EVAL_STR;T2Ap  = EVAL_STR;T2Am = EVAL_STR;
T2Rp = EVAL_STR;T2Rm = EVAL_STR; T3Ap  = EVAL_STR;T3Am = EVAL_STR;T3Rp = EVAL_STR;T3Rm = EVAL_STR;
T4Ap  = EVAL_STR;T4Am = EVAL_STR;T4Rp = EVAL_STR;T4Rm = EVAL_STR;
PS = EVAL_STR;P1= EVAL_STR;P2= EVAL_STR;P3= EVAL_STR;U1= EVAL_STR;U2= EVAL_STR;U3= EVAL_STR;
I1= EVAL_STR;I2= EVAL_STR;I3= EVAL_STR;QS= EVAL_STR;Q1= EVAL_STR;Q2= EVAL_STR;Q3= EVAL_STR;
SS= EVAL_STR;S1= EVAL_STR;S2= EVAL_STR;S3= EVAL_STR;KS= EVAL_STR;K1= EVAL_STR;K2= EVAL_STR;
K3= EVAL_STR;F1= EVAL_STR;N1= EVAL_STR;return;}
else answer =req.text();
//Энергия от сброса сумма Т
if(TS){
req = SYS.XMLNode().setAttr("ProtIt","m230").setAttr("netaddr",netaddr).setAttr("data",SYS.strFromCharCode(0x05,0x00,0x00));
SYS.Transport["Serial"]["out_"+transport].messIO(req,"UserProtocol");
if(!req.text()){TSAp  = EVAL_STR;TSAm = EVAL_STR;TSRp = EVAL_STR;TSRm = EVAL_STR; }
else {
if(req.text().slice(0,10)!=2147483647)TSAp=req.text().slice(0,10)/1000;
else TSAp= EVAL_STR;
if(req.text().slice(10,20)!=2147483647)TSAm=req.text().slice(10,20)/1000;
else TSAm= EVAL_STR;
if(req.text().slice(20,30)!=2147483647)TSRp=req.text().slice(20,30)/1000;
else TSRp= EVAL_STR;
if(req.text().slice(30,40)!=2147483647)TSAm=req.text().slice(30,40)/1000;
else TSRm= EVAL_STR;}
}
if(T1){
//Энергия от сброса Т1
req = SYS.XMLNode().setAttr("ProtIt","m230").setAttr("netaddr",netaddr).setAttr("data",SYS.strFromCharCode(0x05,0x00,0x01));
SYS.Transport["Serial"]["out_"+transport].messIO(req,"UserProtocol");
if(!req.text()){T1Ap  = EVAL_STR;T1Am = EVAL_STR;T1Rp = EVAL_STR;T1Rm = EVAL_STR; }
else {
if(req.text().slice(0,10)!=2147483647)T1Ap=req.text().slice(0,10)/1000;
else T1Ap= EVAL_STR;
if(req.text().slice(10,20)!=2147483647)T1Am=req.text().slice(10,20)/1000;
else T1Am= EVAL_STR;
if(req.text().slice(20,30)!=2147483647)T1Rp=req.text().slice(20,30)/1000;
else T1Rp= EVAL_STR;
if(req.text().slice(30,40)!=2147483647)T1Am=req.text().slice(30,40)/1000;
else T1Rm= EVAL_STR;}
}
//Энергия от сброса Т2
if(T2){
req = SYS.XMLNode().setAttr("ProtIt","m230").setAttr("netaddr",netaddr).setAttr("data",SYS.strFromCharCode(0x05,0x00,0x02));
SYS.Transport["Serial"]["out_"+transport].messIO(req,"UserProtocol");
if(!req.text()){T2Ap  = EVAL_STR;T2Am = EVAL_STR;T2Rp = EVAL_STR;T2Rm = EVAL_STR; }
else {
if(req.text().slice(0,10)!=2147483647)T2Ap=req.text().slice(0,10)/1000;
else T2Ap= EVAL_STR;
if(req.text().slice(10,20)!=2147483647)T2Am=req.text().slice(10,20)/1000;
else T2Am= EVAL_STR;
if(req.text().slice(20,30)!=2147483647)T2Rp=req.text().slice(20,30)/1000;
else T2Rp= EVAL_STR;
if(req.text().slice(30,40)!=2147483647)T2Am=req.text().slice(30,40)/1000;
else T2Rm= EVAL_STR;}
}
//Энергия от сброса Т3
if(T3){
req = SYS.XMLNode().setAttr("ProtIt","m230").setAttr("netaddr",netaddr).setAttr("data",SYS.strFromCharCode(0x05,0x00,0x03));
SYS.Transport["Serial"]["out_"+transport].messIO(req,"UserProtocol");
if(!req.text()){T3Ap  = EVAL_STR;T3Am = EVAL_STR;T3Rp = EVAL_STR;T3Rm = EVAL_STR; }
else {
if(req.text().slice(0,10)!=2147483647)T3Ap=req.text().slice(0,10)/1000;
else T3Ap= EVAL_STR;
if(req.text().slice(10,20)!=2147483647)T3Am=req.text().slice(10,20)/1000;
else T3Am= EVAL_STR;
if(req.text().slice(20,30)!=2147483647)T3Rp=req.text().slice(20,30)/1000;
else T3Rp= EVAL_STR;
if(req.text().slice(30,40)!=2147483647)T3Am=req.text().slice(30,40)/1000;
else T3Rm= EVAL_STR;}
}
//Энергия от сброса Т4
if(T4){
req = SYS.XMLNode().setAttr("ProtIt","m230").setAttr("netaddr",netaddr).setAttr("data",SYS.strFromCharCode(0x05,0x00,0x04));
SYS.Transport["Serial"]["out_"+transport].messIO(req,"UserProtocol");
if(!req.text()){T4Ap  = EVAL_STR;T4Am = EVAL_STR;T4Rp = EVAL_STR;T4Rm = EVAL_STR; }
else {
if(req.text().slice(0,10)!=2147483647)T4Ap=req.text().slice(0,10)/1000;
else T4Ap= EVAL_STR;
if(req.text().slice(10,20)!=2147483647)T4Am=req.text().slice(10,20)/1000;
else T4Am= EVAL_STR;
if(req.text().slice(20,30)!=2147483647)T4Rp=req.text().slice(20,30)/1000;
else T4Rp= EVAL_STR;
if(req.text().slice(30,40)!=2147483647)T4Am=req.text().slice(30,40)/1000;
else T4Rm= EVAL_STR;}
}
//Мощность P
if(P){
req = SYS.XMLNode().setAttr("ProtIt","m230").setAttr("netaddr",netaddr).setAttr("data",SYS.strFromCharCode(0x08,0x16,0x00));
SYS.Transport["Serial"]["out_"+transport].messIO(req,"UserProtocol");
if(!req.text()){PS=EVAL_STR;P1=EVAL_STR;P2=EVAL_STR;P3=EVAL_STR;}
else PS=req.text().slice(0,7)/100;P1=req.text().slice(7,14)/100;P2=req.text().slice(14,21)/100;P3=req.text().slice(21,28)/100;}
//Мощность Q
if(Q){
req = SYS.XMLNode().setAttr("ProtIt","m230").setAttr("netaddr",netaddr).setAttr("data",SYS.strFromCharCode(0x08,0x16,0x04));
SYS.Transport["Serial"]["out_"+transport].messIO(req,"UserProtocol");
if(!req.text()){QS=EVAL_REAL;Q1=EVAL_REAL;Q2=EVAL_REAL;Q3=EVAL_REAL;}
else 
{if (req.text().slice(28,29)==4||req.text().slice(28,29)=="C")QS=(req.text().slice(0,7)/100)*-1;
else QS=(req.text().slice(0,7)/100);
if (req.text().slice(29,30)==4||req.text().slice(29,30)=="C")Q1=(req.text().slice(7,14)/100)*-1;
else Q1=(req.text().slice(7,14)/100);
if (req.text().slice(30,31)==4||req.text().slice(30,31)=="C")Q2=(req.text().slice(14,21)/100)*-1;
else Q2=(req.text().slice(14,21)/100);
if (req.text().slice(31,32)==4||req.text().slice(31,32)=="C")Q3=(req.text().slice(21,28)/100)*-1;
else Q3=(req.text().slice(21,28)/100);}}
//Мощность S
if(S){
req = SYS.XMLNode().setAttr("ProtIt","m230").setAttr("netaddr",netaddr).setAttr("data",SYS.strFromCharCode(0x08,0x16,0x08));
SYS.Transport["Serial"]["out_"+transport].messIO(req,"UserProtocol");
if(!req.text()){SS=EVAL_STR;S1=EVAL_STR;S2=EVAL_STR;S3=EVAL_STR;}
else SS=req.text().slice(0,7)/100;S1=req.text().slice(7,14)/100;S2=req.text().slice(14,21)/100;S3=req.text().slice(21,28)/100;}
// Напряжение U
if(U){
req = SYS.XMLNode().setAttr("ProtIt","m230").setAttr("netaddr",netaddr).setAttr("data",SYS.strFromCharCode(0x08,0x16,0x11));
SYS.Transport["Serial"]["out_"+transport].messIO(req,"UserProtocol");
if(!req.text()){U1=EVAL_STR;U2=EVAL_STR;U3=EVAL_STR;}
else U1=req.text().slice(0,8)/100;U2=req.text().slice(8,16)/100;U3=req.text().slice(16,24)/100;}
//Ток I
if(I){
req = SYS.XMLNode().setAttr("ProtIt","m230").setAttr("netaddr",netaddr).setAttr("data",SYS.strFromCharCode(0x08,0x16,0x21));
SYS.Transport["Serial"]["out_"+transport].messIO(req,"UserProtocol");
if(!req.text()){I1=EVAL_STR;I2=EVAL_STR;I3=EVAL_STR;}
else I1=req.text().slice(0,8)/1000;I2=req.text().slice(8,16)/1000;I3=req.text().slice(16,24)/1000;}
//Коэффициент мощности
if(K){
req = SYS.XMLNode().setAttr("ProtIt","m230").setAttr("netaddr",netaddr).setAttr("data",SYS.strFromCharCode(0x08,0x16,0x30));
SYS.Transport["Serial"]["out_"+transport].messIO(req,"UserProtocol");
if(!req.text()){KS=EVAL_STR;K1=EVAL_STR;K2=EVAL_STR;K3=EVAL_STR;}
else KS=req.text().slice(0,7)/1000;K1=req.text().slice(7,14)/1000;K2=req.text().slice(14,21)/1000;K3=req.text().slice(21,28)/1000;}
//частота F
if(F){
req = SYS.XMLNode().setAttr("ProtIt","m230").setAttr("netaddr",netaddr).setAttr("data",SYS.strFromCharCode(0x08,0x16,0x40));
SYS.Transport["Serial"]["out_"+transport].messIO(req,"UserProtocol");
if(!req.text())F1= EVAL_STR;
else F1 =req.text();}
//серийный номер
if(N){
req = SYS.XMLNode().setAttr("ProtIt","m230").setAttr("netaddr",netaddr).setAttr("data",SYS.strFromCharCode(0x08,0x00));
SYS.Transport["Serial"]["out_"+transport].messIO(req,"UserProtocol");
if(!req.text())N1= EVAL_STR;
else N1 =req.text();}
','','',1580047532);
INSERT INTO tmplib_DevLib VALUES('Nik2303I','Nik2303I','','','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.0
License: GPLv2','','',120,0,'JavaLikeCalc.JavaScript
using Special.FLibSYS;

if(f_start) {
	transport_ = transport;
	tr = SYS.Transport.nodeAt(transport,".");
	LLC = SYS.strFromCharCode(0xe6, 0xe6, 0x00);
	F_id = SYS.strFromCharCode(0xC0, 0x01, 0x81, 0x00, 0x03);
	end = SYS.strFromCharCode(0x02, 0x00);
	Ae=Ae_=Re=Re_=Ae1=Ae_1=Re1=Re_1=Ae2=Ae_2=Re2=Re_2=Ae3=Ae_3=Re3=Re_3=EVAL_REAL;
	U1=U2=U3=I1=I2=I3=Psum=P1=P2=P3=Qsum=Q1=Q2=Q3=kP1=kP2=kP3=EVAL_REAL;
	V12=V13=EVAL_INT;

}
if(f_stop) {
	return;
}
 
tErr="0";
//Check for the transport change and connect
if(!tr || transport != transport_)	{
	tr = SYS.Transport.nodeAt(transport,".");
	transport_ = transport;
}
tr.start(true, 0);
/* timeout=SYS.time();
	while(!tr.start()){
		if(SYS.time()>timeout+5)break;
	}	
*/	
if(!tr.start()){ tErr = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
			Ae=Ae_=Re=Re_=Ae1=Ae_1=Re1=Re_1=U1=U2=U3=I1=I2=I3=Psum=P1=P2=P3=Qsum=Q1=Q2=Q3=kP1=kP2=kP3=EVAL_REAL;
			V12=V13=EVAL_INT;
}else{
	req = SYS.XMLNode("mess").setAttr("ProtIt","NIK2303").setAttr("SN",serial.toInt(10));
	//COMAND_SNRM
	req.setAttr("cntr",0x93).setText("");
	tr.messIO(req, "UserProtocol");
	if (req.attr("err").toInt()){SYS.messDebug("/NIK2303I/TMPL",tr("Error response")+": "+req.attr("err"));
		Ae=Ae_=Re=Re_=Ae1=Ae_1=Re1=Re_1=U1=U2=U3=I1=I2=I3=Psum=P1=P2=P3=Qsum=Q1=Q2=Q3=kP1=kP2=kP3=EVAL_REAL;
		V12=V13=EVAL_INT;
		return;}
	//COMAND_AARQ (передача пароля)	
	mess = SYS.strFromCharCode(0xa1, 0x09, 0x06, 0x07, 0x60,
		0x85, 0x74, 0x05, 0x08, 0x01, 0x01, 0x8a, 0x02,
		0x07, 0x80, 0x8b, 0x07, 0x60, 0x85, 0x74, 0x05,
		0x08, 0x02, 0x01, 0xac, 0x12, 0x80, 0x11, 0x00)
		+ passw + SYS.strFromCharCode(0xbe, 0x10, 0x04,	
		0x0e, 0x01, 0x00, 0x00, 0x00, 0x06, 0x5f, 0x1f,
		0x04, 0x00, 0x00, 0x08, 0xcb, 0x00, 0x80 );
	req.setAttr("cntr",0x10).setText(LLC + SYS.strFromCharCode(0x60, mess.length) + mess);
	tr.messIO(req, "UserProtocol");
	if (req.attr("err").toInt()){SYS.messDebug("/NIK2303I/TMPL",tr("Error response")+": "+req.attr("err"));
		Ae=Ae_=Re=Re_=Ae1=Ae_1=Re1=Re_1=Ua=Ub=Uc=Ia=Ib=Ic=Pa=Pb=Pc=Qa=Qb=Qc=kPa=kPb=kPc=EVAL_REAL;
		Vab=Vac=EVAL_INT;
		return;}
//ЗАПИТИ ПОТОЧНИХ ПОКАЗНИКІВ ЛІЧИЛЬНИКА T0
	if (T0)	{
		//Запит показник Активна енергія	(1.8.0)
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 1, 8, 0, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){Ae = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	Ae = kT*((req.text().charCodeAt(9)<<40)+(req.text().charCodeAt(10)<<32)+(req.text().charCodeAt(11)<<24)+(req.text().charCodeAt(12)<<16)+(req.text().charCodeAt(13)<<8)+req.text().charCodeAt(14).toInt())/100000;
	//Запит показник Активна енергія генерація	(2.8.0)
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 2, 8, 0, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){Ae_ = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	Ae_ = kT*((req.text().charCodeAt(9)<<40)+(req.text().charCodeAt(10)<<32)+(req.text().charCodeAt(11)<<24)+(req.text().charCodeAt(12)<<16)+(req.text().charCodeAt(13)<<8)+req.text().charCodeAt(14).toInt())/100000;
	//Запит показник реактивна енергія	 (3.8.0)
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 3, 8, 0, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){Re = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	Re = kT*((req.text().charCodeAt(9)<<40)+(req.text().charCodeAt(10)<<32)+(req.text().charCodeAt(11)<<24)+(req.text().charCodeAt(12)<<16)+(req.text().charCodeAt(13)<<8)+req.text().charCodeAt(14).toInt())/100000;
	//Запит показник реактивна енергія генерація (4.8.0)	
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 4, 8, 0, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){Re_ = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	Re_ = kT*((req.text().charCodeAt(9)<<40)+(req.text().charCodeAt(10)<<32)+(req.text().charCodeAt(11)<<24)+(req.text().charCodeAt(12)<<16)+(req.text().charCodeAt(13)<<8)+req.text().charCodeAt(14).toInt())/100000;
	}
	//ЗАПИТИ ПОТОЧНИХ ПОКАЗНИКІВ ЛІЧИЛЬНИКА T1
	if (T1)	{
		//Запит показник Активна енергія	(1.8.1)
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 1, 8, 1, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){Ae1 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	Ae1 = kT*((req.text().charCodeAt(9)<<40)+(req.text().charCodeAt(10)<<32)+(req.text().charCodeAt(11)<<24)+(req.text().charCodeAt(12)<<16)+(req.text().charCodeAt(13)<<8)+req.text().charCodeAt(14).toInt())/100000;
	//Запит показник Активна енергія генерація	(2.8.1)
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 2, 8, 1, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){Ae_1 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	Ae_1 = kT*((req.text().charCodeAt(9)<<40)+(req.text().charCodeAt(10)<<32)+(req.text().charCodeAt(11)<<24)+(req.text().charCodeAt(12)<<16)+(req.text().charCodeAt(13)<<8)+req.text().charCodeAt(14).toInt())/100000;
	//Запит показник реактивна енергія	 (3.8.1)
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 3, 8, 1, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){Re1 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	Re1 = kT*((req.text().charCodeAt(9)<<40)+(req.text().charCodeAt(10)<<32)+(req.text().charCodeAt(11)<<24)+(req.text().charCodeAt(12)<<16)+(req.text().charCodeAt(13)<<8)+req.text().charCodeAt(14).toInt())/100000;
	//Запит показник реактивна енергія генерація (4.8.1)	
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 4, 8, 1, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){Re_1 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	Re_1 = kT*((req.text().charCodeAt(9)<<40)+(req.text().charCodeAt(10)<<32)+(req.text().charCodeAt(11)<<24)+(req.text().charCodeAt(12)<<16)+(req.text().charCodeAt(13)<<8)+req.text().charCodeAt(14).toInt())/100000;
	}
	//ЗАПИТИ ПОТОЧНИХ ПОКАЗНИКІВ ЛІЧИЛЬНИКА T2
	if (T2)	{
		//Запит показник Активна енергія	(1.8.2)
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 1, 8, 2, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){Ae2 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	Ae2 = kT*((req.text().charCodeAt(9)<<40)+(req.text().charCodeAt(10)<<32)+(req.text().charCodeAt(11)<<24)+(req.text().charCodeAt(12)<<16)+(req.text().charCodeAt(13)<<8)+req.text().charCodeAt(14).toInt())/100000;
	//Запит показник Активна енергія генерація	(2.8.2)
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 2, 8, 2, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){Ae_2 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	Ae_2 = kT*((req.text().charCodeAt(9)<<40)+(req.text().charCodeAt(10)<<32)+(req.text().charCodeAt(11)<<24)+(req.text().charCodeAt(12)<<16)+(req.text().charCodeAt(13)<<8)+req.text().charCodeAt(14).toInt())/100000;
	//Запит показник реактивна енергія	 (3.8.2)
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 3, 8, 2, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){Re2 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	Re2 = kT*((req.text().charCodeAt(9)<<40)+(req.text().charCodeAt(10)<<32)+(req.text().charCodeAt(11)<<24)+(req.text().charCodeAt(12)<<16)+(req.text().charCodeAt(13)<<8)+req.text().charCodeAt(14).toInt())/100000;
	//Запит показник реактивна енергія генерація (4.8.2)	
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 4, 8, 2, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){Re_2 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	Re_2 = kT*((req.text().charCodeAt(9)<<40)+(req.text().charCodeAt(10)<<32)+(req.text().charCodeAt(11)<<24)+(req.text().charCodeAt(12)<<16)+(req.text().charCodeAt(13)<<8)+req.text().charCodeAt(14).toInt())/100000;
	}
	//ЗАПИТИ ПОТОЧНИХ ПОКАЗНИКІВ ЛІЧИЛЬНИКА T3
	if (T3)	{
		//Запит показник Активна енергія	(1.8.3)
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 1, 8, 3, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){Ae3 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	Ae3 = kT*((req.text().charCodeAt(9)<<40)+(req.text().charCodeAt(10)<<32)+(req.text().charCodeAt(11)<<24)+(req.text().charCodeAt(12)<<16)+(req.text().charCodeAt(13)<<8)+req.text().charCodeAt(14).toInt())/100000;
	//Запит показник Активна енергія генерація	(2.8.3)
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 2, 8, 3, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){Ae_3 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	Ae_3 = kT*((req.text().charCodeAt(9)<<40)+(req.text().charCodeAt(10)<<32)+(req.text().charCodeAt(11)<<24)+(req.text().charCodeAt(12)<<16)+(req.text().charCodeAt(13)<<8)+req.text().charCodeAt(14).toInt())/100000;
	//Запит показник реактивна енергія	 (3.8.3)
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 3, 8, 3, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){Re3 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	Re3 = kT*((req.text().charCodeAt(9)<<40)+(req.text().charCodeAt(10)<<32)+(req.text().charCodeAt(11)<<24)+(req.text().charCodeAt(12)<<16)+(req.text().charCodeAt(13)<<8)+req.text().charCodeAt(14).toInt())/100000;
	//Запит показник реактивна енергія генерація (4.8.3)	
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 4, 8, 3, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){Re_3 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	Re_3 = kT*((req.text().charCodeAt(9)<<40)+(req.text().charCodeAt(10)<<32)+(req.text().charCodeAt(11)<<24)+(req.text().charCodeAt(12)<<16)+(req.text().charCodeAt(13)<<8)+req.text().charCodeAt(14).toInt())/100000;
	}
//ЗАПИТИ МИТТЄВИХ ЗНАЧЕНЬ	
	if (U )	{ 		//Напруга
	//Запит напруга фаза 1	
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 32, 7, 0, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){U1 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	U1 = (req.text().slice(9, 17).replace(",",".").toReal());
	//Запит напруга фаза  2	
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 52, 7, 0, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){U2 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	U2 = (req.text().slice(9, 17).replace(",",".").toReal());
	//Запит напруга фаза  3	
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 72, 7, 0, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){U3 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	U3 = (req.text().slice(9, 17).replace(",",".").toReal());
	}
	if(I){ 		//СТРУМ	
	//Запит струм фаза 1	
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 31, 7, 0, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){I1 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	I1 = kT*(req.text().slice(9, 17).replace(",",".").toReal());
	//Запит струм фаза 2	
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 51, 7, 0, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){I2 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	I2 = kT*(req.text().slice(9, 17).replace(",",".").toReal());
	//Запит струм фаза 3	
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 71, 7, 0, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){I3 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	I3 = kT*(req.text().slice(9, 17).replace(",",".").toReal());
		}
	if(P){		//Потужність
	//Запит потужність сумарна
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 1, 7, 0, 254)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){Psum = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	Psum = kT*(req.text().slice(9, 17).replace(",",".").toReal());
	//Запит потужність фаза 1
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 128, 21, 7, 0, 0)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){P1 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	P1 = kT*(req.text().slice(9, 17).replace(",",".").toReal());
	//Запит потужність фаза 2	
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 128, 41, 7, 0, 0)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){P2 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	P2 = kT*(req.text().slice(9, 17).replace(",",".").toReal());
	//Запит потужність фаза 3	
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 128, 61, 7, 0, 0)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){P3 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	P3 = kT*(req.text().slice(9, 17).replace(",",".").toReal());
	}
	if(kP){		//Коефіцієнт потужності
	//Запит коефіцієнт потужності фаза 1	
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 33, 7, 0, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){kP1 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	kP1 = (req.text().slice(9, 17).replace(",",".").toReal());		
	//Запит коефіцієнт потужності фаза 2	
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 53, 7, 0, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){kP2 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	kP2 = (req.text().slice(9, 17).replace(",",".").toReal());		
	//Запит коефіцієнт потужності фаза 3	
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 73, 7, 0, 255)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){kP3 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	kP3 = (req.text().slice(9, 17).replace(",",".").toReal());
	}
	if(Q){	//Реактивна потужність
	//Запит реактивна потужність сумарна	
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 0, 3, 7, 0, 254)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){Qsum = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	Qsum = kT*(req.text().slice(9, 17).replace(",",".").toReal());
	//Запит реактивна потужність фаза 1	
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 128, 23, 7, 0, 0)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){Q1 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	Q1 = kT*(req.text().slice(9, 17).replace(",",".").toReal());
	//Запит реактивна потужність фаза B	
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 128, 43, 7, 0, 0)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){Q2 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	Q2 = kT*(req.text().slice(9, 17).replace(",",".").toReal());
	//Запит реактивна потужність фаза 3	
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 128, 63, 7, 0, 0)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){Q3 = EVAL_REAL; tErr = "2:"+req.attr("err");}
		else	Q3 = kT*(req.text().slice(9, 17).replace(",",".").toReal());
	}
	if(K){
	//Запит кута між векторами U1_U2	
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 128, 33, 7, 0, 0)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){V12 = EVAL_INT; tErr = "2:"+req.attr("err");}
		else	V12 = req.text().charCodeAt(8);
	//Запит кута між векторами U1_U3	
		req.setAttr("cntr",0x10).setText(LLC+F_id+SYS.strFromCharCode( 1, 128, 73, 7, 0, 0)+end);
		tr.messIO(req, "UserProtocol");
		if (req.attr("err").toInt()){V13 = EVAL_INT; tErr = "2:"+req.attr("err");}
		else	V13 = req.text().charCodeAt(8);
		tErr = req.attr("err");
	}
	//Закриття зєднання
	//COMAND_SNRM 
	req.setAttr("cntr",0x53).setText("");
	tr.messIO(req, "UserProtocol");
	tr.start(false, 0);
	
}
//Error set
if(tErr.length) {
	SYS.messDebug("/NIK2303I/TMPL",tr("Error response")+": "+tErr);
	f_err = tErr;
}
else f_err = "0";','','',1580046416);
INSERT INTO tmplib_DevLib VALUES('ModBusScan','ModBus scanner','Сканер ModBus','Сканер ModBus','ModBus scanner for Coils (1), Input Coils (2), Registers (3), Input Registers (4); in the range [0...65535]. The template useful to examine unknown ModBus devices, scanning their Coils'' and Registers'' map for detecting the available data addresses. The scanning performs per one item, whether it is a coil or a register, to prevent the data unavailability for strict data accessing devices. The protocol ModBus itself implemented by the OpenSCADA module DAQ.ModBus, together which the template designed to use.

Author: Roman Savochenko <roman@oscada.org>
Total complexity: 0.2 HD
Sponsored by: SVItoVYR Ltd for whole complexity
Version: 1.0.2
License: GPLv2','Сканер ModBus щодо Кілець (1), Вхідних Кілець (2), Регістрів (3), Вхідних Регістрів (4); у діапазоні [0...65535]. Шаблон корисний для вивчення невідомих пристроїв ModBus, скануванням їх мапи Кілець та Регістрів для виявлення наявних адрес даних. Сканування відбувається по одному елементу, чи то кільцю чи регістру, для попередження недоступності даних на пристроях суворого доступу даних. Сам протокол ModBus реалізовано модулем OpenSCADA DAQ.ModBus, разом з яким шаблон і передбачено до використання.

Автор: Роман Савоченко <roman@oscada.org>
Загальна працемісткість: 0.2 ЛД
Спонсорування: ТОВ "СВІТоВИР" на загальну працемісткість
Версія: 1.0.2
Ліцензія: GPLv2','',240,0,'JavaLikeCalc.JavaScript
if(f_start) {
	res = "";
	begin = begin_ = end = 0;
	type_ = type;
	//this.attrAdd("type", "", "integer|sel", "0;1;2;3\nCoils (1);Input Coils (2);Registers (3);Input Registers (4)");
	return;
}
if(f_stop) return;

if(type_ != type || begin < begin_)
	begin = max(0,min(65535,begin)), res = "";

for(stTm = SYS.time(); begin < end && begin < 65536 && (SYS.time()-stTm) < lim; begin++) {
	if(type == 0 || type == 1) {
		pdu = SYS.strFromCharCode((type==0)?1:2, begin/256, begin%256, 0, 1);
		rez = this.cntr().messIO(pdu);
		res += begin.toString() + ":\t" + (rez.length?rez:pdu.charCodeAt(2)&0x01) + "\n";
	}
	else {
		pdu = SYS.strFromCharCode((type==2)?3:4, begin/256, begin%256, 0, 1);
		rez = this.cntr().messIO(pdu);
		res += begin.toString() + ":\t" + (rez.length?rez:pdu.charCodeAt(2)*256+pdu.charCodeAt(3))  + "\n";
	}
}
type_ = type, begin_ = begin;','','',1592158322);
INSERT INTO tmplib_DevLib VALUES('pulsarM','Pulsar-M','Пульсар-М','Пульсар-М','Pulsar-M counters protocol.

Author: Roman Savochenko <roman@oscada.org>
Sponsored: Zubarev Dmitry (IP INTEGRAL<http://kip-i-a.pro>)
Version: 1.0.0
License: GPLv2','','',10,0,'JavaLikeCalc.JavaScript
//Same request to the device
function req(tr, iaddr, func, data) {
	addr_ = iaddr.toString(10).toInt(16);
	reqId = rand(65535);
	req = Special.FLibSYS.IO("", "", "l");
	req.wr(addr_, "uint32", "b").wr(func, "uint8").wr(data.length+10, "uint8").wr(data.string, "char").wr(reqId, "uint16");
	req.wr(Special.FLibSYS.CRC(req.string), "uint16");
	if(func == 3)	SYS.messInfo("/Pulsar",tr("Request")+": "+SYS.strDecode(req.string,"Bin"," "));
	resp = Special.FLibSYS.IO("", "", "l");
	resp.wr(tr.messIO(req.string),"char");
	while(resp.length && (resp.length < 10 || resp.length < resp.string.charCodeAt(5)) &&
		(tresp=tr.messIO("")).length) resp.wr(tresp,"char");
	//SYS.messInfo("/Pulsar",tr("Response")+": "+SYS.strDecode(resp.string,"Bin"," "));
	//data = resp;
	if(resp.length < 10)							return tr("3:No response or the response is short.");
	resp.pos = 0;
	if(resp.read("uint32",1,"b") != addr_)	return tr("4:Disparity of the request address and the response one.");
	if(resp.read("uint8",1) != func)			return tr("4:Disparity of the request function and the response one.");
	if(resp.read("uint8",1) > resp.length)	return tr("4:Incomplete response.");
	data.string = resp.read("char", resp.length-10);
	if(resp.read("uint16",1) != reqId.toInt())
		return tr("4:Disparity of the request ID and the response one.");
	if(resp.read("uint16",1) != Special.FLibSYS.CRC(resp.string.slice(0,-2)))
		return tr("4:Error CRC.");
	return "0";
}

//Set transport and init
if(f_start) {
	transport_ = transport;
	tr = SYS.Transport.nodeAt(transport,".");
	nChnl_ = 0;
	wrBuf = new Array();
	wrBufW = new Array();
}
if(f_stop) return;

t_err = "0";

if(nChnl != nChnl_) {
	nChnl = max(0, min(32,nChnl));
	for( ; nChnl_ < nChnl; nChnl_++) {
		this.attrAdd("chnl"+nChnl_, tr("Channel")+" "+(nChnl_+1)+": "+tr("value"), "real");
		this.attrAdd("w"+nChnl_, tr("Channel")+" "+(nChnl_+1)+": "+tr("weight"), "real");
		this.attrAdd("avF"+nChnl_, tr("Channel")+" "+(nChnl_+1)+": "+tr("average flow"), "real|ro");
		this.attrAdd("in"+nChnl_, tr("Channel")+" "+(nChnl_+1)+": "+tr("input"), "boolean|ro");
	}
	for( ; nChnl_ > nChnl; nChnl_--) {
		this.attrDel("chnl"+(nChnl_-1));
		wrBuf[nChnl_-1] = EVAL;
		this.attrDel("w"+(nChnl_-1));
		wrBufW[nChnl_-1] = EVAL;
		this.attrDel("avF"+(nChnl_-1));
		this.attrDel("in"+(nChnl_-1));
	}
}
//Check for the transport change and connect
if(!tr || transport != transport_)	{
	transport_ = transport;
	tr = SYS.Transport.nodeAt(transport,".");
}
if(!tr)	t_err = "1:"+tr("Error the output transport ''%1''.").replace("%1",transport);
else if(addr < 0 || addr > 99999999)
	t_err = "2:"+tr("The address ''%1'' is out of the range [0...99999999].").replace("%1",addr.toString());
else {
	//Processing for writing channels and their weights
	for(iCh = 0; iCh < nChnl && !t_err.toInt(); iCh++) {
		//Checking channels to write
		if(!wrBuf[iCh].isEVal() && !(aVl=this["chnl"+iCh].get()).isEVal() && aVl != wrBuf[iCh]) {
			io = Special.FLibSYS.IO("", "", "l");
			io.wr((0x01<<iCh), "uint32");
			io.wr(aVl, "real*8");
			if(!(t_err=req(tr,addr,3,io)).toInt())	wrBuf[iCh] = aVl;
		}
		//Checking weights to write
		if(!wrBufW[iCh].isEVal() && !(aVl=this["w"+iCh].get()).isEVal() && aVl != wrBufW[iCh]) {
			io = Special.FLibSYS.IO("", "", "l");
			io.wr((0x01<<iCh), "uint32");
			io.wr(aVl, "real*4");
			if(!(t_err=req(tr,addr,8,io)).toInt())	wrBufW[iCh] = aVl;
		}
		//Reading channels
		/*io = Special.FLibSYS.IO("", "", "l");
		io.wr((0x01<<iCh), "uint32");
		if(!(t_err=req(tr,addr,1,io)).toInt()) {
			wrBuf[iCh] = io.read("real*8",1);
			this["chnl"+iCh].set(wrBuf[iCh], 0, 0, true);	
		}
		//Reading weights
		io = Special.FLibSYS.IO("", "", "l");
		io.wr((0x01<<iCh), "uint32");
		if(!(t_err=req(tr,addr,7,io)).toInt()) {
			wrBufW[iCh] = io.read("real*4",1);
			this["w"+iCh].set(wrBufW[iCh], 0, 0, true);	
		}
		//Reading average floats
		io = Special.FLibSYS.IO("", "", "l");
		io.wr((0x01<<iCh), "uint32");
		if(!(t_err=req(tr,addr,0x3E,io)).toInt())
			this["avF"+iCh].set(io.read("real*8",1), 0, 0, true);*/
	}

	//Reading for channels, their weights, average floats and inputs
	chMask = 0;
	for(iCh = 0; iCh < nChnl; iCh++)	chMask = chMask | (1<<iCh);

	// Reading channels
	if(!t_err.toInt()) {
		io = Special.FLibSYS.IO("", "", "l");
		io.wr(chMask, "uint32");
		if(!(t_err=req(tr,addr,1,io)).toInt())
			for(iCh = 0; iCh < nChnl; iCh++) {
				wrBuf[iCh] = io.read("real*8",1);
				this["chnl"+iCh].set(wrBuf[iCh], 0, 0, true);	
			}
	}
	// Reading weights
	if(!t_err.toInt()) {
		io = Special.FLibSYS.IO("", "", "l");
		io.wr(chMask, "uint32");
		if(!(t_err=req(tr,addr,7,io)).toInt())
			for(iCh = 0; iCh < nChnl; iCh++) {
				wrBufW[iCh] = io.read("real*4",1);
				this["w"+iCh].set(wrBufW[iCh], 0, 0, true);	
			}
	}
	// Reading average floats
	if(!t_err.toInt()) {
		io = Special.FLibSYS.IO("", "", "l");
		io.wr(chMask, "uint32");
		if(!(t_err=req(tr,addr,0x3E,io)).toInt())
			for(iCh = 0; iCh < nChnl; iCh++)
				this["avF"+iCh].set(io.read("real*8",1), 0, 0, true);
	}
	// Reading for inputs
	if(!t_err.toInt()) {
		io = Special.FLibSYS.IO("", "", "l");
		io.wr(chMask, "uint32");
		if(!(t_err=req(tr,addr,0x19,io)).toInt()) {
			vMask = io.read("uint32", 1);
			for(iCh = 0; iCh < nChnl; iCh++)
				this["in"+iCh].set((vMask&(1<<iCh))?true:false, 0, 0, true);
		}
	}
}

f_err = t_err;','','',1580047532);
INSERT INTO tmplib_DevLib VALUES('OWEN','OWEN','','','OWEN data sources implementation in the OWEN protocol. Implemented wholly in the template for the protocol requesting and for dynamic data model of the OWEN data sources'' data with support of the writing and original names of the parameters.

The field "addr" of the items can be wrote positive, for the 8 bit address [0...255], and negative, for the 11 bit address[-2047...0].

Author: Roman Savochenko <roman@oscada.org>, Constantine (IrmIngeneer)
State of the protocol implementing: Client of the protocol for reading and writing the specified PLC properties.
Total complexity: 0.5 HD
Actual issues: The undocumented behaviour:
  - МВ110-8А responds error 0x31 for reading "in-t": >1003023331932DCB43 <1010932D4791;
  - and error 0x34 for it writing: >1003023334932D2A42 <1001932D0BEDBC.
Version: 0.8.1
License: GPLv2','','',30,0,'JavaLikeCalc.JavaScript
function hash(data, isName) {
	for(CRC = 0, i = 0; i < data.length; i++) {
		b = data.charCodeAt(i);
		for(j = 0; j < ((isName==true)?7:8); j++, b = b << 1)
			CRC = ((b^(CRC>>8)&0x80)?(CRC<<1)^0x8F57:CRC<<1) & 0xFFFF;
	}
	return CRC;
}

function nmHash(name) {
	for(srcF = name + "    ", hashF = "", iV = 0; iV < srcF.length; iV++) {
		tVl = srcF[iV]; tVl2 = srcF.charCodeAt(iV);
		if(tVl == "." && iV > 0)	{ hashF = hashF.slice(0,-1)+SYS.strFromCharCode(hashF.charCodeAt(hashF.length-1)+1); continue; }
		if(tVl2 >= 0x30 && tVl2 <= 0x39)		tVl = tVl2-0x30;
		else if(tVl2 >= 0x41 && tVl2 <= 0x5A)	tVl = tVl2-0x41+10;
		else if(tVl2 >= 0x61 && tVl2 <= 0x7A)	tVl = tVl2-0x61+10;
		else if(tVl == "-")	tVl = 10+26;
		else if(tVl == "_")	tVl = 10+26+1;
		else if(tVl == "/")	tVl = 10+26+2;
		else if(tVl == " ")	tVl = 10+26+3;
		else break;
		hashF += SYS.strFromCharCode(tVl*2);
	}
	if(iV < name.length || hashF.length < 4)	return -1;
	for(rez = "", iV = 0; iV < 4; iV++)
		rez += SYS.strFromCharCode(hashF.charCodeAt(iV)*2);

	//SYS.messDebug("/OWEN", "func="+name+"; code="+SYS.strDecode(rez));

	return hash(rez, true);
}

//Same request to the device
//  addr - negative for 11 bit type
function req(tr, addr, data) {
	if(addr < -2047 || addr > 255)	return "1:"+tr("Address out of the range [-2047...255].");
	req = SYS.strFromCharCode((addr>=0)?abs(addr):abs(addr)>>3, ((addr>=0)?0:abs(addr<<5))|((data.length>2)?data.length-2:0x10)) + data;

	// Appending CRC
	CRC = hash(req);
	req += SYS.strFromCharCode(CRC>>8, CRC&255);

	SYS.messDebug("/OWEN", "req="+SYS.strDecode(req));

	// Converting to ASCII
	for(reqASCII = "", i = 0; i < req.length; i++)
		reqASCII += SYS.strFromCharCode((req.charCodeAt(i)>>4)+71, (req.charCodeAt(i)&0x0F)+71);
	req = "#" + reqASCII + "\r";

	// Sending the request and getting response
	for(resp = tr.messIO(req); resp[resp.length-1] != "\r" && (respTail=tr.messIO("")).length; )
		resp += respTail;

	// Processing the response
	if(resp.length < req.length || resp[resp.length-1] != "\r" || resp[0] != "#" || (resp.length%2) != 0)
		return "10:"+tr("Wrong or no response.");

	//SYS.messDebug("/OWEN", "resp="+resp);

	resp = resp.slice(1, resp.length-1);

	// Converting to binary
	for(respBin = "", b = 0, i = 0, fullB = false; i < resp.length; i++, fullB = !fullB) {
		b += resp.charCodeAt(i) - 71;
		if(fullB) { respBin += SYS.strFromCharCode(b); b = 0; } else b *= 16;
	}
	resp = respBin;

	SYS.messDebug("/OWEN", "resp="+SYS.strDecode(resp));

	// Checking for CRC
	CRC = hash(resp.slice(0,-2));
	if(CRC != ((resp.charCodeAt(resp.length-2)<<8)+resp.charCodeAt(resp.length-1)))
		return "11:"+tr("Error CRC.");

	if(resp.charCodeAt(1) != (resp.length-6))
		return "12:"+tr("The data size is not equal to pointed one.");
	else if(resp.slice(2,4) != data.slice(0,2) && resp.charCodeAt(2,"UTF-16BE") != nmHash("n.Err"))
		return "21:Unpaired function in the response.";

	data = resp.slice(2, resp.length-2);
	//if(data.length < 3)	return "49:"+tr("Data size error.");

	return "0";
}

//Set transport and init
if(f_start) {
	dt = new Object();
	items_ = "";
	transport_ = transport;
	tr = SYS.Transport.nodeAt(transport,".");

	t_infW = ""; infWHoldTm = 0;
}
if(f_stop) return;

//Parsing the items to set and to create the user attributes
if(items != items_) {
	items_ = items;
	// Mark of checking to deletion needs
	for(var iDt in dt)	dt[iDt].mark = false;
	// Appending/Updating present ones
	for(itOff = 0; (sIt=items.parseLine(0,itOff)).length; ) {
		if(sIt[0] == "#")	continue;
		off1 = 0;
		itO = new Object();
		itO.addr = sIt.parse(0, ":", off1).toInt();
		itO.func = sIt.parse(0, ":", off1);
		itO.tp = sIt.parse(0, ":", off1);
		itO.md = sIt.parse(0, ":", off1);
		itO.id = sIt.parse(0, ":", off1);
		itO.nm = sIt.slice(off1);
		if(!itO.nm.length)	itO.nm = itO.id.length ? itO.id : itO.func;
		if(!itO.id.length)	itO.id = itO.func.replace(new RegExp("[.]"), "_");
		SYS.messDebug("/OWEN", "id="+itO.id);
		if(itO.tp == "u" || itO.tp == "i") {
			wTp = "integer";
			itO.sz = itO.tp.slice(1).toInt();
			itO.sz = (itO.sz?itO.sz:16)/8;
			if(!(itO.sz==1||itO.sz==2||itO.sz==4))	continue; 
		}
		else if(itO.tp == "s")	wTp = "string";
		else if(itO.tp == "f")	wTp = "real";
		else continue;
		itO.rd = (itO.md.indexOf("r") >= 0); itO.wr = (itO.md.indexOf("w") >= 0);
		if(!itO.wr)	wTp += "|ro";
		if((itO.funcH=nmHash(itO.func)) < 0)	continue;
		// Apending
		dt[itO.id] = itO;
		if(itO.rd || itO.wr) {
			this.attrAdd(itO.id, itO.nm, wTp);
			if(itO.wr)	itO.val = this[itO.id].get();
			itO.mark = true;
			//SYS.messDebug("/OWEN", "itO="+itO.id+"; tmpAddr="+tmpAddr+"; addr="+itO.addr);
		}
	}
	// Check, remove item and set to EVAL the attribute
	for(var iDt in dt) {
		if(dt[iDt].mark)	continue;
		this[dt[iDt].id].set(EVAL, 0, 0, true);	//If the attribute locked
		this.attrDel(dt[iDt].id);
		delete dt[iDt];
	}
}

t_err = "0";

//Check for the transport change and connect
if(!tr || transport != transport_)	{
	transport_ = transport;
	tr = SYS.Transport.nodeAt(transport,".");
}
if(!tr)	t_err = "1:"+tr("Error the output transport ''%1''.").replace("%1",transport);
else {
	t_inf = "";
	if(infWHoldTm && SYS.time() > infWHoldTm)	t_infW = "", infWHoldTm = 0;
	
	//The parameters processing
	for(var iDt in dt) {
		itO = dt[iDt];
		if(!t_err.toInt()) {
			SYS.messDebug("/OWEN", "iDt="+iDt);

			// Writing
			if(itO.wr && !(tVl=this[itO.id].get()).isEVal() && tVl != itO.val) {
				data = SYS.strFromCharCode(itO.funcH>>8, itO.funcH&0xFF);
				if(itO.tp == "f") {
					tVl2 = tVl3 = 0;
					Special.FLibSYS.floatSplitWord(tVl, tVl2, tVl3);
					data += SYS.strFromCharCode(tVl3>>8, tVl3&0xFF, tVl2>>8) + ((tVl2&0xFF)?SYS.strFromCharCode(tVl2&0xFF):"");
				}
				else if(itO.tp == "u" || itO.tp == "i") {
					for(tVl2 = SYS.strFromCharCode(tVl&0xFF), tVl = tVl>>8; tVl; tVl = tVl>>8)
						tVl2 = SYS.strFromCharCode(tVl&0xFF) + tVl2;
					data += tVl2;
				}
				else if(itO.tp == "s")
					for(iV = tVl.length-1; iV >= 0; iV--)	data += tVl[iV];

				if(!(t_err=req(tr,itO.addr,data)).toInt()) {
					if(data.charCodeAt(0,"UTF-16BE") == nmHash("n.Err")) {
						t_infW += (t_infW.length?"; ":": ")+itO.nm+":nErr=0x"+data.charCodeAt(2).toString(16);
						infWHoldTm = SYS.time()+10;
					} else itO.val = tVl;
				}
			}

			// Reading
			data = SYS.strFromCharCode(itO.funcH>>8, itO.funcH&0xFF);
			if(itO.rd && !(t_err=req(tr,itO.addr,data)).toInt()) {
				if(data.charCodeAt(0,"UTF-16BE") == nmHash("n.Err") && itO.funcH != nmHash("n.Err")) {
					t_inf += (t_inf.length?"; ":": ")+itO.nm+":nErr=0x"+data.charCodeAt(2).toString(16);
					this[itO.id].set(EVAL, 0, 0, true); itO.val = EVAL;
				}
				else {
					data = data.slice(2);
					if(itO.tp == "f") {
						if(data.length == 1)
							tVl = EVAL, t_inf += (t_inf.length?"; ":": ")+itO.nm+":err=0x"+data.charCodeAt(0).toString(16);
						else if(data.length >= 3)
							tVl = Special.FLibSYS.floatMergeWord(
								data.charCodeAt(2)*256+((data.length < 4)?0:data.charCodeAt(3)),
								data.charCodeAt(0)*256+data.charCodeAt(1));
						else tVl = EVAL;
						this[itO.id].set(tVl, 0, 0, true); itO.val = tVl;
					}
					else if(itO.tp == "u" || itO.tp == "i") {
						for(tVl = 0, iV = 0; iV < data.length; iV++)
							tVl = (tVl<<8) | data.charCodeAt(iV);
						this[itO.id].set(tVl, 0, 0, true); itO.val = tVl;
						//SYS.messDebug("/OWEN", "Integer data="+SYS.strDecode(data)+"; Sz="+itO.sz);
					}
					else if(itO.tp == "s")	{
						for(tVl = "", iV = 0; iV < data.length; iV++)	tVl = data[iV] + tVl;
						this[itO.id].set(tVl, 0, 0, true);	 itO.val = tVl;
					}
					continue;
				}
			}
		}
		this[itO.id].set(EVAL, 0, 0, true); itO.val = EVAL;
	}
}

f_err = t_err;
if(!f_err.toInt())	f_err += t_inf + (t_infW.length?" "+tr("Write")+t_infW:"");','','',1592158749);
INSERT INTO tmplib_DevLib VALUES('DCON_Prt','DCON Protocol + Input AI','','','Input and output parts of the protocol DCON implementation with the input part designed for typical AI modules at up to 20 channels. The protocol DCON client part also implemented by the OpenSCADA module DAQ.DCON.

The DCON commands are supported: #AA

Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.1
License: GPLv2','','',10,0,'JavaLikeCalc.JavaScript
if(f_start)	io = tr = EVAL;

function CRC( inS ) {
	sumV = 0;
	for(i = 0; i < inS.length; i++) sumV += inS.charCodeAt(i);

	return sumV&0xFF;
}

//---------------------------------------
//Output request form of the UserProtocol
//---------------------------------------
//<ReqSymb addr="{toAddr}" err="{err}" CRC="{CRC}">{req}</ReqSymb> - tag of the output message
//  ReqSymb - symbol of the request type into the tag name, like to: ''#'', ''@'', etc;
//  toAddr - remote host address [0...240];
//  CRC - sign [0,1] of using CRC;
//  req - data of the request and response;
//  err - sets for the request result.
if(!tr.isEVal() && !io.isEVal() && io.attr("ProtIt").length) {
	io.setAttr("err", "0");

	req = io.name().slice(0,1) + io.attr("addr").toInt().toString(16,2) + io.text();
	if(io.attr("CRC").toInt())
		req += (CRC(req)&0xFF).toString(16,2);
	req += "\r";

	//Sending the request
	resp = tr.messIO(req);
	while(resp.length && resp[resp.length-1] != "\r")
		if((tresp=tr.messIO("")).length) resp += tresp;
		else break;

	//Analysis of the response
	if(io.attr("CRC").toInt()) {
		if(resp.length < 4 || resp[resp.length-1] != "\r")
		{ io.setAttr("err","10:"+tr("Error or no response.")); return; }
		//Checking the response to the integrity (CRC)
		if(CRC(resp.slice(0,-3)) != resp.slice(-3,-1).toInt(16))
		{ io.setAttr("err","11:"+tr("CRC error.")); return; }
	}
	else if(resp.length < 2 || resp[resp.length-1] != "\r")
	{ io.setAttr("err","10:"+tr("Error or no response.")); return; }
	if(resp[0] != ">") { io.setAttr("err","12:"+resp[0]+":"+tr("DCON error.")); return; }

	//Returning the result
	io.setText(resp.slice(1,(io.attr("CRC").toInt()?-3:-1)));

	return;
}

//---------------------------------------------
//Input requests processing of the UserProtocol
//---------------------------------------------
//Testing the request for completity
if(request.length < 4 || request[request.length-1] != "\r") {
	if(request.length > 10) request = "";
	return true;
}
addr = max(0, min(240,addr));
n = max(1, min(20,n));
req = request; request = "";

//SYS.messInfo("DCON/in", "TEST 01: "+req);

//Checking the request for the integrity (CRC) and the address
if((enCRC && CRC(req.slice(0,-3)) != req.slice(-3,-1).toInt(16)) || !(addr == 0 || req.slice(1,3).toInt(16) == addr))
	return true;

//Analysis the request and the response preparing
if(req[0] == "#") {	//Commands #AA
	answer = ">";
	for(iA = 0; iA < n; iA++)
		answer += ((tVl=arguments["ai"+iA]).isEVal()?0:tVl).toFixed(3,7,true);
} else answer = "?";	//return true;

//Finishing the response
if(enCRC) answer += (CRC(answer)&0xFF).toString(16,2);
answer += "\r";

return true;','','',1580046089);
INSERT INTO tmplib_DevLib VALUES('DCON_Prt_DI','DCON Protocol + Input DI','','','Input and output parts of the protocol DCON implementation with the input part designed for typical DI modules at up to 32 channels. The protocol DCON client part also implemented by the OpenSCADA module DAQ.DCON.

The DCON commands are supported: @AA, $AA6

Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.1
License: GPLv2','','',10,0,'JavaLikeCalc.JavaScript
if(f_start)	io = tr = EVAL;

function CRC( inS ) {
	sumV = 0;
	for(i = 0; i < inS.length; i++) sumV += inS.charCodeAt(i);

	return sumV&0xFF;
}

//---------------------------------------
//Output request form of the UserProtocol
//---------------------------------------
//<ReqSymb addr="{toAddr}" err="{err}" CRC="{CRC}">{req}</ReqSymb> - tag of the output message
//  ReqSymb - symbol of the request type into the tag name, like to: ''#'', ''@'', etc;
//  toAddr - remote host address [0...240];
//  CRC - sign [0,1] of using CRC;
//  req - data of the request and response;
//  err - sets for the request result.
if(!tr.isEVal() && !io.isEVal() && io.attr("ProtIt").length) {
	io.setAttr("err", "0");

	req = io.name().slice(0,1) + io.attr("addr").toInt().toString(16,2) + io.text();
	if(io.attr("CRC").toInt())
		req += (CRC(req)&0xFF).toString(16,2);
	req += "\r";

	//Sending the request
	resp = tr.messIO(req);
	while(resp.length && resp[resp.length-1] != "\r")
		if((tresp=tr.messIO("")).length) resp += tresp;
		else break;

	//Analysis of the response
	if(io.attr("CRC").toInt()) {
		if(resp.length < 4 || resp[resp.length-1] != "\r")
		{ io.setAttr("err","10:"+tr("Error or no response.")); return; }
		//Checking the response to the integrity (CRC)
		if(CRC(resp.slice(0,-3)) != resp.slice(-3,-1).toInt(16))
		{ io.setAttr("err","11:"+tr("CRC error.")); return; }
	}
	else if(resp.length < 2 || resp[resp.length-1] != "\r")
	{ io.setAttr("err","10:"+tr("Error or no response.")); return; }
	if(resp[0] != ">") { io.setAttr("err","12:"+resp[0]+":"+tr("DCON error.")); return; }

	//Returning the result
	io.setText(resp.slice(1,(io.attr("CRC").toInt()?-3:-1)));

	return;
}

//---------------------------------------------
//Input requests processing of the UserProtocol
//---------------------------------------------
//Testing the request for completity
if(request.length < 4 || request[request.length-1] != "\r") {
	if(request.length > 10) request = "";
	return true;
}
addr = max(0, min(240,addr));
n = max(1, min(32,n));
req = request; request = "";

//SYS.messInfo("DCON/in", "TEST 01: "+req);

//Checking the request for the integrity (CRC) and the address
if((enCRC && CRC(req.slice(0,-3)) != req.slice(-3,-1).toInt(16)) || !(addr == 0 || req.slice(1,3).toInt(16) == addr))
	return true;

//Analysis the request and the response preparing
if(req[0] == "@" || (req[0] == "$" && req[3] == "6")) {	//Commands @AA and $AA6
	for(ib = 0; ib < n; ib++) {
		if(ib%8 == 0)	tVl = 0;
		tVl += ((arguments["di"+ib] == true)?1:0) << (ib%8);
		if(ib%8 == 7 || (ib+1) == n)	answer = tVl.toString(16,2) + answer;
	}
	if(req[0] == "$")	answer += "00";
	answer = "!" + answer;
} else answer = "?";	//return true;


//Finishing the response
if(enCRC) answer += (CRC(answer)&0xFF).toString(16,2);
answer += "\r";

return true;','','',1580047532);
INSERT INTO tmplib_DevLib VALUES('DCON_Prt_AO','DCON Protocol + Input AO','','','Input and output parts of the protocol DCON implementation with the input part designed for typical AO modules at up to 8 channels. The protocol DCON client part also implemented by the OpenSCADA module DAQ.DCON.

The DCON commands are supported: $AA8N, #AAN{Data}

Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.1
License: GPLv2','','',10,0,'JavaLikeCalc.JavaScript
if(f_start)	io = tr = EVAL;

function CRC( inS ) {
	sumV = 0;
	for(i = 0; i < inS.length; i++) sumV += inS.charCodeAt(i);

	return sumV&0xFF;
}

//---------------------------------------
//Output request form of the UserProtocol
//---------------------------------------
//<ReqSymb addr="{toAddr}" err="{err}" CRC="{CRC}">{req}</ReqSymb> - tag of the output message
//  ReqSymb - symbol of the request type into the tag name, like to: ''#'', ''@'', etc;
//  toAddr - remote host address [0...240];
//  CRC - sign [0,1] of using CRC;
//  req - data of the request and response;
//  err - sets for the request result.
if(!tr.isEVal() && !io.isEVal() && io.attr("ProtIt").length) {
	io.setAttr("err", "0");

	req = io.name().slice(0,1) + io.attr("addr").toInt().toString(16,2) + io.text();
	if(io.attr("CRC").toInt())
		req += (CRC(req)&0xFF).toString(16,2);
	req += "\r";

	//Sending the request
	resp = tr.messIO(req);
	while(resp.length && resp[resp.length-1] != "\r")
		if((tresp=tr.messIO("")).length) resp += tresp;
		else break;

	//Analysis of the response
	if(io.attr("CRC").toInt()) {
		if(resp.length < 4 || resp[resp.length-1] != "\r")
		{ io.setAttr("err","10:"+tr("Error or no response.")); return; }
		//Checking the response to the integrity (CRC)
		if(CRC(resp.slice(0,-3)) != resp.slice(-3,-1).toInt(16))
		{ io.setAttr("err","11:"+tr("CRC error.")); return; }
	}
	else if(resp.length < 2 || resp[resp.length-1] != "\r")
	{ io.setAttr("err","10:"+tr("Error or no response.")); return; }
	if(resp[0] != ">") { io.setAttr("err","12:"+resp[0]+":"+tr("DCON error.")); return; }

	//Returning the result
	io.setText(resp.slice(1,(io.attr("CRC").toInt()?-3:-1)));

	return;
}

//---------------------------------------------
//Input requests processing of the UserProtocol
//---------------------------------------------
//Testing the request for completity
if(request.length < 4 || request[request.length-1] != "\r") {
	if(request.length > 10) request = "";
	return true;
}
addr = max(0, min(240,addr));
req = request; request = "";

//SYS.messInfo("DCON/in", "TEST 01: "+req);

//Checking the request for the integrity (CRC) and the address
if((enCRC && CRC(req.slice(0,-3)) != req.slice(-3,-1).toInt(16)) || !(addr == 0 || req.slice(1,3).toInt(16) == addr))
	return true;

//Analysis the request and the response preparing
if(req[0] == "$" && req[3] == "8") {	//Command $AA8N to read back the output value
	answer = "!";
	n = req.slice(4,5).toInt(16);
	answer += req.slice(1,3) + ((tVl=arguments["ao"+n]).isEVal()?0:tVl).toFixed(3,7,true);
}
else if(req[0] == "#" && req.length >= 10) {	//Command #AAN{Data} to write the output
	n = req.slice(3,4).toInt(16);
	arguments["ao"+n] = req.slice(4,(enCRC?-3:-1)).toReal();
	answer = ">";
}
else answer = "?";	//return true;

//Finishing the response
if(enCRC) answer += (CRC(answer)&0xFF).toString(16,2);
answer += "\r";

return true;','','',1580047532);
INSERT INTO tmplib_DevLib VALUES('DCON_Prt_DO','DCON Protocol + Input DO','','','Input and output parts of the protocol DCON implementation with the input part designed for typical DO modules at up to 32 channels. The protocol DCON client part also implemented by the OpenSCADA module DAQ.DCON.

The DCON commands are supported: @AA, @AA{Data}

Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.1
License: GPLv2','','',10,0,'JavaLikeCalc.JavaScript
if(f_start)	io = tr = EVAL;

function CRC( inS ) {
	sumV = 0;
	for(i = 0; i < inS.length; i++) sumV += inS.charCodeAt(i);

	return sumV&0xFF;
}

//---------------------------------------
//Output request form of the UserProtocol
//---------------------------------------
//<ReqSymb addr="{toAddr}" err="{err}" CRC="{CRC}">{req}</ReqSymb> - tag of the output message
//  ReqSymb - symbol of the request type into the tag name, like to: ''#'', ''@'', etc;
//  toAddr - remote host address [0...240];
//  CRC - sign [0,1] of using CRC;
//  req - data of the request and response;
//  err - sets for the request result.
if(!tr.isEVal() && !io.isEVal() && io.attr("ProtIt").length) {
	io.setAttr("err", "0");

	req = io.name().slice(0,1) + io.attr("addr").toInt().toString(16,2) + io.text();
	if(io.attr("CRC").toInt())
		req += (CRC(req)&0xFF).toString(16,2);
	req += "\r";

	//Sending the request
	resp = tr.messIO(req);
	while(resp.length && resp[resp.length-1] != "\r")
		if((tresp=tr.messIO("")).length) resp += tresp;
		else break;

	//Analysis of the response
	if(io.attr("CRC").toInt()) {
		if(resp.length < 4 || resp[resp.length-1] != "\r")
		{ io.setAttr("err","10:"+tr("Error or no response.")); return; }
		//Checking the response to the integrity (CRC)
		if(CRC(resp.slice(0,-3)) != resp.slice(-3,-1).toInt(16))
		{ io.setAttr("err","11:"+tr("CRC error.")); return; }
	}
	else if(resp.length < 2 || resp[resp.length-1] != "\r")
	{ io.setAttr("err","10:"+tr("Error or no response.")); return; }
	if(resp[0] != ">") { io.setAttr("err","12:"+resp[0]+":"+tr("DCON error.")); return; }

	//Returning the result
	io.setText(resp.slice(1,(io.attr("CRC").toInt()?-3:-1)));

	return;
}

//---------------------------------------------
//Input requests processing of the UserProtocol
//---------------------------------------------
//Testing the request for completity
if(request.length < 4 || request[request.length-1] != "\r") {
	if(request.length > 10) request = "";
	return true;
}
addr = max(0, min(240,addr));
n = max(1, min(32,n));
req = request; request = "";

//SYS.messInfo("DCON/in", "TEST 01: "+req);

//Checking the request for the integrity (CRC) and the address
if((enCRC && CRC(req.slice(0,-3)) != req.slice(-3,-1).toInt(16)) || !(addr == 0 || req.slice(1,3).toInt(16) == addr))
	return true;
req = req.slice(0, (enCRC?-3:-1));

//Analysis the request and the response preparing
if(req[0] == "@" && req.length == 3) {	//Commands @AA
	for(ib = 0; ib < n; ib++) {
		if(ib%8 == 0)	tVl = 0;
		tVl += ((arguments["do"+ib] == true)?1:0) << (ib%8);
		if(ib%8 == 7 || (ib+1) == n)	answer = tVl.toString(16,2) + answer;
	}
	answer = ">" + answer;
}
else if(req[0] == "@" && req.length > 3) {	//Commands @AA{Data}
	for(iB = req.length-2, iC = 0; iB >= 3; iB -= 2) {
		tVl = req.slice(iB, iB+2).toInt(16);
		for(ib = 0; ib < 8; ib++, iC++)
			arguments["do"+iC] = tVl&(1<<ib);
	}
	answer = ">";
}
else answer = "?";	//return true;

//Finishing the response
if(enCRC) answer += (CRC(answer)&0xFF).toString(16,2);
answer += "\r";

return true;','','',1580047532);
INSERT INTO tmplib_DevLib VALUES('DNP3','Distributed Network Protocol 3','','','Distributed Network Protocol 3 (DNP3) is a set of communications protocols used between components in process automation systems. Its main use is in utilities such as electric and water companies. Usage in other industries is not common. It was developed for communications between various types of data acquisition and control equipment. Competing standards include the older Modbus protocol and the newer IEC 61850 protocol and IEC 61870.

The template implements currently for the client, which uses the followed functions: Confirm (0x00), Read (0x01), Write (0x02), Enable (0x14) and Disable (0x15) Spontaneous Messages.

And which implements and tested for the followed point-oriented objects (data types):
- 1.[2] — Binary Input [with status]
- 2.[1] — Binary Input Changed [w/o time]
- 3.[2] — Double-bit Input [with status]
- 4.[1] — Double-bit Input Changed [w/o time]
- 10.[2] — Binary Output (reading back and used to write also) [with status]
- 20,21.[1] — Binary Counter (Typical,Frozen) [32-Bit]
- 22,23.[1] — Binary Counter Changed (Typical,Frozen) [32-Bit w/o time]
- 30.[1,5] — Analog Input [32-bit integer value with flag, 32-bit floating point value with flag]
- 32.[7] — Analog Input Changed [32-bit floating point value with flag and event time]
- 40.[1] — Analog Output (reading back and used to write also) [32-bit integer value with flag]
- 50 — Time and Date (just passed and not read) [indexed absolute time and long interval]
- 60.[1,2,3,4] — Classes [0,1,2,3]
- 80.[1] — Device [Restart 7=Off(0x01)]
- 110.[1] — Octet String (just passed and not read) [Sized by variation]

Considering the purpose and the application area, the protocol does not provide the direct determination of the requested data and the device, that implements it, provides all available ones upon request for the Class 0 data, after which only changes are requested for the Classes 1, 2, 3.

All received and controlled data is accumulated in an object of the attribute "items" for further control as an object in a table of the widget "Main.objProps", according to the complex object definition.

For standard control over parameter attributes, including archiving, it is possible to define the necessary data to create a separate representative attribute for them, which is described in the attribute "itemsSet", where they can be defined individually, with a semantic identifier and name, or in a range named by a template. The data in the attribute "itemsSet" is defined by records in the format ai|ao|cnt|di|do:{pnt}[-{endPnt}][:a[:{NameBase}]], where:
- ai|ao|cnt|di|do — data type, where:
  - "ai" — analog inputs, transmitted by the functions 30 and 32;
  - "ao" — analog outputs, transmitted by the function 40;
  - "cnt" — counters, transmitted by the functions 20, 21, 22, 23; so Typical and Frozen counters is in one space;
  - "di" — discrete inputs, transmitted by the functions 1, 2, 3, 4; so Binary Input and Double-bit Input is in one space;
  - "do" — discrete outputs, transmitted by the function 10.
- {pnt} — point number of the range begin;
- {endPnt} — the range end (the last point number);
- a — sign of creating the representative attribute for the parameter or the parameters group;
- {NameBase} — naming base of the attributes.

Examples of the parameters definition, rows of the attribute "itemsSet":
- ai:0-9:a — analog inputs in the points range [0...9] with forming the attributes;
- ao:0-9:a — analog outputs in the points range [0...9] with forming the attributes;
- di:0-9:a — discrete inputs in the points range [0...9] with forming the attributes;
- do:0-9:a — discrete outputs in the points range [0...9] with forming the attributes;
- cnt:0-9:a — counters in the points range [0...9] with forming the attributes.

Author: Roman Savochenko <roman@oscada.org>
Total complexity: 2.0 HD
Sponsored by: Hadi Ramezani and Hossein Malakooti for whole complexity
Version: 0.8.1
License: GPLv2','','',10,0,'JavaLikeCalc.JavaScript
function CRC( inSeq ) { return Special.FLibSYS.CRC(inSeq, 16, 0x3D65, 0, true, true, -1); }

function inVal(aId, aVal, QLT, wr) {
	if(!QLT.isEVal() && (!(QLT&0x01) || (QLT&0x1E)))	aVal = EVAL;
	if(wr.isEVal())	wr = false;
	if(items[aId].isEVal()) {
		items[aId] = itW = new Object();
		itW.descr = aId;
		itW.wr = wr; itW.alarm = 0;
	} else itW = items[aId];
	itW.val = aVal;
	if((aO=this[aId])) {
		aO.set(aVal, 0, 0, true);
		if(itW.wr) oAVals[aId] = aVal;
	}
}

//Forming of the message and placing it to the output buffer
function mess( func, objs, ac ) {
	if(ac.isEVal())	{
		ac = 0x80 | 0x40 | (ctx.appSeq&0xF);	//FIR, FIN, Sequence
		ctx.reqToResp = ctx.appSeq.toString()+":"+func;
		if(ctx.appSeq >= 15)	ctx.appSeq = 0;	else ctx.appSeq++;
	} 
	aMess = SYS.strFromCharCode(ac, func) + objs;
	//Splitting the application message to the transport messages,
	//		limited in the length in 255 symbols without CRC
	for(off = 0; off < aMess.length; ) {
		trMess = aMess.slice(off, off+min(255-6,aMess.length-off));
		trMess = SYS.strFromCharCode(((off==0)?0x80:0)|((off+trMess.length)>=aMess.length?0x40:0)|(ctx.trSeq&0x3F)) +		//FIR, FIN, Sequence
			trMess;
		if(ctx.trSeq >= 63)	ctx.trSeq = 0;	else ctx.trSeq++;
		off += trMess.length;

		//Splitting the transport message to the data chunks,
		//		limited in 16 symbols and appending CRC
		for(mess = "", chOff = 0; chOff < trMess.length; chOff += 16) {
			tVl = trMess.slice(chOff, chOff+min(16,trMess.length-chOff));
			mess += tVl + SYS.strFromCharUTF("UTF-16",CRC(tVl));
		}

		//Appending the transport message header and it CRC
		tVl = SYS.strFromCharCode(0x05, 0x64, (trMess.length+5), 0x80|0x40|0x4) +	//DIR, PRM, Unconfirmed User Data (4) ????
				SYS.strFromCharUTF("UTF-16", dest, src);
		mess = tVl + SYS.strFromCharUTF("UTF-16",CRC(tVl)) + mess;
		ctx.out += mess;				//Buffering
		//tr.messIO(mess, 0, 0);	//Just direct writing per one message
	}
}

function processIn( ) {
	t_err = "";
	//Parse for packages in general - the transport layer
	for(waitSz = 0; ctx.in.length; ctx.in = ctx.in.slice(waitSz)) {
		// Checking the header entirety
		if((ctx.in.length >= 3 && (ctx.in.charCodeAt(0,"UTF-16") != 0x6405 || (dataSz=ctx.in.charCodeAt(2)) < 8)) ||
				(ctx.in.length >= 10 && CRC(ctx.in.slice(0,8)) != ctx.in.charCodeAt(8,"UTF-16"))) {
			SYS.messDebug("/DNP3",tr("Wrong or CRC-error sequence, rejected")+": "+SYS.strDecode(ctx.in,"Bin"," "));
			ctx.in = "";
			break;
		}
		waitSz = 10 + floor((dataSz-5)/16)*18 + ((tVl=(dataSz-5)%16)?tVl+2:0);
		if(ctx.in.length < 13 || ctx.in.length < waitSz)	break;	//Not full, waiting

		SYS.messDebug("/DNP3",tr("Good input sequence")+": "+SYS.strDecode(ctx.in.slice(0,waitSz),"Bin"," "));

		if(ctx.in.charCodeAt(4,"UTF-16") != src || ctx.in.charCodeAt(6,"UTF-16") != dest) {
			SYS.messDebug("/DNP3",tr("Foreign package, rejected")+": "+SYS.strDecode(ctx.in.slice(waitSz),"Bin"," "));
			continue;
		}

		// Getting the data chunks
		for(data = "", off = 10, iCh = 0; off < waitSz; off += 18, iCh++) {
			chSz = min(waitSz, off+18) - 2;
			if(CRC((tVl=ctx.in.slice(off,chSz))) != ctx.in.charCodeAt(chSz,"UTF-16"))	break;
			data += tVl;
		}
		if(off < waitSz) {
			SYS.messDebug("/DNP3",tr("CRC error in chunk %1, rejected").replace("%1",iCh.toString()));
			continue;
		}

		// Control of the transport packages sequence
		//  First
		if((tc=data.charCodeAt(0))&0x40) {
			ctx.inAMess = data;
			if(!(tc&0x80)) {							//Not Final
				if(ctx.firSeq >= 0 && (((wSeq=ctx.firSeq+1)>=64)?0:wSeq) != (tc&0x3F))	SYS.messDebug("/DNP3",tr("Broken sequence, rejected"));
				ctx.firSeq = tc&0x3F;
			}
		}
		//  Final or Pass
		else if(ctx.firSeq < 0 || (((wSeq=ctx.firSeq+1)>=64)?0:wSeq) != (tc&0x3F)) {	//Broken sequence
			SYS.messDebug("/DNP3",tr("Broken sequence, rejected"));
			ctx.firSeq = -1;
		}
		else if(ctx.firSeq >= 0) {																		//Good sequence
			ctx.inAMess += data.slice(1);
			ctx.firSeq = (tc&0x80) ? -1 : (tc&0x3F);	//Not Final
		}

		// Process of the reassigned application message
		if(ctx.inAMess.length && (tc&0x80)) {
			am = ctx.inAMess.slice(1), ctx.inAMess = "";	//Get the application message
			ac = am.charCodeAt(0);
			SYS.messDebug("/DNP3", "Input application message: "+SYS.strDecode(am,"Bin"," "));

			//  Checking and disabling the Spontaneous Messages
			if(am.charCodeAt(1) == 0x82 && !ctx.spontEn)	mess(0x15, SYS.strFromCharCode(0x3C,0x02,0x06,0x3C,0x03,0x06,0x3C,0x04,0x06));
			//  Confirmation
			if(ac&0x20)	mess(0x00, "", 0x80|0x40|(ac&0x1F));
			//  Processing the response package
			if((am.charCodeAt(1) == 0x81 && ctx.reqToResp.length && ctx.reqToResp.toInt() == (ac&0x0F)) || am.charCodeAt(1) == 0x82) {
				intInd = am.charCodeAt(2,"UTF-16BE");
				if(intInd&0x8000) t_err += tr("Device Restart")+"; ";
				if(intInd&0x4000) t_err += tr("Device Trouble")+"; ";
				if(intInd&0x2000) t_err += tr("Digital Outputs in Local")+"; ";
				if(intInd&0x1000) t_err += tr("Time Sync Required")+"; ";
				if(intInd&0x0800) t_err += tr("Class 3 Data Available")+"; ";
				if(intInd&0x0400) t_err += tr("Class 2 Data Available")+"; ";
				if(intInd&0x0200) t_err += tr("Class 1 Data Available")+"; ";
				if(intInd&0x0020) t_err += tr("Configuration Corrupt")+"; ";
				if(intInd&0x0010) t_err += tr("Operation Already Executing")+"; ";
				if(intInd&0x0008) t_err += tr("Event Buffer Overflow")+"; ";
				if(intInd&0x0004) t_err += tr("Parameters Invalid or Out of Range")+"; ";
				if(intInd&0x0002) t_err += tr("Requested Objects Unknown")+"; ";
				if(intInd&0x0001) t_err += tr("Function Code not implemented")+"; ";
				//respToF = ctx.reqToResp.parse(1,":").toInt();
				ctx.reqToResp = "";	//The propper response came
				if(intInd&0x8000)	mess(0x2, SYS.strFromCharCode(0x50,0x01,0x00,0x07,0x07,0x00));	//Restarting the server: Write 0x5001, 07, 0 (false)
				//Process the function result
				else //if(respToF == 0x01)	//Read
					for(off = 4; off < am.length; ) {
						if((off+3) >= am.length)	{ SYS.messDebug("/DNP3",tr("Not enough of the data in %1, the processing terminated").replace("%1",off.toString())); break; }
						dtObj = am.charCodeAt(off), dtObjV = am.charCodeAt(off+1), dtQf = am.charCodeAt(off+2), off += 3;
						dtRcode = (dtQf&0xF);	//Range code
						pntStart = pntStop = 0;
						if(dtRcode == 0)				//8-bit start and stop
							pntStart = am.charCodeAt(off), pntStop = am.charCodeAt(off+1), off += 2;
						else if(dtRcode == 1)		//16-bit start and stop
							pntStart = am.charCodeAt(off, "UTF-16"), pntStop = am.charCodeAt(off+2, "UTF-16"), off += 4;
						else if(dtRcode == 7)		//8-bit Single Field Quantity
							pntStop = am.charCodeAt(off)-1, off += 1;
						else if(dtRcode == 8)		//16-bit Single Field Quantity
							pntStop = am.charCodeAt(off, "UTF-16")-1, off += 2;
						else { SYS.messWarning("/DNP3",tr("Unsupported data range code %1 in %2, the processing terminated").replace("%1",dtRcode.toString()).replace("%2",off.toString())); break; }
						if(pntStart > pntStop)	{ SYS.messDebug("/DNP3",tr("Wrong points range [%1...%2] in %3, the processing terminated").replace("%1",pntStart.toString()).replace("%2",pntStop.toString()).replace("%3",off.toString())); break; }
						SYS.messDebug("/DNP3","pntStart="+pntStart+"; pntStop="+pntStop+"; off="+off);
						
						if(dtObj == 1) {					//Binary Input
							if(dtObjV == 2) {			//.with status
								if((off+(pntStop-pntStart+1)) > am.length)	{ SYS.messDebug("/DNP3",tr("Not enough of the data in %1, the processing terminated").replace("%1",off.toString())); break; }
								for( ; pntStart <= pntStop; pntStart++, off++)
									inVal("di"+pntStart, ((QLT=am.charCodeAt(off))&0x80)?true:false, QLT);
							} else { SYS.messWarning("/DNP3",tr("Binary Input (1): unsupported variant ''%1'', the processing terminated").replace("%1",dtObjV.toString())); 	break; }
						}
						else if(dtObj == 2) {			//Binary Input Changed
							if(dtObjV == 1) {			//.with status
								if((off+3*(pntStop-pntStart+1)) > am.length)	{ SYS.messDebug("/DNP3",tr("Not enough of the data in %1, the processing terminated").replace("%1",off.toString())); break; }
								for( ; pntStart <= pntStop; pntStart++, off += 3)
									inVal("di"+am.charCodeAt(off,"UTF-16"), ((QLT=am.charCodeAt(off+2))&0x80)?true:false, QLT);
							} else { SYS.messWarning("/DNP3",tr("Binary Input Changed (2): unsupported variant ''%1'', the processing terminated").replace("%1",dtObjV.toString())); 	break; }
						}
						else if(dtObj == 3) {			//Double-bit Input
							if(dtObjV == 2) {			//.with status
								if((off+(pntStop-pntStart+1)) > am.length)	{ SYS.messDebug("/DNP3",tr("Not enough of the data in %1, the processing terminated").replace("%1",off.toString())); break; }
								for( ; pntStart <= pntStop; pntStart++, off++)
									inVal("di"+pntStart, (((QLT=am.charCodeAt(off))&0xC0)==0xC0||(QLT&0xC0)==0)?EVAL:(QLT&0x40)?true:false, QLT);
							} else { SYS.messWarning("/DNP3",tr("Double-bit Input (3): unsupported variant ''%1'', the processing terminated").replace("%1",dtObjV.toString())); 	break; }
						}
						else if(dtObj == 4) {			//Double-bit Input Changed
							if(dtObjV == 1) {			//.with status
								if((off+3*(pntStop-pntStart+1)) > am.length)	{ SYS.messDebug("/DNP3",tr("Not enough of the data in %1, the processing terminated").replace("%1",off.toString())); break; }
								for( ; pntStart <= pntStop; pntStart++, off += 3)
									inVal("di"+am.charCodeAt(off,"UTF-16"), (((QLT=am.charCodeAt(off+2))&0xC0)==0xC0||(QLT&0xC0)==0)?EVAL:(QLT&0x40)?true:false, QLT);
							} else { SYS.messWarning("/DNP3",tr("Double-bit Input Changed (4): unsupported variant ''%1'', the processing terminated").replace("%1",dtObjV.toString())); 	break; }
						}
						else if(dtObj == 10) {			//Binary Output
							if(dtObjV == 2) {			//.with status
								if((off+(pntStop-pntStart+1)) > am.length)	{ SYS.messDebug("/DNP3",tr("Not enough of the data in %1, the processing terminated").replace("%1",off.toString())); break; }
								for( ; pntStart <= pntStop; pntStart++, off++)
									inVal("do"+pntStart, ((QLT=am.charCodeAt(off))&0x80)?true:false, QLT, true);
							} else { SYS.messWarning("/DNP3",tr("Binary Output (10): unsupported variant ''%1'', the processing terminated").replace("%1",dtObjV.toString())); 	break; }
						}
						else if(dtObj == 20 || dtObj == 21) {	//Binary Counter and Frozen Binary Counter
							if(dtObjV == 1) {							//.default
								if((off+5*(pntStop-pntStart+1)) > am.length)	{ SYS.messDebug("/DNP3",tr("Not enough of the data in %1, the processing terminated").replace("%1",off.toString())); break; }
								for( ; pntStart <= pntStop; pntStart++, off += 5)
									inVal("cnt"+pntStart, am.charCodeAt(off+1,"UTF-32"), am.charCodeAt(off));
							} else { SYS.messWarning("/DNP3",tr("Binary Counter (20) or Frozen Binary Counter (21): unsupported variant ''%1'', the processing terminated").replace("%1",dtObjV.toString())); 	break; }
						}
						else if(dtObj == 22 || dtObj == 23) {	//Binary Counter Changed and Frozen Binary Counter Changed
							if(dtObjV == 1) {							//.default
								if((off+7*(pntStop-pntStart+1)) > am.length)	{ SYS.messDebug("/DNP3",tr("Not enough of the data in %1, the processing terminated").replace("%1",off.toString())); break; }
								for( ; pntStart <= pntStop; pntStart++, off += 7)
									inVal("cnt"+am.charCodeAt(off,"UTF-16"), am.charCodeAt(off+3,"UTF-32"), am.charCodeAt(off+2));
							} else { SYS.messWarning("/DNP3",tr("Binary Counter Changed (22) or Frozen Binary Counter Changed (23): unsupported variant ''%1'', the processing terminated").replace("%1",dtObjV.toString())); 	break; }
						}
						else if(dtObj == 30) {			//Analog Input
							if(dtObjV == 1) {			//.32-bit integer value with flag
								if((off+5*(pntStop-pntStart+1)) > am.length)	{ SYS.messDebug("/DNP3",tr("Not enough of the data in %1, the processing terminated").replace("%1",off.toString())); break; }
								for( ; pntStart <= pntStop; pntStart++, off += 5)
									inVal("ai"+pntStart, am.charCodeAt(off+1,"UTF-32"), am.charCodeAt(off));
							} else if(dtObjV == 5) {	//.32-bit floating point value with flag
								if((off+5*(pntStop-pntStart+1)) > am.length)	{ SYS.messDebug("/DNP3",tr("Not enough of the data in %1, the processing terminated").replace("%1",off.toString())); break; }
								for( ; pntStart <= pntStop; pntStart++, off += 5)
									inVal("ai"+pntStart, Special.FLibSYS.floatMergeWord(am.charCodeAt(off+1,"UTF-16"),am.charCodeAt(off+3,"UTF-16")), am.charCodeAt(off));
							} else { SYS.messWarning("/DNP3",tr("Analog Input (30): unsupported variant ''%1'', the processing terminated").replace("%1",dtObjV.toString())); 	break; }
						}
						else if(dtObj == 32) {			//Analog Input Changed
							if(dtObjV == 7) {			//.32-bit floating point value with flag and event time
								if((off+13*(pntStop-pntStart+1)) > am.length)	{ SYS.messDebug("/DNP3",tr("Not enough of the data in %1, the processing terminated").replace("%1",off.toString())); break; }
								for( ; pntStart <= pntStop; pntStart++, off += 13)
									inVal("ai"+am.charCodeAt(off,"UTF-16"), Special.FLibSYS.floatMergeWord(am.charCodeAt(off+3,"UTF-16"),am.charCodeAt(off+5,"UTF-16")), am.charCodeAt(off+2));
							} else { SYS.messWarning("/DNP3",tr("Analog Input Changed (32): unsupported variant ''%1'', the processing terminated").replace("%1",dtObjV.toString())); 	break; }
						}
						else if(dtObj == 40) {			//Analog Output
							if(dtObjV == 1) {			//.32-bit integer value with flag
								if((off+5*(pntStop-pntStart+1)) > am.length)	{ SYS.messDebug("/DNP3",tr("Not enough of the data in %1, the processing terminated").replace("%1",off.toString())); break; }
								for( ; pntStart <= pntStop; pntStart++, off += 5)
									inVal("ao"+pntStart, am.charCodeAt(off+1,"UTF-32"), am.charCodeAt(off), true);
							} else { SYS.messWarning("/DNP3",tr("Analog Output (40): unsupported variant ''%1'', the processing terminated").replace("%1",dtObjV.toString())); 	break; }
						}
						else if(dtObj == 50) {			//Time and Date
							if(dtObjV == 4) {			//.Indexed absolute time and long interval
								if((off+5*(pntStop-pntStart+1)) > am.length)	{ SYS.messDebug("/DNP3",tr("Not enough of the data in %1, the processing terminated").replace("%1",off.toString())); break; }
								for( ; pntStart <= pntStop; pntStart++, off += 11) ;
									//inVal("ao"+pntStart, am.charCodeAt(off+1,"UTF-32"), am.charCodeAt(off));
							} else { SYS.messWarning("/DNP3",tr("Time and Date (4): unsupported variant ''%1'', the processing terminated").replace("%1",dtObjV.toString())); 	break; }
						}
						else if(dtObj == 110) {			//Time and Date
							if((off+dtObjV*(pntStop-pntStart+1)) > am.length)	{ SYS.messDebug("/DNP3",tr("Not enough of the data in %1, the processing terminated").replace("%1",off.toString())); break; }
							for( ; pntStart <= pntStop; pntStart++, off += dtObjV) ;
						}
						else { SYS.messWarning("/DNP3",tr("Unsupported data object ''%1'', the processing terminated").replace("%1",dtObj.toString())); break; }
					}
			}
		}
	}
	return "0"+(t_err.length?":"+t_err:"");
}

if(f_start) {
	itemsSet_ = "";
	items = new Object();
	oAVals = new Object();
	transport_ = "", tr = EVAL;
	tmRetr_ = tmResp_ = tmRetr;
	tmPoolEv_ = 0;
	tmPoolAll_ = tmPoolAll;
	ctx = new Object();

	return;
}
if(f_stop) {
	if(!tr.isEVal()) tr.start(false);
	return;
}

//Items set changing process
if(itemsSet != itemsSet_) {
	for(off = 0; (iIt=itemsSet.parse(0,"\n",off)).length; ) {
		iIt_tp = iIt.parse(0, ":");
		iIt_IOA = iIt.parse(1, ":");
		iIt_flgs = iIt.parse(2, ":");
		iIt_nmBase = iIt.parse(3, ":");
		iIt_sDscr = false;
		if((iIt_EndIOA=iIt_IOA.indexOf("-")) >= 0) {
			iIt_EndIOA = iIt_IOA.slice(iIt_EndIOA+1).toInt(0);
			iIt_IOA = iIt_IOA.slice(0,iIt_EndIOA).toInt(0);
		}
		else { iIt_IOA = iIt_EndIOA = iIt_IOA.toInt(0); iIt_sDscr = iIt_nmBase.length; }
		if(iIt_tp == "ai")			{ iIt_nmBase = iIt_nmBase.length ? iIt_nmBase : "AI"; iIt_vtp = "real,ro"; }
		else if(iIt_tp == "ao")	{ iIt_nmBase = iIt_nmBase.length ? iIt_nmBase : "AO"; iIt_vtp = "real"; }
		else if(iIt_tp == "cnt")	{ iIt_nmBase = iIt_nmBase.length ? iIt_nmBase : "CNTR"; iIt_vtp = "int,ro"; }
		else if(iIt_tp == "di")	{ iIt_nmBase = iIt_nmBase.length ? iIt_nmBase : "DI"; iIt_vtp = "boolean,ro"; }
		else if(iIt_tp == "do")	{ iIt_nmBase = iIt_nmBase.length ? iIt_nmBase : "DO"; iIt_vtp = "boolean"; }
		//else if(iIt_tp == "s")		{ iIt_nmBase = iIt_nmBase.length ? iIt_nmBase : "STR"; iIt_vtp = "string,ro"; }
		else continue;
		while(iIt_IOA <= iIt_EndIOA) {
			aId = iIt_tp+iIt_IOA; aDscr = iIt_nmBase+(iIt_sDscr?"":"["+iIt_IOA+"]"); aWr = (iIt_tp == "ao" || iIt_tp == "do");
			if(items[aId].isEVal()) { items[aId] = itW = new Object(); itW.descr = aDscr; itW.wr = aWr; itW.alarm = 0; }
			if(iIt_flgs.indexOf("a") >= 0) {
				this.attrAdd(aId, aDscr, iIt_vtp);
				if(aWr)	oAVals[aId] = EVAL_INT;
				this[aId].set(EVAL_INT, 0, 0, true);
			}
			iIt_IOA++;
		}
	}
	itemsSet_ = itemsSet;

	tmPoolAll_ = tmPoolAll;	//and reread
}

//Check for the transport change and connect
t_err = "0";
if(tr.isEVal() || transport != transport_) {
	itemsSet_ = "";
	items = new Object();
	oAVals = new Object();
	tr = (tr_=SYS.Transport.nodeAt(transport, ".")) ? tr_ : EVAL;
	transport_ = transport;
	if(!tr.isEVal()) tr.start(false);
	tmRetr_ = tmResp_ = tmRetr;
	tmPoolEv_ = 0;
	tmPoolAll_ = tmPoolAll;
}
if(tr.isEVal())	t_err = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
else if(dest < 0 || dest > 65520)
	t_err = "2:"+tr("Destination address ''%1'' out of range [0...65520].").replace("%1",dest.toString());
else if(src < 0 || src > 65520)
	t_err = "2:"+tr("Source address ''%1'' out of range [0...65520].").replace("%1",src.toString());
else if(!tr.start()) {
	if(tmRetr_ >= tmRetr) {
		tr.start(true);
		itemsSet_ = "";
		tmRetr_ = tmResp_ = 0;
		tmPoolEv_ = 0;
		tmPoolAll_ = tmPoolAll;
		ctx = new Object();
	} else tmRetr_ += 1/f_frq;
	if(!tr.start())	t_err = "1:"+tr("Output transport ''%1'' stoped.").replace("%1",transport);
}
else {
	//Init the context
	if(ctx.in.isEVal()) {
		ctx.in = ctx.out = ctx.inAMess = "";
		ctx.trSeq = ctx.appSeq = 0;
		ctx.firSeq = -1;
		ctx.spontEn = false;		//Spontanous messages disabled
		ctx.readAll = false;		//All values readed, stage
		ctx.reqToResp = "";
	}

	//Send output buffer and read the transport for an input data
	if(ctx.out.length) SYS.messDebug("/DNP3",tr("Good output sequence")+": "+SYS.strDecode(ctx.out,"Bin"," "));
	ctx.in += tr.messIO(ctx.out, -0.001);
	ctx.out = "";

	//Input processing
	t_err = processIn();

	//Response timeout
	if(ctx.reqToResp.length) {
		if(tmResp_ > tmRetr)	tr.start(false);	//No response long time
		tmResp_ += 1/f_frq;
	}
	else {
		//Writing
		// By attributes
		for(iIt in oAVals)
			if((aO=this[iIt]) && !(aOval=aO.get()).isEVal() && aOval != oAVals[iIt]) {
				if(iIt.slice(0,2) == "do")
					mess(0x02, SYS.strFromCharCode(10,2,0,iIt.slice(2).toInt(),iIt.slice(2).toInt(),aOval?1:0));
				else if(iIt.slice(0,2) == "ao")
					mess(0x02, SYS.strFromCharCode(40,1,0,iIt.slice(2).toInt(),iIt.slice(2).toInt(),0)+SYS.strFromCharUTF("UTF-32",aOval));
				oAVals[iIt] = aOval;
			}
		// By items object
		for(iIt in items)
			if((aO=items[iIt]) && !(aOval=aO.set).isEVal()) {
				if(iIt.slice(0,2) == "do")
					mess(0x02, SYS.strFromCharCode(10,2,0,iIt.slice(2).toInt(),iIt.slice(2).toInt(),aOval?1:0));
				else if(iIt.slice(0,2) == "ao")
					mess(0x02, SYS.strFromCharCode(40,1,0,iIt.slice(2).toInt(),iIt.slice(2).toInt(),0)+SYS.strFromCharUTF("UTF-32",aOval));
				aO.set = EVAL_BOOL;
			}

		//Polling
		// Read all classes
		if(tmPoolAll_ > tmPoolAll)		{ mess(0x1, SYS.strFromCharCode(0x3C,0x02,0x06,0x3C,0x03,0x06,0x3C,0x04,0x06,0x3C,0x01,0x06));	tmPoolAll_ = 0; ctx.readAll = true; }
		//Read periodically the class 1
		else if(tmPoolEv_ > tmPoolEv) {
			// Enable the spontanous messages
			if(!ctx.spontEn)	{ mess(0x14, SYS.strFromCharCode(0x3C,0x02,0x06,0x3C,0x03,0x06,0x3C,0x04,0x06)); ctx.spontEn = true; }
			else mess(0x1, SYS.strFromCharCode(0x3C,0x02,0x06));
			tmPoolEv_ = 0;
		}

		if(ctx.readAll)	tmPoolEv_ += 1/f_frq;
		tmPoolAll_ += 1/f_frq;
		tmResp_ = 0;
	}
}

//Error set
if(t_err.toInt()) {
	if(!tr.isEVal() && tr.start()) tr.start(false);
	if(f_err != t_err) {
		items = new Object();
		oAVals = new Object();
		aLs = this.nodeList("a_");
		for(iA = 0; iA < aLs.length; iA++)
			if((aPref=aLs[iA].slice(2,4)) == "di" || aPref == "do" || aPref == "ai" || aPref == "ao")
				this[aLs[iA].slice(2)].set(EVAL_INT, 0, 0, true);
		SYS.messDebug("/DNP3", tr("Error")+": "+t_err);
	}
}
f_err = t_err;
','','',1592158403);
INSERT INTO tmplib_DevLib VALUES('IEC62056','IEC-62056','IEC-62056','','IEC 62056 part 46 is one from the IEC 62056 set of standards which defines systems used for telecontrol (supervisory control and data acquisition) in electrical engineering and power system automation applications. Part 46 defines Data link layer using HDLC protocol, which uses mostly on serial interfaces.

Author: Roman Savochenko <roman@oscada.org>
State of the protocol implementing: Client of the specification part 46, reading of the directly specified OBIS
Total complexity: 3.2 HD
Sponsored by: SVItoVYR Ltd for whole complexity
Version: 1.1.2
License: GPLv2','IEC 62056 у частині 46 є одним з набору стандартів IEC 62056 який визначає системи, що використовуються у віддаленому контролі (телемеханіці — диспетчерському контролі та зборі даних) у інженерній електриці та у застосунках автоматизації енергетичних систем. Частина 46 надає шар підключення Даних з використанням протоколу HDLC, який переважно використовується на послідовних інтерфейсах.

Автор: Роман Савоченко <roman@oscada.org>
Стан реалізації протоколу: Клієнт частини 46 специфікації, читання прямо визначених OBIS
Загальна працемісткість: 3.2 ЛД
Спонсорування: ТОВ "СВІТоВИР АВТоМАТИК" на загальну працемісткість
Версія: 1.1.2
Ліцензія: GPLv2','',10,0,'JavaLikeCalc.JavaScript
function CRC( inSeq ) { return Special.FLibSYS.CRC(inSeq, 16, 0x1021, -1, true, true, 0xFFFF); }

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

	return val;
}

function inVal( aId, aVal ) {
	aId_ = aId.replace(".", "_"); 
	if(items[aId_].isEVal()) {
		items[aId_] = itW = new Object();
		itW.descr = aId;
		itW.wr = false; itW.alarm = 0;
	} else itW = items[aId_];
	itW.val = aVal;
	if((aO=this[aId_]).isEVal()) {
		if((aTp=typeof(aVal)) == "array")	aTp = "object";
		else if(aTp == "real") ;
		else if(aTp == "int")	aTp = "integer";
		else aTp = "string";
		this.attrAdd(aId_, aId, aTp+",ro");
		aO = this[aId_];
	}
	aO.set(aVal, 0, 0, true);
}

function processIn( isSync ) {
	t_err = "";
	//Parse for packages in general - the transport layer
	for(waitSz = 0; ctx.in.length; ctx.in = ctx.in.slice(waitSz)) {
		// Checking for very short
		if(ctx.in.length < 9)	{ if(isSync == true) return "10:"+tr("Too short"); break; }
		// Checking the header entirety
		if(ctx.in[0] != "~" || ((form=ctx.in.charCodeAt(1))&0xF0) != 0xA0 || ((dataSz=ctx.in.charCodeAt(1,"UTF-16BE")&0x07FF) < 7) ||
				//  Checking the source address
				!((src_=ctx.in.charCodeAt(3))&1) || (src_>>1) != src) {
			SYS.messDebug("/IEC62056",tr("Wrong header or source address, rejected")+": "+SYS.strDecode(ctx.in,"Bin"," "));
			if(isSync == true) return "11:"+tr("Wrong header or source address, rejected");
			ctx.in = "";
			break;
		}
		//  Checking the destination address
		if(((dst_=ctx.in.charCodeAt(4))&1) && (dst_>>1) == ctx.destUp && ctx.destLow == 0)
			dst_sz = 1;
		else if(!((dst_=ctx.in.charCodeAt(4))&1) && ((dst_2=ctx.in.charCodeAt(5))&1) && (dst_>>1) == ctx.destUp && (dst_2>>1) == ctx.destLow)
			dst_sz = 2;
		else if(!((dst_=ctx.in.charCodeAt(4))&1) && !(ctx.in.charCodeAt(5)&1) && !((dst_2=ctx.in.charCodeAt(6))&1) && (ctx.in.charCodeAt(7)&1) &&
				(((dst_>>1)<<7)|(ctx.in.charCodeAt(5)>>1)) == ctx.destUp && (((dst_2>>1)<<7)|(ctx.in.charCodeAt(7)>>1)) == ctx.destLow)
			dst_sz = 4;
		else {
			SYS.messDebug("/IEC62056",tr("Wrong destination address, rejected")+": "+SYS.strDecode(ctx.in,"Bin"," "));
			if(isSync == true) return "11:"+tr("Wrong destination address, rejected");
			ctx.in = "";
			break;
		}
		// Short yet, waiting
		if(ctx.in.length < (7+dst_sz))	{ if(isSync == true) return "10:"+tr("Too short"); break; }
		//  Checking the header CRC
		if(CRC(ctx.in.slice(1,5+dst_sz)) != ctx.in.charCodeAt(5+dst_sz,"UTF-16LE")) {
			SYS.messDebug("/IEC62056",tr("CRC-error, rejected")+": "+SYS.strDecode(ctx.in,"Bin"," "));
			if(isSync == true) return "11:"+tr("CRC-error, rejected");
			ctx.in = "";
			break;
		}
		// Checking the frame entirety
		//  Not full yet, waiting
		if(ctx.in.length < (waitSz=2+dataSz)) { if(isSync == true) return "10:"+tr("Too short"); break; }
		if(ctx.in[waitSz-1] != "~") {
			SYS.messDebug("/IEC62056",tr("The end symbol is wrong")+": "+SYS.strDecode(ctx.in,"Bin"," "));
			if(isSync == true) return "11:"+tr("The end symbol is wrong");
			ctx.in = "";
			break;
		}
		//  Checking the frame CRC with the data
		if(dataSz > (6+dst_sz) && CRC(ctx.in.slice(1,1+dataSz-2)) != ctx.in.charCodeAt(1+dataSz-2,"UTF-16LE")) {
			SYS.messDebug("/IEC62056",tr("CRC-error, rejected")+": "+SYS.strDecode(ctx.in,"Bin"," "));
			if(isSync == true) return "11:"+tr("CRC-error, rejected");
			ctx.in = "";
			break;
		}

		SYS.messDebug("/IEC62056",tr("Good input sequence")+": "+SYS.strDecode(ctx.in.slice(0,waitSz),"Bin"," "));

		cntr = ctx.in.charCodeAt(4+dst_sz);
		// Checking the control byte, sequinces and the frame segments
		if((cntr&0x7) == 1) {	//RR
			SYS.messDebug("/IEC62056", "RR"+(cntr>>5)+((cntr&0x10)?"F":""));
			if(ctx.reqToResp == "RR")	ctx.reqToResp = "";
			continue;
		}
		else if(!(cntr&1)) {	//I
			SYS.messDebug("/IEC62056", "I"+((cntr>>1)&0x7)+","+(cntr>>5)+((cntr&0x10)?"F":""));
			if(ctx.sendSeq != (cntr>>5)) {
				SYS.messDebug("/IEC62056", tr("Broken sequence %1, rejected").replace("%1",ctx.sendSeq.toString()));
				continue;
			}
			if(ctx.readSeq >= 7) ctx.readSeq = 0; else ctx.readSeq++;
			if(cntr&0x10)	ctx.sendRR = true; //mess("RR");		//F - final
		}

		ctx.inAMess += ctx.in.slice(7+dst_sz, 1+dataSz-2);
		if(form&0x8)	continue;	//Waiting the next segment

		// Getting the data
		if(cntr == 0x73)	{	//UA
			SYS.messDebug("/IEC62056", "UA");
			if(ctx.toInit == 1 && ctx.reqToResp == "SNRM")	{
				ctx.toInit++;
				//!!!! Parsing the SNRM result
			}
			ctx.reqToResp = "";
		}
		else if((cntr&0x11) == 0x10) {	//I,F
			if(ctx.inAMess.slice(0,4) == SYS.strFromCharCode(0xE6,0xE7,0x00,0x61)) {	//LPDU, APPLICATION 1, Initial parameter acquiring
				if(ctx.inAMess.indexOf(SYS.strFromCharCode(0xA2,0x03,0x02,0x01,0x00)) >= 0) ctx.toInit++;
				else { t_err += tr("Error the authentication")+"; "; tr.start(false); }
				//!!!! Deep parsing the initial parameter acquiring
			}
			else if(ctx.inAMess.slice(0,5) == SYS.strFromCharCode(0xE6,0xE7,0x00,0xC4,0x01)) {	//LPDU, GET-Response-Normal
				tVl = ctx.inAMess.charCodeAt(5);		//Invoke-Id-And-Priority
				// Get-Data-Result
				if((tVl=ctx.inAMess.charCodeAt(6)) == 0) {	// Data
					val = ASN1_Data(ctx.inAMess, 7);
					//Processing the result
					// Array of the variables with OBIS and values
					if(typeof(val) == "array" && val.length == 1 && typeof(val[0]) == "array" && val[0].length && typeof(val[0][0]) == "array" && val[0][0].length == 3)
						for(iIt = 0; iIt < val[0].length; iIt++) {
							wIt = val[0][iIt];
							tVl = val[0][iIt][0];
							for(iIt_OBIS = "", iN = 0; iN < tVl.length; iN++)
								iIt_OBIS += (iIt_OBIS.length?".":"") + tVl.charCodeAt(iN).toString();
							inVal(iIt_OBIS, val[0][iIt][1]*pow(10,val[0][iIt][2][0]));
						}
					// Single variable
					else inVal(ctx.reqOBIS, val);
					SYS.messDebug("/IEC62056", "Data value: "+typeof(val));
				}
				else if(tVl == 1)										// Error
					t_err += tr("Error the data %1").replace("%1",ctx.inAMess.charCodeAt(7).toString())+"; ";
			}
		}
		ctx.inAMess = "";
	}
	return "0"+(t_err.length?":"+t_err:"");
}

//Forming of the message and placing it to the output buffer
//  Commands: "SNRM", "RR", "I", "DISC"
function mess( com, data ) {
	if(data.isEVal())	data = "";
	mLen = 10 + (data.length ? data.length+2 : 0);
	 //SYS.strFromCharCode(ac, func) + objs;
	aMess = SYS.strFromCharCode(0xA0|(mLen>>8), mLen&0xFF);
	if(!ctx.destLow && ctx.destUp < 128)					aMess += SYS.strFromCharCode(((ctx.destUp&0x7F)<<1)|1);
	else if(ctx.destUp < 128 && ctx.destLow < 128)	aMess += SYS.strFromCharCode((ctx.destUp&0x7F)<<1, ((ctx.destLow&0x7F)<<1)|1);
	else aMess += SYS.strFromCharCode((ctx.destUp>>7)<<1, (ctx.destUp&0x7F)<<1, (ctx.destLow>>7)<<1, ((ctx.destLow&0x7F)<<1)|1);
	aMess += SYS.strFromCharCode((src<<1)|1);
	ctx.reqToResp = com;
	if(com == "SNRM")		aMess += SYS.strFromCharCode(0x93);
	else if(com == "RR")	aMess += SYS.strFromCharCode((ctx.readSeq<<5)|0x11);
	else if(com == "DISC")	aMess += SYS.strFromCharCode(0x53);
	else if(com == "I") {
		aMess += SYS.strFromCharCode(0x10|(ctx.readSeq<<5)|(ctx.sendSeq<<1));
		if(ctx.sendSeq >= 7) ctx.sendSeq = 0; else ctx.sendSeq++;
	} else return;	
	aMess += SYS.strFromCharUTF("UTF-16LE",CRC(aMess)) + data;
	if(data.length)	aMess += SYS.strFromCharUTF("UTF-16LE",CRC(aMess));
	aMess = SYS.strFromCharCode(0x7E) + aMess + SYS.strFromCharCode(0x7E);
	ctx.out += aMess;
	//Just direct writing per one message
	//SYS.messDebug("/IEC62056",tr("Good output sequence")+": "+SYS.strDecode(aMess,"Bin"," "));
	//tr.messIO(aMess, 0, 0);

	//Synchronous request-response
	SYS.messDebug("/IEC62056",tr("Good output sequence")+": "+SYS.strDecode(ctx.out,"Bin"," "));
	t_err = "0";
	ctx.in = tr.messIO(ctx.out); ctx.out = "";
	if(!ctx.in.length)	{ t_err = "10:"+tr("No response"); tr.start(false); }
	else while((t_err=processIn(true)).toInt() == 10 && (respTail=tr.messIO("")).length) ctx.in += respTail;

	return t_err;
}

if(f_start) {
	itemsSet_ = "";
	items = new Object();
	//oAVals = new Object();
	transport_ = "", tr = EVAL;
	tmRetr_ = tmResp_ = tmRetr;
	tmPollAll_ = tmPollAll; offPoll = 0;
	ctx = new Object();

	return;
}

t_err = "0";

if(f_stop)	t_err = mess("DISC");

//Items set changing process
if(itemsSet != itemsSet_) {
	itemsSet_ = itemsSet;
	tmPollAll_ = tmPollAll; offPoll = 0;		//and reread
}

//Check for the transport change and connect
destUp = dest.parse(0,":").toInt(); destLow = dest.parse(1,":").toInt();
if(tr.isEVal() || transport != transport_)	{
	itemsSet_ = "";
	items = new Object();
	//oAVals = new Object();
	tr = SYS.Transport.nodeAt(transport, ".");
	transport_ = transport;
	if(!tr.isEVal()) tr.start(false);
	tmRetr_ = tmResp_ = tmRetr;
	tmPollAll_ = tmPollAll; offPoll = 0;
}
if(tr.isEVal())	t_err = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
else if(destUp < 0 || destUp > 16383)
	t_err = "2:"+tr("Upper HDLC destination address ''%1'' out of range [0...16383].").replace("%1",destUp.toString());
else if(destLow < 0 || destLow > 16383)
	t_err = "2:"+tr("Lower HDLC destination address ''%1'' out of range [0...16383].").replace("%1",destUp.toString());
else if(src < 0 || src > 127)
	t_err = "2:"+tr("Source address ''%1'' out of range [0...127].").replace("%1",src.toString());
else if(!tr.start()) {
	if(tmRetr_ >= tmRetr) {
		tr.start(true);
		itemsSet_ = "";
		tmRetr_ = tmResp_ = 0;
		tmPollAll_ = tmPollAll; offPoll = 0;
		ctx = new Object();
		ctx.destUp = destUp; ctx.destLow = destLow;
		pass_ = pass;
	} else tmRetr_ += 1/f_frq;
	if(!tr.start())	t_err = "1:"+tr("Output transport ''%1'' stoped.").replace("%1",transport);
}
else if(ctx.destUp != destUp || ctx.destLow != destLow || pass != pass_)	tr.start(false);
else {
	//Init the context
	if(ctx.in.isEVal()) {
		ctx.in = ctx.out = ctx.inAMess = "";
		ctx.sendSeq = ctx.readSeq = 0;
		ctx.toInit = 1;
		ctx.firSeq = -1;
		ctx.sendRR = false;
		ctx.reqToResp = "";
	}

	//Send output buffer and read the transport for an input data
	// Asynchronous mode
	//if(ctx.out.length) SYS.messDebug("/IEC62056",tr("Good output sequence")+": "+SYS.strDecode(ctx.out,"Bin"," "));
	//ctx.in += tr.messIO(ctx.out, -0.001);
	//ctx.out = "";

	//Input processing
	//t_err = processIn();

	//Response timeout
	/*if(ctx.reqToResp.length) {
		if(tmResp_ > tmRetr)	tr.start(false);	//No response long time
		tmResp_ += 1/f_frq;
	}
	// Initial parameters setting
	else*/
	if(!t_err.toInt() && ctx.toInit == 1)
		t_err = mess("SNRM", SYS.strFromCharCode(
			0x81,0x80,0x12,		//format and group identifier, length 18 bytes
				0x05,0x01,0x80,	//the parameter "Maximum information field length - transmit" = 128
				0x06,0x01,0x80,	//the parameter "Maximum information field length - receive" = 128
				0x07,0x04,0x00,0x00,0x00,0x01,	//the parameter "Window size - transmit" = 1
				0x08,0x04,0x00,0x00,0x00,0x07	//the parameter "Window size - receive" = 7
			));
	// Initial parameter acquiring
	if(!t_err.toInt() && ctx.toInit == 2)
		t_err = mess("I", SYS.strFromCharCode(
			0xE6,								//Destination (remote) LSAP, fixed in COSEM
			0xE6,								//Source (local) LSAP, fixed in COSEM E6 for command E7 for response
			0x00,								//Quality, Control byte, Reserved and 00 always
			0x60, 46+pass.length,		//APPLICATION 0, Length 54
			0xA1, 0x09, 0x06, 0x07,	//Application-context-name [1] (9 byte), OBJECT IDENTIFIER (7 byte)
				0x60, 0x85, 0x74, 0x05, 0x08, 0x01, 0x01,
			0x8A, 0x02, 0x07, 0x80,	//ACSE-requirements [10], BIT STRING (7) = 0x80
			0x8B, 0x07,						//Security Mechanism-name [11]
				0x60, 0x85, 0x74, 0x05, 0x08, 0x02, 0x01,
			0xAC, pass.length+2, 0x80, pass.length) + //Authentication-value [12], GraphicString (0x80), Key value
				pass + SYS.strFromCharCode(
			0xBE, 0x10, 0x04, 0x0E,	//Association-information [30], Client information, OCTET STRING (4)
				0x01,							// Data tag for the request
				0x00, 0x00, 0x00,		// Flags field
				0x06, 0x5F, 0x1F,		// Application tag, 31
				0x04, 0x00, 0x00, 0x18, 0x1D,	// Client side services
				0x00, 0x00					// PDU maximum size
			));
	if(!t_err.toInt() && ctx.toInit > 2) {
		if(ctx.sendRR)	{ t_err = mess("RR"); ctx.sendRR = false; }
		//Polling
		// Read all items
		if(!t_err.toInt() && tmPollAll_ > tmPollAll) {
			if((iIt=itemsSet.parse(0,"\n",offPoll)).length) {
				iIt_ClassId = iIt.parse(0, "-");
				iIt_OBIS = iIt.parse(1, "-");
				iIt_a = iIt.parse(2, "-");
				ctx.reqOBIS = iIt_OBIS;
				t_err = mess("I", SYS.strFromCharCode(0xE6, 0xE6, 0x00,
					0xC0, 0x01, 0x81,	//Get-Request-Normal, Invoke-Id-And-Priority
					0x00, iIt_ClassId.toInt(),	//Cosem-Class-Id, ?
					iIt_OBIS.parse(0,".").toInt(), iIt_OBIS.parse(1,".").toInt(), iIt_OBIS.parse(2,".").toInt(), iIt_OBIS.parse(3,".").toInt(), iIt_OBIS.parse(4,".").toInt(), iIt_OBIS.parse(5,".").toInt(),
					iIt_a.toInt(),		//Cosem-Object-Attribute-Id
					0x00						//Selective-Access-Descriptor, 0
				));
			}
			if(offPoll >= itemsSet.length) { tmPollAll_ = 0; offPoll = 0; }
		}

		tmPollAll_ += 1/f_frq;
		tmResp_ = 0;
	}
}

//Error set
if(t_err.toInt()) {
	if(!tr.isEVal() && tr.start()) tr.start(false);
	if(f_err != t_err) {
		for(var iIt in items)
			this[iIt].set(EVAL_REAL, 0, 0, true);
		items = new Object();
		SYS.messDebug("/IEC62056", tr("Error")+": "+t_err);
	}
}
f_err = t_err;

if(f_stop && !tr.isEVal()) tr.start(false);','','',1614260893);
INSERT INTO tmplib_DevLib VALUES('MTP4D','MTP 4D','MTP 4D','','The connection template of a simple vaccuumeter MTP 4D of the firm "Erstevak Ltd (http://www.erstvak.com/)".

Author: Roman Savochenko <roman@oscada.org>
Total complexity: 0.3 HD
Sponsored by, for whole complexity: Magomed, SAVTECH
Version: 1.1.0
License: GPLv2','','',10,0,'JavaLikeCalc.JavaScript
//Same request to the device
function req(data) {
	data = addr.toString(10,3) + data;

	//CRC calculation
	for(CRC = 0, iS = 0; iS < data.length; iS++) CRC += data.charCodeAt(iS);
	data += SYS.strFromCharCode((CRC%64)+64, 0x0D);

	for(resp = tr.messIO(data); resp[resp.length-1] != "\r" && (respTail=tr.messIO("")).length; )
		resp += respTail;

	if(resp.length < 6 || resp[resp.length-1] != "\r" || resp.slice(0,4) != data.slice(0,4))
		return "10:"+tr("Wrong or no response.");
	resp = resp.slice(0,-1);
	for(CRC = 0, iS = 0; iS < resp.length-1; iS++) CRC += resp.charCodeAt(iS);
	if(((CRC%64)+64) != resp.charCodeAt(resp.length-1))
		return "11:"+tr("Error CRC.");

	data = resp.slice(3,-1);

	return "0";
}

if(f_start) {
	f_err = "0";
	transport_ = transport;
	type = P = EVAL;
	zeroP = zeroAP = false;
	tr = EVAL;
}

if(f_start || f_stop) return;

t_err = "";

//Check for the transport change and connect
if(tr.isEVal() || transport != transport_)	{
	tr = SYS.Transport[transport.parse(0)]["out_"+transport.parse(1)];
	transport_ = transport;
}
if(tr.isEVal())	t_err = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
else if(addr < 1 || addr > 999)
	t_err = "2:"+tr("Address ''%1'' out of range [1...999].").replace("%1",addr.toString());
else {
	//Setting
	if(zeroAP && !t_err.toInt()) {
		data = "j1";
		if(!(t_err=req(data)).toInt()) {
			data = "j100023";
			if(!(t_err=req(data)).toInt())	zeroAP = false;
		}
	}
	if(zeroP && !t_err.toInt()) {
		data = "j0";
		if(!(t_err=req(data)).toInt()) {
			data = "j000000";
			if(!(t_err=req(data)).toInt())	zeroP = false;
		}
	}

	//Reading
	data = "T";
	if(!t_err.toInt() && !(t_err=req(data)).toInt())	type = data.slice(1);
	else type = EVAL;

	data = "M";
	if(!t_err.toInt() && !(t_err=req(data)).toInt())
		P = data.slice(1,5).toReal()*1e-3*pow(10,data.slice(5,7).toReal()-20);
	else P = EVAL;
}

if(t_err.toInt()) { f_err = t_err; type = P = EVAL; }
else f_err = "0";','','',1603038629);
INSERT INTO tmplib_DevLib VALUES('mbBase','ModBus base','','','ModBus base template of the protocol implementing on the Logical Level. Designed mostly to use in the base of creation specific ModBus-based protocols for the standard-part registers'' access (function 3 for reading and function 16 for writing) and appending the new protocol specific part. The template was designed to work both under control of the controller objects of the module ModBus and LogicLev and can be managed from the template of the initiative connections processing for assigned output transports.

Author: Roman Savochenko <roman@oscada.org>
Total complexity: 0.5 HD
Sponsored by, for whole complexity: Elyor Turaboev, BLUE STAR GROUP Ltd
Version: 1.0.0
License: GPLv2','','',30,0,'JavaLikeCalc.JavaScript
//Same request to the device
function req(PDU) {
	// For placing into the module ModBus
	if(!transport.length)	return this.cntr().messIO(PDU);

	// For other logical level
	reqO = SYS.XMLNode(mbType).setAttr("id","Goboy").setAttr("node",addr).setText(PDU);
	if((rez=tr.messIO(reqO,"ModBus")).length)	return "10:"+rez;
	PDU = reqO.text();

	return reqO.attr("err");
}

if(f_start) {
	transport_ = transport;
	tr = EVAL;
	dt = new Object();
	items_ = "";
}

//Parse the items set and create user attributes
if(items != items_) {
	items_ = items;
	// Mark for check to deletion needs
	for(var iDt in dt)
		if(iDt != "10000") dt[iDt].mark = false;
	// Append/Update present ones
	for(off = 0; (sIt=items.parseLine(0,off)).length; ) {
		if(sIt[0] == "#")	continue;
		off1 = 0;
		itO = new Object();
		itO.tp = sIt.parse(0, ":", off1);
		tmpAddr = sIt.parse(0, ":", off1);
		itO.addr = tmpAddr.toInt();
		itO.md = sIt.parse(0, ":", off1);
		itO.id = sIt.parse(0, ":", off1);
		itO.nm = sIt.slice(off1);
		dt[itO.addr.toString(16,5)] = itO;
		if(itO.tp == "u" || itO.tp == "i" || itO.tp == "u2" || itO.tp == "i2")	{ wTp = "integer"; itO.sz = 2; }
		else if(itO.tp == "u4" || itO.tp == "i4")	{ wTp = "integer"; itO.sz = 4; }
		else if(itO.tp == "s")	{ wTp = "string"; itO.sz = 16; }
		else { wTp = "real"; itO.sz = 4; }
		itO.rd = (itO.md.indexOf("r") >= 0);
		itO.wr = (itO.md.indexOf("w") >= 0);
		itO.rev = (itO.md.indexOf("~") >= 0);
		if(!itO.wr)	wTp += "|ro";
		if(itO.rd || itO.wr) {
			this.attrAdd(itO.id, itO.nm, wTp);
			if(itO.wr)	itO.val = this[itO.id].get();
			itO.mark = true;
			//SYS.messInfo("/ED","itO="+itO.id+"; tmpAddr="+tmpAddr+"; addr="+itO.addr);
		}
	}
	dt["10000"] = EVAL;
	// Check, remove item and set to EVAL the attribute
	for(var iDt in dt) {
		if(iDt == "10000" || dt[iDt].mark)	continue;
		this[dt[iDt].id].set(EVAL, 0, 0, true);
		delete dt[iDt];
	}
}

if(f_start || f_stop) return;

tErr = "";

//Checking for the transport change and connect
if(tr.isEVal() || transport != transport_)	{
	if(!transport.length) {
		if(!((tVl=this.cntr().cfg("PROT")) == "RTU" || tVl == "ASCII" || tVl == "TCP")) {
			tr = EVAL;
			tErr = "1:"+tr("Output transport is empty and the controller object is not ModBus.");
		}
		else {
			tr = new Object();
			maxBlkSz = this.cntr().cfg("MAX_BLKSZ");
			if(maxBlkSz.isEVal())	maxBlkSz = 12;
			fragMerge = this.cntr().cfg("FRAG_MERGE");
			if(fragMerge.isEVal())	fragMerge = false;
		}
	} else tr = SYS.Transport[transport.parse(0)]["out_"+transport.parse(1)];
	maxBlkSz = max(10, min(200,maxBlkSz));
	transport_ = transport;
}
if(tErr.toInt()) ;
else if(tr.isEVal())
	tErr = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
else if(addr < 0 || addr > 247)
	tErr = "2:"+tr("Address ''%1'' out of range [0...247].").replace("%1",addr.toString());
else {
	//Check for changed attributes and perform writing
	for(var iDt in dt) {
		isEOL = (iDt == "10000");
		itO = dt[iDt];
		tVl = this[itO.id].get();
		if(isEOL || !itO.wr || tVl.isEVal() || itO.val == tVl)	continue;
		PDU = Special.FLibSYS.IO("", "", "b");
		PDU.wr(16,"uint8").wr(itO.addr,"uint16").wr(floor(itO.sz/2),"uint16").wr(itO.sz,"uint8");
		if(itO.tp == "u" || itO.tp == "u2")			PDU.wr(tVl,"uint16");
		else if(itO.tp == "i" || itO.tp == "i2")	PDU.wr(tVl,"int16");
		else if(itO.tp == "u4")	{
			if(itO.rev)	PDU.wr(tVl>>16, "uint16").wr(tVl&0xFFFF, "uint16");
			else	PDU.wr(tVl&0xFFFF, "uint16").wr(tVl>>16, "uint16");
		}
		else if(itO.tp == "i4") {
			if(itO.rev)	PDU.wr(tVl>>16, "int16").wr(tVl&0xFFFF, "uint16");
			else	PDU.wr(tVl&0xFFFF, "uint16").wr(tVl>>16, "int16");
		}
		else if(itO.tp == "s")		PDU.wr(tVl.slice(0,16),"char").wr(" ","char",16-min(16,tVl.length));
		else {
			w1 = w2 = 0;
			if(itO.rev)	Special.FLibSYS.floatSplitWord(tVl, w2, w1);
			else	Special.FLibSYS.floatSplitWord(tVl, w1, w2);
			PDU.wr(w1, "uint16").wr(w2, "uint16");
		}
		//SYS.messInfo("/ED","reqPDU="+SYS.strDecode(PDU.string,"Bin"," "));
		if(!req(PDU.string).toInt())	itO.val = tVl;
		//SYS.messInfo("/ED","respPDU="+SYS.strDecode(PDU.string,"Bin"," "));
	}

	//Same requests for the data
	blk = new Array();
	for(var iDt in dt) {
		isEOL = (iDt == "10000");
		itO = dt[iDt];
		//SYS.messInfo("/ED","iDt="+iDt+"; isEOL="+isEOL);
		if(!isEOL && (!blk.length || (
				(itO.addr-blk[0].addr+1+floor((itO.sz-2)/2)) <= floor(maxBlkSz/2) && (fragMerge || (itO.addr-blk[blk.length-1].addr-floor((blk[blk.length-1].sz-2)/2)) <= 1) ))) {
			if(itO.rd) blk.push(itO);
			continue;
		}
		//Send request for this block
		if(blk.length) {
			regN = (blk[blk.length-1].addr - blk[0].addr) + 1 + floor((blk[blk.length-1].sz-2)/2);
			PDU = SYS.strFromCharCode(3, (blk[0].addr>>8)&0xFF, blk[0].addr&0xFF, 0, regN);
			//SYS.messInfo("/ED","reqPDU="+SYS.strDecode(PDU,"Bin"," "));
			if((tErr=req(PDU)).toInt()) break;
			//SYS.messInfo("/ED","respPDU="+SYS.strDecode(PDU,"Bin"," "));
			io = Special.FLibSYS.IO(PDU, "", "b");
			rF = io.read("uint8", 1); rN = io.read("uint8", 1);
			if(rF != 3 || (io.length-2) != rN) { tErr = "10:"+tr("Inconsistent respond''s length."); break; }
			for(iB = 0; iB < blk.length; iB++) {
				itO1 = blk[iB];
				io.pos = 2 + (itO1.addr-blk[0].addr)*2;
				if(itO1.tp == "u" || itO1.tp == "u2")			tVl = io.read("uint16", 1);
				else if(itO1.tp == "i" || itO1.tp == "i2")	tVl = io.read("int16", 1);
				else if(itO1.tp == "u4")
					tVl = itO1.rev ? io.read("uint16", 1)*65536 + io.read("uint16", 1) :
											io.read("uint16", 1) + io.read("uint16", 1)*65536;
				else if(itO1.tp == "i4")
					tVl = itO1.rev ? io.read("int16", 1)*65536 + io.read("uint16", 1) :
											io.read("uint16", 1) + io.read("int16", 1)*65536;
				else if(itO1.tp == "s")	tVl = io.read("char", blk[0].sz);
				else {
					w1 = io.read("uint16", 1); w2 = io.read("uint16", 1);
					tVl = itO1.rev ? Special.FLibSYS.floatMergeWord(w2, w1) :
											Special.FLibSYS.floatMergeWord(w1, w2);
					//tVl = io.read("float", 1, "l");
				}
				if(itO1.wr && itO1.val != this[itO1.id].get())	{ itO1.val = tVl; continue; }
				this[itO1.id].set(tVl, 0, 0, true);
				itO1.val = tVl;
			}
			blk = new Array(); blk.push(itO);
		}
	}
}

if(!tErr.length)	tErr = "0";
if(tErr.toInt()) {
	if(!tr.isEVal() && tr.start()) tr.start(false);
	if(f_err != tErr)
		for(var iDt in dt) {
			itO = dt[iDt];
			if(iDt == "10000")	continue;
			this[itO.id].set(EVAL, 0, 0, true);
		}
}
f_err = tErr;','','',1601827422);
CREATE TABLE IF NOT EXISTS 'tmplib_PrescrTempl' ("ID" TEXT DEFAULT '' ,"NAME" TEXT DEFAULT '' ,"uk#NAME" TEXT DEFAULT '' ,"ru#NAME" TEXT DEFAULT '' ,"DESCR" TEXT DEFAULT '' ,"uk#DESCR" TEXT DEFAULT '' ,"ru#DESCR" TEXT DEFAULT '' ,"MAXCALCTM" INTEGER DEFAULT '10' ,"PR_TR" INTEGER DEFAULT '1' ,"PROGRAM" TEXT DEFAULT '' ,"uk#PROGRAM" TEXT DEFAULT '' ,"ru#PROGRAM" TEXT DEFAULT '' ,"TIMESTAMP" INTEGER DEFAULT '' , PRIMARY KEY ("ID"));
INSERT INTO tmplib_PrescrTempl VALUES('timer','Command — Timer','Команда — Таймер','Команда — Таймер','Template of a command of the prescription typical timer. The timer is only designed to hold time between other action steps and for example, so it only has one attribute, "Time" in seconds.

Author: Roman Savochenko <roman@oscada.org>
Version: 2.0.0
License: GPLv2','Шаблон команди типового таймеру рецепту. Таймер розроблено лише для витримки часу між іншими дієвими кроками та для прикладу, відтак він містить лише один атрибут, це "Час" у секундах.

Автор: Роман Савоченко <roman@oscada.org>
Версія: 2.0.0
Ліцензія: GPLv2','Шаблон команды типового таймера рецепта. Таймер разработан только для удержания времени между другими действенными шагами и для примера, т.е. он содержит только один атрибут, это "Время" в секундах.

Автор: Роман Савоченко <roman@oscada.org>
Версия: 2.0.0
Лицензия: GPLv2',10,0,'JavaLikeCalc.JavaScript
//Reset to default
if(f_start || (!run && tmp1)) { run = pause = error = abort = false; tmp1 = 0; rez = 1; }
//Run
if(run && !pause) {
	if(tmp1 == 0) tmp1 = arg1+1/f_frq;
	tmp1 = max(0, tmp1 - 1/f_frq);
	if(tmp1 <= 0) { rez = "1:"+tr("Waiting %1s expired").replace("%1",arg1.toPrecision(3)); run = false; }
	else rez = "0:"+tr("Waiting %1s").replace("%1",tmp1.toPrecision(3));
}','','',1584814597);
INSERT INTO tmplib_PrescrTempl VALUES('backTimer','Command — Background timer','Команда — Фоновий таймер','Команда — Фоновый таймер','Template of a command of the prescription background timer. The timer designed only for hold a time in the background to an example, then it contains only one attribute, it is "Time" in seconds.

Author: Roman Savochenko <roman@oscada.org>
Version: 2.0.0
License: GPLv2','Шаблон команди фонового таймеру рецепту. Таймер розроблено лише для витримки часу у фоні для прикладу, відтак він містить лише один атрибут, це "Час" у секундах.

Автор: Роман Савоченко <roman@oscada.org>
Версія: 2.0.0
Ліцензія: GPLv2','Шаблон команды фонового таймера рецепта. Таймер разработан только для удержания времени в фоне для примера, т.е. он содержит только один атрибут, это "Время" в секундах.

Автор: Роман Савоченко <roman@oscada.org>
Версия: 2.0.0
Лицензия: GPLv2',10,0,'JavaLikeCalc.JavaScript
//Reset to default
if(f_start || (!run && tmp1)) { run = pause = error = abort = false; tmp1 = 0; rez = 1; }
//Run
if(run && !pause) {
	if(tmp1 == 0) tmp1 = arg1+1/f_frq;
	tmp1 = max(0, tmp1 - 1/f_frq);
	if(tmp1 <= 0) { rez = "1:"+tr("Waiting %1s expired").replace("%1",arg1.toPrecision(3)); run = false; }
	//if(tmp1 <= 10)	rez = "-1:Waiting "+arg1.toPrecision(3)+"s expired";
	else rez = "10:"+tr("Background waiting %1s").replace("%1",tmp1.toPrecision(3));
}','','',1584814597);
INSERT INTO tmplib_PrescrTempl VALUES('manager','Prescriptions manager','Менеджер рецептів','Менеджер рецептов','Basic, representative and unified template of a manager and a controller of the prescriptions, of their processing and direct execution in the "command-macro" mode. The template forms a structure of the prescription manager parameter which can be easily connected to all frames of this library.

The representative structure of the prescription manager is:
- "DB: DB name with the tables (dbDB)" — DB, like to "SQLite.vcaBase", where stored or must be stored the prescription-program tables.
- "DB: Table with programs (dbProgs)" — name of the prescriptions-programs table, typically "PrescrProgs".
- "Commands list (comLs)" — the allowed to use commands list.
- "Name of the selected program (prog)" — state and setting of the selected program name in processing by the manager.
- "Mode (mode)", "Current mode (curMode)" — the setting and the current manager mode of the selected prescription-program execution: Finish(-2), Error(-1), Stop(0), Run(1), Pause(2), Pass command(3).
- "Start time (startTm)" — the start time in seconds.
- "Current command-step (curCom)" — the currently processing command.
- "Work program (work)" — the actual and processing now program in an object.

Functions:
- Forms the "Commands list (comLs)", allowed to use in the user prescriptions-programs.
- Process the "Name of the selected program (prog)", modifying it will either load its command sequence to the "Work program (work)" or create a new program.
- Execute the program in the "Name of the selected program (prog)" after receiving the command "Run(1)" in the "Mode (mode)" and indicate this process in: the "Current mode (curMode)", the "Start time (startTm)", the "Current command-step (curCom)" and the "Work program (work)".
- Generate the action messages during the prescription execution for:
  CATEGORY: defines the user prescription-program ID ProgNM in the form "uprg{ProgNM}", where:
    - "uprg*" — the typical template-sign of a user prescription-program, that can be directly used in the category filter to determine in the messages only the user prescriptions-programs;
    - ProgNM — the prescription-program name.
  TEXT: the action description in the form "{ActDescr} "{ProgNM}" : {StartTm} : {ActTm}", where:
    - ActDescr — the action description:
      - "No current node present";
      - "Terminated program session by the user";
      - "Terminated program session by the error";
      - "Successful session of the program".
    - ProgNM — the prescription-program name;
    - StartTm — start time of the prescription-program, in the form "2020-03-14 16:05:01";
    - ActTm — action time of the prescription-program, in the form "2020-03-14 16:05:52".

Author: Roman Savochenko <roman@oscada.org>
Version: 2.2.0
License: GPLv2','Основний, представницький та уніфікований шаблон менеджеру та контролеру рецептів, їх опрацювання та прямого виконання у режимі "команда-макрос". Шаблон формує структуру параметру менеджеру рецептів, який може бути легко підключений до всіх кадрів цієї бібліотеки.

Представницькою структурою менеджеру рецептів є:
- "БД: Ім''я БД з таблицями (dbDB)" — БД, на кшталт "SQLite.vcaBase", де зберігаються або мають зберігатися таблиці рецептів-програм.
- "БД: Таблиця з програмами (dbProgs)" — назва таблиці рецептів-програм, типово "PrescrProgs".
- "Перелік команд (comLs)" — перелік команд доступних до використання.
- "Ім''я обраної програми (prog)" — стан та встановлення назви обраної програми у обробці менеджером.
- "Режим (mode)", "Поточний режим (curMode)" — встановлення та поточний режим менеджеру, виконання обраного рецепту-програми: Завершення(-2); Помилка(-1); Стоп(0); Виконання(1); Пауза(2); Пропустити команду(3).
- "Час запуску (startTm)" — час запуску у секундах.
- "Поточна команда-крок (curCom)" — поточна опрацьовувана команда.
- "Робоча програма (work)" — актуальна та опрацьовувана наразі програма у об''єкті.

Функції:
- Формує "Перелік команд (comLs)", доступних до використання у користувацьких рецептах-програмах.
- Опрацьовує "Ім''я обраної програми (prog)", зміна якої призводить до завантаження її послідовності команд до "Робоча програма (work)" або створення нової програми.
- Виконує програму у "Ім''я обраної програми (prog)" після отримання команди "Виконання(1)" у "Режим (mode)" та відзначає цей процес у: "Поточний режим (curMode)", "Час запуску (startTm)", "Поточна команда-крок (curCom)" та "Робоча програма (work)".
- Генерує повідомлення дій протягом виконання рецепту щодо:
  КАТЕГОРІЯ: визначає ідентифікатор користувацького рецепту-програми ProgNM у форматі "uprg{ProgNM}", де:
    - "uprg*" — типовий шаблон-ознака користувацького рецепту-програми, який може бути безпосередньо використано у фільтрі категорії для визначення у повідомленнях суто користувацьких рецептів-програм;
    - ProgNM — ім''я рецепту-програми.
  ТЕКСТ: опис дії у форматі "{ActDescr} "{ProgNM}" : {StartTm} : {ActTm}", де:
    - ActDescr — опис дії:
      - "Поточний вузол відсутній";
      - "Перерваний користувачем сеанс програми";
      - "Перерваний помилкою сеанс програми";
      - "Вдалий сеанс програми".
    - ProgNM — ім''я рецепту-програми;
    - StartTm — час запуску рецепту-програми, у форматі "2020-03-14 16:05:01";
    - ActTm — час дії рецепту-програми, у форматі "2020-03-14 16:05:52".

Автор: Роман Савоченко <roman@oscada.org>
Версія: 2.2.0
Ліцензія: GPLv2','Основной, представительский и унифицированный шаблон менеджера и контроллера рецептов, их обработки и прямого исполнения в режиме "команда-макрос". Шаблон формирует структуру параметра менеджера рецептов, который может быть легко подключен ко всем кадрам этой библиотеки.

Представительской структурой менеджера рецептов является:
- "БД: Имя БД с таблицами" — БД, вроде "SQLite.vcaBase", где хранятся или должны храниться таблицы рецептов-программ.
- "БД: Таблица с программами" — название таблицы рецептов-программ, типично "PrescrProgs".
- "Список команд (comLs)" — перечень команд доступных для использования.
- "Имя выбранной программы (prog)" — состояние и установка названия выбранной программы в обработке менеджером.
- "Режим (mode)", "Текущий режим (curMode)" — установка и текущий режим менеджера, исполнения выбранного рецепта-программы: Завершение(-2); Ошибка(-1); Стоп(0); Исполнение(1); Пауза(2); Пропустить команду(3);
- "Время запуска (startTm)" — время запуска в секундах.
- "Текущая команда-шаг (curCom)" — текущая обрабатываемая команда.
- "Рабочая программа (work)" — актуальная и обрабатываемая на данный момент программа в объекте.

Функции:
- Формирует "Список команд (comLs)", доступных для использования в пользовательских рецептах-программах.
- Обрабатывает "Имя выбранной программы (prog)", смена которой приводит к загрузке её последовательности команд в Рабочая программа (work)" или созданию новой программы.
- Исполняет программу в "Имя выбранной программы (prog)" после получения команды "Исполнение(1)" в "Режим (mode)" и отмечает этот процесс в: "Текущий режим (curMode)", "Время запуска (startTm)", "Текущая команда-шаг (curCom)" и "Рабочая программа (work)".
- Генерирует сообщения действий в течении исполнения рецепта на предмет:
  КАТЕГОРИЯ: определяет идентификатор пользовательского рецепта-программы ProgNM в формате "uprg{ProgNM}", где:
    - "uprg*" — типовой шаблон-признак пользовательского рецепта-программы, который может быть непосредственно использован в фильтре категории для определения чисто пользовательских рецептов-программ;
    - ProgNM — имя рецепта-программы.
  ТЕКСТ: описание действия в формате "{ActDescr} "{ProgNM}" : {StartTm} : {ActTm}", где:
    - ActDescr — описание действия:
      - "Текущий узел отсутствует";
      - "Прерванный пользователем сеанс программы";
      - "Прерванный ошибкой сеанс программы";
      - "Успешный сеанс программы".
    - ProgNM — имя рецепта-программы;
    - StartTm — время запуска рецепта-программы, в формате "2020-03-14 16:05:01";
    - ActTm — время действия рецепта-программы, в формате "2020-03-14 16:05:52".

Автор: Роман Савоченко <roman@oscada.org>
Версия: 2.2.0
Лицензия: GPLv2',10,0,'JavaLikeCalc.JavaScript
clcCnt++;

if(f_start) {
	dbDB_ = ""; dbO = false;
	work = SYS.XMLNode("prg");
}

//Checking for the commands controller presence
comCntrO = false;
if(comsCntr.length)	comCntrO = (comsCntr=="*") ? this.nodePrev() : SYS.DAQ.nodeAt(comsCntr,".");

t_err = "0";

//Check for the transport change and connect
if(!dbO || dbDB != dbDB_) {
	dbDB_ = dbDB;
	dbO = SYS.BD.nodeAt(dbDB_, ".");
	if(dbO) {
		dbO.SQLReq("CREATE TABLE IF NOT EXISTS PrescrProgs (name TEXT, prgTxt TEXT, PRIMARY KEY (name));");
		if(!comCntrO)
			dbO.SQLReq("CREATE TABLE IF NOT EXISTS PrescrComs (name TEXT, descr TEXT, proc TEXT, arg1 TEXT, arg2 TEXT, arg3 TEXT, arg4 TEXT, arg5 TEXT, PRIMARY KEY (name));");
	}
}
if(!dbO)	t_err = "1:"+tr("Error the DB ''%1''.").replace("%1", dbDB);
else {

//Commands list update
if(f_start || !(clcCnt%(60*f_frq))) {
	if(!comCntrO) {
		rez = dbO.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]];
			if(!comO.nodeAt("start") || !comO.nodeAt("stop") || !comO.nodeAt("run"))	continue;
			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 = dbO.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(iC = 0; iC < cL.length; iC++) {
			var cLi = comCntrO[cL[iC]];
			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(iC = 0; iC < cL.length; iC++)
			comCntrO[cL[iC]].pause.set(mode == 2);
	}
}

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

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

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

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

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

			//Place to program last execution time
			rez = dbO.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);
				dbO.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(iC = 0; iC < cL.length; iC++) {
					var cLi = comCntrO[cL[iC]];
					cLi.run.set(false);
					cLi.start.set(false);
					cLi.abort.set(true);
				}
				SYS.messInfo("uprg"+prog, tr("Terminated program session by the user")+" \""+prog+"\" : "+SYS.strftime(startTm)+" : "+SYS.strftime(SYS.time()));
				curMode = mode = -3;
			}
			//Commands process
			else {
				// Start command
				if(!comEl.attr("tm").length) {
					curComPrm.run.set(false);	//Stop for possible background call
					comEl.setAttr("tm", SYS.time());
					for(i_a = 1; i_a <= 5; i_a++)
						if(!(comA=curComPrm["arg"+i_a]).isEVal())
							comA.set(comEl.attr("arg"+i_a));
					curComPrm.rez.set(0);
					curComPrm.run.set(true);
					if(mode == 2) curComPrm.pause.set(true);
				}
				isRun = true;
			}
		}
		//Update steps status, up to the current comand
		isCurCmd = false;
		for(comElN = work, comPos = 0, comLev = 0, comCur = true; isRun || toPass || toStop; comPos++) {
			if(comPos >= comElN.childSize()) {
				if((comElN=comElN.parent())) {
					comLev--;
					comPos = comElN.attr("seekPos").toInt();
					comCur = comElN.attr("comCur").toInt();
					continue;
				}
				if(actBackGrnd && toNext) toNext = false;
				break;
			}
			comElI = comElN.childGet(comPos);
			isBackgrnd = comElI.attr("backgrnd").toInt();
			if(!isBackgrnd && actBackGrnd)	toNext = false;
			if(isCurCmd) break;
			isCurCmd = comCur && comLev == (curComLev-1) && curCom.parse(comLev,":").toInt() == comPos;
			curComPI = comCntrO["prm_"+comLs[comElI.attr("id")].prmID];
			rez = curComPI.rez.get();
			if(isCurCmd || ((toStop || curComPI.run.get()) && comElI.attr("rez").toInt() != 1 && comElI.attr("rez").toInt() > -10)) {
				if(toStop)	rez = "-12:" + tr("Program terminated");
				else if(toPass) {
					rez = "-10:" + tr("Step missed");
					curComPI.run.set(false);
				}
				else if(rez.toInt() < 0) {
					// Stop all typical and call "error" command
					var cL = comCntrO.nodeList("prm_");
					for(iC = 0; iC < cL.length; iC++) {
						var cLi = comCntrO[cL[iC]];
						cLi.run.set(false);
						cLi.start.set(false);
						cLi.error.set(true);
					}
					SYS.messInfo("uprg"+prog,tr("Terminated program session by the error")+" \""+prog+"\" : "+SYS.strftime(startTm)+" : "+SYS.strftime(SYS.time()));
					curMode = mode = -1;
				}
				if(!actBackGrnd && isBackgrnd && rez.toInt() == 0) actBackGrnd = true;
				if(isCurCmd && (rez.toInt() > 0 || isBackgrnd) && !toStop)	toNext = true;
				comElI.setAttr("rez", rez);
				for(i_a = 1; i_a <= 5; i_a++)
					if(!(comA=curComPI["arg"+i_a]).isEVal())
						comElI.setAttr("arg"+i_a, comA.get());
			}
			if(isCurCmd) {
				if(curComPI.pause.get() == true)	mode = 2;
				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(iC = 0, curComN = ""; iC < (curComLev-1); iC++) curComN += curCom.parse(iC,":");
			curCom = curComN.length ? curComN+":"+curComPos : curComPos;
		}
		
		//End call
		if(curComLev <= 0) {
			// Stop all typical and call "stop" command
			var cL = comCntrO.nodeList("prm_");
			for(iC = 0; iC < cL.length; iC++) {
				var cLi = comCntrO[cL[iC]];
				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 = dbO.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);
				dbO.SQLReq("UPDATE "+dbProgs+" SET prgTxt=''"+comTree.save().replace("''","''''")+"'' WHERE name=''"+prog+"'';");
			}
		}
	}
}
curMode = mode;
//mode = curMode;

}

f_err = t_err;','','',1616351798);
CREATE TABLE IF NOT EXISTS 'tmplib_base' ("ID" TEXT DEFAULT '' ,"NAME" TEXT DEFAULT '' ,"uk#NAME" TEXT DEFAULT '' ,"ru#NAME" TEXT DEFAULT '' ,"DESCR" TEXT DEFAULT '' ,"uk#DESCR" TEXT DEFAULT '' ,"ru#DESCR" TEXT DEFAULT '' ,"MAXCALCTM" INTEGER DEFAULT '10' ,"PR_TR" INTEGER DEFAULT '1' ,"PROGRAM" TEXT DEFAULT '' ,"uk#PROGRAM" TEXT DEFAULT '' ,"ru#PROGRAM" TEXT DEFAULT '' ,"TIMESTAMP" INTEGER DEFAULT '' , PRIMARY KEY ("ID"));
INSERT INTO tmplib_base VALUES('digAlarm','Alarm discrete','Сигналізація дискретна','Сигнализация дискретная','Common, representative and unified template of separate discrete parameter (complex tag) processing with formation of signaling by the defined criterion. The template forms a structure of discrete parameter (complex tag) which can be easily connected to most widgets and cadres of the main elements library of the user interface just pointing the parameter object.

The template provides some fields of the representative structure of the discrete parameters and adds the error field:
- "State "Opened" (st_open)" — the state "Opened".
- "State "Closed" (st_close)" — the state "Closed".
- "Error (err)" — obligatory attribute of any parameter, consist of the combination of the error code and short text description, like to "2:No data or connection with source". The template reserves next error codes:
  - 1 — hardware errors, when the data mostly wrong;
  - 3 — violation of the specified condition.

Functions:
- Direct obtaining, by link, of the initial state and formation of two representative states with the possibility of their inversion stInv.
- Complex preprocessing and complete generation-imitation of the discrete input on the internal programming language of OpenSCADA into the configuration IO inProc, where you can operate next service parameters:
  - in [IN|OUT] — the same raw discrete input IO;
  - levErr, tErr [IN|OUT] — alarm level [-7...0] and text of the error (the attribute "err" format);
  - f_frq [IN] — frequency of calculation, the template IO;
  - this [IN] — reference to the parameter object, what executing the template;
  - ctx [IN] — object of the user context, what saved between the execution cycles.
- Processing of the discrete input in for the purpose of creating a violation and correspondingly setting the attribute err, at the condition "{st}:{lev}:{mess}", that is generation violation of the level lev and the text mess for the input status st (0|1).

Author: Roman Savochenko <roman@oscada.org>
Version: 1.1.0
License: GPLv2','Загальний, представницький шаблон обробки окремого дискретного параметру (складного тегу) із формування сигналізації за визначеним критерієм. Шаблон формує структуру дискретного параметру (складного тегу) який може бути легко підключений до більшості віджетів та кадрів бібліотеки основних елементів інтерфейсу користувача просто вказавши об''єкт параметру.

Шаблон надає окремі поля представницької структури дискретного параметру та додає поле помилки:
- "Стан "Відкрито" (st_open)" — стан "Відкрито".
- "Стан "Закрито" (st_close)" — стан "Відкрито".
- "Помилка (err)" — обов''язковий атрибут будь якого параметру, що містить комбінацію коду помилки та короткого опису, на кшталт "1:Немає даних або підключення до джерела". Шаблон резервує наступні коди помилок:
  - 1 — помилки обладнання, коли дані переважно помилкові;
  - 3 — порушення за визначеної умови.

Функції
- Пряме отримання, за посиланням, первинного стану та формування двох репрезентативних станів з можливістю їх інверсії stInv.
- Складна передобробка та повна генерація-імітація дискретного входу на внутрішній мові програмування OpenSCADA у конфігураційному ВВ inProc, де Ви можете оперувати наступними сервісними параметрами:
  - in [IN|OUT] — той самий сирий дискретний ВВ;
  - levErr, tErr [IN|OUT] — рівень аварії [-7...0] та текст помилки (формат атрибуту "err");
  - f_frq [IN] — частота обчислення, шаблонний ВВ;
  - this [IN] — посилання на об''єкт параметру, що виконує шаблон;
  - ctx [IN] — об''єкт користувацького контексту, що зберігається між циклами виклику.
- Опрацювання дискретного входу in на предмет формування порушення та відповідного встановлення атрибуту err, за умови "{st}:{lev}:{mess}", тобто генерація порушення з рівнем lev та текстом mess за стану входу st (0|1).

Автор: Роман Савоченко <roman@oscada.org>
Версія: 1.1.0
Ліцензія: GPLv2','',10,0,'JavaLikeCalc.JavaScript
if(f_start) {
	f_err = "0";
	alDelay_ = 0;
	//Prepare data for preprocessing
	inPrcId = this.nodePath("_");
	inPrcLng = "JavaLikeCalc.JavaScript";
	inPrcArgs = new Object();
	inPrcArgs.this = this;
	inPrcArgs.ctx = new Object();
	return;
}
if(f_stop) return;

tErr = "0", levErr = 0;

//Call a specific preprocessing procedure
if(inProc.length)	{
	inPrcArgs.f_frq = f_frq;
	inPrcArgs.in = in;
	inPrcArgs.levErr = levErr; inPrcArgs.tErr = tErr;
	SYS.DAQ.funcCall(inPrcLng, inPrcArgs, inProc, inPrcId);
	levErr = inPrcArgs.levErr; tErr = inPrcArgs.tErr;
	in = inPrcArgs.in;
}

//State set
if(in.isEVal()) {
	tErr = "1:"+tr("No data or connection with source");
	levErr = -5;
	st_open = st_close = in;
}
else {
	if(alrm.length && in == alrm.toInt())
		tErr = "3:"+alrm.parse(2,":"), levErr = -alrm.parse(1,":").toInt();
	st_open = stInv ? !in : in;
	st_close = !st_open;
}

//Alarms forming
if(alSup) {
	if(f_err.toInt())	this.alarmSet(DESCR+": "+tr("SUPPRESSED"), 1);
	f_err = "0";
}
else {
	if(tErr.toInt() && tErr.toInt() != f_err.toInt() && this.cntr().status().toInt())	return;
	if(alDelay > 0 && alDelay_ <= 0 && tErr.toInt() != f_err.toInt())	tErr1 = tErr.toInt();
	if(alDelay > 0 && alDelay_ > 0 && tErr.toInt() != tErr1)	{ alDelay_ = 0; tErr1 = tErr.toInt(); }
	if(alDelay > 0 && alDelay_ < alDelay){ alDelay_ += 1/f_frq; return; }
	if(tErr.toInt())	this.alarmSet(DESCR+": "+tErr.parse(1,":"), levErr);
	else	this.alarmSet(DESCR+": "+tr("NORM"), 1);
	f_err = tErr;
	alDelay_ = 0;
}','','',1572284562);
INSERT INTO tmplib_base VALUES('simleBoard','Analog alarm by borders (obsolete)','Сигнал аналоговий за границями (застаріле)','Сигнал аналоговый по границам (устаревшее)','The template of simple parameter included borders and dimension variable.

Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.0','Шаблон простого параметру з перевіркою границь та одиницею виміру.

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

Автор: Роман Савоченко <roman@oscada.org>
Версия: 1.0.0',10,1,'JavaLikeCalc.JavaScript
var=iMult*(in+iAdd);
if(var>max)			f_err="1:Upper work border violation";
else if(var<min)			f_err="2:Bottom work border violation";
else if(aMax>aMin && var>aMax)		f_err="3:Upper alarm border violation";
else if(aMax>aMin && var<aMin)		f_err="4:Bottom alarm border violation";
else if(wMax>wMin && var>wMax)	f_err="5:Upper warning border violation";
else if(wMax>wMin && var<wMin)	f_err="6:Bottom warning border violation";
else f_err="0";','JavaLikeCalc.JavaScript
var=iMult*(in+iAdd);
if(var>max)			f_err="1:Порушення верхньої робочої границі";
else if(var<min)			f_err="2:Порушення нижньої робочої границі";
else if(aMax>aMin && var>aMax)		f_err="3:Порушення верхньої аварійної границі";
else if(aMax>aMin && var<aMin)		f_err="4:Порушення нижньої аварійної границі";
else if(wMax>wMin && var>wMax)	f_err="5:Порушення верхньої попереджувальної границі";
else if(wMax>wMin && var<wMin)	f_err="6:Порушення нижньої попереджувальної границі";
else f_err="0";','JavaLikeCalc.JavaScript
var=iMult*(in+iAdd);
if(var>max)		f_err="1:Нарушение верхней рабочей границы";
else if(var<min)	f_err="2:Нарушение нижней рабочей границы";
else if(aMax>aMin && var>aMax)	f_err="3:Нарушение верхней аварийной границы";
else if(aMax>aMin && var<aMin)	f_err="4:Нарушение нижней аварийной границы";
else if(wMax>wMin && var>wMax)	f_err="5:Нарушение верхней предупредительной границы";
else if(wMax>wMin && var<wMin)	f_err="6:Нарушение нижней предупредительной границы";
else f_err="0";','');
INSERT INTO tmplib_base VALUES('digitBlock','Discrete parameters block (obsolete)','Блок дискр. параметрів (застаріле)','Блок дискр. параметров (устарелое)','The block for union of Discrete parameters for one device control.

Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.0','Блок для збору дискретних параметрів, керуючих одним апаратом.

Автор: Роман Савоченко <roman@oscada.org>
Версія: 1.0.0','Блок для сборки дискретных параметров управляющих одним аппаратом.

Автор: Роман Савоченко <roman@oscada.org>
Версия: 1.0.0',10,0,'JavaLikeCalc.JavaScript
set = false;
if(cmdOpen && !(last_cmd&0x1))	{ last_cmd = last_cmd|0x1; set = true; }
if(cmdClose && !(last_cmd&0x2))	{ last_cmd = last_cmd|0x2; set = true; }
if(cmdStop && !(last_cmd&0x4))	{ last_cmd = last_cmd|0x4; set = true; }
if(set && tCmd > 0) w_tm = tCmd;
if(w_tm > 0) w_tm -= 1/f_frq;
else {
	w_tm = 0;
	if(tCmd > 0) {
		if(last_cmd&0x1) { cmdOpen = false; last_cmd = last_cmd&(~0x1); }
		if(last_cmd&0x2) { cmdClose = false; last_cmd = last_cmd&(~0x2); }
		if(last_cmd&0x4) { cmdStop = false; last_cmd = last_cmd&(~0x4); }
	}
}','','',1416656400);
INSERT INTO tmplib_base VALUES('manInUnif','Manual input, unified','Ручний ввід, уніфікований','Ручной ввод, унифицированный','Common, representative and unified template of a manual input and an analog output signal. The template forms a structure of parameter (complex tag) of the manual input what can be easily connected to most widgets and cadres of the main elements library of the user interface just pointing the parameter object, the control mostly can be performed through the Element cadre control panel.

The representative structure of the parameter (complex tag) of the manual input is:
- "Variable (var)" — end variable of the parameter — engineeric real value, allowed and suitable for archiving.
- "Variable input (varIn)" — attribute for entering a new engineering value, which in the next falls into the variable.
- "Error (err)" — obligatory attribute of any parameter, consist of the combination of the error code and short text description, like to "2:No data or connection with source". The template reserves next error codes:
  - 1, 2 — hardware errors, when the data mostly wrong;
  - 3, 4 — alarm border errors;
  - 5, 6 — warning border errors;
  - 7 — the speed error of the variable changing.
- "Violations suppress (alSup) and delay (alDelay)" — violations generic control for suppressing and delaying.
- "Scale: minimum (min)" and "Scale: maximum (max)" — main scale of the end variable, the output of which causes a hardware border error. The scale is also used as a scale of the variable trend display on the graphs.
- "Logarithmic scale (log)" — the scale sign of the variable trend display on the graphs in the logarithmic scale.
- "Dimension (ed)" — dimension (unit of measurement) of the variable.
- "Precision (prec)" — number signs after the dot, for positive values, and precision (signs in whole), for negative ones, to display the real end variable on the screen.

Functions:
- Generic processing of the raw analog input for:
  - simple correction-calibration at the formula "in = iMult*(in+iAdd)";
  - scaling from the hardware border [plcMin...plcMax] to the main border [min...max], applying the square scale scSqr;
￼ !!!! The hardware borders can be inverted ones and disabled in whole for equal values of the maximum and minimum, when the main border only used.
  - permission to exceed the border for the input value on the value of plcExcess percent;
  - filtering the input value changing in the time Tf.
- Processing the result variable var for violation some borders and criteria, generating the alarms and proper setting the attribute err. The alarm borders can have some hysteresis HystBnd, can be delayed on the time alDelay and can be suppressed setting alSup. Next borders and criteria are provided:
  - missing for data or connection with source at the EVAL input value — error code 1 and alarm level 5;
  - exceeding the input value of the hardware border — error code 1,2 and alarm level 5;
  - exceeding the end variable of the alarm border [aMin...aMax] — error code 3,4 and alarm level 4;
￼ !!!! Disabled at setting for equal or inverted values of the border
  - exceeding the end variable of the warning border [wMin...wMax] — error code 5,6 and alarm level 2;
￼ !!!! Disabled at setting for equal or inverted values of the border
  - big motion speed of the end variable — error code 7 and alarm level 2.
- Reverse conversion of the engineered variable value and writing to the output.
￼ !!!! In the absence of the "Input/Output" connection, the writing is not performed and there is the stand-alone operation.

Author: Roman Savochenko <roman@oscada.org>
Version: 1.2.0
License: GPLv2','Загальний, представницький та уніфікований шаблон ручного вводу та сигналу аналогового виходу. Шаблон формує структуру параметру (складного тегу) ручного вводу, який може бути легко підключений до більшості віджетів та кадрів бібліотеки основних елементів інтерфейсу користувача просто вказавши об''єкт параметру, контроль переважно можна здійснювати через панель контролю Елемент кадр.

Представницькою структурою параметру (складного тегу) ручного вводу є:
- "Змінна (var)" — кінцева змінна параметру — інженерне реальне значення, доступне та придатне до архівації.
- "Введення змінної (varIn)" — атрибут введення нового інженерного значення, який надалі потрапляє до змінної.
- "Помилка (err)" — обов''язковий атрибут будь якого параметру, що містить комбінацію коду помилки та короткого опису, на кшталт "2:Немає даних або підключення до джерела". Шаблон резервує наступні коди помилок:
  - 1, 2 — помилки обладнання, коли дані переважно помилкові;
  - 3, 4 — помилки аварійної границі;
  - 5, 6 — помилки попереджувальної границі;
  - 7 — помилка швидкості зміни змінної.
- "Придушення (alSup) та затримка (alDelay) порушень" — загальний контроль порушень щодо придушення та затримки.
- "Шкала: мінімум (min)" та "Шкала: максимум (max)" — головна шкала кінцевої змінної, вихід за яку призводить до помилки апаратної границі. Шкала також використовується як шкала відображення тренду змінної на графіках.
- "Логарифмічна шкала (log)" — ознака відображення тренду змінної на графіках у логарифмічній шкалі.
- "Одиниця виміру (ed)" — одиниця виміру змінної.
- "Точність (prec)" — кількість знаків після крапки, для позитивних значень, та точність (знаків загалом), для негативних, для відображення реальної кінцевої змінної на екрані.

Функції:
- Загальне опрацювання аналогового входу щодо:
  - проста корекція-калібрування за формулою "in = iMult*(in+iAdd)";
  - масштабування від апаратної границі [plcMin...plcMax] у головну границю [min...max], враховуючи квадратичну шкалу scSqr;
￼ !!!! Апаратні границі може бути інвертовано та взагалі вимкнено однаковими значеннями максимуму та мінімуму, коли використовується тільки головна шкала.
  - дозвіл на перевищення границі вхідним значенням на величину plcExcess відсотків;
  - фільтрування зміни вхідного значення на час Tf.
- Опрацювання кінцевої змінної var на предмет порушення деяких границь та критеріїв, генеруючи аварії та відповідно встановлюючи атрибут err. Границі аварії можуть мати деякий гістерезис HystBnd, можуть бути затримані на час alDelay та можуть бути придушені встановленням alSup. Надаються наступні границі та критерії:
  - відсутність даних або підключення до джерела за вхідним значенням EVAL — код помилки 1 та рівень аварії 5;
  - перевищення апаратної границі вхідним значенням — код помилки 1,2 та рівень аварії 5;
  - перевищення аварійної границі кінцевою змінною [aMin...aMax] — код помилки 3,4 та рівень аварії 4;
￼ !!!! Вимикається встановленням однакових або інверсних значень границі
  - перевищення попереджувальної границі кінцевою змінною [wMin...wMax] — код помилки 5,6 та рівень аварії 2;
￼ !!!! Вимикається встановленням однакових або інверсних значень границі
  - велика швидкість зміни speed кінцевої змінної — код помилки 7 та рівень аварії 2.
- Зворотне перетворення введеного інженерного значення змінної та запис у вихід.
￼ !!!! За відсутності підключення "Входу/Виходу" запису не здійснюється та відбувається автономна робота.

Автор: Роман Савоченко <roman@oscada.org>
Версія: 1.2.0
Ліцензія: GPLv2','',10,0,'JavaLikeCalc.JavaScript
if(f_start)	{ f_err = "0"; alDelay_ = 0; }

pMax = plcMax; pMin = plcMin;	//Copy for local modifies using
if(passIn=(pMax==pMin)) { pMax = max/iMult - iAdd; pMin = min/iMult - iAdd; }

//Manual input set process
if(!varIn.isEVal() && (varIn != var || inout.isEVal())) {
	if(passIn) { pMin = iMult*(pMin+iAdd); pMax = iMult*(pMax+iAdd); }
	vCalibr = (varIn-min)/(max-min);
	if(scSqr)	vCalibr = pow(vCalibr,2);
	if(pMax < pMin) vCalibr = 1-vCalibr;
	vCalibr = min(pMax,pMin)+vCalibr*abs(pMax-pMin);
	inout = vCalibr/iMult - iAdd;
}

levErr = 0;
tErr = "0";
//Input data check and postprocess
if(inout > (max(pMax,pMin)+plcExcess*abs(pMax-pMin)/100)) {
	tErr = "1:"+tr("The signal exceed to upper hardware border"); levErr = -5;
	var = max + plcExcess*(max-min)/100;
}
else if(inout < (min(pMax,pMin)-plcExcess*abs(pMax-pMin)/100)) {
	tErr = "2:"+tr("The signal exceed to bottom hardware border"); levErr = -5;
	var = min - plcExcess*(max-min)/100;
}
if(!tErr) {
	vCalibr = iMult*(inout+iAdd);
	if(passIn) { pMin = iMult*(pMin+iAdd); pMax = iMult*(pMax+iAdd); }
	if(!passIn || scSqr) {
		vCalibr = (vCalibr-min(pMax,pMin))/abs(pMax-pMin);
		if(pMax < pMin) vCalibr = 1-vCalibr;
		vCalibr = min + (max-min)*(scSqr?pow(vCalibr,0.5):vCalibr);
	}
	if(var.isEVal())	var = vCalibr;
	varDt = vCalibr - var;
	var += varDt/max(1,Tf*f_frq);
	varIn = var;

	bndVarHyst = (max-min)*HystBnd/100;
	if(aMax < max && aMax > aMin && (var >= aMax || (f_err.toInt() == 3 && var >= (aMax-bndVarHyst))))
	{ tErr="3:"+tr("Upper alarm border error"); levErr = -4; }
	else if(aMin > min && aMax > aMin && (var <= aMin || (f_err.toInt() == 4 && var <= (aMin+bndVarHyst))))
	{ tErr="4:"+tr("Lower alarm border error"); levErr = -4; }
	else if(wMax < max && wMax > wMin && (var >= wMax || (f_err.toInt() == 5 && var >= (wMax-bndVarHyst))))
	{ tErr="5:"+tr("Upper warning border error"); levErr = -2; }
	else if(wMin > min && wMax > wMin && (var <= wMin || (f_err.toInt() == 6 && var <= (wMin+bndVarHyst))))
	{ tErr="6:"+tr("Lower warning border error"); levErr = -2; }
	else if(speed && varDt > speed)	{ tErr="7:"+tr("Too big parameter''s motion speed"); levErr = -2; }
}

//Alarms forming
if(alSup) {
	if(f_err.toInt())	this.alarmSet(DESCR+": "+tr("SUPPRESSED"), 1);
	f_err = "0";
}
else {
	if(tErr.toInt() && tErr.toInt() != f_err.toInt() && this.cntr().status().toInt())	return;
	if(alDelay > 0 && alDelay_ <= 0 && tErr.toInt() != f_err.toInt())	tErr1 = tErr.toInt();
	if(alDelay > 0 && alDelay_ > 0 && tErr.toInt() != tErr1)	{ alDelay_ = 0; tErr1 = tErr.toInt(); }
	if(alDelay > 0 && alDelay_ < alDelay){ alDelay_ += 1/f_frq; return; }
	if(tErr.toInt())	this.alarmSet(DESCR+": "+tErr.parse(1,":"), levErr);
	else	this.alarmSet(DESCR+": "+tr("NORM"), 1);
	f_err = tErr;
	alDelay_ = 0;
}','','',1572284562);
INSERT INTO tmplib_base VALUES('anUnif','Analog signal, unified','Аналоговий сигнал, уніфікований','Аналоговый сигнал, унифицированный','Common, representative and unified template of analog input signals processing. The template forms a structure of complex analog parameter (tag) which can be easily connected to most widgets and cadres of the main elements library of the user interface just pointing the parameter object.

Functions:
- Generic processing of the raw analog input for:
  - simple correction-calibration at the formula "in = iMult*(in+iAdd)";
  - scaling from the hardware border [plcMin...plcMax] to the main border [min...max], applying the square scale scSqr;
     !!! The hardware borders can be inverted ones and disabled in whole for equal values of the maximum and minimum, when the main border only used.
  - permission to exceed the border for the input value on the value of plcExcess percent;
  - filtering the input value changing in the time Tf.
- Simple imitation of the analog input, setting plcImit and linking the imitation-simulation source in plcImitIn.
     !!! Missing the link plcImitIn (EVAL) causes to generating random analog input
- Complex preprocessing and complete generation-imitation of the analog input on the internal programming language of OpenSCADA into the configuration IO inProc, where you can operate next service parameters:
  - f_frq [IN] — frequency of calculation, the template IO;
  - in [IN|OUT] — the same raw analog input IO;
  - var [IN] — the end variable IO;
  - min, max [IN] — the main border IOs;
  - plcMin, plcMax [IN] — the hardware border IOs;
  - plcImit, plcImitIn [IN] — IOs of the simple imitation;
  - levErr, tErr [IN|OUT] — alarm level [-7...0] and text of the error (the attribute "err" format).
- Simple substitution for the variable subVar at the error (not valid) input variable (EVAL) in the modes: "no", "last" and "substitute".
- Processing the result variable var for violation some borders and criteria, generating the alarms and proper setting the attribute err. The alarm borders can have some hysteresis HystBnd, can be delayed for the time alDelay and can be worked in the modes alSup: "init NORM", "suppress", "save on change". Next borders and criteria are provided:
  - missing for data or connection with source at the EVAL input value — error code 1 and alarm level 5;
  - exceeding the input value of the hardware border — error code 1,2 and alarm level 5;
  - exceeding the end variable of the alarm border [aMin...aMax] — error code 3,4 and alarm level 4;
     !!! Disabled at setting for equal or inverted values of the border
  - exceeding the end variable of the warning border [wMin...wMax] — error code 5,6 and alarm level 2;
     !!! Disabled at setting for equal or inverted values of the border
  - big motion speed of the end variable — error code 7 and alarm level 2.

Author: Roman Savochenko <roman@oscada.org>
Version: 2.1.2
License: GPLv2','Загальний, представницький та уніфікований шаблон обробки аналогових вхідних сигналів. Шаблон формує структуру складного аналогового параметру (тегу) який може бути легко підключений до більшості віджетів та кадрів бібліотеки основних елементів інтерфейсу користувача просто вказавши об''єкт параметру.

Функції:
- Загальне опрацювання аналогового входу щодо:
  - проста корекція-калібрування за формулою "in = iMult*(in+iAdd)";
  - масштабування від апаратної границі [plcMin...plcMax] у головну границю [min...max], враховуючи квадратичну шкалу scSqr;
     !!! Апаратні границі може бути інвертовано та взагалі вимкнено однаковими значеннями максимуму та мінімуму, коли використовується тільки головна шкала.
  - дозвіл на перевищення границі вхідним значенням на величину plcExcess відсотків;
  - фільтрування зміни вхідного значення на час Tf.
- Проста імітація аналогового входу, встановленням plcImit та підключенням джерела імітації-симуляції до plcImitIn.
     !!! Відсутність зв''язка plcImitIn (EVAL) призводить до генерації випадкового аналогового входу
- Складна передобробка та повна генерація-імітація аналогового входу на внутрішній мові програмування OpenSCADA у конфігураційному ВВ inProc, де Ви можете оперувати наступними сервісними параметрами:
  - f_frq [IN] — частота обчислення, шаблонний ВВ;
  - in [IN|OUT] — той самий сирий аналоговий ВВ;
  - var [IN] — ВВ кінцевої змінної;
  - min, max [IN] — ВВ головної шкали;
  - plcMin, plcMax [IN] — ВВ апаратної шкали;
  - plcImit, plcImitIn [IN] — ВВ простої імітації;
  - levErr, tErr [IN|OUT] — рівень аварії [-7...0] та текст помилки (формат атрибуту "err").
- Проста підстановка значення subVar за помилкою (недостовірністю) вхідної змінної (EVAL) у режимах: "немає", "останнє" та "підстановка".
- Опрацювання кінцевої змінної var на предмет порушення деяких границь та критеріїв, генеруючи аварії та відповідно встановлюючи атрибут err. Границі аварії можуть мати деякий гістерезис HystBnd, можуть бути затримані на час alDelay та можуть працювати у режимах alSup: "ініціювати НОРМА", "придушувати", "зберігати при зміні". Надаються наступні границі та критерії:
  - відсутність даних або підключення до джерела за вхідним значенням EVAL — код помилки 1 та рівень аварії 5;
  - перевищення апаратної границі вхідним значенням — код помилки 1,2 та рівень аварії 5;
  - перевищення аварійної границі кінцевою змінною [aMin...aMax] — код помилки 3,4 та рівень аварії 4;
     !!! Вимикається встановленням однакових або інверсних значень границі
  - перевищення попереджувальної границі кінцевою змінною [wMin...wMax] — код помилки 5,6 та рівень аварії 2;
     !!! Вимикається встановленням однакових або інверсних значень границі
  - велика швидкість зміни speed кінцевої змінної — код помилки 7 та рівень аварії 2.

Автор: Роман Савоченко <roman@oscada.org>
Версія: 2.1.2
Ліцензія: GPLv2','',10,0,'JavaLikeCalc.JavaScript
if(f_start) {
	prevVar = EVAL_REAL;
	alDelay_ = 0; firstNorm = alNormForceStart;
	//Prepare data for preprocessing
	inPrcId = this.nodePath("_");
	inPrcLng = "JavaLikeCalc.JavaScript";
	inPrcArgs = new Object();
	inPrcArgs.this = this;
	inPrcArgs.ctx = new Object();
	return;
}
if(f_stop) return;

pMax = plcMax; pMin = plcMin;	//Copy for local modifies using
if(passIn=(pMax==pMin)) { pMax = max/iMult - iAdd; pMin = min/iMult - iAdd; }

if(plcImit) {	//Data imitation
	if(!plcImitIn.isEVal()) in = plcImitIn;
	else {
		var plcOff;
		plcDif = abs(pMax-pMin);
		if(plcOff.isEVal())		plcOff = rand(plcDif/2);
		in = pMin + plcDif/4 + plcOff + rand(plcDif/10)-plcDif/20;
  }
}

levErr = 0;
tErr = "0";

//Call specific preprocessing procedure
if(inProc.length)	{
	inPrcArgs.f_frq = f_frq;
	inPrcArgs.in = in; inPrcArgs.var = var; inPrcArgs.min = min; inPrcArgs.max = max;
	inPrcArgs.plcMin = pMin; inPrcArgs.plcMax = pMax;
	inPrcArgs.plcImit = plcImit; inPrcArgs.plcImitIn = plcImitIn;
	inPrcArgs.levErr = levErr; inPrcArgs.tErr = tErr;
	SYS.DAQ.funcCall(inPrcLng, inPrcArgs, inProc, inPrcId);
	in = inPrcArgs.in;
	levErr = inPrcArgs.levErr; tErr = inPrcArgs.tErr;
}

//Input data check and postprocess
if(in.isEVal()) {
	tErr = "1:"+tr("No data or connection with source"); levErr = -5;
	var = EVAL_REAL;
	if(subMode == 1) var = prevVar;
	else if(subMode == 2) var = subVar;
}
else if(in > (max(pMax,pMin)+plcExcess*abs(pMax-pMin)/100)) {
	tErr = "1:"+tr("The signal exceed to upper hardware border"); levErr = -5;
	var = EVAL_REAL;
	if(subMode == 1) var = prevVar.isEVal() ? max+plcExcess*(max-min)/100 : prevVar;
	else if(subMode == 2) var = subVar;
}
else if(in < (min(pMax,pMin)-plcExcess*abs(pMax-pMin)/100)) {
	tErr = "2:"+tr("The signal exceed to bottom hardware border"); levErr = -5;
	var = EVAL_REAL;
	if(subMode == 1) var = prevVar.isEVal() ? min-plcExcess*(max-min)/100 : prevVar;
	else if(subMode == 2) var = subVar;
}
else {
	vCalibr = iMult*(in+iAdd);
	if(passIn) { pMin = iMult*(pMin+iAdd); pMax = iMult*(pMax+iAdd); }
	if(!passIn || scSqr) {
		vCalibr = (vCalibr-min(pMax,pMin))/abs(pMax-pMin);
		if(pMax < pMin) vCalibr = 1-vCalibr;
		vCalibr = min + (max-min)*(scSqr?pow(vCalibr,0.5):vCalibr);
	}
	if(var.isEVal())	var = vCalibr;
	varDt = vCalibr - var;
	var += varDt/max(1,Tf*f_frq);
	prevVar = var;

	if(!tErr) {
		bndVarHyst = (max-min)*HystBnd/100;
		if(aMax < max && aMax > aMin && (var >= aMax || (f_err.toInt() == 3 && var >= (aMax-bndVarHyst))))
		{ tErr = "3:"+tr("Upper alarm border error"); levErr = -4; }
		else if(aMin > min && aMax > aMin && (var <= aMin || (f_err.toInt() == 4 && var <= (aMin+bndVarHyst))))
		{ tErr = "4:"+tr("Lower alarm border error"); levErr = -4; }
		else if(wMax < max && wMax > wMin && (var >= wMax || (f_err.toInt() == 5 && var >= (wMax-bndVarHyst))))
		{ tErr = "5:"+tr("Upper warning border error"); levErr = -2; }
		else if(wMin > min && wMax > wMin && (var <= wMin || (f_err.toInt() == 6 && var <= (wMin+bndVarHyst))))
		{ tErr = "6:"+tr("Lower warning border error"); levErr = -2; }
		else if(speed && varDt > speed)	{ tErr = "7:"+tr("Too big parameter''s motion speed"); levErr = -2; }
	}
}

//Alarms forming
if(alSup) {
	if(f_err.toInt())	this.alarmSet(DESCR+": "+tr("SUPPRESSED"), 1);
	f_err = "0";
}
else {
	if(!firstNorm && tErr.toInt() && tErr.toInt() != f_err.toInt() && this.cntr().status().toInt())	return;
	if(alDelay > 0 && alDelay_ <= 0 && tErr.toInt() != f_err.toInt())	tErr1 = tErr.toInt();
	if(alDelay > 0 && alDelay_ > 0 && tErr.toInt() != tErr1)	{ alDelay_ = 0; tErr1 = tErr.toInt(); }
	if(alDelay > 0 && alDelay_ < alDelay){ alDelay_ += 1/f_frq; return; }
	if(tErr.toInt())	this.alarmSet(DESCR+": "+tErr.parse(1,":"), levErr);
	else	this.alarmSet(DESCR+": "+tr("NORM"), 1, firstNorm);
	f_err = tErr;
	alDelay_ = 0; firstNorm = false;
}','','',1615650088);
INSERT INTO tmplib_base VALUES('digitBlockUnif','Discrete block, unified','Блок дискретних, уніфікований','Блок дискретных, унифицированный','Common, representative and unified template of the block for union of Discrete parameters for the common control device. The template forms a structure of discrete parameter-block (complex tag) which can be easily connected to most widgets and cadres of the main elements library of the user interface just pointing the parameter object.

The representative structure of discrete parameters (complex tags) is a latch object with two characteristic states and three commands, which in the final representation may have a different meaning and name:
- "State "Opened" (st_open)" — the state "Opened" of the latch. Is the primary status signal that can be used without the next one, when 1 (true) is "Opened", and 0 (false) is ''Closed''.
- "State "Closed" (st_close)" — the state "Closed".
- "Command "Open" (com)" — the command "Open" of the latch. Is the primary command signal that can be used without the next one, when 1 (true) is "Open", and 0 (false) is ''Close''.
- "Command "Close" (close)" — the command "Close" of the latch.
- "Command "Stop" (stop)" — the command "Stop" of the latch.

Functions:
- Direct obtaining, by link, of the two states (read only) and three commands (reading and writing).
- Possibility of holding the commands for a certain time and removing them.
- Ability to redefine the states and commands by the appropriate settings of the interface elements, such as digStts, for statuses, and digComs, for commands.

Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.0
License: GPLv2','Загальний, представницький та уніфікований шаблон блоку поєднання дискретних параметрів у пристрій загального контролю. Шаблон формує структуру дискретного параметру-блоку (складного тегу) який може бути легко підключений до більшості віджетів та кадрів бібліотеки основних елементів інтерфейсу користувача просто вказавши об''єкт параметру.

Представницькою структурою дискретного параметру (складного тегу) є об''єкт засувки із характерними двома станами та трьома командами, які у кінцевому представленні можуть мати інше значення та назву:
- "Стан "Відкрито" (st_open)" — стан засувки "Відкрито". Є первинним сигналом стану, який може використовуватися без наступного, коли 1 (true) — "Відкрито", а 0 (false) — "Закрито".
- "Стан "Закрито" (st_close)" — стан засувки "Відкрито".
- "Команда "Відкрити" (com)" — команда засувки "Відкрити". Є первинним сигналом команди, який може використовуватися без наступного, коли 1 (true) — "Відкрити", а 0 (false) — "Закрити".
- "Команда "Закрити" (close)" — команда засувки "Закрити".
- "Команда "Зупинити" (stop)" — команда засувки "Зупинити".

Функції:
- Пряме отримання, за посиланням, двох станів (тільки читання) та трьох команд (читання та запис).
- Можливість утримання команди протягом визначеного часу та зняття їх.
- Можливість перевизначення станів та команд відповідним налаштуванням елементів інтерфейсу, як то digStts, для статусів, та digComs, для команд.

Автор: Роман Савоченко <roman@oscada.org>
Версія: 1.0.0
Ліцензія: GPLv2','',10,0,'JavaLikeCalc.JavaScript
set = false;
if(!com.isEVal() && com && last_cmd != 1)		last_cmd = 1, set = true;
if(!close.isEVal() && close && last_cmd != 2)last_cmd = 2, set = true;
if(!stop.isEVal() && stop && last_cmd != 3)	last_cmd = 3, set = true;
if(set && tCmd > 0) w_tm = tCmd;
if(w_tm > 0) w_tm -= 1/f_frq;
else {
	w_tm = 0;
	if(tCmd > 0) {
		if(last_cmd == 1)	com = false;
		if(last_cmd == 2)	close = false;
		if(last_cmd == 3)	stop = false;
		last_cmd = 0;
	}
}','','',1441903511);
INSERT INTO tmplib_base VALUES('pidUnifImp','Impulse PID signal, unified, violation statuses','Імпульсний ПІД сигнал, уніфікований, стани порушень','ПИД импульсный сигнал, унифицированный, состояния нарушений','Common, representative and unified template of an interface to the Proportional Integral Differential (PID) regulator based on the common analog input signal but with the PWM-output, extended by binary states of indication the violations and mostly like to the template "PID signal, unified, violation statuses". The template forms a structure of the impulse PID-regulator which can be easily connected to some cadres of the main elements library of the user interface just pointing the parameter object.

The representative structure of the impulse PID-regulators is mostly equal to the template "PID signal, unified, violation statuses" and contains a number of attributes specific to the pulse output and the attribute of determining the operation of this regulator in the analog mode, that is, this template is even more generalizable:
- "Impulse output up (impQup)" and "Impulse output down (impQdwn)" — signals of the PWM-output.
- "Mode: analog (analog)" — a sign of working the regulator in the analog output mode.

Functions:
Mostly the same as the basic template "PID signal, unified, violation statuses" and the impulse-specific attributes are additionally provided: "Impulse output up", "Impulse output down", "Mode: analog", "T impulses period", "T minimal impulse", "Impulse rate factor".

Author: Roman Savochenko <roman@oscada.org>
Sponsored by: DIYA LTD
Version: 1.1.0
License: GPLv2','Загальний, представницький та уніфікований шаблон інтерфейсу до Пропорційного Інтегрального Диференційного (ПІД) регулятору заснованому на аналоговому вхідному сигналі але із ШІМ-виходом, розширеному бінарними станами індикації порушень та переважно схоже на шаблон "ПІД сигнал, уніфікований, стани порушень". Шаблон формує структуру імпульсного ПІД-регулятору, який може бути легко підключений до деяких кадрів та віджетів бібліотеки основних елементів інтерфейсу користувача, просто вказавши об''єкт параметру.

Представницька структура імпульсних ПІД-регуляторів переважно еквівалентна шаблону "ПІД сигнал, уніфікований, стани порушень" та містить низку атрибутів, специфічних до імпульсного виходу та атрибут визначення роботи цього регулятору у аналоговому режимі, тобто цей шаблон є навіть більш узагальнювальним:
- "Імпульсний вихід у гору (impQup)" та "Імпульсний вихід до низу (impQdwn)" — сигнали ШІМ-виходу.
- "Режим: аналоговий (analog)" — ознака роботи регулятору у режимі аналогового виходу.

Функції:
Переважно те саме що у базового шаблону "ПІД сигнал, уніфікований, стани порушень" та додатково надаються імпульсно-специфічні атрибути: "Імпульсний вихід у гору", "Імпульсний вихід до низу", "Режим: аналоговий", "T період імпульсів", "Мінімальна довжина імпульсу", "Рейтинг фактор імпульсів".

Автор: Роман Савоченко <roman@oscada.org>
Спонсоровано: ТОВ "ДІЯ"
Версія: 1.1.0
Ліцензія: GPLv2','',10,0,'JavaLikeCalc.JavaScript
if(f_start) {
	f_err = "0";
	alDelay_ = 0;
}

if(plcImit) {
	if(!plcImitIn.isEVal()) in = plcImitIn;
	else {
		plcDif = plcMax-plcMin;
		in = plcMin + plcDif/2 + rand(plcDif/10)-plcDif/20;
	}
}

tErr = "0";
if(in > (plcMax+plcExcess*(plcMax-plcMin)/100))
{ tErr = "1:"+tr("Signal exceed to upper hardware border"); var = max+plcExcess*(max-min)/100; }
else if(in < (plcMin-plcExcess*(plcMax-plcMin)/100))
{ tErr = "2:"+tr("Signal exceed to bottom hardware border"); var = min-plcExcess*(max-min)/100; }
if(tErr)	EVAL = true;
else {
	vCalibr = iMult*(in+iAdd);

	vCalibr = (vCalibr-plcMin)/(plcMax-plcMin);
	varDt = min + (max-min)*(scSqr?pow(vCalibr,0.5):vCalibr) - var;
	var += varDt/max(1,Tf/1000*f_frq);

	bndVarHyst = (aMax-aMin)*HystBnd/100;
	if(aMax > aMin && (var >= aMax || (HH && var >= (aMax-bndVarHyst))))
	{ tErr = "3:"+tr("Upper alarm border error"); HH = true; EVAL = H = LL = L = false; }
	else if(aMax > aMin && (var <= aMin || (LL && var <= (aMin+bndVarHyst))))
	{ tErr="4:"+tr("Lower alarm border error"); LL = true; EVAL = HH = H = L = false; }
	else if(wMax > wMin && (var >= wMax || (H && var >= (wMax-bndVarHyst))))
	{ tErr="5:"+tr("Upper warning border error"); H = true; EVAL = HH = LL = L = false; }
	else if(wMax > wMin && (var <= wMin || (L && var <= (wMin+bndVarHyst))))
	{ tErr="6:"+tr("Lower warning border error"); L = true; EVAL = HH = H = LL = false; }
	else if(speed && varDt > speed)
	{ tErr = "7:"+tr("Too big parameter''s motion speed"); EVAL = true; HH = H = LL = L = false; }
	else EVAL = HH = H = LL = L = false;
}

//Alarms forming
if(alSup) {
	if(f_err.toInt())	this.alarmSet(DESCR+": "+tr("SUPPRESSED"), 1);
	f_err = "0";
}
else {
	if(tErr.toInt() && tErr.toInt() != f_err.toInt() && this.cntr().status().toInt())	return;
	if(alDelay > 0 && alDelay_ <= 0 && tErr.toInt() != f_err.toInt())	tErr1 = tErr.toInt();
	if(alDelay > 0 && alDelay_ > 0 && tErr.toInt() != tErr1)	{ alDelay_ = 0; tErr1 = tErr.toInt(); }
	if(alDelay > 0 && alDelay_ < alDelay){ alDelay_ += 1/f_frq; return; }
	if(tErr.toInt())	this.alarmSet(DESCR+": "+tErr.parse(1,":"), -4);
	else	this.alarmSet(DESCR+": "+tr("NORM"), 1);
	f_err = tErr;
	alDelay_ = 0;
}','','',1572862020);
INSERT INTO tmplib_base VALUES('anUnifSt','Analog signal, unified, violation statuses','Аналоговий сигнал, уніфікований, стани порушень','Аналоговый сигнал, унифицированный, состояния нарушений','Common, representative and unified template of analog input signals processing, extended by binary states of indication the violations. The template grounded on the base template "Analog signal, unified" with its representative structure and some differences characteristic to independent development and expansion.

Binary states were provided for the convenient use of algorithms of operation at the borders of violations of the parameter object, preferably in the block diagrams of general control algorithms.

Functions:
Mostly the same as the basic "Analog signal, unified" and the borderline signs are additionally provided: "H", "L", "HH", "LL", "EVAL".

Author: Roman Savochenko <roman@oscada.org>
Version: 1.2.0
License: GPLv2','Загальний, представницький та уніфікований шаблон обробки аналогових вхідних сигналів, розширений бінарними станами індикації порушень. Шаблон засновано на базовому шаблоні "Аналоговий сигнал, уніфікований" з відповідною до нього представницькою структурою та деякими відмінностями, характерними до незалежного розвитку та розширення.

Бінарні стани передбачалося для зручного використання у алгоритмах роботи за границями порушення об''єкту параметру, переважно у блокових схемах загальних алгоритмів керування.

Функції:
Переважно відповідають базовому шаблону "Аналоговий сигнал, уніфікований" та додатково надаються ознаки порушень границь: "H", "L", "HH", "LL", "EVAL".

Автор: Роман Савоченко <roman@oscada.org>
Версія: 1.2.0
Ліцензія: GPLv2','',10,0,'JavaLikeCalc.JavaScript
if(f_start) {
	prevVar = EVAL_REAL;
	alDelay_ = 0;
	//Prepare data for preprocessing
	inPrcId = this.nodePath("_");
	inPrcLng = "JavaLikeCalc.JavaScript";
	inPrcArgs = new Object();
	inPrcArgs.this = this;
	inPrcArgs.ctx = new Object();
	return;
}
if(f_stop) return;

pMax = plcMax; pMin = plcMin;	//Copy for local modifies using
if(passIn=(pMax==pMin)) { pMax = max/iMult - iAdd; pMin = min/iMult - iAdd; }

if(plcImit) {	//Data imitation
	if(!plcImitIn.isEVal()) in = plcImitIn;
	else {
		var plcOff;
		plcDif = abs(pMax-pMin);
		if(plcOff.isEVal())	plcOff = rand(plcDif/2);
		in = pMin + plcDif/4 + plcOff + rand(plcDif/10)-plcDif/20;
	}
}

//Call specific preprocessing procedure
if(inProc.length)	{
	inPrcArgs.f_frq = f_frq;
	inPrcArgs.in = in; inPrcArgs.var = var; inPrcArgs.min = min; inPrcArgs.max = max;
	inPrcArgs.plcMin = pMin; inPrcArgs.plcMax = pMax;
	inPrcArgs.plcImit = plcImit; inPrcArgs.plcImitIn = plcImitIn;
	SYS.DAQ.funcCall(inPrcLng, inPrcArgs, inProc, inPrcId);
	in = inPrcArgs.in;
}

levErr = 0;
tErr = "0";
//Input data check and postprocess
if(in.isEVal()) {
	tErr = "1:"+tr("No data or connection with source"); levErr = -5;
	var = EVAL_REAL;
	if(subMode == 1) var = prevVar;
	else if(subMode == 2) var = subVar;
}
else if(in > (max(pMax,pMin)+plcExcess*abs(pMax-pMin)/100)) {
	tErr = "1:"+tr("The signal exceed to upper hardware border"); levErr = -5;
	var = EVAL_REAL;
	if(subMode == 1) var = prevVar.isEVal() ? max+plcExcess*(max-min)/100 : prevVar;
	else if(subMode == 2) var = subVar;
}
else if(in < (min(pMax,pMin)-plcExcess*abs(pMax-pMin)/100)) {
	tErr = "2:"+tr("The signal exceed to bottom hardware border"); levErr = -5;
	var = EVAL_REAL;
	if(subMode == 1) var = prevVar.isEVal() ? min-plcExcess*(max-min)/100 : prevVar;
	else if(subMode == 2) var = subVar;
}
if(tErr)	EVAL = true, HH = H = LL = L = false;
else {
	vCalibr = iMult*(in+iAdd);
	if(passIn) { pMin = iMult*(pMin+iAdd); pMax = iMult*(pMax+iAdd); }
	if(!passIn || scSqr) {
		vCalibr = (vCalibr-min(pMax,pMin))/abs(pMax-pMin);
		if(pMax < pMin) vCalibr = 1-vCalibr;
		vCalibr = min + (max-min)*(scSqr?pow(vCalibr,0.5):vCalibr);
	}
	if(var.isEVal())	var = vCalibr;
	varDt = vCalibr - var;
	var += varDt/max(1,Tf*f_frq);
	prevVar = var;

	bndVarHyst = (max-min)*HystBnd/100;
	EVAL = HH_ = H_ = LL_ = L_ = false;
	if(speed && varDt > speed)	{ tErr = "7:"+tr("Too big parameter''s motion speed"); levErr = -2; }
	if(wMin > min && wMax > wMin && (var <= wMin || (L && var <= (wMin+bndVarHyst))))
	{ tErr = "6:"+tr("Lower warning border error"); levErr = -2; L_ = true; }
	if(wMax < max && wMax > wMin && (var >= wMax || (H && var >= (wMax-bndVarHyst))))
	{ tErr = "5:"+tr("Upper warning border error"); levErr = -2; H_ = true; }
	if(aMin > min && aMax > aMin && (var <= aMin || (LL && var <= (aMin+bndVarHyst))))
	{ tErr = "4:"+tr("Lower alarm border error"); levErr = -4; LL_ = true; }
	if(aMax < max && aMax > aMin && (var >= aMax || (HH && var >= (aMax-bndVarHyst))))
	{ tErr = "3:"+tr("Upper alarm border error"); levErr = -4; HH_ = true; }
	HH = HH_; H = H_; LL = LL_; L = L_;
}

//Alarms forming
if(alSup) {
	if(f_err.toInt())	this.alarmSet(DESCR+": "+tr("SUPPRESSED"), 1);
	f_err = "0";
}
else {
	if(tErr.toInt() && tErr.toInt() != f_err.toInt() && this.cntr().status().toInt())	return;
	if(alDelay > 0 && alDelay_ <= 0 && tErr.toInt() != f_err.toInt())	tErr1 = tErr.toInt();
	if(alDelay > 0 && alDelay_ > 0 && tErr.toInt() != tErr1)	{ alDelay_ = 0; tErr1 = tErr.toInt(); }
	if(alDelay > 0 && alDelay_ < alDelay){ alDelay_ += 1/f_frq; return; }
	if(tErr.toInt())	this.alarmSet(DESCR+": "+tErr.parse(1,":"), levErr);
	else	this.alarmSet(DESCR+": "+tr("NORM"), 1);
	f_err = tErr;
	alDelay_ = 0;
}','','',1572284562);
INSERT INTO tmplib_base VALUES('pidUnif','PID signal, unified, violation statuses','ПІД сигнал, уніфікований, стани порушень','ПИД сигнал, унифицированный, состояния нарушений','Common, representative and unified template of an interface to the Proportional Integral Differential (PID) regulator based on the common analog input signal, extended by binary states of indication the violations. The template forms a structure of the analog PID-regulator which can be easily connected to some cadres and widgets of the main elements library of the user interface just pointing the parameter object.

The representative structure of the analog PID-regulators is:
- "Variable (var)" — end variable of the parameter — engineeric real value, allowed and suitable for archiving.
- "Error (err)" — obligatory attribute of any parameter, consist of the combination of the error code and short text description, like to "2:No data or connection with source". The template reserves next error codes:
  - 1, 2 — hardware errors, when the data mostly wrong;
  - 3, 4 — alarm border errors;
  - 5, 6 — warning border errors;
  - 7 — the speed error of the variable changing.
- "Violations suppress (alSup) and delay (alDelay)" — violations generic control for suppressing and delaying.
- "Setpoint (sp)" — setpoint for the analog signal value achievement in the automatic mode.
- "Manual input (manIn)" — attribute for entering a new output value, which in the next falls into the output at the manual mode.
- "Output (out)" — output (control signal) of the PID-regulator.
- "Mode: automate (auto)" — the PID-regulator mode "Automatic".
- "Mode: cascade (casc)" — the PID-regulator mode "Cascade".
- "Scale: minimum (min)" and "Scale: maximum (max)" — main scale of the end variable, the output of which causes a hardware border error. The scale is also used as a scale of the variable trend display on the graphs.
- "Logarithmic scale (log)" — the scale sign of the variable trend display on the graphs in the logarithmic scale.
- "Dimension (ed)" — dimension (unit of measurement) of the variable.
- "Precision (prec)" — number signs after the dot, for positive values, and precision (signs in whole), for negative ones, to display the real end variable on the screen.

Functions:
- Generic processing of the raw analog input for:
  - simple correction-calibration at the formula "in = iMult*(in+iAdd)";
  - scaling from the hardware border [plcMin...plcMax] to the main border [min...max], applying the square scale scSqr;
  - permission to exceed the border for the input value on the value of plcExcess percent;
  - filtering the input value changing in the time Tf.
- Processing the result variable var for violation some borders and criteria, generating the alarms and proper setting the attribute err. The alarm borders can have some hysteresis HystBnd and can be suppressed setting alSup. Next borders and criteria are provided:
- missing for data or connection with source at the EVAL input value — error code 1 and alarm level 5;
- exceeding the input value of the hardware border — error code 1,2 and alarm level 5;
- exceeding the end variable of the alarm border [aMin...aMax] — error code 3,4 and alarm level 4;
  !!! Disabled at setting for equal or inverted values of the border
- exceeding the end variable of the warning border [wMin...wMax] — error code 5,6 and alarm level 2;
  !!! Disabled at setting for equal or inverted values of the border
big motion speed of the end variable — error code 7 and alarm level 2.
- Transmission of the PID-specific information, control and coefficient attributes to and from the PLC or other data source with the regulation.

Author: Roman Savochenko <roman@oscada.org>
Sponsored by: DIYA LTD
Version: 1.2.0
License: GPLv2','Загальний, представницький та уніфікований шаблон інтерфейсу до Пропорційного Інтегрального Диференційного (ПІД) регулятору заснованому на аналоговому вхідному сигналі, розширеному бінарними станами індикації порушень. Шаблон формує структуру аналогового ПІД-регулятору, який може бути легко підключений до деяких кадрів та віджетів бібліотеки основних елементів інтерфейсу користувача, просто вказавши об''єкт параметру.

Представницькою структурою аналогового ПІД-регулятору є:
- "Змінна (var)" — кінцева змінна параметру — інженерне реальне значення, доступне та придатне до архівації.
- "Помилка (err)" — обов''язковий атрибут будь якого параметру, що містить комбінацію коду помилки та короткого опису, на кшталт "2:Немає даних або підключення до джерела". Шаблон резервує наступні коди помилок:
  - 1, 2 — помилки обладнання, коли дані переважно помилкові;
  - 3, 4 — помилки аварійної границі;
  - 5, 6 — помилки попереджувальної границі;
  - 7 — помилка швидкості зміни змінної.
- "Придушення (alSup) та затримка (alDelay) порушень" — загальний контроль порушень щодо придушення та затримки.
- "Завдання (sp)" — завдання для досягнення значенням аналоговим сигналом у автоматичному режимі.
- "Ручний ввід (manIn)" — атрибут введення нового значення виходу, яке надалі потрапляє до виходу у ручному режимі.
- "Вихід (out)" — вихід (сигнал контролю) ПІД-регулятору.
- "Режим: автомат (auto)" — режим "Автомат" ПІД-регулятору.
- "Режим: каскад (casc)" — режим "Каскад" ПІД-регулятору.
- "Шкала: мінімум (min)" та "Шкала: максимум (max)" — головна шкала кінцевої змінної, вихід за яку призводить до помилки апаратної границі. Шкала також використовується як шкала відображення тренду змінної на графіках.
- "Логарифмічна шкала (log)" — ознака відображення тренду змінної на графіках у логарифмічній шкалі.
- "Одиниця виміру (ed)" — одиниця виміру змінної.
- "Точність (prec)" — кількість знаків після крапки, для позитивних значень, та точність (знаків загалом), для негативних, для відображення реальної кінцевої змінної на екрані.

Функції:
- Загальне опрацювання аналогового входу щодо:
  - проста корекція-калібрування за формулою "in = iMult*(in+iAdd)";
  - масштабування від апаратної границі [plcMin...plcMax] у головну границю [min...max], враховуючи квадратичну шкалу scSqr;
  - дозвіл на перевищення границі вхідним значенням на величину plcExcess відсотків;
  - фільтрування зміни вхідного значення на час Tf.
- Проста імітація аналогового входу, встановленням plcImit та підключенням джерела імітації-симуляції до plcImitIn.
- Опрацювання кінцевої змінної var на предмет порушення деяких границь та критеріїв, генеруючи аварії та відповідно встановлюючи атрибут err. Границі аварії можуть мати деякий гістерезис HystBnd, можуть бути затримані на час alDelay та можуть бути придушені встановленням alSup. Надаються наступні границі та критерії:
  - відсутність даних або підключення до джерела за вхідним значенням EVAL — код помилки 1 та рівень аварії 5;
  - перевищення апаратної границі вхідним значенням — код помилки 1,2 та рівень аварії 5;
  - перевищення аварійної границі кінцевою змінною [aMin...aMax] — код помилки 3,4 та рівень аварії 4;
￼ !!! Вимикається встановленням однакових або інверсних значень границі
  - перевищення попереджувальної границі кінцевою змінною [wMin...wMax] — код помилки 5,6 та рівень аварії 2;
￼ !!! Вимикається встановленням однакових або інверсних значень границі
  - велика швидкість зміни speed кінцевої змінної — код помилки 7 та рівень аварії 2.
- Передавання специфічної ПІД інформації, контролю та атрибутів коефіцієнтів до та з ПЛК або іншого джерела даних із регулюванням.

Автор: Роман Савоченко <roman@oscada.org>
Спонсоровано: ТОВ "ДІЯ"
Версія: 1.2.0
Ліцензія: GPLv2','',10,0,'JavaLikeCalc.JavaScript
if(f_start) {
	f_err = "0";
	alDelay_ = 0;
}

if(plcImit) {
	if(!plcImitIn.isEVal()) in = plcImitIn;
	else {
		plcDif = plcMax-plcMin;
		in = plcMin + plcDif/2 + rand(plcDif/10)-plcDif/20;
	}
}

tErr = "0";
if(in > (plcMax+plcExcess*(plcMax-plcMin)/100))
{ tErr = "1:"+tr("Signal exceed to upper hardware border"); var = max+plcExcess*(max-min)/100; }
else if(in < (plcMin-plcExcess*(plcMax-plcMin)/100))
{ tErr = "2:"+tr("Signal exceed to bottom hardware border"); var = min-plcExcess*(max-min)/100; }
if(tErr)	EVAL = true;
else {
	vCalibr = iMult*(in+iAdd);

	vCalibr = (vCalibr-plcMin)/(plcMax-plcMin);
	varDt = min + (max-min)*(scSqr?pow(vCalibr,0.5):vCalibr) - var;
	var += varDt/max(1,Tf/1000*f_frq);

	bndVarHyst = (aMax-aMin)*HystBnd/100;
	if(aMax > aMin && (var >= aMax || (HH && var >= (aMax-bndVarHyst))))
	{ tErr = "3:"+tr("Upper alarm border error"); HH = true; EVAL = H = LL = L = false; }
	else if(aMax > aMin && (var <= aMin || (LL && var <= (aMin+bndVarHyst))))
	{ tErr="4:"+tr("Lower alarm border error"); LL = true; EVAL = HH = H = L = false; }
	else if(wMax > wMin && (var >= wMax || (H && var >= (wMax-bndVarHyst))))
	{ tErr="5:"+tr("Upper warning border error"); H = true; EVAL = HH = LL = L = false; }
	else if(wMax > wMin && (var <= wMin || (L && var <= (wMin+bndVarHyst))))
	{ tErr="6:"+tr("Lower warning border error"); L = true; EVAL = HH = H = LL = false; }
	else if(speed && varDt > speed)
	{ tErr = "7:"+tr("Too big parameter''s motion speed"); EVAL = true; HH = H = LL = L = false; }
	else EVAL = HH = H = LL = L = false;
}

//Alarms forming
if(alSup) {
	if(f_err.toInt())	this.alarmSet(DESCR+": "+tr("SUPPRESSED"), 1);
	f_err = "0";
}
else {
	if(tErr.toInt() && tErr.toInt() != f_err.toInt() && this.cntr().status().toInt())	return;
	if(alDelay > 0 && alDelay_ <= 0 && tErr.toInt() != f_err.toInt())	tErr1 = tErr.toInt();
	if(alDelay > 0 && alDelay_ > 0 && tErr.toInt() != tErr1)	{ alDelay_ = 0; tErr1 = tErr.toInt(); }
	if(alDelay > 0 && alDelay_ < alDelay){ alDelay_ += 1/f_frq; return; }
	if(tErr.toInt())	this.alarmSet(DESCR+": "+tErr.parse(1,":"), -4);
	else	this.alarmSet(DESCR+": "+tr("NORM"), 1);
	f_err = tErr;
	alDelay_ = 0;
}','','',1572861425);
INSERT INTO tmplib_base VALUES('SNMP','Network devices by SNMP','Мережеві пристрої за SNMP','Сетевые устройства по SNMP','Data combination of a device, accessible by the protocol SNMP, in a complex object of the attribute "All items". The template designed to use together with the DAQ-module "SNMP" and to represent this data by the frame "Object properties" as a complex object with properties, signalling at single borders and writing. Considering the great specificity and variety of SNMP-accessible devices, this template is actually a billet and an example of creating a template based on it to a corresponding device with a specific to it processing of input properties, forming of violations and write capability.

Author: Roman Savochenko <roman@oscada.org>
Sponsored by: PAC HARTRON
Version: 1.1.0
License: GPLv2','Об''єднання даних пристрою, доступного за протоколом SNMP, у комплексному об''єкті атрибуту "Всі елементи". Шаблон розроблено для використання із модулем джерела даних "SNMP" та представлення цих даних кадром "Властивості об''єкту" як комплексний об''єкт із властивостями, сигналізацією за окремими границями та записом. Враховуючи велику специфіку та розмаїття пристроїв доступних за протоколом SNMP, цей шаблон фактично є заготовкою та прикладом створення на його основі шаблону до відповідного пристрою із специфічною до нього обробкою вхідних властивостей, формування порушень та можливістю запису.

Автор: Роман Савоченко <roman@oscada.org>
Спонсорування: ПАС ХАРТРОН
Версія: 1.1.0
Ліцензія: GPLv2','',10,0,'JavaLikeCalc.JavaScript
if(f_start)	{ srcPrm = false; items = new Object(); alDelay_ = 0; }

alLev = 0;
tErr = "";

//Connect to source
if(typeof(srcPrm) != "TCntrNode:TValue:TParamContr") srcPrm = SYS.DAQ.nodeAt(srcAddr,".");
if(!srcPrm) { tErr = tr("No connection to source object"); alLev = 3; }
else if(srcPrm.err.get() != 0)	 { tErr = tr("Source error")+": "+srcPrm.err.get().parse(1,":"); alLev = 3; }
else {
	//Attributes list get and "items" update
	nLst = srcPrm.nodeList("a_");
	lstTable = "";
	for(i_n = 0; i_n < nLst.length; i_n++) {
		aNd = srcPrm[nLst[i_n]];
		aId = nLst[i_n].slice(2);
		aIdPrc = aNd.descr();
		if(!aIdPrc.parse(1,"::").length) continue;
		if(aIdPrc.slice(-13) == "TotalNumber.0")	{ lstTable = aIdPrc.slice(0,-13); continue; }

		//Manual tables check
		if(!lstTable.length || lstTable != aIdPrc.slice(0,lstTable.length))
			for(off = 0; (iMnT=manTables.parse(0,";",off)).length; )
				if(iMnT == aIdPrc.slice(0,iMnT.length)) { lstTable = iMnT; break; }
		
		if(lstTable.length && lstTable == aIdPrc.slice(0,lstTable.length))
			aIdPrc = lstTable.parse(1,"::")+(prt1=aIdPrc.slice(lstTable.length)).parse(1,".")+prt1.parse(0,".");
		else aIdPrc = aIdPrc.parse(1,"::").parse(0,".");

		if(items[aIdPrc].isEVal()) {
			items[aIdPrc] = itW = new Object();
			itW.descr = aIdPrc;//aNd.descr();
			itW.id = aId;

			// Writeable check
			//SYS.messInfo("UPS test", aId+": aNd.flg()="+aNd.flg());
			if((itW.wr=!(aNd.flg()&0x04)) && aNd.flg()&0x01) {
				itW.wr = "";
				for(off = 0, pos = 0; (selId=aNd.values().parse(0,";",off)).length; pos++)
					itW.wr += ((selId==(selNm=aNd.selNames().parse(pos,";")))?selId:(selNm+" ("+selId+")"))+";";
			}
			itW.alarm = 0;
		}
		cVl = aNd.get();
		// Selectable value specifying
		if(aNd.flg()&0x01)
			for(off = 0, pos = 0; (selId=aNd.values().parse(0,";",off)).length; pos++)
				if(cVl == selId && cVl != aNd.selNames().parse(pos,";")) {		
					cVl = aNd.selNames().parse(pos,";")+"("+cVl+")";
					break;
				}
		items[aIdPrc].val = cVl;
	}

	//Alarms process and mark
	/*varS = "battery_packs";
	if(tP=srcPrm[varS])
	{
		if(tP.get().toInt() == 0)	{ items[varS].alarm = 2; tErr += "None good battery present; "; }
		else items[varS].alarm = 0;
	}*/

	//Set variables process
	for(var aIt in items) {
		it = items[aIt];
		if(!it.set.isEVal()) {
			aNd = srcPrm["a_"+it.id];
			if(aNd.flg()&0x01 && (selV=it.set.match(".+\\((.+)\\)$")).length) it.set = selV[1];
			aNd.set(it.set);
			it.set = EVAL_REAL;
		}
		alLev = max(alLev, it.alarm);
	}
}

//SYS.messInfo("UPS test", "tErr="+tErr+"; alLev="+alLev);
tErr = tErr.length ? ""+alLev+":"+tErr : "0";

//Alarms forming
if(alSup) {
	if(f_err.toInt())	this.alarmSet(DESCR+": "+tr("SUPPRESSED"), 1);
	f_err = "0";
}
else {
	if(tErr.toInt() && tErr.toInt() != f_err.toInt() && this.cntr().status().toInt())	return;
	if(alDelay > 0 && alDelay_ <= 0 && tErr.toInt() != f_err.toInt())	tErr1 = tErr.toInt();
	if(alDelay > 0 && alDelay_ > 0 && tErr.toInt() != tErr1)	{ alDelay_ = 0; tErr1 = tErr.toInt(); }
	if(alDelay > 0 && alDelay_ < alDelay){ alDelay_ += 1/f_frq; return; }
	if(tErr.toInt())	this.alarmSet(DESCR+": "+tErr.parse(1,":"), -(2+alLev));
	else	this.alarmSet(DESCR+": "+tr("NORM"), 1);
	f_err = tErr;
	alDelay_ = 0;
}','','',1572284562);
INSERT INTO tmplib_base VALUES('codeState','Code state','Стан за кодом','Состояние по коду','A variant of the common, representative and unified template of the block for union of Discrete parameters, expanded by more than two states and three commands that can be encoded by an integer value. The template forms a structure of parameter (complex tag) which can be easily connected to most widgets and cadres of the main elements library of the user interface just pointing the parameter object.

The representative structure of the parameter (complex tag) of the code state is:
- "State "Text" (st_text)" — text description of the state.
- "Command "Text" (com_text)" — text of the command.
- "Commands (digComs)" — common list of the allowed commands, to form the selecting.
- "Error (err)" — obligatory attribute of any parameter, consist of the combination of the error code and short text description, like to "2:No data or connection with source". The template reserves next error codes:
  - 1 — hardware errors, when the data mostly wrong.
- "Violations suppress (alSup) and delay (alDelay)" — violations generic control for suppressing and delaying.

Functions:
- Direct receiving, by link, the status code and the command (read only), as well as setting the status text according to the list of states stats.
- Working out the text of the command com_text to find it in the general list coms of commands and issuing the received command code to the output.
- Generating from the general list coms of commands the list of commands to be selected in digComs.
- Complex processing and complete generation-imitation of the code state, as well as processing the code or the command text for targeted actions, on the internal programming language of OpenSCADA into the configuration IO inProc, where you can operate next service parameters:
  - in [IN|OUT] — IO of the code state;
  - text [OUT] — direct text status setting;
  - coms [IN] — list of available commands, of the corresponded IO;
  - com_text [IN|OUT] — text of the received command;
  - levErr, tErr [IN|OUT] — alarm level [-7...0] and text of the error (the attribute "err" format);
  - f_frq [IN] — frequency of the calculation, the template IO;
  - this [IN] — reference to the parameter object, what executing the template;
  - ctx [IN] — object of the user context, what saved between the execution cycles.
- Formation of the violation and the corresponding setting of the attribute err, provided at setting in the processing procedure or lack of the communication (in = EVAL). What can be delayed on the time alDelay and can be suppressed setting alSup.

Author: Roman Savochenko <roman@oscada.org>
Version: 1.2.0
License: GPLv2','Варіант загального, представницького та уніфікованого шаблону блоку поєднання дискретних параметрів, розширений більш ніж двома станами та трьома командами які можна закодувати цілим значенням. Шаблон формує структуру параметру (складного тегу) стану за кодом який може бути легко підключений до більшості віджетів та кадрів бібліотеки основних елементів інтерфейсу користувача просто вказавши об''єкт параметру.

Представницькою структурою параметру (складного тегу) стану за кодом є:
- "Стан "Текст" (st_text)" — текстовий опис стану.
- "Команда "Текст" (com_text)" — текст команди.
- "Команди (digComs)" — загальний перелік можливих команд, для формування обрання.
- "Помилка (err)" — обов''язковий атрибут будь якого параметру, що містить комбінацію коду помилки та короткого опису, на кшталт "1:Немає даних або підключення до джерела". Шаблон резервує наступні коди помилок:
  - 1 — помилки обладнання, коли дані переважно помилкові.
- "Придушення (alSup) та затримка (alDelay) порушень" — загальний контроль порушень щодо придушення та затримки.

Функції:
- Пряме отримання, за посиланням, коду стану та команди (тільки читання), а також встановлення тексту стану згідно до переліку станів stats.
- Опрацювання тексту команди com_text на предмет пошуку її у загальному переліку команд coms та видача отриманого коду команди на вихід.
- Формування із загальному переліку команд coms переліку команд для обрання у digComs.
- Складна обробка та повна генерація-імітація коду стану, а також обробка коду або тексту команди для цільових дій, на внутрішній мові програмування OpenSCADA у конфігураційному ВВ inProc, де Ви можете оперувати наступними сервісними параметрами:
  - in [IN|OUT] — ВВ коду статусу;
  - text [OUT] — пряме встановлення тексту статусу;
  - coms [IN] — перелік наявних команд, відповідного ВВ;
  - com_text [IN|OUT] — текст отриманої команди;
  - levErr, tErr [IN|OUT] — рівень аварії [-7...0] та текст помилки (формат атрибуту "err");
  - f_frq [IN] — частота обчислення, шаблонний ВВ;
  - this [IN] — посилання на об''єкт параметру, що виконує шаблон;
  - ctx [IN] — об''єкт користувацького контексту, що зберігається між циклами виклику.
- Формування порушення та відповідне встановлення атрибуту err, за умови встановлення у процедурі обробки або відсутності зв''язку (in = EVAL). Що може бути затримано на час alDelay та придушено встановленням alSup.

Автор: Роман Савоченко <roman@oscada.org>
Версія: 1.2.0
Ліцензія: GPLv2','',10,0,'JavaLikeCalc.JavaScript
if(f_start) {
	f_err = "0";
	alDelay_ = 0;
	//Prepare data for preprocessing
	inPrcId = this.nodePath("_");
	inPrcLng = "JavaLikeCalc.JavaScript";
	inPrcArgs = new Object();
	inPrcArgs.this = this;
	inPrcArgs.ctx = new Object();
	return;
}
if(f_stop) return;

tErr = "0", levErr = 0;

//Call a specific preprocessing procedure
st_text = "";
if(inProc.length)	{
	inPrcArgs.f_frq = f_frq;
	inPrcArgs.in = in;
	inPrcArgs.text = "";
	inPrcArgs.levErr = levErr; inPrcArgs.tErr = tErr;
	inPrcArgs.coms = coms; inPrcArgs.com_text = com_text;
	SYS.DAQ.funcCall(inPrcLng, inPrcArgs, inProc, inPrcId);
	in = inPrcArgs.in;
	levErr = inPrcArgs.levErr; tErr = inPrcArgs.tErr;
	st_text = inPrcArgs.text;
	com_text = inPrcArgs.com_text;
}

//Commands processing
digComs = "";
if(!coms.length)	com_text = EVAL;
else if(com_text.isEVal())	com_text = "";
for(off = 0; (tVl=coms.parseLine(0,off)).length; ) {
	off1 = 0;
	code = tVl.parse(0,":",off1).toInt();
	st = tVl.slice(off1);
	digComs += (digComs.length?"\n":"") + st;
	if(com_text.length && com_text == st)	out = code;
}
if(!com_text.isEVal()) com_text = "";

//State set
if(st_text.length) ;
else if(in.isEVal()) {
	tErr = "1:"+tr("No data or connection with source");
	levErr = -5;
	st_text = tr("No data");
}
else {
	st_text = tr("Unknown")+" "+in;
	for(off = 0; (tVl=stats.parseLine(0,off)).length; ) {
		off1 = 0;
		if(in == tVl.parse(0,":",off1).toInt())	{ st_text = tVl.slice(off1); break; }
	}
}

//Alarms forming
if(alSup) {
	if(f_err.toInt())	this.alarmSet(DESCR+": "+tr("SUPPRESSED"), 1);
	f_err = "0";
}
else {
	if(tErr.toInt() && tErr.toInt() != f_err.toInt() && this.cntr().status().toInt())	return;
	if(alDelay > 0 && alDelay_ <= 0 && tErr.toInt() != f_err.toInt())	tErr1 = tErr.toInt();
	if(alDelay > 0 && alDelay_ > 0 && tErr.toInt() != tErr1)	{ alDelay_ = 0; tErr1 = tErr.toInt(); }
	if(alDelay > 0 && alDelay_ < alDelay){ alDelay_ += 1/f_frq; return; }
	if(tErr.toInt())	this.alarmSet(DESCR+": "+tErr.parse(1,":"), levErr);
	else	this.alarmSet(DESCR+": "+tr("NORM"), 1);
	f_err = tErr;
	alDelay_ = 0;
}','','',1572284562);
INSERT INTO tmplib_base VALUES('ntf','Notificator by SMS, EMail-SMTP','Повідомлювач за SMS, EMail-SMTP','Уведомитель посредством SMS, EMail-SMTP','The complex template of the notification contains of parts of the dispatcher and the output user protocol of notification by EMail(SMTP) and SMS.

THE DISPATCHER can be performed for pointed messages of the message buffer of OpenSCADA and applied on the Logical level or the controller object of the module JavaLikeCalc.

THE OUTPUT USER PROTOCOL PART "SMS" provides operations with SMS by a GSM-modem connected as serial device. For now, there supported only sending SMS messages to a number of remote cell phone or GSM-modem. For sending there are supported: ECHO disable, PIN check and set, sending in PDU and the Text mode.

THE OUTPUT USER PROTOCOL PART "SMTP" provides operations with a SMTP-server for delivering EMails by their sending through TCP-Socket or SSL. For now, there are supported: TCP, SSL, AUTH, topic encoding for UTF-8.

Author: Roman Savochenko <roman@oscada.org>
Sponsored by: SVItoVYR LTD
Version: 1.0.1
License: GPLv2','Комплексний шаблон повідомлення містить частини диспетчеру та вихідний користувацький протокол повідомлення за EMail(SMTP) та SMS.

ДИСПЕТЧЕР може виконуватися для вказаних повідомлень буферу повідомлень OpenSCADA та застосовується на Логічному рівні або об''єкті контролеру модуля JavaLikeCalc.

"SMS" ЧАСТИНА ВИХІДНОГО КОРИСТУВАЦЬКОГО ПРОТОКОЛУ надає операції із SMS за допомогою GSM-модему, підключеного як послідовний пристрій. Наразі підтримується лише відправка SMS повідомлень на номер віддаленого мобільного телефону або GSM-модему. Для надсилання підтримується: вимкнення ЛУНИ, перевірка та встановлення PIN, надсилання у PDU та текстовому режимі.

"SMTP" ЧАСТИНА ВИХІДНОГО КОРИСТУВАЦЬКОГО ПРОТОКОЛУ надає операції із SMTP-сервером щодо доставки електронної пошти відправленням через TCP-Сокет або SSL. Наразі підтримуються: TCP, SSL, AUTH, кодування теми у UTF-8.

Автор: Роман Савоченко <roman@oscada.org>
Спонсоровано: ТОВ "СВІТоВИР АВТоМАТИК"
Версія: 1.0.1
Ліцензія: GPLv2','',10,0,'JavaLikeCalc.JavaScript
if(f_start)	io = tr = EVAL;

//-----------------------------------------------
//Request form of the UserProtocol ProtIt="SMTP":
//-----------------------------------------------
//<send auth="{user}:{pass}" from="{FromAddr}" to="{ToAddr}" topic="{My Topic}" err="1:Error">{MessageText}</send>
//  topic - topic of the message;
//  from - sender address;
//  to - destination address of the receiver;
//  auth - authenticate user and password;
//  MessageText - text of the message;
//  err - sets for the request result.
function SMTP( ) {
	//Transport''s timings check
	if(tr.timings() != "5:1")	tr.timings("5:1");
	io.setAttr("err", "");
	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);
}

if(!tr.isEVal() && !io.isEVal() && io.attr("ProtIt") == "SMTP") { SMTP(); return; }

//--------------------------------------------
//Request form of the UserProtocol ProtIt="SMS":
//--------------------------------------------
//<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 of the 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.
function SMS( ) {
	//Transport''s timings check
	if(tr.timings() != "5000:100")	tr.timings("5000:100");
	io.setAttr("err", "");
	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");
	}
}

if(!tr.isEVal() && !io.isEVal() && io.attr("ProtIt") == "SMS")	{ SMS(); return; }

//---------------------------
//The notification dispatcher
//---------------------------
if(f_start) {
	queue = new Object();
	queueSMS = new Object();
	prTm = SYS.time();
	prTmU = 0;
	emailSentN = SMSSentN = 0;
	emailTrAddr_ = emailTrAddr;
	SMSTrAddr_ = SMSTrAddr;
	SMTPTr = emailTrAddr.length ? SYS.Transport.nodeAt(emailTrAddr, ".") : false;
	SMSTr = SMSTrAddr.length ? SYS.Transport.nodeAt(SMSTrAddr, ".") : false;
	return;	//Don''t real process at start
}
if(f_stop)	{ emailState = SMSState = ""; return; }
if(!SMTPTr || SMTPTr.isEVal() || emailTrAddr_ != emailTrAddr) {
	SMTPTr = emailTrAddr.length ? SYS.Transport.nodeAt(emailTrAddr, ".") : false;
	emailTrAddr_ = emailTrAddr;
}
if(!SMSTr || SMSTr.isEVal() || SMSTrAddr_ != SMSTrAddr)	{
	SMSTr = SMSTrAddr.length ? SYS.Transport.nodeAt(SMSTrAddr, ".") : false;
	SMSTrAddr_ = SMSTrAddr;
}

//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(emailTrAddr.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;

	tr = SMTPTr, io = SYS.XMLNode("send");
	io.setAttr("ProtIt", "SMTP").setAttr("auth", emailAuth).setAttr("from", emailSender).setAttr("to", emailReceiver).setAttr("topic", topic).setText(mess);
	SMTP();
	//SYS.messInfo("NTF", "Send error: "+io.attr("err"));
	if(!(tVl=io.attr("err").toInt())) { delete queue[nM]; emailSentN++; }
	else {
		if(tVl == 10)	SMTPTr = false;	//Reconnect the transport
		emailQueueN++;
		if(!emailErr.length)	emailErr = io.attr("err");
	}
	io = tr = EVAL;

	/*req = "sendemail -q -f " + emailSender + " -t " + emailReceiver +
			" -u \"=?utf8?B?" + SYS.strEncode(topic,"Base64") + "?=\" -m \"" + mess + "\" -s " + emailTrAddr;
	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;
	tr = SMSTr, io = SYS.XMLNode("send");
	io.setAttr("ProtIt", "SMS").setAttr("pin", SMSPin).setAttr("text",SMSTextMd).setAttr("tel", SMSTel).setText(mess);
	SMS();
	if(!io.attr("err").toInt()) { delete queueSMS[nM]; SMSSentN++; }
	else {
		SMSQueueN++;
		if(!SMSErr.length)	SMSErr = io.attr("err");
	}
	io = tr = EVAL;
}
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);
}','','',1550428714);
INSERT INTO tmplib_base VALUES('UPS','Uninterruptible Power Supply','Джерело Безперебійного Живлення','Источник Бесперебойного Питания','Data combination of the Uninterruptible Power Supply in a complex object of the attribute "All items". The template designed to use together with the DAQ-module "System" in the part "UPS" and to represent this data by the frame "Object properties" as a complex object with properties, signalling at single borders and writing.

Author: Roman Savochenko <roman@oscada.org>
Sponsored by: DIYA LTD
Version: 1.3.0
License: GPLv2','Об''єднання даних Джерела Безперебійного Живлення у комплексному об''єкті атрибуту "Всі елементи". Шаблон розроблено для використання із модулем джерела даних "Система" у частині "ДБЖ" та представлення цих даних кадром "Властивості об''єкту" як комплексний об''єкт із властивостями, сигналізацією за окремими границями та записом.

Автор: Роман Савоченко <roman@oscada.org>
Спонсоровано: ТОВ "ДІЯ"
Версія: 1.3.0
Ліцензія: GPLv2','',10,0,'JavaLikeCalc.JavaScript
if(f_start)	{ srcPrm = false; items = new Object(); alDelay_ = 0; }

alLev = 0;
tErr = "";

//Connect to source
if(typeof(srcPrm) != "TCntrNode:TValue:TParamContr") srcPrm = SYS.DAQ.nodeAt(srcAddr,".");
if(!srcPrm) { tErr = tr("No connection to source object"); alLev = 3; }
else if(srcPrm.err.get() != 0)	 { tErr = tr("Source error")+": "+srcPrm.err.get().parse(1,":"); alLev = 3; }
else {
	//Attributes list get and "items" update
	nLst = srcPrm.nodeList("a_");
	for(i_n = 0; i_n < nLst.length; i_n++) {
		aId = nLst[i_n].slice(2);
		aNd = srcPrm[nLst[i_n]];
		if(items[aId].isEVal()) {
			items[aId] = itW = new Object();
			itW.descr = aNd.descr();

			// Writeable check
			//SYS.messInfo("UPS test", aId+": aNd.flg()="+aNd.flg());
			if((itW.wr=!(aNd.flg()&0x04)) && aNd.flg()&0x01) {
				itW.wr = "";
				for(off = 0, pos = 0; (selId=aNd.values().parse(0,";",off)).length; pos++)
					itW.wr += ((selId==(selNm=aNd.selNames().parse(pos,";")))?selId:(selNm+" ("+selId+")"))+";";
			}
			itW.alarm = 0;
		}
		items[aId].val = aNd.get();
	}

	//Alarms process and mark
	varS = "ups_status";
	if(!(tP=srcPrm[varS]).isEVal() && !(tVl=tP.get()).isEVal()) {
		if(tVl == "OB")	{ items[varS].alarm = 1; tErr += tr("Status")+" \""+tr("On battery")+"\"; "; }
		else if(tVl == "LB")	{ items[varS].alarm = 2; tErr += tr("Status")+" \""+tr("Low battery")+"\"; "; }
		else if(tVl == "SD")	{ items[varS].alarm = 2; tErr += tr("Status")+" \""+tr("Shutdown load")+"\"; "; }
		else if(tVl.indexOf("ALARM") != -1)	{ items[varS].alarm = 2; tErr += tr("Status")+" \""+tr("ALARM")+"\"; "; }
		else items[varS].alarm = 0;
	}
	varS = "battery_packs";
	if(!(tP=srcPrm[varS]).isEVal() && !(tVl=tP.get()).isEVal()) {
		if(tVl.toInt() == 0)	{ items[varS].alarm = 2; tErr += tr("None of good battery present")+"; "; }
		else items[varS].alarm = 0;
	}
	varS = "battery_charge";
	if(!(tP=srcPrm[varS]).isEVal() && !(tVl=tP.get()).isEVal()) {
		if(tVl.toReal() < bChL) { items[varS].alarm = 1; tErr += tr("Battery charge low")+"; "; }
		else if(tVl.toReal() < bChLL) { items[varS].alarm = 2; tErr += tr("Battery charge critical")+"; "; }
		else items[varS].alarm = 0;
	}
	varS = "battery_packs_bad";
	if(!(tP=srcPrm[varS]).isEVal() && !(tVl=tP.get()).isEVal()) {
		if(tVl.toInt())	{ items[varS].alarm = 1; tErr += tr("Bad %1 batteries present").replace("%1",tVl); }
		else items[varS].alarm = 0;
	}
	varS = "input_voltage";
	if(!(tP=srcPrm[varS]).isEVal() && !(tVl=tP.get()).isEVal()) {
		if(tVl.toReal() > 10 && tVl.toReal() < inVL) { items[varS].alarm = 1; tErr += tr("Input voltage low")+"; "; }
		else if(tVl.toReal() > inVH) { items[varS].alarm = 1; tErr += tr("Input voltage high")+"; "; }
		else items[varS].alarm = 0;
	}
	varS = "input_frequency";
	if(!(tP=srcPrm[varS]).isEVal() && !(tVl=tP.get()).isEVal()) {
		if(tVl.toReal() < inFLL) { items[varS].alarm = 2; tErr += tr("Input frequency too low")+"; "; }
		else if(tVl.toReal() > inFHH)	{ items[varS].alarm = 2; tErr += tr("Input frequency too high")+"; "; }
		else if(tVl.toReal() < inFL) { items[varS].alarm = 1; tErr += tr("Input frequency low")+"; "; }
		else if(tVl.toReal() > inFH) { items[varS].alarm = 1; tErr += tr("Input frequency high")+"; "; }
		else items[varS].alarm = 0;
	}
	varS = "ups_load";
	if(!(tP=srcPrm[varS]).isEVal() && !(tVl=tP.get()).isEVal()) {
		if(tVl.toReal() > loadHH)		{ items[varS].alarm = 2; tErr += tr("UPS overloaded")+"; "; }
		else if(tVl.toReal() > loadH)	{ items[varS].alarm = 1; tErr += tr("UPS load high")+"; "; }
		else items[varS].alarm = 0;
	}
	varS = "ups_temperature";
	if(!(tP=srcPrm[varS]).isEVal() && !(tVl=tP.get()).isEVal()) {
		if(tVl.toReal() > tHH) 		{ items[varS].alarm = 2; tErr += tr("UPS overheated")+"; "; }
		else if(tVl.toReal() > tH)	{ items[varS].alarm = 1; tErr += tr("Temperature high")+"; "; }
		else items[varS].alarm = 0;
	}

	//Set variables process
	for(var aIt in items) {
		it = items[aIt];
		if(!it.set.isEVal()) {
			aNd = srcPrm["a_"+it.id];
			if(aNd.flg()&0x01 && (selV=it.set.match(".+\\((.+)\\)$")).length) it.set = selV[1];
			aNd.set(it.set);
			it.set = EVAL_REAL;
		}
		alLev = max(alLev, it.alarm);
	}
}

//SYS.messInfo("UPS test", "tErr="+tErr+"; alLev="+alLev);
tErr = tErr.length ? ""+alLev+":"+tErr : "0";

//Alarms forming
if(alSup) {
	if(f_err.toInt())	this.alarmSet(DESCR+": "+tr("SUPPRESSED"), 1);
	f_err = "0";
}
else {
	if(tErr.toInt() && tErr.toInt() != f_err.toInt() && this.cntr().status().toInt())	return;
	if(alDelay > 0 && alDelay_ <= 0 && tErr.toInt() != f_err.toInt())	tErr1 = tErr.toInt();
	if(alDelay > 0 && alDelay_ > 0 && tErr.toInt() != tErr1)	{ alDelay_ = 0; tErr1 = tErr.toInt(); }
	if(alDelay > 0 && alDelay_ < alDelay){ alDelay_ += 1/f_frq; return; }
	if(tErr.toInt())	this.alarmSet(DESCR+": "+tErr.parse(1,":"), -(2+alLev));
	else	this.alarmSet(DESCR+": "+tr("NORM"), 1);
	f_err = tErr;
	alDelay_ = 0;
}','','',1572284562);
INSERT INTO tmplib_base VALUES('initConsAssignTrs','Initiative connections processing for assigned output transports','Опрацювання ініціативних підключень щодо асоціативних вихідних транспортів','Обработка инициативных подключений на предмет ассоциативных выходных транспортов','The template of processing input initiative connections was created to provide the latest chain of implementation and support for conception of data acquisition in the passive mode and the initiative connection, that is — the control of associative connections, created by the input transport at each connection.

The function of creation associative output transports at connections is currently only supported by the module "Sockets" and for TCP-sockets.

The control of associative transport means:
  - obtaining a list of the associative transports, by the function assTrsList() of the input transports;
  - reading from new transports (opened) of identification information, which currently represents only the source object identifier;
    - search, by the received identifier, the source object and assigning to it of this transport;
    - registering in the attribute "prcTr" of the object type.
  - checking of existing transports for their closure (detachment), for which performed the deregistration in the attribute "prcTr" of the object type and the establishment of the source object in the empty value; disconnected output associated transports are reused by the input one upon receiving a new connection then it is switched on.

The template can control the containers of data source objects, constructed both as DAQ-objects of the controllers and logical level parameters, what representing the data source and having a transport address definition field, and what generally defined by the configuration field "srcObjPath" of the format {CntrAddr}:{TrCntrFldAddr}, where:
  - CntrAddr — container address, for example: "ModBus" — for DAQ-objects of the controllers, "LogicLev.CntrPLC" — for logical level parameters;
  - TrCntrFldAddr — address of the transport control field in the source object, for example — "%2fcntr%2fcfg%2fADDR" for "ModBus"; what can be obtained in a configurator at the history of pointing on the control field in the status line.

Author: Roman Savochenko <roman@oscada.org>
Sponsored by: Elyor Turaboev, BLUE STAR GROUP Ltd
Version: 1.1.2
License: GPLv2','Шаблон служби опрацювання вхідних ініціативних підключень створено для надання останньої ланки реалізації та підтримки концепції збору даних у пасивному режимі та ініціативного підключення, а саме — контролю асоціативних транспортів, створюваних вхідним транспортом за кожним підключенням.

Функцію створення вихідних асоціативних транспортів за підключенням на вхідний наразі підтримує лише модуль транспортів "Сокети" та для TCP-сокетів.

Контроль асоціативних транспортів передбачає:
  - отримання переліку асоціативних транспортів, функцією вхідного транспорту assTrsList();
  - читання із нових транспортів (відкритих) ідентифікаційної інформації, яка наразі репрезентує тільки ідентифікатор об''єкту джерела;
    - пошук, за отриманим ідентифікатором, об''єкта джерела та призначення йому адреси цього транспорту;
    - реєстрацію у атрибуті об''єктного типу "prcTr".
  - перевірка діючих транспортів на предмет їх закриття (від''єднання), щодо чого здійснюється дереєстрація у атрибуті об''єктного типу "prcTr" та встановлення об''єкту джерела порожнього значення; вимкнені вихідні асоціативні транспорти повторно використовуються вхідним за отриманням нового підключення, після чого він вмикається.

Шаблон може керувати контейнерами об''єктів джерел даних, побудованих як у вигляді DAQ-об''єктів контролерів, так і параметрів логічного рівня, що репрезентують джерело даних та мають поле визначення адреси транспорту та що загалом визначається конфігураційним полем "srcObjPath" із форматом {CntrAddr}:{TrCntrFldAddr}, де:
  - CntrAddr — адреса контейнеру, наприклад: "ModBus" — для DAQ-об''єктів контролерів, "LogicLev.CntrPLC" — для параметрів логічного рівня;
  - TrCntrFldAddr — адреса поля контролю транспорту у об''єкті джерела, наприклад — "%2fcntr%2fcfg%2fADDR" для "ModBus"; що може бути отримано у конфігураторі за історією наведення на поля контролю у рядку статусу.

Автор: Роман Савоченко <roman@oscada.org>
Спонсорування: Еліор Турабоєв, ТОВ BLUE STAR GROUP
Версія: 1.1.2
Ліцензія: GPLv2','',10,0,'JavaLikeCalc.JavaScript
if(f_start) {
	inTransport_ = "", inTr = EVAL;
	prcTr = new Object();
}

//Check for the transport change and connect
tErr = "";
if(inTr.isEVal() || inTransport != inTransport_)	{
	inTr = SYS.Transport.Sockets["in_"+inTransport];
	inTransport_ = inTransport;
}

if(inTr.isEVal())	tErr = "1:"+tr("Input transport ''%1'' error.").replace("%1",inTransport);
else if(!(CNTR_O=SYS.DAQ.nodeAt(srcObjPath.parse(0,":"),".")))
	tErr = "2:"+tr("Error the source object path ''%1''.").replace("%1", srcObjPath.parse(0,":"));
else if(!(CntrFldPath=srcObjPath.parse(1,":")).length)
	tErr = "3:"+tr("The control field path is empty.");
else {
	isLogPrm = srcObjPath.parse(0,":").parse(1).length;

	outTrs = inTr.assTrsList();
	for(iTr = 0; iTr < outTrs.length; iTr++) {
		oTrNm = outTrs[iTr];
		// The transport is present and active
		if(!(oTrO=SYS.Transport.Sockets["out_"+oTrNm]).isEVal() && oTrO.start()) {
			//  and not registered
			if(prcTr[oTrNm].isEVal()) {
				//   reading the source identifier
				idSeq = oTrO.messIO("").parse(0, ":");
				if(idSeq.length > 20)	idSeq = idSeq.slice(0, 20);
				//   stop for empty-missed identification or missed PLC object
				if(!idSeq.length || (PLC_O=CNTR_O[(isLogPrm?"prm_":"")+idSeq]).isEVal())	{
					oTrO.start(false);
					if(idSeq.length)
						SYS.messNote("initConsAssignTrs", tr("The requested identificator ''%1'' is mіssing in the container ''%2'' of the data source objects!").replace("%1",idSeq).replace("%2",srcObjPath.parse(0,":")));
					continue;
				}

				//   A duple connection is detected - stopping that
				req = SYS.XMLNode("get").setAttr("path",PLC_O.nodePath()+"/"+CntrFldPath);
				SYS.cntrReq(req);
				if((tVl=req.text()).length && tVl != oTrNm && !outTrs[tVl].isEVal() && SYS.Transport.Sockets["out_"+tVl].start()) {
				    if(prcTr[tVl] == idSeq) { oTrO.start(false); continue; }
				    else if(prcTr[tVl].isEVal())	SYS.Transport.Sockets["out_"+tVl].start(false);
				}

				//   connect the transport to a PLC object
				if(outTrTm.length)	oTrO.timings(outTrTm);
				prcTr[oTrNm] = idSeq;
				req.setName("set").setText("Sockets."+oTrNm);
				SYS.cntrReq(req);
			}
		}
		// is not present or active
		else if(!prcTr[oTrNm].isEVal() && !(PLC_O=CNTR_O[(isLogPrm?"prm_":"")+prcTr[oTrNm]]).isEVal()) {
			delete prcTr[oTrNm];
			SYS.cntrReq(SYS.XMLNode("set").setAttr("path",PLC_O.nodePath()+"/"+CntrFldPath).setText(""));
		}
	}
	delete oTrO;
}

//Error set
if(tErr.length)	f_err = tErr;
else f_err = "0";','','',1571249552);
