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).
- Specific parameters redefinition in the common attribute DESCR:
  - the first line — as description of the alarm messages;
  - the field "CustomFlds" — specifying of the custom fields of the alarm messages in the form "CustomFlds: {CustFld0} => {CustFld1} => ... => {CustFldN}".

Author: Roman Savochenko <roman@oscada.org>
Version: 1.2.1
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).
- Перевизначення специфічних параметрів у загальному атрибуті DESCR:
  - перший рядок — як опис повідомлень порушень;
  - поле "CustomFlds" — визначення користувацьких полів повідомлень порушень у формі "CustomFlds: {CustFld0} => {CustFld1} => ... => {CustFldN}".

Автор: Роман Савоченко <roman@oscada.org>
Версія: 1.2.1
Ліцензія: GPLv2','',10,0,'JavaLikeCalc.JavaScript
if(f_start) {
	f_err = "0";
	alDelay_ = 0;
	//Prepare data for preprocessing
	inPrcId = this.nodePath("_");
	inPrcLng = "JavaLikeCalc.JavaScript";
	inPrcLn = 0;
	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)	{
	// Checking the builtin syntax highlight
	if(inProc.length != inPrcLn) {
		inPrcLn = inProc.length;
		if(!inProc.match("<SnthHgl\\b.*>.*<\\/ *SnthHgl>","gm").length)
			inProc = SYS.DAQ.funcSnthHgl(inPrcLng).replace(new RegExp("^","gm"),"//") + "\n\n" + inProc;
	}

	// Same calculation
	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.parseLine(0)+((tVl=DESCR.match("^CustomFlds: *(.+?) *$","m")).length?" [["+tVl[1]+"]]":"")+": "+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.parseLine(0)+((tVl=DESCR.match("^CustomFlds: *(.+?) *$","m")).length?" [["+tVl[1]+"]]":"")+": "+tErr.parse(1,":"), levErr);
	else	this.alarmSet(DESCR.parseLine(0)+((tVl=DESCR.match("^CustomFlds: *(.+?) *$","m")).length?" [["+tVl[1]+"]]":"")+": "+tr("NORM"), 1);
	f_err = tErr;
	alDelay_ = 0;
}','','',1686917512);
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.1','Шаблон простого параметру з перевіркою границь та одиницею виміру.

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

Автор: Роман Савоченко <roman@oscada.org>
Версия: 1.0.1',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";',1627833238);
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.2
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.2
Ліцензія: 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;
}','','',1686917547);
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 [-79...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;
  - toSave [OUT] — to force save the parameter object at exit.
- 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.
- Specific parameters redefinition in the common attribute "DESCR":
  - the first line — as description of the alarm messages;
  - the field "CustomFlds" — specifying of the custom fields of the alarm messages in the form "CustomFlds: {CustFld0} => {CustFld1} => ... => {CustFldN}";
  - the field "CustomAlarms" — redefinition of the standard alarm levels of the alarm messages in the form "CustomAlarms: {ConErr};{BrdAlarm};{BrdWarn}".

Author: Roman Savochenko <roman@oscada.org>
Version: 2.4.1
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] — рівень аварії [-79...0] та текст помилки (формат атрибуту "err");
  - f_frq [IN] — частота обчислення, шаблонний ВВ;
  - this [IN] — посилання на об''єкт параметру, що виконує шаблон;
  - ctx [IN] — об''єкт користувацького контексту, що зберігається між циклами виклику;
  - toSave [OUT] — примусово зберігати об''єкт параметру при виході.
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.
- Перевизначення специфічних параметрів у загальному атрибуті "DESCR":
  - перший рядок — як опис повідомлень порушень;
  - поле "CustomFlds" — визначення користувацьких полів повідомлень порушень у формі "CustomFlds: {CustFld0} => {CustFld1} => ... => {CustFldN}";
  - поле "CustomAlarms" — перевизначення стандартних рівнів порушень для повідомлень порушень у формі "CustomAlarms: {ConErr};{BrdAlarm};{BrdWarn}".

Автор: Роман Савоченко <roman@oscada.org>
Версія: 2.4.1
Ліцензія: GPLv2','',10,0,'JavaLikeCalc.JavaScript
function custAlarm(lev) {
	rez = 0;
	if(lev == 0)			rez = 5;
	else if(lev == 1)	rez = 4;
	else if(lev == 2)	rez = 2;

	if((tVl=DESCR.match("^CustomAlarms: *(.+?) *$","m")).length)
		rez = tVl[1].parse(lev, ";").toInt();

	return rez;
}

if(f_start) {
	f_err = "0";
	prevVar = EVAL_REAL;
	alDelay_ = 0; firstNorm = alNormForceStart;
	//Prepare data for preprocessing
	inPrcId = this.nodePath("_");
	inPrcLng = "JavaLikeCalc.JavaScript";
	inPrcLn = 0;
	inPrcArgs = new Object();
	inPrcArgs.this = this;
	inPrcArgs.ctx = new Object();
	inPrcArgs.toSave = false;
	return;
}
if(f_stop) {
	if(inProc.length && inPrcArgs.toSave)
		SYS.cntrReq(SYS.XMLNode("save").setAttr("path",this.nodePath()+"/%2fobj").setAttr("force","1"));
	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) {
	// Checking the builtin syntax highlight
	if(inProc.length != inPrcLn) {
		inPrcLn = inProc.length;
		if(!inProc.match("<SnthHgl\\b.*>.*<\\/ *SnthHgl>","gm").length)
			inProc = SYS.DAQ.funcSnthHgl(inPrcLng).replace(new RegExp("^","gm"),"//") + "\n\n" + inProc;
	}

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

//Alarms forming
if(alSup) {
	if(f_err.toInt())	this.alarmSet(DESCR.parseLine(0)+((tVl=DESCR.match("^CustomFlds: *(.+?) *$","m")).length?" [["+tVl[1]+"]]":"")+": "+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.parseLine(0)+((tVl=DESCR.match("^CustomFlds: *(.+?) *$","m")).length?" [["+tVl[1]+"]]":"")+": "+tErr.parse(1,":"), levErr);
	else	this.alarmSet(DESCR.parseLine(0)+((tVl=DESCR.match("^CustomFlds: *(.+?) *$","m")).length?" [["+tVl[1]+"]]":"")+": "+tr("NORM"), 1, firstNorm);
	f_err = tErr;
	alDelay_ = 0; firstNorm = false;
}','','',1686917407);
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.2
License: GPLv2','Загальний, представницький та уніфікований шаблон інтерфейсу до Пропорційного Інтегрального Диференційного (ПІД) регулятору заснованому на аналоговому вхідному сигналі але із ШІМ-виходом, розширеному бінарними станами індикації порушень та переважно схоже на шаблон "ПІД сигнал, уніфікований, стани порушень". Шаблон формує структуру імпульсного ПІД-регулятору, який може бути легко підключений до деяких кадрів та віджетів бібліотеки основних елементів інтерфейсу користувача, просто вказавши об''єкт параметру.

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

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

Автор: Роман Савоченко <roman@oscada.org>
Спонсоровано: ТОВ "ДІЯ"
Версія: 1.1.2
Ліцензія: 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;
}','','',1686917610);
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.3.1
License: GPLv2','Загальний, представницький та уніфікований шаблон обробки аналогових вхідних сигналів, розширений бінарними станами індикації порушень. Шаблон засновано на базовому шаблоні "Аналоговий сигнал, уніфікований" з відповідною до нього представницькою структурою та деякими відмінностями, характерними до незалежного розвитку та розширення.

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

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

Автор: Роман Савоченко <roman@oscada.org>
Версія: 1.3.1
Ліцензія: GPLv2','',10,0,'JavaLikeCalc.JavaScript
if(f_start) {
	f_err = "0";
	prevVar = EVAL_REAL;
	alDelay_ = 0;
	//Prepare data for preprocessing
	inPrcId = this.nodePath("_");
	inPrcLng = "JavaLikeCalc.JavaScript";
	inPrcLn = 0;
	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)	{
	// Checking the builtin syntax highlight
	if(inProc.length != inPrcLn) {
		inPrcLn = inProc.length;
		if(!inProc.match("<SnthHgl\\b.*>.*<\\/ *SnthHgl>","gm").length)
			inProc = SYS.DAQ.funcSnthHgl(inPrcLng).replace(new RegExp("^","gm"),"//") + "\n\n" + inProc;
	}

	// Same calculation
	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;
}','','',1686917435);
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.2
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.2
Ліцензія: 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;
}','','',1686917590);
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.1
License: GPLv2','Об''єднання даних пристрою, доступного за протоколом SNMP, у комплексному об''єкті атрибуту "Всі елементи". Шаблон розроблено для використання із модулем джерела даних "SNMP" та представлення цих даних кадром "Властивості об''єкту" як комплексний об''єкт із властивостями, сигналізацією за окремими границями та записом. Враховуючи велику специфіку та розмаїття пристроїв доступних за протоколом SNMP, цей шаблон фактично є заготовкою та прикладом створення на його основі шаблону до відповідного пристрою із специфічною до нього обробкою вхідних властивостей, формування порушень та можливістю запису.

Автор: Роман Савоченко <roman@oscada.org>
Спонсорування: ПАС ХАРТРОН
Версія: 1.1.1
Ліцензія: 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;
}','','',1686920823);
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.4.1
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.4.1
Ліцензія: GPLv2','',10,0,'JavaLikeCalc.JavaScript
if(f_start) {
	f_err = "0";
	alDelay_ = 0;
	//Prepare data for preprocessing
	inPrcId = this.nodePath("_");
	inPrcLng = "JavaLikeCalc.JavaScript";
	inPrcLn = 0;
	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)	{
	// Checking the builtin syntax highlight
	if(inProc.length != inPrcLn) {
		inPrcLn = inProc.length;
		if(!inProc.match("<SnthHgl\\b.*>.*<\\/ *SnthHgl>","gm").length)
			inProc = SYS.DAQ.funcSnthHgl(inPrcLng).replace(new RegExp("^","gm"),"//") + "\n\n" + inProc;
	}

	// Same calculation
	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;
}','','',1686917483);
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.7
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.7
Ліцензія: GPLv2','',120,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 "+io.attr("from").parse(1,"@")+"\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) ||
		(messCatExcl.length && (new RegExp(messCatExcl)).test(oM.categ)))	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();

	//Remove the message from the queue anytime
	delete queue[nM]; emailSentN++;
	// and just notify the SMTP errors
	if((tVl=io.attr("err").toInt())) {
		if(tVl == 10)	SMTPTr = false;	//Reconnect the transport
		SYS.messWarning("/service/NTF", "EMail(SMTP) sending error: "+io.attr("err"));
	}
	io = tr = EVAL;

	//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);
}','','',1686898650);
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.1
License: GPLv2','Об''єднання даних Джерела Безперебійного Живлення у комплексному об''єкті атрибуту "Всі елементи". Шаблон розроблено для використання із модулем джерела даних "Система" у частині "ДБЖ" та представлення цих даних кадром "Властивості об''єкту" як комплексний об''єкт із властивостями, сигналізацією за окремими границями та записом.

Автор: Роман Савоченко <roman@oscada.org>
Спонсоровано: ТОВ "ДІЯ"
Версія: 1.3.2
Ліцензія: 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]) != null && (tVl=tP.get()) != null) {
		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]) != null && (tVl=tP.get()) != null) {
		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]) != null && (tVl=tP.get()) != null) {
		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]) != null && (tVl=tP.get()) != null) {
		if(tVl.toInt())	{ items[varS].alarm = 1; tErr += tr("Bad %1 batteries present").replace("%1",tVl); }
		else items[varS].alarm = 0;
	}
	noPower = false;
	varS = "input_voltage";
	if((tP=srcPrm[varS]) != null && (tVl=tP.get()) != null) {
		tVl = tVl.toReal();
		if(tVl < 0.1*inVL)	{ items[varS].alarm = 2; tErr += tr("No powernet")+"; "; noPower = true; }
		else if(tVl < inVL)	{ items[varS].alarm = 1; tErr += tr("Input voltage low")+"; "; }
		else if(tVl > inVH)	{ items[varS].alarm = 1; tErr += tr("Input voltage high")+"; "; }
		else items[varS].alarm = 0;
	}
	varS = "input_frequency";
	if((tP=srcPrm[varS]) != null && (tVl=tP.get()) != null) {
		tVl = tVl.toReal();
		if(tVl < 0.1*inFLL)		{ items[varS].alarm = 2; if(!noPower) tErr += tr("No powernet")+"; "; }
		else if(tVl < inFLL)	{ items[varS].alarm = 2; tErr += tr("Input frequency too low")+"; "; }
		else if(tVl > inFHH)	{ items[varS].alarm = 2; tErr += tr("Input frequency too high")+"; "; }
		else if(tVl < inFL)		{ items[varS].alarm = 1; tErr += tr("Input frequency low")+"; "; }
		else if(tVl > inFH)		{ items[varS].alarm = 1; tErr += tr("Input frequency high")+"; "; }
		else items[varS].alarm = 0;
	}
	varS = "ups_load";
	if((tP=srcPrm[varS]) != null && (tVl=tP.get()) != null) {
		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]) != null && (tVl=tP.get()) != null) {
		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;
}','','',1711718569);
INSERT INTO tmplib_base VALUES('initConAssociateTrs','Initiative connections processing for the associated output transports','Опрацювання ініціативних підключень щодо асоційованих вихідних транспортів','Обработка инициативных подключений на предмет ассоциированных выходных транспортов','
The template of processing input initiative connections was created to provide the last link 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.

!!!! After implementation this control together the new common-unified output transport connection function outAt() that template became deprecated for sources adapted to use the new mechanism, but you still can use the template for not adapted sources.

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

The control of associated transport means:
  - obtaining a list of the associated transports, by the function assignTrsList() 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.2.0
License: GPLv2','Шаблон служби опрацювання вхідних ініціативних підключень створено для надання останньої ланки реалізації та підтримки концепції збору даних у пасивному режимі та ініціативного підключення, а саме — контролю асоційованих транспортів створюваних вхідним транспортом за кожного підключення.

!!!! Після реалізації цього контролю разом із новою функцією загального-уніфікованого підключення вихідного транспорту outAt() цей шаблон став застарілим для джерел адаптованих до використання нового механізму, але ви все ще можете використовувати шаблон для неадаптованих джерел.

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

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

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

Автор: Роман Савоченко <roman@oscada.org>
Спонсорування: Еліор Турабоєв, ТОВ BLUE STAR GROUP
Версія: 1.2.0
Ліцензія: 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.associateTrsList();
	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.conPrm("initConID"); //messIO("").parse(0, ":");
				if(idSeq.length > 20)	idSeq = idSeq.slice(0, 20);
				//   stop for empty-missed identification or missed PLC object
				if(!idSeq.length || idSeq.isEVal() || (PLC_O=CNTR_O[(isLogPrm?"prm_":"")+idSeq]).isEVal())	{
					oTrO.start(false);
					if(idSeq.length)
						SYS.messNote("initConAssociateTrs", 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";','','',1657998607);
INSERT INTO tmplib_base VALUES('fileServerHTTP','WebUser: HTTP File Server','WebUser: HTTP Файловий Сервер','','The template for implementing a HTTP File Server directly in OpenSCADA, which is suitable one at missing a full-featured one for functions of servicing file requests from OpenSCADA Web-interfaces.

Currently there implemented only requesting files by the GET request of HTTP. There are supported also chunks and the Partial Content requests in single range and with forcing to this mode at some configured file size.

Author: Roman Savochenko <roman@oscada.org>
Version: 1.2.0
License: GPLv2','Шаблон для реалізації Файлового Серверу HTTP безпосередньо в OpenSCADA, який корисний за відсутності повнофункціонального для функцій обслуговування файлових запитів із OpenSCADA Web-інтерфейсів.

Наразі реалізовано лише запити файлів HTTP запитом GET. Також реалізуються шматки і запити Часток Контенту за одним діапазоном зі змушуванням до цього режиму за певного налаштованого розміру файлу.

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

offPath = 0; url.parsePath(0, 0, offPath);
reqF = url.slice(offPath);
if(!(fSz=SYS.fileSize(baseD+reqF)))	{
	page = "<center><h1>The file ''%1'' not found!</h1></center>".replace("%1", reqF);
	HTTPvars["Content-Type"] = "text/html;charset=UTF-8";
	rez = "404 Not Found";
	return;
}

HTTPvars["Content-Type"] = SYS.UI.mimeGet(reqF);

fSzLim = max(10e3, fSzSolidLim);	//File''s block size for reading

//Range at request
if(HTTPvars["Range"] != null) {
	tVl = HTTPvars["Range"].parse(1, "bytes=");
	fOff = tVl.parse(0,"-").toInt();
	if((tVl=tVl.parse(1,"-")).toInt())	fSzLim = tVl.toInt() - fOff + 1;

	page = SYS.fileRead(baseD+reqF, fOff, fSzLim);
	HTTPvars["Content-Range"] = "bytes "+fOff+"-"+(fOff+page.length-1)+"/"+fSz;
	rez = "206 Partial Content";
}
//By chunks with direct writing
else if(fSz > fSzLim) {
	tr.writeTo(sender, prt.pgCreator("","200 OK",
			"Content-Type: "+HTTPvars["Content-Type"]+"\x0D\x0ATransfer-Encoding: chunked"));
	for(fOff = 0; fOff < fSz; fOff += page.length) {
		page = SYS.fileRead(baseD+reqF, fOff, fSzLim);
		tr.writeTo(sender, page.length.toString(16)+"\x0D\x0A"+page+"\x0D\x0A");
	}
	tr.writeTo(sender, "0\x0D\x0A\x0D\x0A");
	rez = "";
}
//Simple data in single package
else { page = SYS.fileRead(baseD+reqF); rez = "200 OK"; }','','',1734640136);
INSERT INTO tmplib_base VALUES('weather','Weather','Погода','','The template of acquiring weather data from different weather services in Internet and initially it is only Open Weather (https://openweathermap.org/).

The weather data divided on current and forecast with their placing in corresponded objects, where current attributes placed directly in the root and forecast days (the "day" object) and times (the "time" object) inwardly corresponded day according to the current timezone. These data acquired at specified schedule independently for current and forecast, and by default the current ones are performed per hour when forecast ones per day. The data can be accessible by user both as directly and through a specially created widget of the main library.

Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.1
License: GPLv2','Шаблон отримання даних погоди з різних погодних служб у Інтернет і первинно лише Open Weather (https://openweathermap.org/).

Погодні дані діляться на поточні і прогноз із їх розміщенням у відповідних об''єктах, де поточні атрибути розташовуються прямо у корені та прогноз на день (об''єкт "day") і час (об''єкт "time") всередині відповідного дня згідно до поточної часової зони. Ці дані отримуються за відповідним розкладом незалежно для поточних і прогнозу, та типово поточні запитуються щогодинно, а прогноз один раз на день. Дані можуть бути доступні користувачу як прямо, так і посередництвом спеціально-створеного віджету основної бібліотеки.

Автор: Роман Савоченко <roman@oscada.org>
Версія: 1.0.1
Ліцензія: GPLv2','',10,0,'JavaLikeCalc.JavaScript
if(f_start) {
	transport = "Sockets.weather:api.openweathermap.org:80";
	APPID = "ba3444a90f0c5863879bdb495edf8989";	//MeeCast ID

	schedCurTrueTm = schedFCTrueTm = 1;
	current = new Object(), forecast = new Array();
}

tErr = "";

if(!(tr=SYS.Transport.outAt(transport)) || !tr.start(true))
	tErr = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
else {
	//Requesting current day data
	if(!schedCur.length || schedCur == null || SYS.time() >= SYS.cron(schedCur,schedCurTrueTm)) {
		req = SYS.XMLNode("GET");
		req.setAttr("URI", "/data/2.5/weather?id="+city+"&mode=xml&units=metric&APPID="+APPID);
		if(!(tErr=tr.messIO(req,"HTTP")).toInt() && !(tErr=(respTree=SYS.XMLNode()).load(req.text())).toInt()) {
			schedCurTrueTm = SYS.time();
			current = new Object();
			current.city = respTree.childGet("city").attr("name");
			current.country = respTree.childGet("city").childGet("country").text();
			current.sunRise = respTree.childGet("city").childGet("sun").attr("rise");
			current.sunSet = respTree.childGet("city").childGet("sun").attr("set");
			current.tm = respTree.childGet("lastupdate").attr("value");
			current.t = respTree.childGet("temperature").attr("value");
			current.tMin = respTree.childGet("temperature").attr("min");
			current.tMax = respTree.childGet("temperature").attr("max");
			current.tFeel = respTree.childGet("feels_like").attr("value");
			current.h = respTree.childGet("humidity").attr("value");
			current.p = respTree.childGet("pressure").attr("value");
			current.windSpeed = respTree.childGet("wind").childGet("speed").attr("value");
			current.windDir = respTree.childGet("wind").childGet("direction").attr("code");
			current.windDirDeg = respTree.childGet("wind").childGet("direction").attr("value");
			current.symbol = respTree.childGet("weather").attr("icon");
			current.symbolNm = respTree.childGet("weather").attr("value");
		}
	}

	//Requesting forecast
	if(!schedFC.length || schedFC == null || SYS.time() >= SYS.cron(schedFC,schedFCTrueTm)) {
		req = SYS.XMLNode("GET");
		//Daily
		req.setAttr("URI", "/data/2.5/forecast/daily?id="+city+"&mode=xml&units=metric&cnt=10&APPID="+APPID);
		if(!tErr.toInt() && !(tErr=tr.messIO(req,"HTTP")).toInt() && !(tErr=(respTree=SYS.XMLNode()).load(req.text())).toInt()) {
			forecastN = respTree.childGet("forecast");
			forecast = new Array();
			for(iTm = 0; (timeN=forecastN.childGet("time",iTm)); iTm++) {
				forecast.push(dayO=new Object());
				dayO.tm = timeN.attr("day");
				dayO.sunRise = timeN.childGet("sun").attr("rise");
				dayO.sunSet = timeN.childGet("sun").attr("set");
				dayO.t = timeN.childGet("temperature").attr("day");
				dayO.tMin = timeN.childGet("temperature").attr("min");
				dayO.tMax = timeN.childGet("temperature").attr("max");
				dayO.tNight = timeN.childGet("temperature").attr("night");
				dayO.tEve = timeN.childGet("temperature").attr("eve");
				dayO.tMorn = timeN.childGet("temperature").attr("morn");
				dayO.tFeel = timeN.childGet("feels_like").attr("day");
				dayO.tFeelNight = timeN.childGet("feels_like").attr("night");
				dayO.tFeelEve = timeN.childGet("feels_like").attr("eve");
				dayO.tFeelMorn = timeN.childGet("feels_like").attr("morn");
				dayO.h = timeN.childGet("humidity").attr("value");
				dayO.p = timeN.childGet("pressure").attr("value");
				dayO.windSpeed = timeN.childGet("windSpeed").attr("mps");
				dayO.windDir = timeN.childGet("windDirection").attr("code");
				dayO.windDirDeg = timeN.childGet("windDirection").attr("deg");
				dayO.symbol = timeN.childGet("symbol").attr("var");
				dayO.symbolNm = timeN.childGet("symbol").attr("name");
			}

			//Per time
			req.clear(true).setAttr("URI", "/data/2.5/forecast?id="+city+"&mode=xml&units=metric&APPID="+APPID);
			if(forecast.length && !(tErr=tr.messIO(req,"HTTP")).toInt() && !(tErr=(respTree=SYS.XMLNode()).load(req.text())).toInt()) {
				forecastN = respTree.childGet("forecast");
				iDay = 0, dayO = forecast[iDay], dayO.times = forecastTimes = new Array();
				for(iTm = 0; (timeN=forecastN.childGet("time",iTm)); iTm++) {
					timeO = new Object();
					timeO.tmFrom = timeN.attr("from");
					timeO.tmTo = timeN.attr("to");
					timeO.t = timeN.childGet("temperature").attr("value");
					timeO.tMin = timeN.childGet("temperature").attr("min");
					timeO.tMax = timeN.childGet("temperature").attr("max");
					timeO.tFeel = timeN.childGet("feels_like").attr("value");
					timeO.h = timeN.childGet("humidity").attr("value");
					timeO.p = timeN.childGet("pressure").attr("value");
					timeO.windSpeed = timeN.childGet("windSpeed").attr("mps");
					timeO.windDir = timeN.childGet("windDirection").attr("code");
					timeO.windDirDeg = timeN.childGet("windDirection").attr("deg");
					timeO.symbol = timeN.childGet("symbol").attr("var");
					timeO.symbolNm = timeN.childGet("symbol").attr("name");

					while(SYS.strftime(SYS.strptimegm(timeO.tmFrom,"%Y-%m-%dT%H:%M:%S"),"%Y-%m-%d") != dayO.tm) {
							//timeO.tmFrom.indexOf(dayO.tm+"T") < 0) {
						iDay++;
						if(iDay >= forecast.length)	{ iDay = 0; break; }
						dayO = forecast[iDay], dayO.times = forecastTimes = new Array();
					}
					forecastTimes.push(timeO);
				}
				schedFCTrueTm = SYS.time();
			}
		}
	}
}

//Error set
if(!tErr.length)	tErr = "0";
if(tErr.toInt()) {
	if(tr != null && tr.start()) tr.start(false);
	if(f_err != tErr)
		SYS.messDebug("/WEATH/"+this.cfg("SHIFR"), tr("Error")+": "+tErr);
}
else {
	tVl = "";
	if(schedCur.length && schedCur != null)
		tVl += tr("Scheduled currents call")+" "+SYS.strftime(SYS.cron(schedCur,schedCurTrueTm))+". ";
	if(schedFC.length && schedFC != null)
		tVl += tr("Scheduled forecast call")+" "+SYS.strftime(SYS.cron(schedFC,schedFCTrueTm))+". ";
	if(tVl.length)	tErr += ": "+tVl;
}
f_err = tErr;','','',1714665270);
INSERT INTO tmplib_base VALUES('DiskSMART','Disk SMART','Дисковий SMART','','Data combination of the disk SMART in a complex object of the attribute "All items". The template designed to use together with the DAQ-module "System" in the part "Disk SMART" and to represent this data by the frame "Object properties" as a complex object with properties and signalling at common errors. Some SMART attributes can be unknown or wrong for specified disk due to their missing in the disks DB, especially for modern SSD, so the template provides a feature to redefine names the attributes.

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

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

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();
			if((tVl=defs.match("^"+aId+":([^\n]+)","m")).length)
				itW.descr = tVl[1];

			// 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;
		}
		itW = items[aId];
		tvl = aNd.get();
		if(aId == 5) {	//Realocated sectors count
			if(tvl > 100)	{ itW.alarm = 2; tErr += tr("Too many realocated sectors")+"; "; }
			else if(tvl)	{ itW.alarm = 1; tErr += tr("Some reallocated sectors are presented")+"; "; }
			else itW.alarm = 0;
		}
		else if(aId == 190) {	//Airflow temperature
			if(tvl > 255)	tvl = tvl&0xFF;
			if(tvl > 70)		{ itW.alarm = 2; tErr += tr("Hard disk is overheated")+"; "; }
			else if(tvl > 50) { itW.alarm = 1; tErr += tr("Temperature high")+"; "; }
			else itW.alarm = 0;
		}
		else if(aId == 194) {	//Temperature
			if(tvl > 255)	{ tvl = tvl&0xFF; itW.val = tvl; }
			if(tvl > 70)		{ itW.alarm = 2; tErr += tr("Hard disk is overheated")+"; "; }
			else if(tvl > 50) { itW.alarm = 1; tErr += tr("Temperature high")+"; "; }
			else itW.alarm = 0;
		}
		else if(aId == 197) {	//Current pending sectors
			if(tvl > 10) { itW.alarm = 2; tErr += tr("Too many pending sectors")+"; "; }
			else if(tvl) { itW.alarm = 1; tErr += tr("Some pending sectors are presented")+"; "; }
			else itW.alarm = 0;
		}
		itW.val = tvl;
	}

	//Alarms process and mark
	//SYS.messInfo("HDDSMART", "TEST 00");
	/*varS = 5;	//Realocated sectors count
	if(!(tP=srcPrm[varS]).isEVal()) {
		if(tP.get() > 10) { items[varS].alarm = 2; tErr += tr("Too many realocated sectors")+"; "; }
		else if(tP.get()) { items[varS].alarm = 1; tErr += tr("A realocated sectors present")+"; "; }
		else items[varS].alarm = 0;
	}
	varS = 190;	//Airflow temperature
	if(!(tP=srcPrm[varS]).isEVal()) {
		tvl = tP.get();
		if(tvl > 255) { tvl = tvl&0xFF; items[varS].val = tvl; }
		if(tvl > 70) { items[varS].alarm = 2; tErr += tr("Hard disk overheated")+"; "; }
		else if(tvl > 50) { items[varS].alarm = 1; tErr += tr("Temperature high")+"; "; }
		else items[varS].alarm = 0;
	}
	varS = 194;	//Temperature
	if(!(tP=srcPrm[varS]).isEVal()) {
		tvl = tP.get();
		if(tvl > 255) { tvl = tvl&0xFF; items[varS].val = tvl; }
		if(tvl > 70) { items[varS].alarm = 2; tErr += tr("Hard disk overheated")+"; "; }
		else if(tvl > 50) { items[varS].alarm = 1; tErr += tr("Temperature high")+"; "; }
		else items[varS].alarm = 0;
	}
	varS = 197;	//Current pending sectors
	if(!(tP=srcPrm[varS]).isEVal()) {
		if(tP.get() > 10) { items[varS].alarm = 2; tErr += tr("Too many pending sectors")+"; "; }
		else if(tP.get()) { items[varS].alarm = 1; tErr += tr("A pending sectors 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(tErr.toInt() && tErr.toInt() != f_err.toInt())
	this.cntr().alarmSet((NAME.length?NAME:SHIFR)+": "+DESCR+": "+tErr.parse(1,":"), -(2+alLev), SHIFR);
else if(f_err.toInt() && !tErr.toInt())
	this.cntr().alarmSet((NAME.length?NAME:SHIFR)+": "+DESCR+": "+tr("NORMA"), 1, SHIFR);
f_err = tErr;','','',1714665209);
INSERT INTO tmplib_base VALUES('initRemCntr','Control the remote initiative connections','Контроль віддалених ініціативних підключень','','The template of processing input initiative connections expands early one template initiative connections processing for the associated output transports by complete representing data of the remote OpenSCADA hosts on some aggregation server and with a possibility of control the remote hosts through the server, that is finishing the conception of data acquisition in the passive mode and the initiative connection.

The template does in whole:
- detecting a new connection from remote host, which has no ID;
- sending an acquaintance direct request to the established connection for getting KEY of the host, when it was already connected early, and for getting information about the host for creating ID of the host;
- setting ID for the connection;
- creating a record in the OpenSCADA hosts table for the connection and sending all followed requests already through the host;
- creating on the remote host a representative Logical Controller;
- creating on the remote host a representative DAQ.System Controller Object in the automatic mode, at missing any other there;
- processing the present DAQ.System Controller Objects to link the Logical Level parameters to them and creating some processing logical parameters;
- writing the generated key by a line "SrcKey: {KEY}" to description of the Logical Controller as a sign of finalising the initialisation;
- creating a Controller Object in DAQ.DAQGate with connection to the remote Logical Controller.

ID of the remote host is formed in the view "{HostDomain}{StatName}{RandKey}" and with limiting to 20 symbols, where:
- {HostDomain} — network host name-domain;
- {StatName} — station name of the OpenSCADA project;
- {RandKey} — random key in sixth digits.

Data model of the representative Logical Controller can contain both the generated links with processing parameters, and custom parameters, when the user is decided to publish some extra data. The generated items from DAQ.System are represents sources currently:
- "CPU", "MEM", "sensors", "Power", "uptime" => links in the "System (SYS)" container parameter;
- "fs", "hddstat", "hddsmart" => links in the "Disk (DISK)" container parameter with a processing one for "hddsmart" by the template base.DiskSMART;
- "netstat" => links in the "Network (NET)" container parameter;
- "ups" => links in the root with a processing one by the template base.UPS.

The aggregated data in DAQ.GAQGate can be lifted to up, or used in derivative DAQ objects, or whether directly on custom frames, or in future on a Dynamic Frame, which scheduled to implement; and without any additional action you will get notification about violations on the hosts.

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

Шаблон загалом здійснює:
- виявлення нового підключення від віддаленого хосту, яке не має ІД;
- надсилання ознайомчого прямого запиту за встановленим підключенням для отримання КЛЮЧА хосту, якщо це підключення вже раніше було, і для отримання інформації щодо хосту для створення ІД хосту;
- встановлення ІД для підключення;
- створення запису у таблиці хостів OpenSCADA для підключення і надсилання подальших запитів вже через хост;
- створення на віддаленому хості представницького Логічного Контролеру;
- створення на віддаленому хості представницького Об''єкту Контролеру DAQ.System у автоматичному режимі, за відсутності інших там;
- опрацювання наявних Об''єктів Контролеру DAQ.System щодо посилань параметрів Логічного Рівня на них і створення деяких логічних параметрів обробки;
- запис згенерованого ключа рядком "SrcKey: {ID}" до опису Логічного Контролеру у якості ознаки завершення ініціалізації;
- створення Об''єкту Контролеру у DAQ.DAQGate із підключенням до віддаленого Логічного Контролеру.

ІД віддаленого хосту формується у вигляді "{HostDomain}{StatName}{RandKey}" і з обмеженням у 20ть символів, де:
- {HostDomain} — мережеве ім''я-домен хосту;
- {StatName} — ім''я станції проєкту OpenSCADA;
- {RandKey} — випадковий ключ у шість цифр.

Модель даних представницького Логічного Контролеру може містити як згенеровані посилання із параметрами обробки, так і користувацькі параметри, якщо користувач вирішить опублікцівати додаткові дані. Згенеровані із DAQ.System елементи наразі представляють джерела:
- "CPU", "MEM", "sensors", "Power", "uptime" => посилання у контейнері параметрів "Система (SYS)";
- "fs", "hddstat", "hddsmart" => посилання у контейнері параметрів "Диск (DISK)" із однією обробкою для "hddsmart" за шаблоном base.DiskSMART;
- "netstat" => посилання у контейнері параметрів "Мережа (NET)";
- "ups" => посилання у корені із однією обробкою за шаблоном base.UPS.

Агреговані дані у DAQ.GAQGate можуть бути підняті на гору, або використані у похідних DAQ об''єктах, або прямо на користувацьких кадрах, або у майбутньому на Динамічному Кадрі, який заплановано до реалізації; та без жодних додаткових дій ви отримаєте сповіщення про порушення на хостах.

Автор: Роман Савоченко <roman@oscada.org>
Версія: 1.1.0
Ліцензія: GPLv2','',10,0,'JavaLikeCalc.JavaScript
function prmAdd(req, path, pId, pNm, pTp, pSrc, pLogLnk) {
	req.childAdd("add").setAttr("path",path+"/%2fbr%2fprm_").setAttr("id",pId).setText(pNm);
	req.childAdd("set").setAttr("path",path+"/prm_"+pId+"/%2fprm%2fst%2ftype").setText(pTp);
	req.childAdd("set").setAttr("path",path+"/prm_"+pId+"/%2fprm%2fcfg%2fEN").setText(1);
	if(pTp == "Prm" && pSrc != null)
		req.childAdd("set").setAttr("path",path+"/prm_"+pId+"/%2fprm%2fcfg%2fPRM").setText(pSrc);
	if(pTp == "PrmRefl" && pSrc != null)
		req.childAdd("set").setAttr("path",path+"/prm_"+pId+"/%2fprm%2fcfg%2fPSRC").setText(pSrc);
	req.childAdd("set").setAttr("path",path+"/prm_"+pId+"/%2fprm%2fst%2fen").setText(1);
	if(pTp == "Prm" && pLogLnk != null)
		req.childAdd("set").setAttr("path",path+"/prm_"+pId+"/%2fcfg%2fprm%2fel_0").setText(pLogLnk);
}

if(f_start) {
	inTransport_ = "", inTr = null;
	prcTr = "";
	prcCon = new Object();
	noAccess = new Object();
	return;	//Do no work on the start time
}

//Checking the transport change and connect
tErr = "";
if(inTr == null || inTransport != inTransport_) {
	inTransport_ = inTransport;
	inTr = SYS.Transport[inTransport.parse(0)]["in_"+inTransport.parse(1)];
}

if(inTr == null)	tErr = "1:"+tr("Input transport ''%1'' error.").replace("%1",inTransport);
else {
	//Processing the input connections
	outTrs = inTr.associateTrsList();
	for(iTr = 0; iTr < outTrs.length; iTr++) {
		oTrNm = outTrs[iTr];
		if((oTrO=SYS.Transport[inTransport.parse(0)]["out_"+oTrNm]) == null || !oTrO.start() || oTrO.conPrm("initConID").length)
			continue;

		//Getting the host address
		hostAddr = (hostAddr=oTrO.status().match(" ''(.+)''\\.")).length ? hostAddr[1] : "";
		
		//Getting the station name and the DataSource key
		req = SYS.XMLNode("CntrReqs").setAttr("rqDir","1").setAttr("rqUser",cntrUser).setAttr("rqPass",cntrPass).setAttr("conTm",conTm)
				.setAttr("path","/");
		req.childAdd("get").setAttr("path","/%2fgen%2fstat");
		req.childAdd("get").setAttr("path","/DAQ/LogicLev/"+cntrObj+"/%2fcntr%2fcfg%2fDESCR");
		req.childAdd("get").setAttr("path","/%2fgen%2fenv%2fhost");
		if((rez=oTrO.messIO(req,"SelfSystem")).toInt()) {
			SYS.messNote("/InitRem/"+this.cfg("SHIFR"), (tVl=tr("Error requesting the remote host ''%1'': %2")
									.replace("%1",hostAddr).replace("%2",rez)));
			//tErr += (tErr.length?"":"10:") + tVl;
		}
		else {
			stNm = req.childGet(2).text()+"-"+req.childGet(0).text();
			if(noAccess[stNm] != null && accessTm >= 0 && (SYS.time()-noAccess[stNm]) > accessTm*60*60) {
				delete noAccess[stNm];
				delete prcCon[stNm];
			}
		}
		if(rez.toInt() || noAccess[stNm] != null) { oTrO.start(false); continue; }

		stSrcKey = rand(999).toString();
		isSrcInited = isHostReqPresent = false;
		if(!req.childGet(1).attr("rez").toInt() && (tVl=req.childGet(1).text().match("^SrcKey: *([^\\n]+)$","m")).length)
			isSrcInited = true, stSrcKey = tVl[1];

		stSrcId = req.childGet(2).text() + req.childGet(0).text() + stSrcKey;
		for(stLen = req.childGet(2).text().length, hostLen = req.childGet(0).text().length; stSrcId.length > 20; ) {
			if(stLen > hostLen)	stLen--; else hostLen--;
			stSrcId = req.childGet(2).text().slice(0,stLen) + req.childGet(0).text().slice(0,hostLen) + stSrcKey;
		}
		stSrcId = oTrO.conPrm("initConID", SYS.strEncode(stSrcId,"OscdID"));
		prcCon[stSrcId] = stSrcId+": "+oTrNm+", "+hostAddr+", "+SYS.strftime(SYS.time());

		//Record to the table of the OpenSCADA remote hosts
		// Checking presence
		req = SYS.XMLNode("get").setAttr("path","/Transport/%2fsub%2fehost");
		SYS.cntrReq(req);
		for(iH = 0; iH < req.childGet(0).childSize(); iH++)
			if(req.childGet(0).childGet(iH).text() == stSrcId)
			{ isHostReqPresent = true; break; }

		// Appending/updating
		if(!rez.toInt() && !isHostReqPresent) {
			req = SYS.XMLNode("CntrReqs").setAttr("path","/Transport");
			req.childAdd("add").setAttr("path","/%2fsub%2fehost");
			req.childAdd("set").setAttr("path","/%2fsub%2fehost").setAttr("key_id","newHost").setAttr("col","id").setText(stSrcId);
			req.childAdd("set").setAttr("path","/%2fsub%2fehost").setAttr("key_id",stSrcId).setAttr("col","name").setText(stNm+" ("+stSrcKey+")");
			req.childAdd("set").setAttr("path","/%2fsub%2fehost").setAttr("key_id",stSrcId).setAttr("col","transp").setText(inTransport.parse(0)+".in_"+inTransport.parse(1));
			req.childAdd("set").setAttr("path","/%2fsub%2fehost").setAttr("key_id",stSrcId).setAttr("col","addr").setText(stSrcId);
			req.childAdd("set").setAttr("path","/%2fsub%2fehost").setAttr("key_id",stSrcId).setAttr("col","user").setText(cntrUser);
			req.childAdd("set").setAttr("path","/%2fsub%2fehost").setAttr("key_id",stSrcId).setAttr("col","pass").setText(cntrPass);
			req.childAdd("set").setAttr("path","/%2fsub%2fehost").setAttr("key_id",stSrcId).setAttr("col","mode").setText(2);
			req.childAdd("save").setAttr("path","/%2fobj");
			SYS.cntrReq(req);
		}

		//Initialisation
		if(!rez.toInt() && !isSrcInited) {
			// Creating the main control object of the Data Sources
			req = SYS.XMLNode("CntrReqs").setAttr("path","/DAQ/LogicLev");
			req.childAdd("add").setAttr("path","/%2fbr%2fcntr_").setAttr("id",cntrObj).setText("Sources for remote aggregator");
			req.childAdd("set").setAttr("path","/"+cntrObj+"/%2fcntr%2fst%2frunSt").setText(1);
			req.childAdd("set").setAttr("path","/"+cntrObj+"/%2fcntr%2fcfg%2fSTART").setText(1);
			req.childAdd("save").setAttr("path","/"+cntrObj+"/%2fobj");
			rez = SYS.cntrReq(req, stSrcId);

			//No access
			if(!rez.toInt() && req.childGet(0).attr("rez").toInt()) {
				req = SYS.XMLNode("CntrReqs").setAttr("path","/Transport");
				req.childAdd("del").setAttr("path","/%2fsub%2fehost").setAttr("key_id",stSrcId);
				req.childAdd("save").setAttr("path","/%2fobj");
				SYS.cntrReq(req);

				noAccess[stNm] = SYS.time();
				delete prcCon[stSrcId];
				prcCon[stNm] = stSrcId+": "+oTrNm+", "+hostAddr+", "+SYS.strftime(SYS.time())+" - NO ACCESS";

				SYS.messNote("/InitRem/"+this.cfg("SHIFR"), (tVl=tr("Error accessing the remote station ''%1'': %2. ")
											.replace("%1",stNm).replace("%2",req.childGet(0).text())));

				oTrO.start(false);
				continue;
			}

			//Primary sources in DAQ.System
			// Getting allowed ones
			reqSys = SYS.XMLNode("get").setAttr("path","/DAQ/System/%2fbr%2fcntr_");
			rez = SYS.cntrReq(reqSys, stSrcId);

			// Creation one at missing any one
			if(!rez.toInt() && !reqSys.childSize()) {
				req = SYS.XMLNode("CntrReqs").setAttr("path","/DAQ/System");
				req.childAdd("add").setAttr("path","/%2fbr%2fcntr_").setAttr("id",cntrObj).setText("Remote sources");
				req.childAdd("set").setAttr("path","/"+cntrObj+"/%2fcntr%2fcfg%2fAUTO_FILL").setText(3);
				req.childAdd("set").setAttr("path","/"+cntrObj+"/%2fcntr%2fst%2frunSt").setText(1);
				req.childAdd("set").setAttr("path","/"+cntrObj+"/%2fcntr%2fcfg%2fSTART").setText(1);
				req.childAdd("save").setAttr("path","/"+cntrObj+"/%2fobj");
				rez = SYS.cntrReq(req, stSrcId);
				reqSys.childAdd("el").setAttr("id",cntrObj).setText("Remote sources");
			}

			// Reading lists of the parameters of the System sources with reading parameters of the sources
			// and creation their representing on the Logical Level
			reqLogic = SYS.XMLNode("CntrReqs").setAttr("path","/DAQ/LogicLev/"+cntrObj);
			prmAdd(reqLogic, "/", "SYS", "System", "Prm");
			prmAdd(reqLogic, "/", "DISK", "Disk", "Prm");
			prmAdd(reqLogic, "/", "NET", "Network", "Prm");

			for(iEl = 0; !rez.toInt() && iEl < reqSys.childSize(); iEl++) {
				cntrId = reqSys.childGet(iEl).attr("id");
				reqSysPrms = SYS.XMLNode("get").setAttr("path","/DAQ/System/"+cntrId+"/%2fbr%2fprm_");
				rez = SYS.cntrReq(reqSysPrms, stSrcId);

				for(iEl2 = 0; !rez.toInt() && iEl2 < reqSysPrms.childSize(); iEl2++) {
					pId = reqSysPrms.childGet(iEl2).attr("id");
					req = SYS.XMLNode("get").setAttr("path","/DAQ/System/"+cntrId+"/prm_"+pId+"/%2fprm%2fcfg%2fTYPE");
					rez = SYS.cntrReq(req, stSrcId);

					//  Completely reflected sources
					if((tVl=req.text()) == "CPU" || tVl == "MEM" || tVl == "sensors" || tVl == "Power" || tVl == "uptime")
						prmAdd(reqLogic, "/prm_SYS", pId, reqSysPrms.childGet(iEl2).text(), "PrmRefl", "System."+cntrId+"."+pId);
					else if(tVl == "fs" || tVl == "hddstat" || tVl == "hddsmart") {
						prmAdd(reqLogic, "/prm_DISK", pId, reqSysPrms.childGet(iEl2).text(), "PrmRefl", "System."+cntrId+"."+pId);
						if(tVl == "hddsmart")
							prmAdd(reqLogic, "/prm_DISK", "log_"+pId, reqSysPrms.childGet(iEl2).text()+" - processing", "Prm", "base.DiskSMART", "System."+cntrId+"."+pId);
					}
					else if(tVl == "netstat")
						prmAdd(reqLogic, "/prm_NET", pId, reqSysPrms.childGet(iEl2).text(), "PrmRefl", "System."+cntrId+"."+pId);
					else if(tVl == "ups") {
						prmAdd(reqLogic, "/", pId, reqSysPrms.childGet(iEl2).text(), "PrmRefl", "System."+cntrId+"."+pId);
						prmAdd(reqLogic, "/", "log_"+pId, reqSysPrms.childGet(iEl2).text()+" - processing", "Prm", "base.UPS", "System."+cntrId+"."+pId);
					}
					else SYS.messNote("/InitRem/"+this.cfg("SHIFR"), "Implement the type ''"+tVl+"''");
				}
			}

			//Mark the Logical Level controller as finished in the initialisation and save
			reqLogic.childAdd("set").setAttr("path","/%2fcntr%2fcfg%2fDESCR").setText("SrcKey: "+stSrcKey+"\n");
			reqLogic.childAdd("save").setAttr("path","/%2fobj");
			rez = SYS.cntrReq(reqLogic, stSrcId);
			delete reqLogic;
			delete reqSysPrms;
			delete reqSys;
		}

		//Creation the DAQGate object
		if(!rez.toInt() && SYS.DAQ.DAQGate[stSrcId] == null) {
			//Creation the DAQGate object in the end and no error
			req = SYS.XMLNode("CntrReqs").setAttr("path","/DAQ/DAQGate");
			req.childAdd("add").setAttr("path","/%2fbr%2fcntr_").setAttr("id",stSrcId).setText(stNm+" ("+stSrcKey+")");
			req.childAdd("set").setAttr("path","/"+stSrcId+"/%2fcntr%2fcfg%2fSTATIONS").setText(stSrcId);
			req.childAdd("set").setAttr("path","/"+stSrcId+"/%2fcntr%2fcfg%2fCNTRPRM").setText("LogicLev."+cntrObj);
			req.childAdd("set").setAttr("path","/"+stSrcId+"/%2fcntr%2fcfg%2fSTART").setText(1);
			req.childAdd("set").setAttr("path","/"+stSrcId+"/%2fcntr%2fst%2frunSt").setText(1);
			req.childAdd("save").setAttr("path","/"+stSrcId+"/%2fobj");
			SYS.cntrReq(req);
		}

		delete req;
	}
	delete oTrO;

	//Checking live of the established connections
	if(accessTm > 0) {
		daqs = SYS.DAQ.DAQGate.nodeList("cntr_");
		for(iEl = 0; iEl < daqs.length; iEl++)
			if((daqO=SYS.DAQ.DAQGate[daqs[iEl]]).cfg("CNTRPRM") == "LogicLev.RemCntr" &&
					daqO.status().toInt() && prcCon[(daqID=daqs[iEl].slice(5))] != null &&
					(tVl=daqO.status().match("([0-9]{2}-[0-9]{2}-[0-9]{4} [0-9]{2}:[0-9]{2}:[0-9]{2})")).length &&
					(tVl=SYS.strptime(tVl[1],"%d-%m-%Y %H:%M:%S")) > 24*60*60 && (SYS.time()-tVl) > accessTm*60*60)
			{
				delete daqO;
				delete prcCon[daqID];

				req = SYS.XMLNode("CntrReqs").setAttr("path","/");
				req.childAdd("del").setAttr("path","/DAQ/DAQGate/%2fbr%2fcntr_").setAttr("id",daqID);
				req.childAdd("del").setAttr("path","/Transport/%2fsub%2fehost").setAttr("key_id",daqID);
				SYS.cntrReq(req);
			}
		delete daqO;
	}
}

//Updating the processed list
prcTr = "";
for(var iEl in prcCon)
	prcTr += (prcTr.length?"\n":"") + prcCon[iEl];

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Author: Roman Savochenko
Version: 1.2.2
','','',1,10,0,'// Testing the string parsing functions
// Testing of the string parsing at a separator
/*str = "1;;2;;3;;4;5;;6;";
for(off = 0; (sIt=str.parse(0,";",off,true)).length || off < str.length; )
	SYS.messInfo("TEST string", "Item ("+off+") = "+sIt);
SYS.messInfo("TEST string (direct)", "Item "+str.parse(4,";",0,true));
SYS.messInfo("TEST string (direct last)", "Item "+"1;2".parse(1,";"));
SYS.messInfo("TEST string (direct after last)", "Item "+"1;2".parse(2,";"));
SYS.messInfo("TEST string (direct last closed)", "Item "+"1;2;".parse(1,";"));
SYS.messInfo("TEST string (direct after last closed)", "Item "+"1;2;".parse(2,";"));*/

// Testing of the string parsing at a separator from the end
/*str = "1;;2;;3;;4;5;;6;";
for(off = str.length; (sIt=str.parseEnd(0,";",off,true)).length || off >= 0; )
	SYS.messInfo("TEST string", "Item ("+off+") = "+sIt);
SYS.messInfo("TEST string (direct)", "Item "+str.parseEnd(4,";",str.length,true));
SYS.messInfo("TEST string (direct first)", "Item "+"1;2".parseEnd(1,";"));
SYS.messInfo("TEST string (direct before first)", "Item "+"1;2".parseEnd(2,";"));
SYS.messInfo("TEST string (direct first closed)", "Item "+";1;2".parseEnd(1,";"));
SYS.messInfo("TEST string (direct before first closed)", "Item "+";1;2".parseEnd(2,";"));*/

// Testing of the string parsing as the path
/*str = "/1//2//3//4/5/6/";
for(off = 0; (sIt=str.parsePath(0,0,off)).length; )
	SYS.messInfo("TEST string", "Item ("+off+") = "+sIt);
SYS.messInfo("TEST string (direct)", "Item "+str.parsePath(4));
SYS.messInfo("TEST string (direct last)", "Item "+"1/2".parsePath(1));
SYS.messInfo("TEST string (direct after last)", "Item "+"1/2".parsePath(2));
SYS.messInfo("TEST string (direct last closed)", "Item "+"1/2/".parsePath(1));
SYS.messInfo("TEST string (direct after last closed)", "Item "+"1/2/".parsePath(2));*/

// Testing of the string parsing as the path from the end
/*str = "/1//2//3//4/5/6/";
for(off = str.length; (sIt=str.parsePathEnd(0,off)).length || off >= 0; )
	SYS.messInfo("TEST string", "Item ("+off+") = "+sIt);
SYS.messInfo("TEST string (direct)", "Item "+str.parsePathEnd(4));
SYS.messInfo("TEST string (direct first)", "Item "+"1/2".parsePathEnd(1));
SYS.messInfo("TEST string (direct before first)", "Item "+"1/2".parsePathEnd(2));
SYS.messInfo("TEST string (direct first closed)", "Item "+"/1/2".parsePathEnd(1));
SYS.messInfo("TEST string (direct before first closed)", "Item "+"/1/2".parsePathEnd(2));*/

// Testing of the string-text parsing as the lines
/*str = "1\n2\n\n3\x0A4\x0D5\x0D\x0A6";
for(off = 0; (sIt=str.parseLine(0,off)).length || off < str.length; )
	SYS.messInfo("TEST string", "Item ("+off+") = "+sIt);
SYS.messInfo("TEST string (direct)", "Item "+str.parseLine(4));*/


/*using Special.FLibSYS;
out+=10;
if(out>100) out=0;

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


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

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

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

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

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

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

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

	return val;
}

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        buf_beg_u++;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

if(!isError)	pagesCur = "", res = "0: Fetched and processed pages="+pCnt+"; images="+imgCnt+"; links="+lnkCnt+"; languages="+lngCnt;','','',1662906388);
CREATE TABLE IF NOT EXISTS 'flb_regEl' ("ID" TEXT DEFAULT '' ,"NAME" TEXT DEFAULT '' ,"uk#NAME" TEXT DEFAULT '' ,"ru#NAME" TEXT DEFAULT '' ,"DESCR" TEXT DEFAULT '' ,"uk#DESCR" TEXT DEFAULT '' ,"ru#DESCR" TEXT DEFAULT '' ,"START" INTEGER DEFAULT '1' ,"MAXCALCTM" INTEGER DEFAULT '10' ,"PR_TR" INTEGER DEFAULT '0' ,"FORMULA" TEXT DEFAULT '' ,"uk#FORMULA" TEXT DEFAULT '' ,"ru#FORMULA" TEXT DEFAULT '' ,"TIMESTAMP" INTEGER DEFAULT '' , PRIMARY KEY ("ID"));
INSERT INTO flb_regEl VALUES('pidUnif','PID unified','ПІД уніфікований','ПИД унифицированный','Composite-unified analog and pulse PID. At the heart of the regulator is core a standard analog PID controller from the library FLibComplex1 and the implementation of the PWM for the pulse part.

Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.0
License: GPLv2','Суміщений-уніфікований аналоговий та імпульсний ПІД-регулятор. У основі регулятора лежить ядро стандартного аналогового ПІД-регулятора з бібліотеки "FLibComplex1" та реалізація ШІМ для імпульсної частини.

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

Автор: Роман Савоченко <roman@oscada.org>
Версия: 1.0.0
Лицензия: GPLv2',1,10,0,'//Call standard analog PID
outA = Special.FLibComplex1.pid(var,sp,max,min,manIn,auto,casc,Kp,Ti,Kd,Td,Tzd,Hup,Hdwn,Zi,followSp,K1,in1,K2,in2,K3,in3,K4,in4,f_frq,int,dif,lag);
if(analog) return outA;

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

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

if(!auto) manIn = 50;

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

if(impAnImit) {
	if(impQup)	out += 100/(impAnOut*f_frq);
	if(impQdwn)	out -= 100/(impAnOut*f_frq);
	return max(0,min(100,out));
}
return impAnOut;','','',1584883408);
INSERT INTO flb_regEl VALUES('pidUnifD','PID dynamic','ПІД динамічний','ПИД динамический','Completely identical to the unified PID-regulator, but it is implemented dynamically on JavaLikeCalc. The dynamic implementation allows you to easily adapt the regulator to the desired requirements, simply editing it.

Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.0
License: GPLv2','Цілковито ідентичний уніфікованому ПІД-регулятору, але реалізований динамічно на JavaLikeCalc. Динамічна реалізація дозволяє легко адаптувати регулятор під потрібні вимоги, просто відредагувавши його.

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

Автор: Роман Савоченко <roman@oscada.org>
Версия: 1.0.0
Лицензия: GPLv2',1,10,0,'if(f_start) {
	auto_ = auto;
	outA = out;
	impQupTm_ = impQdwnTm_ = 0;
	impQup_ = impQdwn_ = 0;
}

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

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

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

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

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

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

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

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

if(analog) return outA;

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

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

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

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

if(impAnImit > 0) {
	if(impQup)	out += 100/(impAnImit*f_frq);
	if(impQdwn)	out -= 100/(impAnImit*f_frq);
	return max(0, min(100,out));
}
return max(0, min(100,impAnOut));','','',1584883408);
INSERT INTO flb_regEl VALUES('pidImp','PID pulse','ПІД імпульсний','ПИД импульсный','Specialized pulse PID regulator is implemented on a special algorithm with compensation of double integration.

Author: Roman Savochenko <roman@oscada.org>
Version: 1.0.0
License: GPLv2','Спеціалізований імпульсний ПІД-регулятор реалізований за особливим алгоритмом із компенсацією подвійного інтегрування.

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

Автор: Роман Савоченко <roman@oscada.org>
Версия: 1.0.0
Лицензия: GPLv2',1,10,0,'//Call standard analog PID
Kzd = min(1e3/(f_frq*Tzd), 1);
Kint = min(1e3/(f_frq*Ti), 1);
Kdif = min(1e3/(f_frq*Td), 1);

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

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

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

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

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

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

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

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

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

if(!auto) manIn = 50;

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

if(impQup || impQdwn) integ = 0;

if(impAnImit) {
	if(impQup) out += 100/(impAnOut*f_frq);
	if(impQdwn) out -= 100/(impAnOut*f_frq);
	return max(0, min(100,out));
}
return impAnOut;','','',1584883408);
CREATE TABLE IF NOT EXISTS 'flb_doc' ("ID" TEXT DEFAULT '' ,"NAME" TEXT DEFAULT '' ,"uk#NAME" TEXT DEFAULT '' ,"ru#NAME" TEXT DEFAULT '' ,"DESCR" TEXT DEFAULT '' ,"uk#DESCR" TEXT DEFAULT '' ,"ru#DESCR" TEXT DEFAULT '' ,"START" INTEGER DEFAULT '1' ,"MAXCALCTM" INTEGER DEFAULT '10' ,"PR_TR" INTEGER DEFAULT '0' ,"FORMULA" TEXT DEFAULT '' ,"uk#FORMULA" TEXT DEFAULT '' ,"ru#FORMULA" TEXT DEFAULT '' ,"TIMESTAMP" INTEGER DEFAULT '0' , PRIMARY KEY ("ID"));
INSERT INTO flb_doc VALUES('getVal','Getting value from the value archive','Отримання значення з архіву значень','Получение значения из архива значений','Query the value for the specified time (time:uTime) from the assigned archiver archtor (FSArch.1m) and issuing results with the specified number of digits prec after the dot.

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

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

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

Author: Roman Savochenko <roman@oscada.org>
Version: 1.1.2
License: GPLv2','Запит значення за визначений час (time:uTime) із пов''язаного архіватору archtor (FSArch.1m) та формування результату із визначеною кількістю знаків prec після коми.

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

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

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

Автор: Роман Савоченко <roman@oscada.org>
Версія: 1.1.2
Ліцензія: GPLv2','Запрос значения за определённое время (time:uTime) из связанного архиватора archtor (FSArch.1m) и формирование результата с определённым количеством знаков prec после запятой.

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

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

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

Автор: Роман Савоченко <roman@oscada.org>
Версия: 1.1.2
Лицензия: GPLv2',1,10,0,'srcTime = time*1e6 + uTime;
aO = SYS.nodeAt(addr).arch();
if(aO) {
	if(!tryTo || tryTo == srcTime)	val = aO.getVal(srcTime, false, archtor);
	else {
		if((aPer=aO.period(archtor)) == 0)	val = EVAL;
		else if(srcTime < tryTo)
			for(srcTime1 = srcTime; srcTime < tryTo && (val=aO.getVal(srcTime1,false,archtor)).isEVal(); )
			{ srcTime += aPer; srcTime1 = srcTime; }
		else
			for(srcTime1 = srcTime; srcTime > tryTo && (val=aO.getVal(srcTime1,false,archtor)).isEVal(); )
			{ srcTime -= aPer; srcTime1 = srcTime; }
	}
	if(strong && (srcTime*1e-6) != time)	val = EVAL;
} else val = EVAL;
if(prec < 0)	return val;
if(val.isEVal())	return tr("Empty");
if(typeof(val) == "real")	return val.toFixed(prec);
return val;','','',1579609648);
INSERT INTO flb_doc VALUES('sumVal','Sum values of the value archive','Підсумок значень архіву значень','Сумма значений архива значений','Calculate the summary value over a specified period of the time [bTime...eTime], for the archiver archtor (FSArch.1m) data and issuing results with the specified number of digits prec after the dot.

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

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

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

Author: Roman Savochenko <roman@oscada.org>
Version: 1.1.1
License: GPLv2','Обчислення суми значень за вказаний період часу [bTime...eTime], для даних архіватору archtor (FSArch.1m) та формування результату із визначеною кількістю знаків prec після коми.

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

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

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

Автор: Роман Савоченко <roman@oscada.org>
Версія: 1.1.1
Ліцензія: GPLv2','Вычисление суммы значений за указанный период времени [bTime...eTime], для данных архиватора archtor (FSArch.1m) и формирование результата с определённым количеством знаков prec после запятой.

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

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

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

Автор: Роман Савоченко <roman@oscada.org>
Версия: 1.1.1
Лицензия: GPLv2',1,10,0,'val = 0;
aO = SYS.nodeAt(addr).arch();
if(aO) {
	bTime = bTime*1e6; eTime = eTime*1e6;
	buf_beg = max(aO.begin(archtor), bTime); buf_end = min(aO.end(archtor), eTime);
	while(true) {
		r_val = aO.getVal(buf_beg, true, archtor);
		if(buf_beg > buf_end)	break;
		if(!r_val.isEVal())	val += r_val;
		if(buf_beg == 0 || buf_beg >= buf_end) break;
		buf_beg++;
	}
}
if(prec < 0)	return val;
if(val.isEVal())	return tr("Empty");
if(typeof(val) == "real")	return val.toFixed(prec);
return val;','','',1579609896);
INSERT INTO flb_doc VALUES('averVal','Average value of the value archive','Середнє значення архіву значень','Среднее значение архива значений','Calculate the average value over a specified period of the time [bTime...eTime], for the archiver archtor (FSArch.1m) data and issuing results with the specified number of digits prec after the dot.

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

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

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

Author: Roman Savochenko <roman@oscada.org>
Version: 1.1.1
License: GPLv2','Обчислення середнього значення за вказаний період часу [bTime...eTime], для даних архіватору archtor (FSArch.1m) та формування результату із визначеною кількістю знаків prec після коми.

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

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

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

Автор: Роман Савоченко <roman@oscada.org>
Версія: 1.1.1
Ліцензія: GPLv2','Вычисление среднего значения за указанный период времени [bTime...eTime], для данных архиватора archtor (FSArch.1m) и формирование результата с определённым количеством знаков prec после запятой.

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

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

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

Автор: Роман Савоченко <roman@oscada.org>
Версия: 1.1.1
Лицензия: GPLv2',1,10,0,'val = EVAL;
actPerc = 0;
aO = SYS.nodeAt(addr).arch();
if(aO) {
	bTime = bTime*1e6; eTime = eTime*1e6;
	vCnt = (eTime-bTime)/aO.period(archtor)+1;
	buf_beg = max(aO.begin(archtor), bTime); buf_end = min(aO.end(archtor), eTime);
	for(vCntA = 0; true; ) {
  		buf_beg1 = buf_beg;
		r_val = aO.getVal(buf_beg, true, archtor);
		if(buf_beg > buf_end)	break;
		if(!r_val.isEVal())	vCntA++;
		if(val.isEVal())	val = r_val;
		else if(!r_val.isEVal())
			val = (val*(buf_beg-bTime) + r_val*(buf_beg-buf_beg1))/(buf_beg - bTime + buf_beg - buf_beg1);
		if(buf_beg == 0 || buf_beg >= buf_end) break;
		buf_beg++;
	}
	//SYS.messInfo("averVal", "vCntA="+vCntA+"("+vCnt+"); archtor="+archtor); 
	actPerc = vCntA ? min(100,100*vCntA/vCnt) : 0;
}
if(prec < 0)	return val;
if(val.isEVal())	return tr("Empty");
if(typeof(val) == "real")	return val.toFixed(prec);
return val;','','',1579609632);
INSERT INTO flb_doc VALUES('diagram','Diagram','Діаграма','Диаграмма','Building trends diagram to SVG, which may be builtin next to the XHTML-document, for specified period of the time [(end-size)...end] and from the sources srcs.

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

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

Currently, only value archives can be used as a data source, either directly to them or through the DAQ-parameter attribute with the archive. The number of data sources is not directly limited and is written in separate lines of the argument srcs in the format "{addr}:{min}:{max}:{color}[:{scale}[:{width}]]", where:
- addr — address to:
  - the value archive ("/Archive/va_CPULoad_load") or the DAQ-parameter attribute ("/DAQ/LogicLev/gen/F3/var");
  - the direct data in the form "<d s=''1'' aprox=''1'' per=''1''>{p0}={pV0},{p1}={pV1},...,{pN}={pVN}</d>" for periodic, by set the attribute "s" it will allows the period setting in seconds; "aprox" — approximate the transition from one point to another instead of the previous value to all the periodicity points from the packaging;
  - the direct data in the form "<d per=''0''>{tm0}={pV0},{tm1}={pV1},...,{tmN}={pVN}</d>" for aperiodic of long intervals from months, where <tm{N}> in seconds.
- min, max — minimum and maximum of the scale, in the scale absence (min >= max), it will be calculated from the real data;
- color — colour of the trend curve;
- scale — separate definition of the trend scale, by default "Global (0)" and generally provided: "Global (0)", "Markers (2)", "Grid and markers (3)", "Markers (log) (6)", "Grid and markers (log) (7)".
- width — width of the trend curve, by default 2.

Author: Roman Savochenko <roman@oscada.org>
Sponsored by: Magomed, SAVTECH
Version: 1.5.1
License: GPLv2','Побудова діаграми трендів у SVG, яка може надалі вбудовуватися у XHTML-документу, для даних за вказаний період часу [(end-size)...end] та із джерел srcs.

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

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

У якості джерела даних наразі можуть використовуватися лише архіви значень, прямою адресою до них або через атрибут DAQ-параметру з архівом. Кількість джерел даних прямо не обмежене та вони записуються окремими рядками аргументу srcs із форматом "{addr}:{min}:{max}:{color}[:{scale}[:{width}]]", де:
- addr — адреса до:
  - архіву значень ("/Archive/va_CPULoad_load") або атрибуту DAQ-параметру ("/DAQ/LogicLev/gen/F3/var");
  - прямі дані у формі "<d s=''1'' aprox=''1'' per=''1''>{p0}={pV0},{p1}={pV1},...,{pN}={pVN}</d>" для періодичних, встановленням атрибуту "s" можна вказати на період у секундах; "aprox" — апроксимувати перехід від однієї точки до іншої замість підставки попереднього значення у всі точки періодичності від упаковки;
  - прямі дані у формі "<d per=''0''>{tm0}={pV0},{tm1}={pV1},...,{tmN}={pVN}</d>" для аперіодичних довгих інтервалів від місяців, де tm{N} у секундах.
- min, max — мінімум та максимум шкали, за відсутності шкали (min >= max) вона буде вираховуватися із реальних даних;
- color — колір кривої тренду;
- scale — окреме визначення шкали тренду, по замовченню "Глобально (0)" та загалом передбачено: "Глобально (0)", "Маркери (2)", "Ґратка та маркери (3)", "Маркери (лог) (6)", "Ґратка та маркери (лог) (7)".
- width — ширина кривої тренду, по замовченню 2.

Автор: Роман Савоченко <roman@oscada.org>
Спонсоровано: Магомед, SAVTECH
Версія: 1.5.1
Ліцензія: GPLv2','Построение диаграммы трендов в SVG, которая может далее встраиваться в XHTML-документ, для данных за указанный период времени [(end-size)...end] и из источников srcs.

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

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

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

Автор: Роман Савоченко <roman@oscada.org>
Спонсировано: Магомед, SAVTECH
Версия: 1.5.1
Лицензия: GPLv2',1,10,0,'function strChars(inS) {
	for(inSz = 0, off = 0; off < inS.length; inSz++)
		inS.charAt(off, "UTF-8");
	return inSz;
}

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

//Constants
RndRange = 5;

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

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

//Getting the trends data
trends = new Array();
for(off = 0; (tEl=srcs.parseLine(0,off)).length; ) {
	elO = new Object();
	elO.addr = tEl.parse(0, ":");
	elO.min = tEl.parse(1, ":"); elO.max = tEl.parse(2, ":");
	elO.color = tEl.parse(3, ":");
	elO.scale = tEl.parse(4, ":").toInt();
	elO.width = max(1, tEl.parse(5,":").toInt());
	elO.beg = begin*1e6; elO.per = reqPer*1e6;
	if((elO.srcO=SYS.nodeAt(elO.addr))) {
		if(typeof(elO.srcO) == "TCntrNode:TVal")	elO.srcO = elO.srcO.arch();
		if(typeof(elO.srcO) != "TCntrNode:TVArchive")	continue;
		elO.val = elO.srcO.getVals(elO.beg, end*1e6, elO.per, arch);
	}
	else if(!(dDt=SYS.XMLNode()).load(elO.addr).toInt() && dDt.name() == "d") {
		elO.val = new Array();
		isSec = dDt.attr("s").toInt();
		isAprox = dDt.attr("aprox").toInt();
		elO.per = dDt.attr("per").toInt() * (isSec?1e6:1);

		for(prevPos = 0, prevVal = EVAL, maxPos = elO.per ? floor(1e6*(end-begin)/elO.per) : 0, off1 = 0; true; ) {
			if((svl=dDt.text().parse(0,",",off1)).length) {
				curPos = svl.parse(0,"=").toInt(), curVal = ((curVal=svl.parse(1,"="))==EVAL)?EVAL:curVal.toReal();
				if(!maxPos) {
					elO.val.push(tVl=new Object());
					tVl.tm = curPos;
					tVl.val = curVal;
					continue;
				}
			}
			else if(!maxPos)	break;
			else curPos = maxPos+1;
			for(stPos = prevPos; prevPos < curPos; prevPos++)
				if(isAprox && !prevVal.isEVal() && !curVal.isEVal())
					elO.val.push(prevVal+(curVal-prevVal)*(prevPos-stPos)/(curPos-stPos));
				else elO.val.push(prevVal);
			if(prevPos > maxPos)	break;
			prevVal = curVal;
		}
	}
	else continue;

	trends.push(elO);
}

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

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

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

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

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

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

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

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

	prmInGrp++; prmGrpLast = iP;
}

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

	trPath = 0; prev_vl = EVAL; prev_pos = prev_vpos = 0;
	for(iDt = 0; iDt < cP.val.length; iDt++) {
		if(cP.per)	c_val = cP.val[iDt], c_tm = 1e-6*(cP.beg + iDt*cP.per);
		else c_val = cP.val[iDt].val, c_tm = cP.val[iDt].tm;
		if(vsPercT && !c_val.isEVal()) c_val = 100*(c_val-bordL)/(bordU-bordL);
		if(c_tm < begin)	continue;
		if(c_tm > end)		break;
		c_pos = floor(tArX + tArW*(c_tm-begin)/(end-begin));
		if(!c_val.isEVal()) {
			c_vpos = max(tArY,min(tArY+tArH,floor(tArY + tArH - tArH*((isLogT?lg(max(1e-100,c_val)):c_val)-vsMinT)/(vsMaxT-vsMinT))));
			if(!trPath) trPath = drawArea.childAdd("path").setAttr("stroke", cP.color).setAttr("stroke-width",cP.width);
			if(prev_vl.isEVal()) trPath.setAttr("d", trPath.attr("d")+"M "+c_pos+" "+c_vpos+" ");
			else if(abs(c_pos-prev_pos) > RndRange && abs(c_vpos-prev_vpos) > RndRange) {
				// Curvature [0.1...0.5] depending the rising level
				curvLev = -0.2*(max(-2*RndRange,min(2*RndRange,prev_vpos-c_vpos))-2*RndRange)/(2*RndRange)+0.1;
				// Same drawing
				trPath.setAttr("d",trPath.attr("d")+"C "+(prev_pos+(tVl=curvLev*(c_pos-prev_pos)))+" "+prev_vpos+", "+(c_pos-tVl)+" "+c_vpos+", "+c_pos+" "+c_vpos+" ");
			}
			else trPath.setAttr("d",trPath.attr("d")+"L "+c_pos+" "+c_vpos+" ");
		}
		else if(!prev_vl.isEVal()) {
			c_vpos = max(tArY,min(tArY+tArH,floor(tArY + tArH - tArH*((isLogT?lg(max(1e-100,prev_vl)):prev_vl)-vsMinT)/(vsMaxT-vsMinT))));
			trPath.setAttr("d", trPath.attr("d")+"L "+prev_pos+".1 "+c_vpos+".1 ");
		}
		prev_vl = c_val, prev_pos = c_pos, prev_vpos = c_vpos;
	}
}

return im.save();','','',1736608631);
