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