PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS 'ParamTemplLibs' ("ID" TEXT DEFAULT '' ,"NAME" TEXT DEFAULT '' ,"uk#NAME" TEXT DEFAULT '' ,"DESCR" TEXT DEFAULT '' ,"uk#DESCR" TEXT DEFAULT '' ,"DB" TEXT DEFAULT '' ,"ru#NAME" TEXT DEFAULT '' ,"ru#DESCR" TEXT DEFAULT '' , PRIMARY KEY ("ID"));
INSERT INTO ParamTemplLibs VALUES('tests','Tests','Тести','Templates library of the test parameters for PLC series S7 of the firm Siemens.

Author: Roman Savochenko <roman@oscada.org>','Бібліотека шаблонів параметрів для контролерів фірми Siemens серії S7.

Автор: Роман Савоченко <roman@oscada.org>','tmplib_tests','Тесты','Библиотека шаблонов параметров для контроллеров фирмы Siemens серии S7.

Автор: Роман Савоченко <roman@oscada.org>');
INSERT INTO ParamTemplLibs VALUES('base','Main library','Основна бібліотека','Library of the main templates of the DAQ-sources processing and related services.

The library was created to provide main templates of the DAQ-sources processing and related services.

Author: Roman Savochenko <roman@oscada.org>
Used by: Most projects on OpenSCADA
Founded: January 2006
Version: 2.3.0
License: GPLv2
DOC: Libs_Main|Libs/Main','Бібліотека основних шаблонів опрацювання DAQ-джерел та похідних сервісів.

Бібліотеку створено для надання основних шаблонів опрацювання DAQ-джерел та похідних сервісів.

Автор: Роман Савоченко <roman@oscada.org>
Використано: Більшість проектів OpenSCADA
Засновано: Січень 2006
Версія: 2.3.0
Ліцензія: GPLv2
DOC: Libs_Main|Libs/Main','tmplib_base','Основная библиотека','Библиотека основных шаблонов обработки DAQ-источников и производных сервисов.

Библиотека создана для предоставления основных шаблонов обработки DAQ-источников и производных сервисов.

Автор: Роман Савоченко <roman@oscada.org>
Использовано: Большинство проектов OpenSCADA
Основано: Январь 2006
Версия: 2.3.0
Лицензия: GPLv2
DOC: Libs_Main|Libs/Main');
INSERT INTO ParamTemplLibs VALUES('DevLib','Industrial devices','Промислові пристрої','The user protocol devices library created to provide access to industrial device''s data through network, like to common industrial automation devices and wide resources counters, with protocols simple enough to implement into the User Protocol module, using the presented complex protocols (ModBus, OPC_UA, HTTP) or directly on the internal like to Java language.

Author: Roman Savochenko <roman@oscada.org>, Constantine (IrmIngeneer) (2018), Arsen Zakojan (2017), Ruslan Yarmoliuk (2017)
Founded: January 2010
Version: 3.1.1
License: GPLv2
DOC: Libs_Devices|Libs/Devices','Бібліотеку пристроїв користувацьких протоколів створено для надання доступу до даних промислових пристроїв через мережу із доволі простим протоколом, на кшталт пристроїв загальної промислової автоматики та лічильників різних ресурсів, із протоколом достатньо простим до реалізації у модулі користувацького протоколу, з використанням наявних комплексних протоколів (ModBus, OPC_UA, HTTP) або безпосередньо на внутрішній мові подібній до Java.

Автор: Роман Савоченко <roman@oscada.org>, Константин (IrmIngeneer) (2018), Арсен Закоян (2017), Руслан Ярмолюк (2017)
Засновано: Січень 2010
Версія: 3.1.1
Ліцензія: GPLv2
DOC: Libs_Devices|Libs/Devices','tmplib_DevLib','Промышленные устройства','');
INSERT INTO ParamTemplLibs VALUES('PrescrTempl','Prescriptions','Рецепти','The library is created to provide an environment of execution of scenarios of the technological operations — prescriptions, and frames of the user interface about them, including the frame of creation/edition the prescriptions and two frames of the execution control and reporting — "Prescription — run" and "Prescription — run, simple". The library is built on the basis primitives of the widgets and the internal programming language JavaLikeCalc, including templates and commands.

The section contains DAQ-templates of the prescription manager and commands of the "command-template" mode, which are intended to be connected to the logic level controller, by creating related parameters of the prescription manager and commands, which available for the user to choose from, and which will carry out all the work with the prescriptions-programs for their processing and execution.

The element''s names and their parameters are available in languages: English, Ukrainian and mRussian. Their source code wrote in human-language independent mode with calls for the translations by the function tr() and the message''s translation also allowed for English, Ukrainian and mRussian.

Author: Roman Savochenko <roman@oscada.org>
Sponsored by: Vassily Grigoriev, the Laboratory of Vacuum Technologies
Sponsored by, about the controller templateization: Magomed, SAVTECH
Founded: April 2012
Version: 1.1.0
License: GPLv2
DOC: Libs_Prescriptions|Libs/Prescriptions','Бібліотека створюється для надання середовища виконання сценаріїв технологічних процесів — рецептів, та кадрів користувацького інтерфейсу довкола нього, включно з кадром створення/редагування рецептів та двома кадрами контролю виконання та звітності — "Рецепт — виконання" та "Рецепт — виконання, простий". Будується бібліотека на основі примітивів віджетів та мови внутрішнього програмування JavaLikeCalc, включно для шаблонів та команд.

Розділ містить DAQ-шаблони менеджеру рецептів та команд режиму "команда-шаблон", які призначено до підключення у контролері логічного рівня, через створення відповідно параметрів менеджеру рецептів та команд, доступних до обрання користувачем, які і здійснюватимуть там всю роботу із рецептами-програмами щодо їх опрацювання та виконання.

Назви елементів та їх параметрів доступні на мовах: Англійська, Українська та mRussian. Їх вихідний код написано у мово(людська)-незалежному режимі з викликом функції перекладу tr() та переклад цих повідомлень також доступний Англійською, Українською та mRussian.

Автор: Роман Савоченко <roman@oscada.org>
Спонсоровано: Василь Григор''єв, Лабораторія Вакуумних Технологій
Спонсоровано, щодо шаблонізації контролеру: Магомед, SAVTECH
Засновано: Квітень 2012
Версія: 1.1.0
Ліцензія: GPLv2
DOC: Libs_Prescriptions|Libs/Prescriptions','tmplib_PrescrTempl','Рецепты','Библиотека создаётся для предоставления среды исполнения сценариев технологических процессов — рецептов, и кадров пользовательского интерфейса около него, включая кадр создания/редактирования рецептов и два кадра контроля исполнения и отчётности — "Рецепт — исполнение" и "Рецепт — исполнение, простой". Строится библиотека на основе примитивов виджетов и языка внутреннего программирования JavaLikeCalc, включая для шаблонов и команд.

Раздел содержит DAQ-шаблоны менеджера рецептов и команд режима "команда-шаблон", которые предназначены к подключению в контроллере логического уровня, путём создания соответственно параметров менеджера рецептов и команд, доступных для выбора пользователем, которые и будут осуществлять там всю роботу с рецептами-програмами на предмет их обработки и исполнения.

Названия элементов и их параметров доступны на языках: Английский, Украинский и mRussian. Их исходный код написан в языко(человеческий)-независимом режиме с вызовом функции перевода tr() и перевод этих сообщений также доступен Английским, Украинским и mRussian.

Автор: Роман Савоченко <roman@oscada.org>
Спонсировано: Василий Григорьев, Лаборатория Вакуумных Технологий
Спонсировано, на предмет шаблонизации контроллера: Магомед, SAVTECH
Основано: Апрель 2012
Версия: 1.1.0
Лицензия: GPLv2
DOC: Libs_Prescriptions|Libs/Prescriptions');
INSERT INTO ParamTemplLibs VALUES('LowDevLib','Low-level devices','Низькорівневі пристрої','Library of templates to provide access to device''s data of the low-level buses.

The user protocol devices library created to provide access to device''s data of low-level buses, with protocols simple enough to implement into the User Protocol module or directly on the internal like to Java language.

The template''s names and their parameters are available in languages: English, Ukrainian and mRussian. Their source code wrote in the human-language independent mode with calls for the translations by the function tr() and the message''s translation also allowed for English, Ukrainian and mRussian.

Author: Roman Savochenko <roman@oscada.org>, Arcadiy Kisel (2017)
Founded: Jul 2016
Version: 1.6.1
License: GPLv2
DOC: Libs_LowLevelDevices|Libs/LowLevelDevices','Бібліотека шаблонів надання доступу до даних пристроїв низькорівневих шин.

Бібліотеку пристроїв користувацьких протоколів створено для надання доступу до даних пристроїв низькорівневих шин, із протоколом достатньо простим до реалізації у модулі користувацького протоколу або безпосередньо на внутрішній мові подібній до Java.

Назви елементів та їх параметрів доступні на мовах: Англійська, Українська та mRussian. Їх вихідний код написано у мово(людська)-незалежному режимі з викликом функції перекладу tr() та переклад цих повідомлень також доступний Англійською, Українською та mRussian.

Автор: Роман Савоченко <roman@oscada.org>, Аркадій Кисіль (2017)
Засновано: Липень 2016
Версія: 1.6.1
Ліцензія: GPLv2
DOC: Libs_LowLevelDevices|Libs/LowLevelDevices','tmplib_LowDevLib','Низкоуровневые устройства','Библиотека шаблонов предоставления доступа к данным устройств низкоуровневых шин.

Библиотека устройств пользовательских протоколов создана для предоставления доступа к данным устройств низкоуровневых шин, с протоколом достаточно простым для реализации в модуле пользовательского протокола или непосредственно на внутреннем языке подобном на Java.

Названия элементов и их параметров доступны на языках: Английский, Украинский и mRussian. Их исходный код написан в языко(человеческий)-независимом режиме с вызовом функции перевода tr() и перевод этих сообщений также доступен Английским, Украинским и mRussian.

Автор: Роман Савоченко <roman@oscada.org>, Аркадий Кысиль (2017)
Основано: Июль 2016
Версия: 1.6.1
Лицензия: GPLv2
DOC: Libs_LowLevelDevices|Libs/LowLevelDevices');
CREATE TABLE IF NOT EXISTS 'UserFuncLibs' ("ID" TEXT DEFAULT '' ,"NAME" TEXT DEFAULT '' ,"DESCR" TEXT DEFAULT '' ,"DB" TEXT DEFAULT '' ,"uk#NAME" TEXT DEFAULT '' ,"uk#DESCR" TEXT DEFAULT '' ,"ru#NAME" TEXT DEFAULT '' ,"ru#DESCR" TEXT DEFAULT '' ,"PROG_TR" INTEGER DEFAULT '' , PRIMARY KEY ("ID"));
INSERT INTO UserFuncLibs VALUES('techApp','Technological apparatuses','Library of models of the technological apparatuses for creating complex simulators of the technological processes of block-schemes of the module DAQ.BlockCalc.

The library is created to provide models of devices of the technological processes. The library is not static, but based on the module JavaLikeCalc, allowing to create calculations on the Java-like language.

To address the library functions you can use the static call address DAQ.JavaLikeCalc.lib_techApp.{Func}() or dynamic SYS.DAQ.JavaLikeCalc["lib_techApp"]["{Func}"].call(), SYS.DAQ.JavaLikeCalc["lib_techApp"].{Func}()". Where {Func} — function identifier in the library.

Author: Roman Savochenko <roman@oscada.org>, Maxim Lysenko (2007,2010), Ksenia Yashina (2007)
Founded: October 2005
Sponsored by: DIYA Ltd
Version: 2.0.1
License: GPLv2
DOC: Libs_Technological_apparatuses|Libs/Technological_apparatuses','flb_techApp','Технологічні апарати','Бібліотека моделей технологічних апаратів для створення комплексних моделей технологічних процесів, блокових схем модуля DAQ.BlockCalc.

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

Для адресації до функцій цієї бібліотеки можна використати статичну адресу виклику DAQ.JavaLikeCalc.lib_techApp.{Func}() або динамічну SYS.DAQ.JavaLikeCalc["lib_techApp"]["{Func}"].call(), SYS.DAQ.JavaLikeCalc["lib_techApp"].{Func}(). Де {Func} — ідентифікатор функції у бібліотеці.

Автор: Роман Савоченко <roman@oscada.org>, Максим Лисенко (2007,2010), Ксенія Яшина (2007)
Засновано: Жовтень 2005
Спонсоровано: ТОВ "ДІЯ"
Версія: 2.0.1
Ліцензія: GPLv2
DOC: Libs_Technological_apparatuses|Libs/Technological_apparatuses','Технологические аппараты','Библиотека моделей технологических аппаратов для создания комплексных моделей технологических процессов, блочных схем модуля DAQ.BlockCalc.

Библиотека создаётся для предоставления моделей аппаратов технологических процессов. Библиотека не является статической, а строится на основе модуля JavaLikeCalc, позволяющего создавать вычисления на Java-подобном языке.

Для адресации к функции этой библиотеки можно использовать статический адрес вызова DAQ.JavaLikeCalc.lib_techApp.{Func}() или динамический SYS.DAQ.JavaLikeCalc["lib_techApp"]["{Func}"].call(), SYS.DAQ.JavaLikeCalc["lib_techApp"].{Func}(). Где {Func} — идентификатор функции в библиотеке.

Автор: Роман Савоченко <roman@oscada.org>, Максим Лысенко (2007,2010), Ксения Яшина (2007)
Основано: Октябрь 2005
Спонсировано: ООО "ДІЯ"
Версия: 2.0.1
Лицензия: GPLv2
DOC: Libs_Technological_apparatuses|Libs/Technological_apparatuses',0);
INSERT INTO UserFuncLibs VALUES('servProc','Service procedures','Library of the service procedures of different application.

The library is created to provide service functions of performing service tasks about the OpenSCADA environment. The library is not static, but based on the module JavaLikeCalc, allowing to create calculations on the Java-like language. The functions'' names and their parameters are available in languages: English, Ukrainian and mRussian.

To addressing the library functions you can use the static call address DAQ.JavaLikeCalc.lib_servProc.{Func}() or dynamic SYS.DAQ.JavaLikeCalc["lib_servProc"]["{Func}"].call(), SYS.DAQ.JavaLikeCalc["lib_servProc"].{Func}()". Where {Func} — function identifier in the library.

Author: Roman Savochenko <roman@oscada.org>
Founded: November 2017
Version: 1.2.1
License: GPLv2
DOC: Libs_Service_procedures|Libs/Service_procedures','flb_servProc','Сервісні процедури','Бібліотека сервісних процедур різноманітного застосування.

Бібліотека створюється для надання сервісних функцій виконання службових завдань довкола оточення OpenSCADA. Бібліотека не є статичною, а будується на основі модуля JavaLikeCalc, який дозволяє створювати обчислення на мові схожій на Java. Назви функцій та їх параметрів доступні на мовах: Англійська, Українська та mRussian.

Для адресації до функцій цієї бібліотеки можна використати статичну адресу виклику DAQ.JavaLikeCalc.lib_servProc.{Func}() або динамічну SYS.DAQ.JavaLikeCalc["lib_servProc"]["{Func}"].call(), SYS.DAQ.JavaLikeCalc["lib_servProc"].{Func}(). Де {Func} — ідентифікатор функції у бібліотеці.

Автор: Роман Савоченко <roman@oscada.org>
Засновано: Листопад 2017
Версія: 1.2.1
Ліцензія: GPLv2
DOC: Libs_Service_procedures|Libs/Service_procedures','Сервисные процедуры','Библиотека сервисных процедур различного применения.

Библиотека создаётся для предоставления сервисных функций исполнения служебных задач около окружения OpenSCADA. Библиотека не является статической, а строится на основе модуля JavaLikeCalc, позволяющего создавать вычисления на Java-подобном языке. Названия функций и их параметров доступны на языках: Английский, Украинский и mRussian.

Для адресации к функции этой библиотеки можно использовать статический адрес вызова DAQ.JavaLikeCalc.lib_servProc.{Func}() или динамический SYS.DAQ.JavaLikeCalc["lib_servProc"]["{Func}"].call(), SYS.DAQ.JavaLikeCalc["lib_servProc"].{Func}(). Где {Func} — идентификатор функции в библиотеке.

Автор: Роман Савоченко <roman@oscada.org>
Основано: Ноябрь 2017
Версия: 1.2.1
Лицензия: GPLv2
DOC: Libs_Service_procedures|Libs/Service_procedures',1);
INSERT INTO UserFuncLibs VALUES('doc','Reports'' and documents'' library','Provides the combined library of the reports, documents and related functions of common and the industrial automation.

The service functions of the frames of reports and documents are intended to perform the characteristic tasks of obtaining reporting data, for which you need to perform some verification or counting, for example, to check the validity and to summarize. Forming complex data, such as charts embedded in a document, can also be a service function.

To address the library functions you can use the static call address DAQ.JavaLikeCalc.lib_doc.{Func}() or dynamic SYS.DAQ.JavaLikeCalc["lib_doc"]["{Func}"].call(), SYS.DAQ.JavaLikeCalc["lib_doc"].{Func}()". Where {Func} — function identifier in the library.

The element''s names and their parameters are available in languages: mEnglish, Ukrainian and mRussian. Their source code wrote in human-language independent mode with calls for the translations by the function tr() and the message''s translation also allowed for English, Ukrainian and mRussian.

Founded: January 2008
Author: Roman Savochenko <roman@oscada.org>
Version: 2.0.3
License: GPLv2
DOC: Libs_Documents|Libs/Documents','flb_doc','Бібліотека звітів та документів','Надає комбіновану бібліотеку звітів, документів та пов''язаних функцій загальної та промислової автоматизації.

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

Для адресації до функцій цієї бібліотеки можна використати статичну адресу виклику DAQ.JavaLikeCalc.lib_doc.{Func}() або динамічну SYS.DAQ.JavaLikeCalc["lib_doc"]["{Func}"].call(), SYS.DAQ.JavaLikeCalc["lib_doc"].{Func}(). Де {Func} — ідентифікатор функції у бібліотеці.

Назви елементів та їх параметрів доступні на мовах: Англійська, Українська та mRussian. Їх вихідний код написано у мово(людська)-незалежному режимі з викликом функції перекладу tr() та переклад цих повідомлень також доступний Англійською, Українською та mRussian.

Засновано: січень 2008р
Автор: Роман Савоченко <roman@oscada.org>
Версія: 2.0.3
Ліцензія: GPLv2
DOC: Libs_Documents|Libs/Documents','Библиотека отчётов и документов','Предоставляет комбинированную библиотеку отчётов, документов и связанных функций общей и промышленной автоматизации.

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

Для адресации к функции этой библиотеки можно использовать статический адрес вызова DAQ.JavaLikeCalc.lib_doc.{Func}() или динамический SYS.DAQ.JavaLikeCalc["lib_doc"]["{Func}"].call(), SYS.DAQ.JavaLikeCalc["lib_doc"].{Func}(). Где {Func} — идентификатор функции в библиотеке.

Названия элементов и их параметров доступны на языках: Английский, Украинский и mRussian. Их исходный код написан в языко(человеческий)-независимом режиме с вызовом функции перевода tr() и перевод этих сообщений также доступен Английским, Украинским и mRussian.

Основано: январь 2008г
Автор: Роман Савоченко <roman@oscada.org>
Версия: 2.0.3
Лицензия: GPLv2
DOC: Libs_Documents|Libs/Documents',1);
INSERT INTO UserFuncLibs VALUES('regEl','Regulation elements','Regulation elements library of block-schemes of the module DAQ.BlockCalc.

The library is created to provide functions of different control algorithms and it can be used in programmable logic controllers (PLC) based on OpenSCADA for construction control schemes in the module DAQ.BlockCalc. The library is not static, but based on the module JavaLikeCalc, allowing to create calculations on the Java-like language. The functions'' names and its parameters are available in languages: English, Ukrainian and mRussian.

To addressing the library functions you can use the static call address DAQ.JavaLikeCalc.lib_regEl.{Func}() or dynamic SYS.DAQ.JavaLikeCalc["lib_regEl"]["{Func}"].call(), SYS.DAQ.JavaLikeCalc["lib_regEl"].{Func}()". Where {Func} — function identifier in the library.

Founded: January 2010
Author: Roman Savochenko <roman@oscada.org>
Sponsored by: DIYA Ltd
Version: 1.0.0
License: GPLv2
DOC: Libs_Regulation_elements|Libs/Regulation_elements','flb_regEl','Елементи регулювання','Бібліотека елементів регулювання блокових схем модуля DAQ.BlockCalc.

Бібліотека створюється для надання функцій різних алгоритмів контролю та вона може бути використана у програмованих логічних контролерах (PLC) заснованих на OpenSCADA для побудови схем контролю у модулі DAQ.BlockCalc. Бібліотека не є статичною, а будується на основі модуля JavaLikeCalc, який дозволяє створювати обчислення на мові схожій на Java. Назви функцій та їх параметрів доступні на мовах: Англійська, Українська та mRussian.

Для адресації до функцій цієї бібліотеки можна використати статичну адресу виклику DAQ.JavaLikeCalc.lib_regEl.{Func}() або динамічну SYS.DAQ.JavaLikeCalc["lib_regEl"]["{Func}"].call(), SYS.DAQ.JavaLikeCalc["lib_regEl"].{Func}(). Де {Func} — ідентифікатор функції у бібліотеці.

Засновано: Січень 2010
Автор: Роман Савоченко <roman@oscada.org>
Спонсоровано: ТОВ "ДІЯ"
Версія: 1.0.0
Ліцензія: GPLv2
DOC: Libs_Regulation_elements|Libs/Regulation_elements','Элементы регулирования','Библиотека элементов регулирования блочных схем модуля DAQ.BlockCalc.

Библиотека создаётся для предоставления функций различных алгоритмов контроля и она может быть использована в программированных логических контроллерах (PLC) основанных на OpenSCADA для построения схем контроля в модуле DAQ.BlockCalc. Библиотека не является статической, а строится на основе модуля JavaLikeCalc, позволяющего создавать вычисления на Java-подобном языке. Названия функций и их параметров доступны на языках: Английский, Украинский и mRussian.

Для адресации к функции этой библиотеки можно использовать статический адрес вызова DAQ.JavaLikeCalc.lib_regEl.{Func}() или динамический SYS.DAQ.JavaLikeCalc["lib_regEl"]["{Func}"].call(), SYS.DAQ.JavaLikeCalc["lib_regEl"].{Func}(). Где {Func} — идентификатор функции в библиотеке.

Основано: Январь 2010
Автор: Роман Савоченко <roman@oscada.org>
Спонсировано: ООО "ДІЯ"
Версия: 1.0.0
Лицензия: GPLv2
DOC: Libs_Regulation_elements|Libs/Regulation_elements',0);
INSERT INTO UserFuncLibs VALUES('Controller','Controllers','Library of programs of controllers based on JavaLikeCalc.

Author: Roman Savochenko <roman@oscada.org>
Version: 1.1.1
License: GPLv2','flb_Controller','Контролери','Програми контролерів базованих на JavaLikeCalc.

Автор: Роман Савоченко <roman@oscada.org>
Версія: 1.1.1
Ліцензія: GPLv2','Контроллеры','Программы контроллеров основанных на JavaLikeCalc.

Автор: Роман Савоченко <roman@oscada.org>
Версия: 1.1.1
Лицензия: GPLv2',0);
INSERT INTO UserFuncLibs VALUES('web','XHTML-template','Pages processing functions library for XHTML-template user''s Web-interface.

Author: Roman Savochenko
Version: 0.2.0
License: GPL','flb_web','XHTML-шаблон','Бібліотека функцій обробки сторінок XHTML-шаблонів користувацткого Web-інтерфейсу.

Автор: Роман Савоченко <roman@oscada.org>
Версія: 0.2.0
Ліцензія: GPL','XHTML-шаблон','Библиотека функций обработки страниц XHTML-шаблонов пользовательского Web-интерфейса.

Автор: Роман Савоченко <roman@oscada.org>
Версия: 0.2.0
Лицензия: GPL',0);
CREATE TABLE IF NOT EXISTS 'flb_servProc_io' ("F_ID" TEXT DEFAULT '' ,"ID" TEXT DEFAULT '' ,"NAME" TEXT DEFAULT '' ,"TYPE" INTEGER DEFAULT '' ,"MODE" INTEGER DEFAULT '' ,"DEF" TEXT DEFAULT '' ,"HIDE" INTEGER DEFAULT '' ,"POS" INTEGER DEFAULT '' ,"uk#NAME" TEXT DEFAULT '' ,"uk#DEF" TEXT DEFAULT '' ,"ru#NAME" TEXT DEFAULT '' ,"ru#DEF" TEXT DEFAULT '' , PRIMARY KEY ("F_ID","ID"));
INSERT INTO flb_servProc_io VALUES('procArh','fromarch','From archive',0,0,'Archive.va_ai3_code',0,0,'З архіву','','Из архива','');
INSERT INTO flb_servProc_io VALUES('procArh','toarch','To archive',0,0,'Archive.va_ai3_dP',0,1,'У архів','','В архив','');
INSERT INTO flb_servProc_io VALUES('procArh','begs','Begin time',0,0,'2006-08-08 9:21:56',0,2,'Час початку','','Время начала','');
INSERT INTO flb_servProc_io VALUES('procArh','ends','End time',0,0,'2006-08-08 11:21:55',0,3,'Час закінчення','','Время конца','');
INSERT INTO flb_servProc_io VALUES('crc16','in','Input',0,0,'',0,0,'Вхід','','Вход','');
INSERT INTO flb_servProc_io VALUES('crc16','out','Result',1,1,'',0,1,'Результат','','Результат','');
INSERT INTO flb_servProc_io VALUES('releaseTests','sub','Subsystem (-1:All;0:SYS;1:Security;2:Transport;...',1,0,'-1',0,0,'Підсистема (-1:Всі;0:SYS;1:Безпека;2:Транспорти;...)','','Подсистема (-1:Все;0:SYS;1:Безопасность;2:Транспорты;...)','');
INSERT INTO flb_servProc_io VALUES('releaseTests','rez','Result',0,5,'',0,1,'Результат','','Результат','');
INSERT INTO flb_servProc_io VALUES('crc16','poly','Polynomial (reversion)',1,0,'40961',0,2,'','','','');
INSERT INTO flb_servProc_io VALUES('archPackFStests','tm','Start time (14.03.2015 21:37)',1,0,'1426361839',0,0,'','','','');
INSERT INTO flb_servProc_io VALUES('archPackFStests','per','Period, seconds',1,0,'60',0,1,'','','','');
INSERT INTO flb_servProc_io VALUES('archPackFStests','addr','Archive address',0,0,'test123',0,2,'','','','');
INSERT INTO flb_servProc_io VALUES('archPackFStests','step','Step (0-8), -1 for all',1,0,'-1',0,4,'','','','');
INSERT INTO flb_servProc_io VALUES('archPackFStests','archiver','Archiver address',0,0,'FSArch.1m',0,3,'','','','');
INSERT INTO flb_servProc_io VALUES('wacko2media','ndb','MySQL DB id',0,0,'wiki',0,0,'','','','');
INSERT INTO flb_servProc_io VALUES('wacko2media','wpg','Wiki page',0,0,'HomePageUk/Doc/SQLite',0,1,'','','','');
INSERT INTO flb_servProc_io VALUES('wacko2media','rez','Result',0,6,'',0,2,'Результат','','Результат','');
INSERT INTO flb_servProc_io VALUES('docOffLine','resDir','Results directory',0,0,'/home/roman/ext/OpenSCADA/Work/doc/',0,2,'','','','');
INSERT INTO flb_servProc_io VALUES('docOffLine','pages','Pages list
Rows in the form "{wiki}:{langs}:{dest}".',0,4,'Documents:en,uk,ru:index.html
Documents/Release_0.9:en,uk,ru:Release_0.9.html
Works/Technical_Support/Agreement:en,uk,ru:Technical_Support_Agreement.html
About:en,uk,ru:About.html
Documents/Terms:en,uk,ru:Terms.html
Functions_and_demands:en,uk,ru:Functions_and_demands.html
Works/To_do:en:To_do.html
Documents/FAQ:en,uk,ru:FAQ.html
Documents/Quick_start:en,uk,ru:Quick_start.html
Documents/Program_manual:en,uk,ru:Program_manual.html
Documents/How_to:en,uk,ru:How_to.html
Documents/How_to/Install:en,uk,ru:How_to_Install.html
Documents/How_to/Live_disk:en,uk,ru:How_to_Live_disk.html
Documents/How_to/Violations,_alarms_and_notifications:en,uk,ru:How_to_Violations.html
Documents/How_to/Cyclic_programming:en,uk,ru:How_to_Cyclic_programming.html
Documents/How_to/Debug:en,uk,ru:How_to_Debug.html
Documents/How_to/Transferring_project_configuration:en,uk,ru:How_to_Transferring_project_configuration.html
Documents/How_to/Build_from_source:en,uk:How_to_Build_from_source.html
Documents/How_to/Crash_report:en,uk,ru:How_to_Crash_report.html
Documents/How_to/Create_module:en,uk,ru:How_to_Create_module.html
Documents/How_to/Create_multi_language_project:en,uk:Create_multi_language_project.html
Documents/DAQ:en,uk,ru:DAQ.html
Documents/User_API:en,uk,ru:User_API.html
Documents/API:en,uk:API.html
Modules/SQLite:en,uk,ru:Modules/SQLite.html
Modules/MySQL:en,uk,ru:Modules/MySQL.html
Modules/FireBird:en,uk,ru:Modules/FireBird.html
Modules/PostgreSQL:en,uk,ru:Modules/PostgreSQL.html
Modules/DBGate:en,uk:Modules/DBGate.html
Modules/DBF:en,uk,ru:Modules/DBF.html
Modules/LDAP:en,uk,ru:Modules/LDAP.html
Modules/Sockets:en,uk,ru:Modules/Sockets.html
Modules/Serial:en,uk,ru:Modules/Serial.html
Modules/SSL:en,uk:Modules/SSL.html
Modules/SelfSystem:en,uk,ru:Modules/SelfSystem.html
Modules/UserProtocol:en,uk,ru:Modules/UserProtocol.html
Modules/HTTP:en,uk,ru:Modules/HTTP.html
Modules/JavaLikeCalc:en,uk,ru:Modules/JavaLikeCalc.html
Modules/LogicLev:en,uk,ru:Modules/LogicLev.html
Modules/BlockCalc:en,uk,ru:Modules/BlockCalc.html
Modules/DAQGate:en,uk,ru:Modules/DAQGate.html
Modules/System:en,uk:Modules/System.html
Modules/ModBus:en,uk,ru:Modules/ModBus.html
Modules/DCON:en,uk,ru:Modules/DCON.html
Modules/OPC_UA:en,uk,ru:Modules/OPC_UA.html
Modules/SNMP:en,uk,ru:Modules/SNMP.html
Modules/ICP_DAS:en,uk,ru:Modules/ICP_DAS.html
Modules/Siemens:en,uk,ru:Modules/Siemens.html
Modules/DiamondBoards:en,uk,ru:Modules/DiamondBoards.html
Modules/Comedi:en,uk,ru:Modules/Comedi.html
Modules/SoundCard:en,uk,ru:Modules/SoundCard.html
Modules/BFN:en,uk,ru:Modules/BFN.html
Modules/SMH2Gi:en,uk,ru:Modules/SMH2Gi.html
Modules/GPIO:en,uk,ru:Modules/GPIO.html
Modules/FSArch:en,uk,ru:Modules/FSArch.html
Modules/DBArch:en,uk,ru:Modules/DBArch.html
Modules/VCAEngine:en,uk,ru:Modules/VCAEngine.html
Modules/QTStarter:en,uk,ru:Modules/QTStarter.html
Modules/QTCfg:en,uk,ru:Modules/QTCfg.html
Modules/Vision:en,uk,ru:Modules/Vision.html
Modules/WebCfgD:en,uk,ru:Modules/WebCfgD.html
Modules/WebCfg:en,uk,ru:Modules/WebCfg.html
Modules/WebVision:en,uk,ru:Modules/WebVision.html
Modules/WebUser:en,uk,ru:Modules/WebUser.html
Modules/FLibSYS:en,uk,ru:Modules/FLibSYS.html
Modules/SystemTests:en,uk,ru:Modules/SystemTests.html
Modules/FLibMath:en,uk,ru:Modules/FLibMath.html
Modules/FLibComplex1:en,uk,ru:Modules/FLibComplex1.html
Libs/Main:en,uk,ru:Libs_Main.html
Libs/Devices:en,uk,ru:Libs_Devices.html
Libs/LowLevelDevices:en,uk,ru:Libs_LowLevelDevices.html
Libs/Service_procedures:en,uk,ru:Libs_Service_procedures.html
Libs/Regulation_elements:en,uk,ru:Libs_Regulation_elements.html
Libs/Technological_apparatuses:en,uk,ru:Libs_Technological_apparatuses.html
Libs/Main_graphical_elements:en,uk,ru:Libs_Main_graphical_elements.html
Libs/Mnemo_elements:en,uk,ru:Libs_Mnemo_elements.html
Libs/Electrical_elements:en,uk,ru:Libs_Electrical_elements.html
Libs/Documents:en,uk,ru:Libs_Documents.html
Libs/Prescriptions:en,uk,ru:Libs_Prescriptions.html',0,3,'','','','');
INSERT INTO flb_servProc_io VALUES('docOffLine','pagesCur','Current pages list
Rows in the form "{wiki}:{langs}:{dest}", for empty here used "pages".',0,5,'',0,4,'','','','');
INSERT INTO flb_servProc_io VALUES('docOffLine','res','Result',0,2,'',0,5,'Результат','','Результат','');
INSERT INTO flb_servProc_io VALUES('docOffLine','reqTr','Tries of the requests',1,1,'3',0,1,'','','','');
INSERT INTO flb_servProc_io VALUES('releaseTests','rezF','Result for HTML file',0,5,'',1,2,'','','','');
INSERT INTO flb_servProc_io VALUES('docOffLine','prgVer','Program version',0,0,'',0,0,'','','','');
CREATE TABLE IF NOT EXISTS 'tmplib_PrescrTempl_io' ("TMPL_ID" TEXT DEFAULT '' ,"ID" TEXT DEFAULT '' ,"NAME" TEXT DEFAULT '' ,"TYPE" INTEGER DEFAULT '' ,"FLAGS" INTEGER DEFAULT '' ,"VALUE" TEXT DEFAULT '' ,"POS" INTEGER DEFAULT '' ,"ru#NAME" TEXT DEFAULT '' ,"ru#VALUE" TEXT DEFAULT '' ,"uk#NAME" TEXT DEFAULT '' ,"uk#VALUE" TEXT DEFAULT '' , PRIMARY KEY ("TMPL_ID","ID"));
INSERT INTO tmplib_PrescrTempl_io VALUES('timer','run','Command: run',3,32,'0',4,'Команда: исполнение','','Команда: виконання','');
INSERT INTO tmplib_PrescrTempl_io VALUES('timer','pause','Command: pause',3,32,'0',5,'Команда: пауза','','Команда: пауза','');
INSERT INTO tmplib_PrescrTempl_io VALUES('timer','error','Prescription: error',3,32,'0',2,'Рецепт: ошибка','','Рецепт: помилка','');
INSERT INTO tmplib_PrescrTempl_io VALUES('timer','abort','Prescription: abort',3,32,'0',3,'Рецепт: прерывание','','Рецепт: перервано','');
INSERT INTO tmplib_PrescrTempl_io VALUES('timer','rez','Command: result',0,32,'1',6,'Команда: результат','','Команда: результат','');
INSERT INTO tmplib_PrescrTempl_io VALUES('timer','arg1','Time, seconds',2,32,'',7,'Время, секунд','','Час, секунд','');
INSERT INTO tmplib_PrescrTempl_io VALUES('timer','tmp1','Temporary 1',2,1,'0',8,'Временный 1','','Тимчасово 1','');
INSERT INTO tmplib_PrescrTempl_io VALUES('backTimer','run','Command: run',3,32,'0',4,'Команда: исполнение','','Команда: виконання','');
INSERT INTO tmplib_PrescrTempl_io VALUES('backTimer','pause','Command: pause',3,32,'0',5,'Команда: пауза','','Команда: пауза','');
INSERT INTO tmplib_PrescrTempl_io VALUES('backTimer','error','Prescription: error',3,32,'0',2,'Рецепт: ошибка','','Рецепт: помилка','');
INSERT INTO tmplib_PrescrTempl_io VALUES('backTimer','abort','Prescription: abort',3,32,'0',3,'Рецепт: прерывание','','Рецепт: перервано','');
INSERT INTO tmplib_PrescrTempl_io VALUES('backTimer','rez','Command: result',0,32,'1',6,'Команда: результат','','Команда: результат','');
INSERT INTO tmplib_PrescrTempl_io VALUES('backTimer','arg1','Time, seconds',2,32,'',7,'Время, секунд','','Час, секунд','');
INSERT INTO tmplib_PrescrTempl_io VALUES('backTimer','tmp1','Temporary 1',2,1,'0',8,'Временный 1','','Тимчасово 1','');
INSERT INTO tmplib_PrescrTempl_io VALUES('backTimer','start','Prescription: start',3,32,'0',0,'Рецепт: старт','','Рецепт: старт','');
INSERT INTO tmplib_PrescrTempl_io VALUES('backTimer','stop','Prescription: stop',3,32,'0',1,'Рецепт: стоп','','Рецепт: стоп','');
INSERT INTO tmplib_PrescrTempl_io VALUES('timer','start','Prescription: start',3,32,'0',0,'Рецепт: старт','','Рецепт: старт','');
INSERT INTO tmplib_PrescrTempl_io VALUES('timer','stop','Prescription: stop',3,32,'0',1,'Рецепт: стоп','','Рецепт: стоп','');
INSERT INTO tmplib_PrescrTempl_io VALUES('manager','dbDB','DB: DB name with the tables',0,80,'',0,'БД: Имя БД с таблицами','','БД: Ім''я БД з таблицями','');
INSERT INTO tmplib_PrescrTempl_io VALUES('manager','dbComs','DB: Table with commands',0,64,'PrescrComs',1,'БД: Таблица с командами','','БД: Таблиця з командами','');
INSERT INTO tmplib_PrescrTempl_io VALUES('manager','dbProgs','DB: Table with programs',0,80,'PrescrProgs',2,'БД: Таблица с программами','','БД: Таблиця з програмами','');
INSERT INTO tmplib_PrescrTempl_io VALUES('manager','comsCntr','Commands controller,
<empty> - for commands into the table,
''*'' - this parameter''s controller',0,64,'*',3,'Контроллер команд,
<пусто> - для команд в таблице,
''*'' - этот параметр контроллер','','Контролер команд,
<порожньо> - для команд у таблиці,
''*'' - цей параметр контролеру','');
INSERT INTO tmplib_PrescrTempl_io VALUES('manager','mode','Mode',1,40,'-2
-2;-1;0;1;2;3
Finish;Error;Stop;Run;Pause;Pass command',4,'Режим','-2
-2;-1;0;1;2;3
Завершение;Ошибка;Стоп;Исполнение;Пауза;Пропустить команду','Режим','-2
-2;-1;0;1;2;3
Завершення;Помилка;Стоп;Виконання;Пауза;Пропустити команду');
INSERT INTO tmplib_PrescrTempl_io VALUES('manager','curMode','Current mode',1,24,'-2
-2;-1;0;1;2;3
Finish;Error;Stop;Run;Pause;Pass command',5,'Текущий режим','-2
-2;-1;0;1;2;3
Завершение;Ошибка;Стоп;Исполнение;Пауза;Пропустить команду','Поточний режим','-2
-2;-1;0;1;2;3
Завершення;Помилка;Стоп;Виконання;Пауза;Пропустити команду');
INSERT INTO tmplib_PrescrTempl_io VALUES('manager','prog','Name of the selected program',0,32,'',6,'Имя выбранной программы','','Ім''я обраної програми','');
INSERT INTO tmplib_PrescrTempl_io VALUES('manager','startTm','Start time, seconds',1,16,'',7,'Время запуска, секунды','','Час запуску, секунди','');
INSERT INTO tmplib_PrescrTempl_io VALUES('manager','curCom','Current command-step',0,16,'',8,'Текущая комманда-шаг','','Поточна команда-крок','');
INSERT INTO tmplib_PrescrTempl_io VALUES('manager','work','Work program',4,16,'',9,'Рабочая программа','','Робоча програма','');
INSERT INTO tmplib_PrescrTempl_io VALUES('manager','comLs','Commands list',4,16,'',10,'Список команд','','Перелік команд','');
INSERT INTO tmplib_PrescrTempl_io VALUES('manager','clcCnt','Cycles counter',1,0,'',11,'Счётчик циклов','','Лічильник циклів','');
INSERT INTO tmplib_PrescrTempl_io VALUES('manager','this','The object',4,0,'',12,'Объект','','Об''єкт','');
CREATE TABLE IF NOT EXISTS 'DAQ_JavaLikeCalc' ("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 '' ,"ENABLE" INTEGER DEFAULT '0' ,"START" INTEGER DEFAULT '0' ,"MESS_LEV" INTEGER DEFAULT '3' ,"REDNT" INTEGER DEFAULT '0' ,"REDNT_RUN" TEXT DEFAULT '<high>' ,"PRM_BD" TEXT DEFAULT 'system' ,"FUNC" TEXT DEFAULT '' ,"SCHEDULE" TEXT DEFAULT '1' ,"PRIOR" INTEGER DEFAULT '0' ,"ITER" INTEGER DEFAULT '1' , PRIMARY KEY ("ID"));
CREATE TABLE IF NOT EXISTS 'DAQ_LogicLev' ("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 '' ,"ENABLE" INTEGER DEFAULT '0' ,"START" INTEGER DEFAULT '0' ,"MESS_LEV" INTEGER DEFAULT '3' ,"REDNT" INTEGER DEFAULT '0' ,"REDNT_RUN" TEXT DEFAULT '<high>' ,"PRM_BD" TEXT DEFAULT '' ,"PRM_BD_REFL" TEXT DEFAULT '' ,"PERIOD" INTEGER DEFAULT '0' ,"SCHEDULE" TEXT DEFAULT '1' ,"PRIOR" INTEGER DEFAULT '0' , PRIMARY KEY ("ID"));
CREATE TABLE IF NOT EXISTS 'WebUser_uPg' ("ID" TEXT DEFAULT '' ,"NAME" TEXT DEFAULT '' ,"uk#NAME" TEXT DEFAULT '' ,"DESCR" TEXT DEFAULT '' ,"uk#DESCR" TEXT DEFAULT '' ,"EN" INTEGER DEFAULT '0' ,"PROG" TEXT DEFAULT '' ,"uk#PROG" TEXT DEFAULT '' ,"TIMESTAMP" INTEGER DEFAULT '' ,"ru#DESCR" TEXT DEFAULT '' ,"ru#NAME" TEXT DEFAULT '' , PRIMARY KEY ("ID"));
INSERT INTO WebUser_uPg VALUES('tmpl','XHTML-template','XHTML-шаблон','Site from XHTML template.','Сайт із XHTML шаблону',1,'JavaLikeCalc.JavaScript
var pgDir = "Web/";
var firstEl = url.parsePath(0);
var procPg = false;
var tmplPg = "";
var tmplPrcPg = false;
var pgTree = false;
var treeEl = false;
var err = false;
var isUser = SYS.Security.access(user,4,"root","Web",0440);
var isRootUser = SYS.Security.access(user,4,"root","WebRoot",0440);

//> Pages requests process
if(!firstEl.length || (tmplPg=SYS.fileRead(pgDir+firstEl+".html")).length)
{
	//> Early call page''s script
	if(tmplPg.length)
	{
		procPg = SYS.DAQ.JavaLikeCalc.lib_web[(URLprms["script"].isEVal() ? firstEl : URLprms["script"])];
		if(typeof(procPg) == "TCntrNode")
		{
			tmplPrcPg = procPg.call(URLprms,tmplPg,cnts,user);
			//>> Check for full XML or resource request
			var poff = 0;
			if(tmplPrcPg.parse(0,"\n",poff) == "200 OK")
			{
				while((pEl=tmplPrcPg.parse(0,"\n",poff)).length) HTTPvars[pEl.parse(0,":")] = pEl.parse(1,":");
				page = tmplPrcPg.slice(poff);
				return "200 OK";
			}
		}
		else tmplPrcPg = tmplPg;
	}

	//> Parse template
	pgTree = SYS.XMLNode();
	err = pgTree.load(pgDir+"main.html",true,true);

	//> Page time place
	pgTree.getElementBy("time_vl").setAttr("value",SYS.time());
	var sessLife = SYS.XMLNode("get").setAttr("path","/sub_Protocol/mod_HTTP/%2fprm%2flf_tm");
	SYS.cntrReq(sessLife);
	pgTree.getElementBy("ses_vl").setAttr("value",sessLife.text().toInt()*60);

	//> Check access to WEB resource
	if(!user.length || !isUser)
	{
		pgTree.getElementBy("ses_vl").setAttr("value",10);
		pgTree.getElementBy("menu_l").setAttr("style","visibility : hidden;");
		pgTree.getElementBy("menu_r").setAttr("style","visibility : hidden;");
		pgTree.getElementBy("status").setAttr("style","visibility : hidden;");
		tmplPrcPg = SYS.fileRead(pgDir+"loginError.html");
	}
	//> Process include page template
	else if(tmplPg.length)
	{
		//>> Select page display
		treeEl = pgTree.getElementBy(firstEl);
		if(typeof(treeEl) == "XMLNode")
			treeEl.setAttr("class",treeEl.attr("class").replace(new RegExp("menu([^\"]*)"),"menuSel$1"));
	}
	else tmplPrcPg = SYS.fileRead(pgDir+"welcome.html");

	//> Status make
	pgTree.getElementBy("path").setText("/"+firstEl);
	pgTree.getElementBy("user_st").childIns(0,"span").setText(user+"("+(isRootUser?"суперпользователь":"пользователь")+")      ");

	//> Super user access
	if(isUser && !isRootUser)
	{
		pgTree.getElementBy("cfg").parent().setAttr("style","visibility : hidden;");
		pgTree.getElementBy("devices").parent().setAttr("style","visibility : hidden;");

		treeEl = SYS.XMLNode("get").setAttr("path","/sub_Security/usr_"+user+"/%2fprm%2fldscr");
		SYS.cntrReq(treeEl);
		//>> Report disable
		re=treeEl.text().match("Report:\\s+([^\\n]*)");
		if(!re.length || re[1] != "true") pgTree.getElementBy("report").parent().setAttr("style","visibility : hidden;");
		//>> Subsystem''s permition
		var catLs = SYS.XMLNode("get").setAttr("path","/DAQ/ModBus/ZegBee/%2fprm%2fprm");
		SYS.cntrReq(catLs);
		for(var i = 0; i < catLs.childSize(); i++)
		{
			re=treeEl.text().match("sub_"+catLs.childGet(i).attr("id")+":\\s+([^\\n]*)");
			if(!re.length || re[1] == "--") pgTree.getElementBy(catLs.childGet(i).attr("id")).parent().setAttr("style","visibility : hidden;");
		}
	}

	//> Save template
	page = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
			"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"+
			pgTree.save();
	page = page.replace("#####CONTEXT#####",tmplPrcPg);
	return "200 OK";
}

//> Resources get process
if(HTTPreq == "GET")
{
	page = SYS.fileRead(pgDir+url);
	if(!page.length) { page = "Resource ''"+url+"'' call error!"; return "404 Not Found"; }
	//>> Extesion types processing
	var fext = url.lastIndexOf(".");
	fext = (fext > 0) ? url.slice(fext+1) : "";
	if(fext == "png" || fext == "jpg" || fext == "ico") HTTPvars["Content-Type"] = "image/"+fext;
	else if(fext == "css" || fext == "html" || fext == "xml") HTTPvars["Content-Type"] = "text/"+fext;
	else if(fext == "js") HTTPvars["Content-Type"] = "text/javascript";
	//SYS.messDebug("TEST Web","TEST 10: ''"+fext+"'' .");
	return "200 OK";
}

return "404 Not Found";','',1377264714,'Сайт из XHTML шаблона.','XHTML-шаблон');
CREATE TABLE IF NOT EXISTS 'tmplib_DevLib' ("ID" TEXT DEFAULT '' ,"NAME" TEXT DEFAULT '' ,"uk#NAME" TEXT DEFAULT '' ,"ru#NAME" TEXT DEFAULT '' ,"DESCR" TEXT DEFAULT '' ,"uk#DESCR" TEXT DEFAULT '' ,"ru#DESCR" TEXT DEFAULT '' ,"MAXCALCTM" INTEGER DEFAULT '10' ,"PR_TR" INTEGER DEFAULT '1' ,"PROGRAM" TEXT DEFAULT '' ,"uk#PROGRAM" TEXT DEFAULT '' ,"ru#PROGRAM" TEXT DEFAULT '' ,"TIMESTAMP" INTEGER DEFAULT '' , PRIMARY KEY ("ID"));
INSERT INTO tmplib_DevLib VALUES('SCU750','EDWARDS TURBOMOLECULAR PUMPS','','','Typical EDWARDS TURBOMOLECULAR PUMPS (http://edwardsvacuum.com) data acquisition by SCU750 Cotrol Unit protocol.

Author: Roman Savochenko <roman@oscada.org>
Sponsored: Vasiliy Grigoriev from "Vacuum technologies laboratory (http://e-beam.ru)".
Version: 1.0.0
License: GPLv2','','',10,0,'JavaLikeCalc.JavaScript
if(f_start) {
	version = "";
	operModes = new Object();
	operModes[1] = tr("Levitation");
	operModes[2] = tr("No Levitation");
	operModes[3] = tr("Acceleration");
	operModes[4] = tr("Normal");
	operModes[5] = tr("Deceleration (Brake)");
	operModes[6] = tr("Autotest");
	operModes[7] = tr("Tuning");
	operModes[8] = tr("Tuning Complete");
}

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

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

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

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

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

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

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

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

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

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

f_err = t_err;','','',1580047532);
INSERT INTO tmplib_DevLib VALUES('TMH','Power supply for turbomolecular pumps','','','Power supply for turbomolecular pumps of firm SHIMADZU (http://www.shimadzu.com), model EI-R04M.

Author: Roman Savochenko <roman@oscada.org>
Sponsored: Vasiliy Grigoriev from "Vacuum technologies laboratory (http://e-beam.ru)".
Version: 1.0.0
License: GPLv2','','',10,0,'JavaLikeCalc.JavaScript
tr = SYS.Transport.Serial.nodeAt("out_"+transport);
req = SYS.XMLNode("mess").setAttr("ProtIt","TMH").setAttr("addr",addr);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

t_err = "";

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

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

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

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

alLev = 0;
tErr = "";

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

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

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

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

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

//Alarms forming
if(alDelay > 0 && conDelay_ <= 0 && tErr.toInt() != f_err.toInt())	tErr1 = tErr.toInt();
if(alDelay > 0 && conDelay_ > 0 && tErr.toInt() != tErr1)	{ conDelay_ = 0; tErr1 = tErr.toInt(); }
if(alDelay > 0 && conDelay_ < alDelay){ conDelay_ += 1/f_frq; return; }
if(tErr.toInt() && tErr.toInt() != f_err.toInt())	this.alarmSet(DESCR+": "+tErr.parse(1,":"), -(2+alLev));
else if(f_err.toInt() && !tErr.toInt())			this.alarmSet(DESCR+": "+tr("NORM"), 1);
f_err = tErr;
conDelay_ = 0;','','',1561317269);
INSERT INTO tmplib_DevLib VALUES('VKT7','VKT-7','','','Firm "Teplocom" (http://www.teplocom.spb.ru) computer "VKT-7" for complex heat measurement and counting. The device complex enough to provide more parameters, more history and accessed by a nonlinear Serial-based protocol at low speed. The template implements acquisition for all significant parameters, gets for their history by hours, days and result months. Also you can to append easily enough for processing of the remained parameters.

Author: Roman Savochenko <roman@oscada.org>
Sponsored: Vladislav Chubuk
Version: 1.0.0
License: GPLv2','','',60,0,'JavaLikeCalc.JavaScript
using Special.FLibSYS;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

if(t_err.length) {
	SYS.messDebug("/VKT7/TMPL",tr("Error response")+": "+t_err);
	f_err = t_err;
	for(var cA in varsLs) arguments[cA] = EVAL_REAL;
}
else f_err = errAttrs.length ? "11:"+tr("Quality errors")+": "+errAttrs : "0";','','',1580047532);
INSERT INTO tmplib_DevLib VALUES('SMDP','Sycon Multi Drop Protocol','','','STM devices for resonant frequency measurement for mass of deposited films attached to their surface by Sycon Instruments, Inc. (http://www.sycon.com).

Author: Roman Savochenko <roman@oscada.org>
Sponsored: Vasiliy Grigoriev from "Vacuum technologies laboratory (http://e-beam.ru)".
Version: 1.0.0
License: GPLv2','','',10,0,'JavaLikeCalc.JavaScript
if(f_stop) return;
if(f_start) {
	cnt = 0;
	version = "";
	SessId_ = SessId = QlvlTrip_ = QlvlTrip = SlvlTrip_ = SlvlTrip = EVAL_INT;
	Fq_ = Fq = Fm_ = Fm = Density_ = Density = Zratio_ = Zratio = Tooling_ = Tooling = RateReq_ = RateReq = EVAL_REAL;
}

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

t_err = "";

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

f_err = t_err.length ? t_err : "0";','','',1580047251);
INSERT INTO tmplib_DevLib VALUES('IT3','Temperature measurement IT-3','','','Temperature measurement IT-3 from OmskEtalon (http://www.omsketalon.ru).

Author: Roman Savochenko <roman@oscada.org>
Sponsored: Vasiliy Grigoriev from "Vacuum technologies laboratory (http://e-beam.ru)".
Version: 1.2.0
License: GPLv2','','',10,0,'JavaLikeCalc.JavaScript
if(mdPass)	{
	if(f_start) { f_err = EVAL; this.cnt.set(0, 0, 0, true); cnt_ = 0; tDl = 5; }
	if((cnt=this.cnt.get()) != cnt_)	{ tDl = 5; cnt_ = cnt; }
	else if(tDl > 0 && (tDl=tDl-1/f_frq) <= 0) {
		this.err.set("1:"+tr("No data"), 0, 0, true);
		this.T.set(EVAL, 0, 0, true);
		this.H.set(EVAL, 0, 0, true);
		this.L.set(EVAL, 0, 0, true);
		this.relSt.set(EVAL, 0, 0, true);
	}
	return;
}

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

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

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

if(t_err.length) { SYS.messDebug("/IT3/TMPL",tr("Error response")+": "+t_err); f_err = t_err; }
else f_err = "0";','','',1580046256);
INSERT INTO tmplib_DevLib VALUES('IVE_452HS_02','IVE-452HS-02','','','Power supply of beam-electrons evaporator of "Plasma Tech" Ltd, from Moskov.

Author: Roman Savochenko <roman@oscada.org>
Sponsored: Vasiliy Grigoriev from "Vacuum technologies laboratory (http://e-beam.ru)".
Version: 1.0.0
License: GPLv2','','',10,0,'JavaLikeCalc.JavaScript
//Set transport
if(f_start) {
	addr_ = addr;
	transport_ = transport;
	COIA = COIA_ = COUA = COUA_ = COPA = COPA_ = DIA = DUA = DPA = DFA = EVAL_INT;
	COM_DEW = COM_DEW_ = COM_OUT = COM_OUT_ = COM_DEP	= COM_DEP_ = COM_DEL = COM_DEL_ = COM_UF = COM_UF_ =
					DKW = DKZ = DK = DE = EVAL_BOOL;
	tr = SYS.Transport.nodeAt(transport,".");
}

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

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

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

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

if(t_err.length) {
	COIA = COIA_ = COUA = COUA_ = COPA = COPA_ = DIA = DUA = DAC = DPA = DFA = EVAL_INT;
	COM_DEW = COM_DEW_ = COM_OUT = COM_OUT_ = COM_DEP	= COM_DEP_ = COM_DEL = COM_DEL_ = COM_UF = COM_UF_ = 
					DKW = DKZ = DK = DE = EVAL_BOOL;
	SYS.messDebug("/IVE452HS02/TMPL",tr("Error response")+": "+t_err);
	f_err = t_err;
}
else f_err = "0";','','',1580046355);
INSERT INTO tmplib_DevLib VALUES('OPTRIS','OPTRIS CT/CTL','','','OPTRIS CT/CTL communication interface of "Optris" GmbH, from Berlin.

Author: Roman Savochenko <roman@oscada.org>
Sponsored: Vasiliy Grigoriev from "Vacuum technologies laboratory (http://e-beam.ru)".
Version: 1.0.0
License: GPLv2','','',10,0,'JavaLikeCalc.JavaScript
//Set transport
if(f_start) {
	transport_ = transport;
	Tproc = Thead = Tbox = Tact = eps = eps_ = trans = trans_ = EVAL_REAL;
	spIll = spIll_ = EVAL_BOOL;
	tr = SYS.Transport.nodeAt(transport, ".");
}

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

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

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

//Error set
if(t_err.length) {
	Tproc = Thead = Tbox = Tact = eps = eps_ = trans = trans_ = spIll = spIll_ = EVAL_REAL;
	spIll = spIll_ = EVAL_BOOL;
	SYS.messDebug("/OPTRIS/TMPL",tr("Error response")+": "+t_err);
	f_err = t_err;
}
else f_err = "0";','','',1580047532);
INSERT INTO tmplib_DevLib VALUES('CTR','CTR 100, 101','','','The RS232C Serial Interface permits the communication between the digital Oerlikon Leybold Vacuum CERAVAC, from Köln.

Author: Roman Savochenko <roman@oscada.org>
Sponsored: Vasiliy Grigoriev from "Vacuum technologies laboratory (http://e-beam.ru)".
Version: 1.0.0
License: GPLv2','','',10,0,'JavaLikeCalc.JavaScript
//Set transport
if(f_start) {
	transport_ = transport;
	press = FSR_Mantissa = FSR_Exp = EVAL_REAL;
	tr = SYS.Transport.nodeAt(transport, ".");
}

u_err = "";

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

t_err = "0";

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

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

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

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

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

Author: Ruslan Yarmoliuk <rylio74@gmail.com>
Version: 1.0.0
License: GPLv2','','',120,0,'JavaLikeCalc.JavaScript
using Special.FLibSYS;

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

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

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

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

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

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

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

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

t_err = "0";

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

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

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

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

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

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

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

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

	return hash(rez, true);
}

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

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

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

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

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

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

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

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

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

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

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

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

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

	return "0";
}

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

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

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

t_err = "0";

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

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

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

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

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

The DCON commands are supported: #AA

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

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

	return sumV&0xFF;
}

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

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

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

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

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

	return;
}

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

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

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

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

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

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

The DCON commands are supported: @AA, $AA6

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

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

	return sumV&0xFF;
}

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

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

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

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

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

	return;
}

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

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

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

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


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

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

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

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

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

	return sumV&0xFF;
}

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

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

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

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

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

	return;
}

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

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

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

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

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

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

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

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

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

	return sumV&0xFF;
}

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

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

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

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

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

	return;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	tmPoolAll_ = tmPoolAll;	//and reread
}

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

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

	//Input processing
	t_err = processIn();

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

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

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

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

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

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

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

	return val;
}

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

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

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

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

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

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

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

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

	return t_err;
}

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

	return;
}

t_err = "0";

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	return "0";
}

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

if(f_start || f_stop) return;

t_err = "";

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

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

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

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

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

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

	return reqO.attr("err");
}

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

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

if(f_start || f_stop) return;

tErr = "";

//Checking for the transport change and connect
if(tr.isEVal() || transport != transport_)	{
	if(!transport.length) {
		if(!((tVl=this.cntr().cfg("PROT")) == "RTU" || tVl == "ASCII" || tVl == "TCP")) {
			tr = EVAL;
			tErr = "1:"+tr("Output transport is empty and the controller object is not ModBus.");
		}
		else {
			tr = new Object();
			maxBlkSz = this.cntr().cfg("MAX_BLKSZ");
			if(maxBlkSz.isEVal())	maxBlkSz = 12;
			fragMerge = this.cntr().cfg("FRAG_MERGE");
			if(fragMerge.isEVal())	fragMerge = false;
		}
	} else tr = (tr_=SYS.Transport.outAt(transport)) ? tr_ : EVAL;
		    //SYS.Transport[transport.parse(0)]["out_"+transport.parse(1)];

	maxBlkSz = max(10, min(200,maxBlkSz));
	transport_ = transport;
}
if(tErr.toInt()) ;
else if(tr.isEVal())
	tErr = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
else if(addr < 0 || addr > 247)
	tErr = "2:"+tr("Address ''%1'' out of range [0...247].").replace("%1",addr.toString());
else {
	//Check for changed attributes and perform writing
	for(var iDt in dt) {
		isEOL = (iDt == "10000");
		itO = dt[iDt];
		tVl = this[itO.id].get();
		if(isEOL || !itO.wr || tVl.isEVal() || itO.val == tVl)	continue;
		PDU = Special.FLibSYS.IO("", "", "b");
		PDU.wr(16,"uint8").wr(itO.addr,"uint16").wr(floor(itO.sz/2),"uint16").wr(itO.sz,"uint8");
		if(itO.tp == "u" || itO.tp == "u2")			PDU.wr(tVl,"uint16");
		else if(itO.tp == "i" || itO.tp == "i2")	PDU.wr(tVl,"int16");
		else if(itO.tp == "u4")	{
			if(itO.rev)	PDU.wr(tVl>>16, "uint16").wr(tVl&0xFFFF, "uint16");
			else	PDU.wr(tVl&0xFFFF, "uint16").wr(tVl>>16, "uint16");
		}
		else if(itO.tp == "i4") {
			if(itO.rev)	PDU.wr(tVl>>16, "int16").wr(tVl&0xFFFF, "uint16");
			else	PDU.wr(tVl&0xFFFF, "uint16").wr(tVl>>16, "int16");
		}
		else if(itO.tp == "s")		PDU.wr(tVl.slice(0,16),"char").wr(" ","char",16-min(16,tVl.length));
		else {
			w1 = w2 = 0;
			if(itO.rev)	Special.FLibSYS.floatSplitWord(tVl, w2, w1);
			else	Special.FLibSYS.floatSplitWord(tVl, w1, w2);
			PDU.wr(w1, "uint16").wr(w2, "uint16");
		}
		//SYS.messInfo("/MB/"+this.cfg("SHIFR"),"reqPDU="+SYS.strDecode(PDU.string,"Bin"," "));
		if(!req(PDU.string).toInt())	itO.val = tVl;
		//SYS.messInfo("/MB/"+this.cfg("SHIFR"),"respPDU="+SYS.strDecode(PDU.string,"Bin"," "));
	}

	//Same requests for the data
	for(iSp = 0; iSp < 2; iSp++) {
		dtW = (iSp == 1) ? dtI : dt;

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

if(!tErr.length)	tErr = "0";
if(tErr.toInt()) {
	if(!tr.isEVal() && tr.start()) tr.start(false);
	if(f_err != tErr)
		for(iSp = 0; iSp < 2; iSp++) {
			dtW = (iSp == 1) ? dtI : dt;
			for(var iDt in dtW) {
				itO = dtW[iDt];
				if(iDt == "10000")	continue;
				this[itO.id].set(EVAL, 0, 0, true);
			}
		}
}
f_err = tErr;','','',1726384559);
INSERT INTO tmplib_DevLib VALUES('SLOT','Slot LTD devices','','','The template implements support for counters and correctors of the "SLOT" LTD production. The devices protocol is flexible and uniform for implementation of reading all available archives also as setting the configuration fields, but currently there was need only for reading the hourly archives, so the template reads the daily and hourly archives and some set of the instantaneous parameters.

The template includes also code for connect the counters through modems using the AT-commands.

Author: Roman Savochenko <roman@oscada.org>
Total complexity: 4.8 HD
Sponsored by: Vinnica Poultry Farm
Version: 0.8.3
License: GPLv2','','',240,0,'JavaLikeCalc.JavaScript
//Modem requests
function modemWr(ln, noNewLn)	{ tr.messIO(ln + ((noNewLn==true)?"":"\x0D\x0A"), 0, 0); }

function modemExpect(vLs, tm) {
	stTm = SYS.time();

	for(resp = ""; (SYS.time()-stTm) < tm; ) {
		resp += tr.messIO("");
		for(off = 0; (tVl=vLs.parseLine(0,off)).length; )
			if(resp.indexOf(tVl+"\x0D\x0A") >= 0)	return tVl;
	}
	return "";	
}

//Same request to the device
function req(data) {
	if(data == EVAL)	data = "";
	addr_ = addr.toString(10).toInt(16);
	req = SYS.strFromCharCode(0x55, max(0,min(1,pipeN)), addr_&0xFF, (addr_>>8)&0xFF, data.length+7) + data;
	//Calc and append the CRC
	CRC = Special.FLibSYS.CRC(req, 16, 0x8005);
	req += SYS.strFromCharCode(CRC&0xFF, CRC>>8);
	SYS.messDebug("/SLOT/"+this.cfg("SHIFR"), tr("Request")+": "+SYS.strDecode(req,"Bin"," "));

	resp = tr.messIO(req);
	//Waiting the acceptance
	while(resp.length && (resp.length < 8 || resp.length < resp.charCodeAt(4)) && (tresp=tr.messIO("")).length) resp += tresp;
	if(resp.length < 8 || resp.charCodeAt(0) != 0x55 || resp.length != resp.charCodeAt(4))
		return tr("3:No response or the response is inconsistent. ");
	if(Special.FLibSYS.CRC(resp.slice(0,-2),16,0x8005) != resp.charCodeAt(resp.length-2,"UTF-16LE"))
		return tr("3:Error the response CRC. ");

	SYS.messDebug("/SLOT/"+this.cfg("SHIFR"), tr("Response")+": "+SYS.strDecode(resp,"Bin"," "));

	data = resp.slice(6,-2);
	if((tVl=resp.charCodeAt(5))&0x80) {
		if(tVl == 0x81)	return tr("4:Error at the package receiving. ");
		else if(tVl == 0x82)	return tr("4:Invalid device address. ");
		else if(tVl == 0x83)	return tr("4:Invalid function number. ");
		else if(tVl == 0x84)	return tr("4:Archive empty on pointed interval. ");
		else if(tVl == 0x85)	return tr("4:Error password. ");
		else if(tVl == 0x86)	return tr("4:Unknown block number at the request. ");
		else if(tVl == 0x87)	return tr("4:The compression constant is outranged. ");
		else if(tVl == 0x88)	return tr("4:The gas density at the writing is outranged. ");
		else if(tVl == 0x89)	return tr("4:The m.p. of CO2 at the writing is outranged. ");
		else if(tVl == 0x8A)	return tr("4:The m.p. of N2 at the writing is outranged. ");
		else	return tr("4:Unknown error %1. ").replace("%1", tVl.toString());
	}
	else if((tVl&0x7F) != (req.charCodeAt(5)&0x7F))
		return tr("4:The function is error or inconsistent to the request one. ");

	return "0";
}

//Parse float
function floatGet(ivl) {
	if((ivl&0xFE) == 0)	return 0;
	if(!((ivl>>24)&0x80))	return EVAL;
	sign = (ivl&0x01);
	rez = pow(2,((ivl>>1)&0x7F)-64) * (ivl>>8) / pow(2,23);
	return sign ? -rez : rez;
}
function floatGet5(ivl) {
	if((ivl&0xFE) == 0)	return 0;
	if(!((ivl>>40)&0x80))	return EVAL;
	sign = (ivl&0x01);

	rez = pow(2,((ivl>>1)&0x7F)-64) * (ivl>>8) / pow(2,39);
	return sign ? -rez : rez;
}

//Produce float
/*function floatSet(vl) {
	exp = 0;
	while(vl/pow(2,exp) > 1 && exp < 128)		exp++;
	while(vl/pow(2,exp) < 0.5 && exp > -127)exp--;
	mts = (vl/pow(2,exp))*pow(2,24) + 0.5;
	if(vl >= 0) mts = mts&0x7FFFFF;
	return ((mts>>16)&0xFF).toString(16,2)+(exp+127).toString(16,2)+(mts&0xFFFF).toString(16,4);
}*/

//Seting value to the archive
function archSet(attr, vTm, vl, arh) {
	SYS.messDebug("/SLOT/"+this.cfg("SHIFR"), "attr="+attr+"; vTm="+SYS.strftime(vTm)+"; arh="+arh+"; vl="+vl);
	atrO = this[attr];
	if(!atrO || atrO.isEVal()) return;
	arguments[attr] = vl;	//Set the current-last value
	//if(vTm > atrO.time())	atrO.set(vl,vTm,0,true);
	if(atrO.arch())	atrO.arch().setVal(vTm*1e6, vl, arh);
}

//Main process
if(f_start)	{ reset = false; schedTrueTm = SYS.time(); }

if(reset) {
	reset = false, arhLastD = arhLastD_U = 0, arhLastH = arhLastH_U = 0; schedTrueTm = 1;

	devTime = devTp = devSWVers = devSN = objNm = devModel = devPollPer = devContrHour = devContrDay = devSesonTimeCh = EVAL;
	apH = apMax = apMin = apConst = EVAL;
	tMax = tMin = tConst = pulseVl = dpMax = dpMin = EVAL;
	fMax = fMin = cntrCutOffConst = flowConst = EVAL;
	pipeD = pipeTExt = pipeBeCoef = pipeCeCoef = pipeRoughCoef = EVAL;
	diaphD = diaphTExt = diaphBeCoef = diaphCeCoef = diaphEdgeBluntCoef = EVAL;

	correctorTime = dP = P = T = Fstd = Fwork = EVAL;
}
if(f_start || f_stop) return;

tErr = "";

if(!(tr=SYS.Transport.outAt(transport)) || !tr.start(true))
	tErr = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
else if(addr < 0 || addr.toString(10).toInt(16) > 65535)
	tErr = "2:"+tr("Address ''%1'' out of range [0...65535].").replace("%1",addr.toString());
else if(!sched.length || sched.isEVal() || SYS.time() >= SYS.cron(sched,schedTrueTm)) {
	//ModemMode: connect
	inMdmCon = false;
	if(modem.length) {
		mdmTm = modemTm.parse(0,":").toReal();
		conTm = modemTm.parse(1,":").toReal();
		mComms = modem + ":ATH";
		// Init the modem
		for(iTr = 2; iTr >= 0; iTr--) {
			tErr = "";
			for(comPos = 1; !tErr.toInt() && (mComm=mComms.parse(comPos,":")).length; comPos++) {
				modemWr(mComm);
				if(!(resp=modemExpect("OK",mdmTm)).length) {
					// Try to return the modem mode
					if(comPos == 1) {
						modemWr("+++", true);
						if(modemExpect("OK",mdmTm).length)	continue;
					}
					tErr = "11:"+tr("No modem response or the response ''%1'' is inconsistent. ").replace("%1",resp);
				}
			}
			if(!tErr.toInt()) break;
		}
		// Call the remote number
		if(!tErr.toInt()) {
			modemWr("ATD"+modem.parse(0,":"));
			if((resp=modemExpect("CONNECT\nBUSY\nNO CARRIER\nNO DIALTONE",conTm)) != "CONNECT")
				tErr = "12:"+tr("Error remote node call ''%1'': ''%2''. ")
						.replace("%1",modem.parse(0,":"))	.replace("%2",resp.length?resp:"NO RESPONSE");
		}
		if(!tErr.toInt())	inMdmCon = true;
	}

	//Reading the System Parameters
	if(!tErr.toInt()) {
		data = SYS.strFromCharCode(1);
		if(!(tErr=req(data)).toInt()) {
			devTime_ = data.charCodeAt(0, "UTF-32LE");
			devTime_U = SYS.strptime("2000-1-1 0:00:00") + (devTime_>>8)*60 + (devTime_&0xFF);
			devTime = SYS.strftimegm(devTime_U) + " ("+devTime_U+")";
			if((tVl=data.charCodeAt(4,"UTF-16LE")) == 1)	devTp = "OE-VPT";
			else if(tVl == 2)	devTp = "OE - VT";
			else if(tVl == 3)	devTp = "OE-VPTf";
			else if(tVl == 4)	devTp = "OE-22ЛА";
			else if(tVl == 11)	devTp = "ОЕ-22ДМ (ОЕ-22ТМ)";
			else if(tVl == 12)	devTp = "ОЕ-22ДА (OE-22ТА)";
			else devTp = "Unknown "+tVl;
			devSWVers = data.slice(6, 8);
			devSN = data.charCodeAt(8, "UTF-16LE");
			objNm = data.slice(10, 26);
			devModel = data.slice(26, 12);
			devPollPer = data.charCodeAt(42);
			devContrHour = data.charCodeAt(43);
			devContrDay = data.charCodeAt(44);
			devSesonTimeCh = data.charCodeAt(45);
			apH = floatGet(data.charCodeAt(50,"UTF-32LE"));
			apMax = floatGet(data.charCodeAt(58,"UTF-32LE"));
			apMin = floatGet(data.charCodeAt(62,"UTF-32LE"));
			apConst = floatGet(data.charCodeAt(66,"UTF-32LE"));
			tMax = floatGet(data.charCodeAt(74,"UTF-32LE"));
			tMin = floatGet(data.charCodeAt(80,"UTF-32LE"));
			tConst = floatGet(data.charCodeAt(84,"UTF-32LE"));
			pulseVl = floatGet(data.charCodeAt(94,"UTF-32LE"));
			dpMax = floatGet(data.charCodeAt(98,"UTF-32LE"));
			dpMin = floatGet(data.charCodeAt(102,"UTF-32LE"));
			fMax = floatGet(data.charCodeAt(106,"UTF-32LE"));
			fMin = floatGet(data.charCodeAt(110,"UTF-32LE"));
			cntrCutOffConst = floatGet(data.charCodeAt(114,"UTF-32LE"));
			flowConst = floatGet(data.charCodeAt(118,"UTF-32LE"));
			pipeD = floatGet(data.charCodeAt(122,"UTF-32LE"));
			pipeTExt = floatGet(data.charCodeAt(126,"UTF-32LE"));
			pipeBeCoef = floatGet(data.charCodeAt(130,"UTF-32LE"));
			pipeCeCoef = floatGet(data.charCodeAt(134,"UTF-32LE"));
			pipeRoughCoef = floatGet(data.charCodeAt(138,"UTF-32LE"));
			diaphD = floatGet(data.charCodeAt(150,"UTF-32LE"));
			diaphTExt = floatGet(data.charCodeAt(154,"UTF-32LE"));
			diaphBeCoef = floatGet(data.charCodeAt(158,"UTF-32LE"));
			diaphCeCoef = floatGet(data.charCodeAt(162,"UTF-32LE"));
			diaphEdgeBluntCoef = floatGet(data.charCodeAt(166,"UTF-32LE"));
		}
	}

	//Reading the Instanceous and Calculated parameters
	if(!tErr.toInt()) {
		data = SYS.strFromCharCode(4);
		if(!(tErr=req(data)).toInt()) {
			correctorTime_ = data.charCodeAt(0, "UTF-32LE");
			tVl = SYS.strptime("2000-1-1 0:00:00") + (correctorTime_>>8)*60 + (correctorTime_&0xFF);
			correctorTime = SYS.strftimegm(tVl) + " ("+tVl+")";
			dP = floatGet(data.charCodeAt(18,"UTF-32LE"));
			P = floatGet(data.charCodeAt(22,"UTF-32LE"));
			T = floatGet(data.charCodeAt(26,"UTF-32LE"));
			Fstd = floatGet(data.charCodeAt(30,"UTF-32LE"));
			Fwork = floatGet(data.charCodeAt(34,"UTF-32LE"));
		}
	}

	tmCur = SYS.time();

	//Requesting the dayly archive
	if(arhD.length) {
		toSave = false;

		for(rBlk = -1; !tErr.toInt() && rBlk != 0 && (SYS.time()-tmCur) < arhTmLim && ((devTime_>>8)-(arhLastD>>8)) > 2*24*60; ) {
			if(rBlk < 0)	data = SYS.strFromCharCode(5) +
											SYS.strFromCharUTF("UTF-32LE",(arhLastD>>8)<<8) +
											SYS.strFromCharUTF("UTF-32LE",(devTime_>>8)<<8);
			else data = SYS.strFromCharCode(6, 0, rBlk);

			if(!(tErr=req(data)).toInt()) {
				itN = data.charCodeAt(0);
				rBlk = data.charCodeAt(1);
				for(iIt = 0; iIt < itN; iIt++) {
					arhLastD = data.charCodeAt(2 + iIt*30, "UTF-32LE");
					arhLastD_U = 24*60*60*floor((SYS.strptime("2000-1-1 0:00:00") + (arhLastD>>8)*60 + (arhLastD&0xFF))/(24*60*60));
					archSet("Fstd", arhLastD_U, floatGet5((data.charCodeAt(2 + iIt*30 + 8,"UTF-16LE")<<32)+data.charCodeAt(2 + iIt*30 + 4,"UTF-32LE")), arhD);
					archSet("Fwork", arhLastD_U, floatGet5((data.charCodeAt(2 + iIt*30 + 14,"UTF-16LE")<<32)+data.charCodeAt(2 + iIt*30 + 10,"UTF-32LE")), arhD);
					archSet("dP", arhLastD_U, floatGet(data.charCodeAt(2 + iIt*30 + 16,"UTF-32LE")), arhD);
					archSet("P", arhLastD_U, floatGet(data.charCodeAt(2 + iIt*30 + 20,"UTF-32LE")), arhD);
					archSet("T", arhLastD_U, floatGet(data.charCodeAt(2 + iIt*30 + 24,"UTF-32LE")), arhD);
					itV4 = data.charCodeAt(2 + iIt*30 + 28,"UTF-16LE");	//Statuses
					archSet("itSt", arhLastD_U, ((itV4&0x80)?"A":"")+((itV4&0x40)?"В":"")+((itV4&0x3F)?"П":""), arhD);
					toSave = true;
				}
			}
		}
		if(toSave)	SYS.cntrReq(SYS.XMLNode("save").setAttr("path",this.nodePath()+"/%2fobj").setAttr("force",1));
	}

	//Requesting the hourly archive
	if(arhH.length) {
		toSave = false;

		for(rBlk = -1; !tErr.toInt() && rBlk != 0 && (SYS.time()-tmCur) < arhTmLim && ((devTime_>>8)-(arhLastH>>8)) > /*2*/60; ) {
			if(rBlk < 0)	data = SYS.strFromCharCode(7) +
											SYS.strFromCharUTF("UTF-32LE",arhLastH) +
											SYS.strFromCharUTF("UTF-32LE",devTime_);
			else data = SYS.strFromCharCode(8, 0, rBlk);

			if(!(tErr=req(data)).toInt()) {
				itN = data.charCodeAt(0);
				rBlk = data.charCodeAt(1);
				for(iIt = 0; iIt < itN; iIt++) {
					arhLastH = data.charCodeAt(2 + iIt*30, "UTF-32LE");
					arhLastH_U = 60*60*floor((SYS.strptime("2000-1-1 0:00:00") + (arhLastH>>8)*60 + (arhLastH&0xFF))/(60*60));
					archSet("Fstd", arhLastH_U, floatGet5((data.charCodeAt(2 + iIt*30 + 8,"UTF-16LE")<<32)+data.charCodeAt(2 + iIt*30 + 4,"UTF-32LE")), arhH);
					archSet("Fwork", arhLastH_U, floatGet5((data.charCodeAt(2 + iIt*30 + 14,"UTF-16LE")<<32)+data.charCodeAt(2 + iIt*30 + 10,"UTF-32LE")), arhH);
					archSet("dP", arhLastH_U, floatGet(data.charCodeAt(2 + iIt*30 + 16,"UTF-32LE")), arhH);
					archSet("P", arhLastH_U, floatGet(data.charCodeAt(2 + iIt*30 + 20,"UTF-32LE")), arhH);
					archSet("T", arhLastH_U, floatGet(data.charCodeAt(2 + iIt*30 + 24,"UTF-32LE")), arhH);
					itV4 = data.charCodeAt(2 + iIt*30 + 28,"UTF-16LE");	//Statuses
					archSet("itSt", arhLastH_U, ((itV4&0x80)?"A":"")+((itV4&0x40)?"В":"")+((itV4&0x3F)?"П":""), arhH);
					toSave = true;
				}
			}
		}
		if(toSave)	SYS.cntrReq(SYS.XMLNode("save").setAttr("path",this.nodePath()+"/%2fobj").setAttr("force",1));
	}

	//Disconnection the modem node
	if(inMdmCon) {
		modemWr("+++", true);
		if(modemExpect("OK",mdmTm).length) { modemWr("ATH"); modemExpect("OK", mdmTm); }
	}
}

//Error set
if(!tErr.length)	tErr = "0";
if(tErr.toInt()) {
	if(!tr.isEVal() && tr.start()) tr.start(false);
	if(f_err != tErr)
		SYS.messDebug("/SLOT/"+this.cfg("SHIFR"), tr("Error")+": "+tErr);
}
else {
	prcSt = "";
	schedContinue = false;
	if(arhD.length) {
		arhLastD_U = 24*60*60*floor((SYS.strptime("2000-1-1 0:00:00") + (arhLastD>>8)*60 + (arhLastD&0xFF))/(24*60*60));
		if(arhLastD && ((devTime_>>8)-(arhLastD>>8)) <= 2*24*60)
			prcSt += tr("Days: ") + tr("Trace for current time=%1").replace("%1",SYS.strftimegm(arhLastD_U,"%Y/%m/%d")) + "; ";
		else {
			prcSt += tr("Days: ") + tr("Initial reading in time=%1").replace("%1",SYS.strftimegm(arhLastD_U,"%Y/%m/%d")) + "; ";
			schedContinue = true;
		}
	}
	if(arhH.length) {
		arhLastH_U = 60*60*floor((SYS.strptime("2000-1-1 0:00:00") + (arhLastH>>8)*60 + (arhLastH&0xFF))/(60*60));
		if(arhLastH && ((devTime_>>8)-(arhLastH>>8)) <= 2*60)
			prcSt += tr("Hours: ") + tr("Trace for current time=%1").replace("%1",SYS.strftimegm(arhLastH_U,"%Y-%m-%d %H")) + "; ";
		else {
			prcSt += tr("Hours: ") + tr("Initial reading in time=%1").replace("%1",SYS.strftimegm(arhLastH_U,"%Y-%m-%d %H")) + "; ";
			schedContinue = true;
		}
	}

	if(!schedContinue) schedTrueTm = SYS.time();
	tErr += ((sched.length && !sched.isEVal())?"; "+tr("Next scheduled call")+" "+SYS.strftime(SYS.cron(sched,schedTrueTm)):"") + (prcSt.length?"; "+prcSt:"");
}
f_err = tErr;','','',1672837570);
INSERT INTO tmplib_DevLib VALUES('FF_LE','FF LE-03MB CT','','','The template implements support for counters of electricity bidirectional on 1-phase/3-phase with analyse the network parameters LE-03MB CT (https://www.fif.com.pl/en/usage-electric-power-meters/639-electric-energy-meter-le-03mb-ct.html) of the firm "F&F (http://www.fif.com.pl/)".

Protocol of the counters is M-bus (https://en.wikipedia.org/wiki/Meter-Bus) and it doesn''t provide of reading the data history, so the template has to obtain instant values only with need periodicity related to the network quality. Data of the counters are completely static, that is all their representing attributes are provided by the template.

!!! Coefficients for P (in 100) and P_{N} (in 1000) was set at demands of the end user, so that is no an error.

Author: Roman Savochenko <roman@oscada.org>
Total complexity: 0.9 HD
Sponsored by: Vinnica Poultry Farm
Version: 1.0.0
License: GPLv2','','',240,0,'JavaLikeCalc.JavaScript
//Same request to the device
function req(data, pref, respPref) {
	if(data == EVAL)	data = "";

	for(SUM = 0, iCh = 0; iCh < data.length; iCh++)
		SUM += data.charCodeAt(iCh);

	req = pref + data + SYS.strFromCharCode(SUM&0xFF, 0x16);

	SYS.messDebug("/FF/"+this.cfg("SHIFR"), tr("Request")+": "+SYS.strDecode(req,"Bin"," "));

	resp = tr.messIO(req);
	//Waiting the acceptance
	while(resp.length && (tresp=tr.messIO("")).length) resp += tresp;
	if(resp.length < 21 || resp.indexOf(respPref) != 0 || resp.charCodeAt(resp.length-1) != 0x16)
		return tr("3:No response or the response is inconsistent. ");

	data = resp.slice(respPref.length, -2);

	SYS.messDebug("/FF/"+this.cfg("SHIFR"), tr("Response")+": "+SYS.strDecode(data,"Bin"," "));

	for(SUM = 0, iCh = 0; iCh < data.length; iCh++)
		SUM += data.charCodeAt(iCh);
	if(resp.charCodeAt(resp.length-2) != (SUM&0xFF))
		return tr("3:Control sum error "+(SUM&0xFF)+" != "+resp.charCodeAt(resp.length-2));

	return "0";
}

if(f_start || f_stop) {
	EnActFull = EnActImp = EnActExp = EnActFullDrop = EnActImpDrop = EnActExpDrop = EVAL;
	EnReactFull = EnReactImp = EnReactExp = EnReactFullDrop = EnReactImpDrop = EnReactExpDrop = EVAL;
	V_L1 = V_L2 = V_L3 = V_L12 = V_L23 = V_L31 = EVAL;
	I_1 = I_2 = I_3 = I_N = EVAL;
	P = P_1 = P_2 = P_3 = EVAL;
	Pr = Pr_1 = Pr_2 = Pr_3 = EVAL;
	CoefP = CoefP_1 = CoefP_2 = CoefP_3 = EVAL;
	return;
}

tErr = "";

if(!(tr=SYS.Transport.outAt(transport)) || !tr.start(true))
	tErr = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
else if(addr < 0 || addr > 255)
	tErr = "2:"+tr("Address ''%1'' out of range [0...255].").replace("%1",addr.toString());
else {
	//Reading the electricity information
	if(!tErr.toInt()) {
		data = SYS.strFromCharCode(0x7B, addr);
		if((tErr=req(data,SYS.strFromCharCode(0x10),SYS.strFromCharCode(0x68,0x5D,0x5D,0x68))).toInt()) ;
		else if(data.charCodeAt(1) != addr)	tErr = tr("4:Error the address response");
		else {
			EnActFull = SYS.strDecode(SYS.strEncode(data.slice(17+6*0,17+6*0+4),"Reverse"),"Bin").toInt() / 100;
			EnActImp = SYS.strDecode(SYS.strEncode(data.slice(17+6*1,17+6*1+4),"Reverse"),"Bin").toInt() / 100;
			EnActExp = SYS.strDecode(SYS.strEncode(data.slice(17+6*2,17+6*2+4),"Reverse"),"Bin").toInt() / 100;
			EnActFullDrop = SYS.strDecode(SYS.strEncode(data.slice(17+6*3,17+6*3+4),"Reverse"),"Bin").toInt() / 100;
			EnActImpDrop = SYS.strDecode(SYS.strEncode(data.slice(17+6*4,17+6*4+4),"Reverse"),"Bin").toInt() / 100;
			EnActExpDrop = SYS.strDecode(SYS.strEncode(data.slice(17+6*5,17+6*5+4),"Reverse"),"Bin").toInt() / 100;

			EnReactFull = (tVl=SYS.strDecode(SYS.strEncode(data.slice(54+7*0,54+7*0+4),"Reverse"),"Bin")).toInt() / 100;
			EnReactImp = (tVl=SYS.strDecode(SYS.strEncode(data.slice(54+7*1,54+7*1+4),"Reverse"),"Bin")).toInt() / 100;
			EnReactExp = (tVl=SYS.strDecode(SYS.strEncode(data.slice(54+7*2,54+7*2+4),"Reverse"),"Bin")).toInt() / 100;
			EnReactFullDrop = (tVl=SYS.strDecode(SYS.strEncode(data.slice(54+7*3,54+7*3+4),"Reverse"),"Bin")).toInt() / 100;
			EnReactImpDrop = (tVl=SYS.strDecode(SYS.strEncode(data.slice(54+7*4,54+7*4+4),"Reverse"),"Bin")).toInt() / 100;
			EnReactExpDrop = (tVl=SYS.strDecode(SYS.strEncode(data.slice(54+7*5,54+7*5+4),"Reverse"),"Bin")).toInt() / 100;
		}
	}

	//Reading the Instanceous
	if(!tErr.toInt()) {
		data = SYS.strFromCharCode(0x53, addr, 0xB1);
		if((tErr=req(data,SYS.strFromCharCode(0x68,0x03,0x03,0x68),SYS.strFromCharCode(0x68,0x90,0x90,0x68))).toInt()) ;
		else if(data.charCodeAt(1) != addr)	tErr = tr("4:Error the address response");
		else {
			V_L1 = SYS.strDecode(SYS.strEncode(data.slice(18+6*0,18+6*0+3),"Reverse"),"Bin").toInt() / 100;
			V_L2 = SYS.strDecode(SYS.strEncode(data.slice(18+6*1,18+6*1+3),"Reverse"),"Bin").toInt() / 100;
			V_L3 = SYS.strDecode(SYS.strEncode(data.slice(18+6*2,18+6*2+3),"Reverse"),"Bin").toInt() / 100;
			V_L12 = SYS.strDecode(SYS.strEncode(data.slice(18+6*3,18+6*3+3),"Reverse"),"Bin").toInt() / 100;
			V_L23 = SYS.strDecode(SYS.strEncode(data.slice(18+6*4,18+6*4+3),"Reverse"),"Bin").toInt() / 100;
			V_L31 = SYS.strDecode(SYS.strEncode(data.slice(18+6*5,18+6*5+3),"Reverse"),"Bin").toInt() / 100;
			I_1 = SYS.strDecode(SYS.strEncode(data.slice(18+6*6,18+6*6+3),"Reverse"),"Bin").toInt() / 1000;
			I_2 = SYS.strDecode(SYS.strEncode(data.slice(18+6*7,18+6*7+3),"Reverse"),"Bin").toInt() / 1000;
			I_3 = SYS.strDecode(SYS.strEncode(data.slice(18+6*8,18+6*8+3),"Reverse"),"Bin").toInt() / 1000;
			I_N = SYS.strDecode(SYS.strEncode(data.slice(18+6*9,18+6*9+3),"Reverse"),"Bin").toInt() / 1000;

			P = SYS.strDecode(SYS.strEncode(data.slice(77+5*0,77+5*0+3),"Reverse"),"Bin").toInt() / 100;
			P_1 = SYS.strDecode(SYS.strEncode(data.slice(77+5*1,77+5*1+3),"Reverse"),"Bin").toInt() / 1000;
			P_2 = SYS.strDecode(SYS.strEncode(data.slice(77+5*2,77+5*2+3),"Reverse"),"Bin").toInt() / 1000;
			P_3 = SYS.strDecode(SYS.strEncode(data.slice(77+5*3,77+5*3+3),"Reverse"),"Bin").toInt() / 1000;

			Pr = SYS.strDecode(SYS.strEncode(data.slice(98+6*0,98+6*0+3),"Reverse"),"Bin").toInt() / 1000;
			Pr_1 = SYS.strDecode(SYS.strEncode(data.slice(98+6*1,98+6*1+3),"Reverse"),"Bin").toInt() / 10000;
			Pr_2 = SYS.strDecode(SYS.strEncode(data.slice(98+6*2,98+6*2+3),"Reverse"),"Bin").toInt() / 10000;
			Pr_3 = SYS.strDecode(SYS.strEncode(data.slice(98+6*3,98+6*3+3),"Reverse"),"Bin").toInt() / 10000;

			CoefP = SYS.strDecode(SYS.strEncode(data.slice(122+5*0,122+5*0+2),"Reverse"),"Bin").toInt() / 1000;
			CoefP_1 = SYS.strDecode(SYS.strEncode(data.slice(122+5*1,122+5*1+2),"Reverse"),"Bin").toInt() / 1000;
			CoefP_2 = SYS.strDecode(SYS.strEncode(data.slice(122+5*2,122+5*2+2),"Reverse"),"Bin").toInt() / 1000;
			CoefP_3 = SYS.strDecode(SYS.strEncode(data.slice(122+5*3,122+5*3+2),"Reverse"),"Bin").toInt() / 1000;
		}
	}
}

//Error set
if(!tErr.length)	tErr = "0";
if(tErr.toInt()) {
	if(!tr.isEVal() && tr.start()) tr.start(false);
	if(f_err != tErr) {
		SYS.messDebug("/FF/"+this.cfg("SHIFR"), tr("Error")+": "+tErr);

		EnActFull = EnActImp = EnActExp = EnActFullDrop = EnActImpDrop = EnActExpDrop = EVAL;
		EnReactFull = EnReactImp = EnReactExp = EnReactFullDrop = EnReactImpDrop = EnReactExpDrop = EVAL;
		V_L1 = V_L2 = V_L3 = V_L12 = V_L23 = V_L31 = EVAL;
		I_1 = I_2 = I_3 = I_N = EVAL;
		P = P_1 = P_2 = P_3 = EVAL;
		Pr = Pr_1 = Pr_2 = Pr_3 = EVAL;
		CoefP = CoefP_1 = CoefP_2 = CoefP_3 = EVAL;
	}
}
f_err = tErr;','','',1707892136);
INSERT INTO tmplib_DevLib VALUES('ergomera625','Ergomera 625','','','The template implements support for Ergomera 625 Counters of electricity and it based on the common template "ModBus base (mbBase) (http://oscada.org/wiki/Libs/Devices#mbBase)" since protocol of the counters grounded on standard ModBus function in main parameters.

!!! The template currently supports only reading the ModBus registers without accessing the history due to the device protocol has got common problems here.

Author: Roman Savochenko <roman@oscada.org>
Total complexity: 0.3 HD
Sponsored by: Vinnica Poultry Farm
Version: 0.1.0
License: GPLv2','','',30,0,'JavaLikeCalc.JavaScript
//Same request to the device
function req(PDU) {
	// For placing into the module ModBus
	if(!transport.length)	return this.cntr().messIO(PDU);

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

	return reqO.attr("err");
}

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

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

if(f_start || f_stop) return;

tErr = "";

//Checking for the transport change and connect
if(tr.isEVal() || transport != transport_)	{
	if(!transport.length) {
		if(!((tVl=this.cntr().cfg("PROT")) == "RTU" || tVl == "ASCII" || tVl == "TCP")) {
			tr = EVAL;
			tErr = "1:"+tr("Output transport is empty and the controller object is not ModBus.");
		}
		else {
			tr = new Object();
			maxBlkSz = this.cntr().cfg("MAX_BLKSZ");
			if(maxBlkSz.isEVal())	maxBlkSz = 12;
			fragMerge = this.cntr().cfg("FRAG_MERGE");
			if(fragMerge.isEVal())	fragMerge = false;
		}
	} else tr = (tr_=SYS.Transport.outAt(transport)) ? tr_ : EVAL;

	maxBlkSz = max(10, min(200,maxBlkSz));
	transport_ = transport;
}
if(tErr.toInt()) ;
else if(tr.isEVal())
	tErr = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
//else if(addr < 0 || addr > 247)
//	tErr = "2:"+tr("Address ''%1'' out of range [0...247].").replace("%1",addr.toString());
else {
	//Check for changed attributes and perform writing
	for(var iDt in dt) {
		isEOL = (iDt == "10000");
		itO = dt[iDt];
		tVl = this[itO.id].get();
		if(isEOL || !itO.wr || tVl.isEVal() || itO.val == tVl)	continue;
		PDU = Special.FLibSYS.IO("", "", "b");
		PDU.wr(16,"uint8").wr(itO.addr,"uint16").wr(floor(itO.sz/2),"uint16").wr(itO.sz,"uint8");
		if(itO.tp == "u" || itO.tp == "u2")			PDU.wr(tVl,"uint16");
		else if(itO.tp == "i" || itO.tp == "i2")	PDU.wr(tVl,"int16");
		else if(itO.tp == "u4")	{
			if(itO.rev)	PDU.wr(tVl>>16, "uint16").wr(tVl&0xFFFF, "uint16");
			else	PDU.wr(tVl&0xFFFF, "uint16").wr(tVl>>16, "uint16");
		}
		else if(itO.tp == "i4") {
			if(itO.rev)	PDU.wr(tVl>>16, "int16").wr(tVl&0xFFFF, "uint16");
			else	PDU.wr(tVl&0xFFFF, "uint16").wr(tVl>>16, "int16");
		}
		else if(itO.tp == "s")		PDU.wr(tVl.slice(0,16),"char").wr(" ","char",16-min(16,tVl.length));
		else {
			w1 = w2 = 0;
			if(itO.rev)	Special.FLibSYS.floatSplitWord(tVl, w2, w1);
			else	Special.FLibSYS.floatSplitWord(tVl, w1, w2);
			PDU.wr(w1, "uint16").wr(w2, "uint16");
		}
		//SYS.messInfo("/ED","reqPDU="+SYS.strDecode(PDU.string,"Bin"," "));
		if(!req(PDU.string).toInt())	itO.val = tVl;
		//SYS.messInfo("/ED","respPDU="+SYS.strDecode(PDU.string,"Bin"," "));
	}

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

	//Requesting the hourly archive
	/*if(!tErr.toInt()) {
		PDU = SYS.strFromCharCode(0x41, 0, 0x07, 0x02, 0x17, 0x18, 0, 0);
		if(!(tErr=req(PDU)).toInt()) {
			SYS.messInfo("/EM625","respPDU="+SYS.strDecode(PDU,"Bin"," "));

			PDU = SYS.strFromCharCode(0x43);
			if(!(tErr=req(PDU)).toInt())
				SYS.messInfo("/EM625","respPDU="+SYS.strDecode(PDU,"Bin"," "));
		}
	}*/
}

if(!tErr.length)	tErr = "0";
if(tErr.toInt()) {
	if(!tr.isEVal() && tr.start()) tr.start(false);
	if(f_err != tErr)
		for(var iDt in dt) {
			itO = dt[iDt];
			if(iDt == "10000")	continue;
			this[itO.id].set(EVAL, 0, 0, true);
		}
}
f_err = tErr;','','',1707748061);
INSERT INTO tmplib_DevLib VALUES('Incubator','Incubator','','','The template implements the data acquisition of the Incubator Machines of firm ? in active and passive modes according to the reverse-engineering.

Data of the machines is static but due to implementing two modes — active and passive, their attributes are created dynamically. The active mode means direct data requesting in a corresponded machine, when the passive mode is filtered whole traffic in the RS422 network and is created child virtual parameters for each machine corresponding to the obtained data.

The template implements the collection of statistics about the network in general and for each individual machine.

General statistics include: the number of read and processed packages, and the time of the last one; the remaining time (seconds) before the restart-reconnection of the transport in the absence of traffic in the input channel from "Reconnection time, data missing for passive (tmCon)".

The statistics of a separate machine include: the number of received and processed packages, and the time of the last one; the current data update periodicity with maximum; the number of machine shutdowns, i.e. setting the attributes of the machine parameters in EVAL due to the lack of packages data during the specified time "Time of missing the machine data to set EVAL (tmToEVAL)".

Author: Roman Savochenko <roman@oscada.org>
Total complexity: 4.3 HD
Sponsored by: Vinnica Poultry Farm
Version: 1.0.1
License: GPLv2','','',240,0,'JavaLikeCalc.JavaScript
//Same request to the device
function req(data, buf) {
	if(data == EVAL)	data = "";

	if(addr >= 0) {
		data = SYS.strFromCharCode(addr) + data;
		for(SUB = 0xFF, iCh = 0; iCh < data.length; iCh++)
			SUB -= data.charCodeAt(iCh);

		req = SYS.strFromCharCode(0x10, 0x02) + data + SYS.strFromCharCode(SUB&0xFF, 0x10, 0x03);

		//SYS.messInfo("/INCB/"+this.cfg("SHIFR"), tr("Request")+": "+SYS.strDecode(req,"Bin"," "));

		resp = tr.messIO(req);
	} resp = tr.messIO("", -10e-3);

	//Waiting the acceptance
	while(resp.length && (resp.length < 100 || resp.slice(-2) != "\x10\x03") && (tresp=tr.messIO("")).length) resp += tresp;
	if(buf != EVAL)	resp = buf + resp;
	if(resp.length == 0) return tr("2:No data. ");
	if(resp.length < 100 || resp.slice(0,2) != "\x10\x02" || resp.slice(-2) != "\x10\x03") {
		if(buf != EVAL)	buf = "";	//Cleaning the buffer at such errors of wrong data
		return tr("3:Input package is inconsistent. ");
	}
	if(addr >= 0 && resp.charCodeAt(2) != addr)
		return tr("4:Not correspond response address. ");

	for(pEnd = 0; !pEnd || (pEnd >= 0 && pEnd < resp.length && resp.indexOf("\x10\x02",pEnd) != pEnd); ) {
		pEnd = resp.indexOf("\x10\x03", pEnd);
		if(pEnd >= 0) pEnd += 2;
	}
	if(pEnd < 0) return tr("5:Wrong package structure. ");
		
	data = resp.slice(2, pEnd-3);
	if(buf != EVAL)	buf = resp.slice(pEnd);
	// Processing of doubling 0x10 for address 16
	if(data.charCodeAt(0) == 0x10 && data.charCodeAt(1) == 0x10)	data = data.slice(1);

	//SYS.messInfo("/INCB/"+this.cfg("SHIFR"), tr("Response")+": "+SYS.strDecode(data,"Bin"," "));

	for(SUB = 0xFF, iCh = 0; iCh < data.length; iCh++)
		SUB -= data.charCodeAt(iCh);
	if(resp.charCodeAt(resp.length-3) != (SUB&0xFF))
		return tr("3:Control sub error "+(SUB&0xFF)+" != "+resp.charCodeAt(resp.length-3));

	return "0";
}

function getSt(code) {
	stVl = tr("Unknown");
	if(code == 1)	stVl = tr("Heating");
	else if(code == 2)	stVl = tr("Cooling");

	return stVl+" ("+code+")";
}

function prmUpd(data) {
	if(data.charCodeAt(1) != 0x1B || data.charCodeAt(2) != 0x03 || data.length != 102)	return false;

	//Connecting or creating the representing parameter
	mO = false;
	if(addr >= 0)	prm = this;
	else {
		pId = "m" + data.charCodeAt(0);
		if(!(prm=this.nodeAt("prm_"+pId))) {
			SYS.cntrReq(SYS.XMLNode("add").setAttr("path",this.nodePath()+"/%2fbr%2fprm_").setAttr("id",pId));//.setText(devNm+": "+decId));
			SYS.cntrReq(SYS.XMLNode("set").setAttr("path",this.nodePath()+"/prm_"+pId+"/%2fprm%2fst%2fen").setText("1"));
			prm = this.nodeAt("prm_"+pId);

			machs[data.charCodeAt(0)] = mO = new Object();
			mO.cnt = 1;
			mO.disconCnt = 0;
			mO.tm = SYS.time();
		} else { mO = machs[data.charCodeAt(0)]; mO.cnt++; }
	}

	//Creating for attributes
	if(!prm.nodeAt("a_T1")) {
		if(mO)	prm.attrAdd("dataSt", tr("Data status"), "string|ro");

		prm.attrAdd("T1", tr("T1"), "real|ro");
		prm.attrAdd("T2", tr("T2"), "real|ro");
		prm.attrAdd("T3", tr("T3"), "real|ro");
		prm.attrAdd("T4", tr("T4"), "real|ro");
		prm.attrAdd("T5", tr("T5"), "real|ro");
		prm.attrAdd("T6", tr("T6"), "real|ro");
		prm.attrAdd("CO2", tr("CO2"), "real|ro");
		prm.attrAdd("H2O", tr("H2O"), "real|ro");
		prm.attrAdd("FAN", tr("FAN"), "real|ro");
		prm.attrAdd("V_H1", tr("Voltage Heater 1"), "real|ro");
		prm.attrAdd("V_H2", tr("Voltage Heater 2"), "real|ro");
		prm.attrAdd("V_H3", tr("Voltage Heater 3"), "real|ro");
		prm.attrAdd("V_H4", tr("Voltage Heater 4"), "real|ro");
		prm.attrAdd("V_H5", tr("Voltage Heater 5"), "real|ro");
		prm.attrAdd("V_H6", tr("Voltage Heater 6"), "real|ro");

		prm.attrAdd("Hum", tr("Humidify"), "real|ro");
		prm.attrAdd("V_C1", tr("Voltage Cooler 1"), "real|ro");
		prm.attrAdd("V_C2", tr("Voltage Cooler 2"), "real|ro");
		prm.attrAdd("V_C3", tr("Voltage Cooler 3"), "real|ro");
		prm.attrAdd("V_C4", tr("Voltage Cooler 4"), "real|ro");
		prm.attrAdd("V_C5", tr("Voltage Cooler 5"), "real|ro");
		prm.attrAdd("V_C6", tr("Voltage Cooler 6"), "real|ro");

		prm.attrAdd("DD", tr("Day"), "integer|ro");
		prm.attrAdd("HH", tr("Hour"), "integer|ro");
		prm.attrAdd("MM", tr("Minute"), "integer|ro");

		prm.attrAdd("st1", tr("Status radiator 1"), "string|ro");
		prm.attrAdd("st2", tr("Status radiator 2"), "string|ro");
		prm.attrAdd("st3", tr("Status radiator 3"), "string|ro");
		prm.attrAdd("st4", tr("Status radiator 4"), "string|ro");
		prm.attrAdd("st5", tr("Status radiator 5"), "string|ro");
		prm.attrAdd("st6", tr("Status radiator 6"), "string|ro");

		prm.attrAdd("setT1", tr("Set T1"), "real|ro");
		prm.attrAdd("setT2", tr("Set T2"), "real|ro");
		prm.attrAdd("setT3", tr("Set T3"), "real|ro");
		prm.attrAdd("setT4", tr("Set T4"), "real|ro");
		prm.attrAdd("setT5", tr("Set T5"), "real|ro");
		prm.attrAdd("setT6", tr("Set T6"), "real|ro");

		prm.attrAdd("setCO2min", tr("Set CO2 minimum"), "real|ro");
		prm.attrAdd("setCO2max", tr("Set CO2 maximum"), "real|ro");
		prm.attrAdd("setH2Omin", tr("Set H2O minimum"), "real|ro");
		prm.attrAdd("setH2Omax", tr("Set H2O maximum"), "real|ro");
	}

	//Updating for values the attributes
	if(mO) {
		if(mO.per.isEVal())		mO.per = 0;
		if(SYS.time() > mO.tm)	mO.per = SYS.time() - mO.tm;
		if(mO.perM.isEVal())	mO.perM = 0;
		mO.perM = max(mO.perM, mO.per);
		mO.tm = SYS.time();
		mO.con = true;
		prm.nodeAt("dataSt").set("Filtering in passive. Packages="+mO.cnt+", time="+SYS.strftime(mO.tm)+"; period="+mO.per+"["+mO.perM+"]; disconnections="+mO.disconCnt, 0, 0, true);
	}

	prm.nodeAt("T1").set(data.charCodeAt(3,"UTF-16BE")/10, 0, 0, true);
	prm.nodeAt("T2").set(data.charCodeAt(5,"UTF-16BE")/10, 0, 0, true);
	prm.nodeAt("T3").set(data.charCodeAt(7,"UTF-16BE")/10, 0, 0, true);
	prm.nodeAt("T4").set(data.charCodeAt(9,"UTF-16BE")/10, 0, 0, true);
	prm.nodeAt("T5").set(data.charCodeAt(11,"UTF-16BE")/10, 0, 0, true);
	prm.nodeAt("T6").set(data.charCodeAt(13,"UTF-16BE")/10, 0, 0, true);
	prm.nodeAt("CO2").set(data.charCodeAt(15,"UTF-16BE"), 0, 0, true);
	prm.nodeAt("H2O").set(data.charCodeAt(17,"UTF-16BE"), 0, 0, true);
	prm.nodeAt("FAN").set(data.charCodeAt(19,"UTF-16BE"), 0, 0, true);
	prm.nodeAt("V_H1").set(data.charCodeAt(21,"UTF-16BE")/100, 0, 0, true);
	prm.nodeAt("V_H2").set(data.charCodeAt(23,"UTF-16BE")/100, 0, 0, true);
	prm.nodeAt("V_H3").set(data.charCodeAt(25,"UTF-16BE")/100, 0, 0, true);
	prm.nodeAt("V_H4").set(data.charCodeAt(27,"UTF-16BE")/100, 0, 0, true);
	prm.nodeAt("V_H5").set(data.charCodeAt(29,"UTF-16BE")/100, 0, 0, true);
	prm.nodeAt("V_H6").set(data.charCodeAt(31,"UTF-16BE")/100, 0, 0, true);

	prm.nodeAt("Hum").set(data.charCodeAt(35,"UTF-16BE"), 0, 0, true);
	prm.nodeAt("V_C1").set(data.charCodeAt(37,"UTF-16BE")/100, 0, 0, true);
	prm.nodeAt("V_C2").set(data.charCodeAt(39,"UTF-16BE")/100, 0, 0, true);
	prm.nodeAt("V_C3").set(data.charCodeAt(41,"UTF-16BE")/100, 0, 0, true);
	prm.nodeAt("V_C4").set(data.charCodeAt(43,"UTF-16BE")/100, 0, 0, true);
	prm.nodeAt("V_C5").set(data.charCodeAt(45,"UTF-16BE")/100, 0, 0, true);
	prm.nodeAt("V_C6").set(data.charCodeAt(47,"UTF-16BE")/100, 0, 0, true);

	prm.nodeAt("DD").set(data.charCodeAt(53), 0, 0, true);
	prm.nodeAt("HH").set(data.charCodeAt(54), 0, 0, true);
	prm.nodeAt("MM").set(data.charCodeAt(55), 0, 0, true);

	prm.nodeAt("st1").set(getSt(data.charCodeAt(59)), 0, 0, true);
	prm.nodeAt("st2").set(getSt(data.charCodeAt(60)), 0, 0, true);
	prm.nodeAt("st3").set(getSt(data.charCodeAt(61)), 0, 0, true);
	prm.nodeAt("st4").set(getSt(data.charCodeAt(62)), 0, 0, true);
	prm.nodeAt("st5").set(getSt(data.charCodeAt(63)), 0, 0, true);
	prm.nodeAt("st6").set(getSt(data.charCodeAt(64)), 0, 0, true);

	prm.nodeAt("setT1").set(data.charCodeAt(81, "UTF-16BE")/10, 0, 0, true);
	prm.nodeAt("setT2").set(data.charCodeAt(83, "UTF-16BE")/10, 0, 0, true);
	prm.nodeAt("setT3").set(data.charCodeAt(85, "UTF-16BE")/10, 0, 0, true);
	prm.nodeAt("setT4").set(data.charCodeAt(87, "UTF-16BE")/10, 0, 0, true);
	prm.nodeAt("setT5").set(data.charCodeAt(89, "UTF-16BE")/10, 0, 0, true);
	prm.nodeAt("setT6").set(data.charCodeAt(91, "UTF-16BE")/10, 0, 0, true);
	prm.nodeAt("setCO2min").set(data.charCodeAt(93), 0, 0, true);
	prm.nodeAt("setCO2max").set(data.charCodeAt(94), 0, 0, true);
	prm.nodeAt("setH2Omin").set(data.charCodeAt(95), 0, 0, true);
	prm.nodeAt("setH2Omax").set(data.charCodeAt(96), 0, 0, true);

	return true;
}

function setEVAL( mach, updSt )
{
	if(!(mO=mach.isEVal()?this:this.nodeAt("prm_m"+mach)))	return false;

	if(updSt == true)	tVl = mO.nodeAt("dataSt").get();

	for(attrs = mO.nodeList("a_"), iA = 0; iA < attrs.length; iA++) {
		if((aId=attrs[iA].slice(2)) == "DESCR" || aId == "NAME" || aId == "SHIFR" || aId == "err")	continue;
		mO.nodeAt(aId).set(EVAL, 0, 0, true);
	}

	if(updSt == true)	mO.nodeAt("dataSt").set("Disconnected. "+tVl, 0, 0, true);

	return true;
}

if(f_start || f_stop) {
	if(f_start) {
		passFBuf = "";
		cntPkg = cntPrc = 0;
		// Reading the child parameters, enabling and registration for control
		if(addr < 0) {
			machs = new Object();
			machs.pkgTm = SYS.time();
			for(prms = this.nodeList("prm_"), iPrm = 0; iPrm < prms.length; iPrm++)
				if(prms[iPrm][4] == "m") {
					machs[mId=prms[iPrm].slice(5).toInt()] = mO = new Object();
					mO.cnt = mO.disconCnt = 0;
					mO.tm = SYS.time();
					SYS.cntrReq(SYS.XMLNode("set").setAttr("path",this.nodePath()+"/"+prms[iPrm]+"/%2fprm%2fst%2fen").setText("1"));
					setEVAL(mId);
				}
		}
	}
	return;
}

tErr = stStr = "";

if(!(tr=SYS.Transport.outAt(transport)) || !tr.start(true))
	tErr = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
else if(addr < -1 || addr > 255)
	tErr = "2:"+tr("Address ''%1'' out of range [0...255].").replace("%1",addr.toString());
else if(addr < 0) {	//Passive filtering
	data = "";
	for( ; (tErr=req(data,passFBuf)).toInt() == 0 || tErr > 2; cntPkg++) {
		if(prmUpd(data))	cntPrc++;
		machs.pkgTm = SYS.time();
	}
	if(tErr >= 2)	tErr = "0";
	stStr = "Filtering in passive. Packages read="+cntPkg+", processed="+cntPrc+", time="+SYS.strftime(machs.pkgTm)+"; left to reset="+(tmCon-(SYS.time()-machs.pkgTm));

	if((SYS.time()-machs.pkgTm) > tmCon) { tr.start(false); machs.pkgTm = SYS.time(); }

	for(var iM in machs)
		if((SYS.time()-machs[iM].tm) > tmToEVAL && machs[iM].con == true) {
			machs[iM].con = false;
			machs[iM].disconCnt++;
			setEVAL(iM, true);
		}
}
else {
	//Reading
	if(!tErr.toInt()) {
		data = SYS.strFromCharCode(0x1B, 0x03);
		if(!(tErr=req(data)).toInt())
			prmUpd(data);
	}
}

//Error set
if(!tErr.length)	tErr = "0";
if(tErr.toInt()) {
	if(!tr.isEVal() && tr.start()) tr.start(false);
	if(f_err != tErr)
		SYS.messDebug("/INCB/"+this.cfg("SHIFR"), tr("Error")+": "+tErr);
	if(stStr.length)	tErr += " " + stStr;
} else if(stStr.length)	tErr += ":" + stStr;
f_err = tErr;','','',1707747618);
INSERT INTO tmplib_DevLib VALUES('VSE002','VSE002','','','The template implements for main data acquisition of the vibration sensors of the firm IFM(https://www.ifm.com/) according to the reverse-engineering. Development and testing was performed on base of the sensor VSE002 (https://www.ifm.com/gb/en/product/VSE002), so you can use the template in compatible devices.

The main data are ones transferred by the packages with indexed parameters: B16 — two different and B20 — one with three additional. The parameters are specific for configuration the device, so their representative attributes are created dynamically at receiving the packages. The template provides also of creation the aliased attributes with user specified ID and some simple processing by their describing in the attribute "User signals (custSign)".

Author: Roman Savochenko <roman@oscada.org>
Total complexity: 3.0 HD
Sponsored by: Vinnica Poultry Farm
Version: 1.1.1
License: GPLv2','','',240,0,'JavaLikeCalc.JavaScript
//Same request to the device
function req(data, toWait) {
	data = tr.messIO(data, data.length ? 0 : -1e-3);

	if(toWait != null) {
		//Waiting the acceptance
		while(data.length && data.indexOf(toWait) < 0 && data.length < 1000 && (tresp=tr.messIO("")).length) data += tresp;

		//SYS.messInfo("/VSE002/"+this.cfg("SHIFR"), tr("Resp")+": "+data.indexOf(toWait)+"("+data.length+")");

		if(data.length == 0) return tr("2:No data. ");
		if(data.indexOf(toWait) < 0) return tr("3:No waiting sequence. ");
	}

	return "0";
}

function setVal(aid, vl) {
	if(!(aO=this.nodeAt("a_"+aid))) {
		this.attrAdd(aid, aid, "real|ro");
		aO = this.nodeAt("a_"+aid);
	}
	aO.set(vl, 0, 0, true);

	// Custom signals
	if((tmpO=custSignO[aid]) == null) return;
	if(!(aO=this.nodeAt("a_"+tmpO.id))) {
		this.attrAdd(tmpO.id, tmpO.id, "real|ro");
		aO = this.nodeAt("a_"+tmpO.id);
	}
	aO.set(vl * ((tmpO.mult==null)?1:tmpO.mult), 0, 0, true);
}

function setEVAL( )
{
	mO = this;

	for(attrs = mO.nodeList("a_"), iA = 0; iA < attrs.length; iA++) {
		if((aId=attrs[iA].slice(2)) == "DESCR" || aId == "NAME" || aId == "SHIFR" || aId == "err")	continue;
		mO.nodeAt(aId).set(EVAL, 0, 0, true);
	}
}

if(f_start || f_stop)	{
	cntRecon = 0;
	initSeq_ = "";
	toInit = true;
	custSign_ = "";
	custSignO = null;

	setEVAL();

	return;
}

if(custSign != custSign_) {
	custSign_ = custSign;

	if(custSign.length) {
		custSignO = new Object();
		for(off = 0; (rec=custSign.parseLine(0,off)).length || off < custSign.length; ) {
			if(!rec.length || rec[0] == "#") continue;
			custSignO[rec.parse(0,":")] = tmpO = new Object();
			tmpO.id = rec.parse(1, ":");
			tmpO.mult = rec.parse(2, ":");
			tmpO.mult = (tmpO.mult.length) ? tmpO.mult.toReal() : null;
		}
	}
	else custSignO = null;
}

tErr = stStr = "";

if(!(tr=SYS.Transport.outAt(transport)) || !tr.start(true))
	tErr = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
else if(toInit || initSeq != initSeq_) {
	cntPkgB8 = cntPkgB12 = cntPkgB20 = 0;
	pkgTm = SYS.time();

	//Restarting
	tr.start(false); tr.start(true);

	if(!initSeq.length)
		initSeq = "2c 00 00 00 00 00 00 ff 03 00 00 00 00 00 00 00 "
			"00 00 00 00 00 00 00 00 00 00 ff ff ff fe 42 01 "
			"00 00 00 00";

	req(SYS.strEncode(initSeq),
		SYS.strEncode("00 00 00 00 2c 00 00 00 00 00 00 00"));

	buf = ""; bOff = 0;
	toInit = false;
	initSeq_ = initSeq;
}
//No data - reconnect
else if((SYS.time()-pkgTm) > tmCon) { toInit = true; pkgTm = SYS.time(); setEVAL(); cntRecon++; }
//Passive filtering
else {
	for(vBuf = new Object(), data = "", stTime = SYS.time(); (tErr=req(data)).toInt() == 0 && (data.length || bOff < buf.length); ) {
		if(data.length) pkgTm = SYS.time();
		buf += data; data = "";
		if((SYS.time()-stTime) > 5)	break;
		if(buf.length > 100000 /*|| (SYS.time()-stTime) > 5*/) {
			SYS.messInfo("/VSE002/"+this.nodePath(), "Data overflow on "+buf.length);
			buf = ""; bOff = 0;
			break;
		}
		if((buf.length-bOff) < 20)	continue;	//Wait more data
		sign = buf.slice(bOff, bOff+2);
		if(sign == "\x6A\x00") {			//B8
			cntPkgB8++;
			io = Special.FLibSYS.IO(buf);
			io.pos = bOff+2;	vBuf["B8_"+io.read("uint16",1)] = io.read("float", 1);
			bOff += 8;
		}
		else if(sign == "\x74\x00") {	//B8_2
			cntPkgB8++;
			io = Special.FLibSYS.IO(buf);
			io.pos = bOff+2;	vBuf["B8_"+io.read("uint16",1)+"_2"] = io.read("float", 1);
			bOff += 8;
		}
		else if(sign == "\x6E\x00") {	//B8_3
			cntPkgB8++;
			io = Special.FLibSYS.IO(buf);
			io.pos = bOff+2;	vBuf["B8_"+io.read("uint16",1)+"_3"] = io.read("float", 1);
			bOff += 8;
		}
		else if(sign == "\x6F\x00" || sign == "\x71\x00") {	//B12
			cntPkgB12++;
			io = Special.FLibSYS.IO(buf);
			io.pos = bOff+2; fId = io.read("uint16", 1);
			vBuf["B12_"+fId+"_1"] = io.read("float", 1);
			vBuf["B12_"+fId+"_2"] = io.read("float", 1);
			bOff += 12;
		}
		else if(sign == "\x66\x00") {	//B20
			cntPkgB20++;
			io = Special.FLibSYS.IO(buf);
			io.pos = bOff+2; fId = io.read("uint16", 1);
			vBuf["B20_"+fId+"_1"] = io.read("float", 1);
			vBuf["B20_"+fId+"_2"] = io.read("float", 1);
			vBuf["B20_"+fId+"_3"] = io.read("float", 1);
			vBuf["B20_"+fId+"_4"] = io.read("float", 1);
			bOff += 20;
		}
		else {
			SYS.messInfo("/VSE002/"+this.nodePath(), "Unknown package: "+SYS.strDecode(buf.slice(bOff,bOff+32),"Bin"," "));
			buf = "", bOff = 0; continue;
		}
	}

	buf = buf.slice(bOff); bOff = 0;

	for(var iV in vBuf) setVal(iV, vBuf[iV]);

	stStr = "Buffer="+buf.length+"; packages 8B="+cntPkgB8+", 12B="+cntPkgB12+", 20B="+cntPkgB20+"; reconnections="+cntRecon+", time="+SYS.strftime(pkgTm)+", left to reset="+(tmCon-(SYS.time()-pkgTm));
}

//Error set
if(!tErr.length)	tErr = "0";
if(tErr.toInt()) {
	if(!tr.isEVal() && tr.start()) toInit = true;
	if(f_err != tErr)
		SYS.messDebug("/VSE002/"+this.cfg("SHIFR"), tr("Error")+": "+tErr);
	if(stStr.length)	tErr += " " + stStr;
} else if(stStr.length)	tErr += ":" + stStr;
f_err = tErr;','','',1720582647);
INSERT INTO tmplib_DevLib VALUES('Goboy1M','Goboy 1M','','','The template implements support for Goboy 1M Gas Counters.

Communication protocol of the devices is based on ModBus but it implements only nonstandard functions.

Parameters, provided by this protocol, are all static, that is the representative attributes are provided by the template. For archiving parameters (7) there are provided hourly, daily and month archives.

The template works in two modes: initial and tracing. Where the initial mode means first reading all archives from hourly one and switching to the tracing mode after finish the reading, that is reading fresh archiving and current data.

Status of the template is shown in the "Error (err)" attribute with information about the mode and position of the read data of the archives.

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

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

	return reqO.attr("err");
}

//Seting value to the archive
function archSet(attr, vTm, arh, vl) {
	SYS.messDebug("/GB/archSet","attr="+attr+"; vTm="+SYS.strftime(vTm)+"; arh="+arh+"; vl="+vl);
	atrO = this[attr];
	if(!atrO || atrO.isEVal()) return;
	arguments[attr] = vl;	//Set the current value
	//if(vTm > atrO.time())	atrO.set(vl,vTm,0,true);
	if(atrO.arch())	atrO.arch().setVal(vTm*1e6, vl, arh);
}

//Main process
if(f_start) {
	f_err = "0:"+tr("Waiting for the full call");
	transport_ = transport;
	tr = EVAL;
	aStStr = ""; aSt = EVAL_INT; arhSt = "";
	cTm = 0;
	devManuf = devNameVer = devSN = curTm = arhStart = arhStart_ = arhLastH = arhLastD = arhLastM = EVAL;
	batV = batMinV = baseL = baseD = sndSpd = p = t = flowSpd = cntH = coefCompr = notWorkTm = vAccWork = vAccResult = vWork = vResult = fWork = fResult = EVAL;
	reset = false;
}

if((f_start && typeof(arhs) != "array") || reset) {
	reset = false;
	//Archives init
	tmCur = SYS.time();
	arhs = new Array();
	a = new Object();
	a.id = "Hours"; a.func = 65; a.cnt = 3840; a.per = 3600; a.arh = arhH; a.tm = 0;
	arhs.push(a);
	a = new Object();
	a.id = "Days"; a.func = 66; a.cnt = 3200; a.per = 24*3600; a.arh = arhD; a.tm = 0;
	arhs.push(a);
	a = new Object();
	a.id = "Months"; a.func = 67; a.cnt = 3200; a.per = 31*24*3600; a.arh = arhM; a.tm = 0;
	arhs.push(a);

	arhLastH = arhLastD = arhLastM = EVAL;
}
if(f_start || f_stop) return;

t_err = "0:";

//Check for the transport change and connect
//Check for the transport change and connect
if(tr.isEVal() || transport != transport_)	{
	if(!transport.length) {
		if(this.cntr().cfg("PROT") != "RTU")
			t_err = "1:"+tr("Output transport is empty and the controller object is not ModBus RTU.");
		else tr = new Object();
	}
	else tr = SYS.Transport[transport.parse(0)]["out_"+transport.parse(1)];
	transport_ = transport;
}
if(t_err.toInt()) ;
else if(tr.isEVal())
	t_err = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
else if(addr < 0 || addr > 247)
	t_err = "2:"+tr("Address ''%1'' out of range [0...247].").replace("%1",addr.toString());
else {
	tmCur = SYS.time();
	//Settings write
	// Concentration N2
	/*if(!stN2.isEVal() && stN2 != stN2_ && !t_err.toInt()) {
		data = "6E36" + floatSet(stN2);
		if(!(t_err=req(tr,15,data,true)).toInt() && data == "OKEY")	stN2_ = stN2;
	}*/

	//Settings read
	// Concentration N2 and CO2
	/*data = "6E3600000000";
	if(!t_err.toInt() && (stN2.isEVal() || stCO2.isEVal()) && !(t_err=req(tr,6,data)).toInt()) {
		stN2 = stN2_ = floatGet(data);
		stCO2 = stCO2_ = floatGet(data.slice(8));
	}*/

	//Current values request
	if((tmCur-cTm) > arhTryPer) {
		devManuf = devNameVer = devSN = curTm = arhStart = arhStart_ = arhLastH = arhLastD = arhLastM = EVAL;
		batV = batMinV = baseL = baseD = sndSpd = p = t = flowSpd = cntH = coefCompr = notWorkTm = vAccWork = vAccResult = vWork = vResult = fWork = fResult = EVAL;
		cTm = tmCur;
	}
	// Device information
	data = SYS.strFromCharCode(0x11);
	if(devManuf.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt()) {
		doff = 2;
		devManuf = data.parse(0,"\000",doff);
		devNameVer = data.parse(0,"\000",doff);
		devSN = data.parse(0,"\000",doff);
	}
	// Battery voltage
	data = SYS.strFromCharCode(0x3, 0, 0xA, 0, 2);
	if(batV.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
		batV = SYS.Special.FLibSYS.floatMergeWord(data.charCodeAt(2,"UTF-16BE"), data.charCodeAt(4,"UTF-16BE"));
	// Battery minimum voltage
	data = SYS.strFromCharCode(0x3, 0, 0xC, 0, 2);
	if(batMinV.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
		batMinV = SYS.Special.FLibSYS.floatMergeWord(data.charCodeAt(2,"UTF-16BE"), data.charCodeAt(4,"UTF-16BE"));
	// Base length
	data = SYS.strFromCharCode(0x3, 0, 0x10, 0, 2);
	if(baseL.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
		baseL = SYS.Special.FLibSYS.floatMergeWord(data.charCodeAt(2,"UTF-16BE"), data.charCodeAt(4,"UTF-16BE"));
	// Base diameter
	data = SYS.strFromCharCode(0x3, 0, 0x12, 0, 2);
	if(baseD.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
		baseD = SYS.Special.FLibSYS.floatMergeWord(data.charCodeAt(2,"UTF-16BE"), data.charCodeAt(4,"UTF-16BE"));
	// Sound speed
	data = SYS.strFromCharCode(0x3, 0, 0x14, 0, 2);
	if(sndSpd.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
		sndSpd = SYS.Special.FLibSYS.floatMergeWord(data.charCodeAt(2,"UTF-16BE"), data.charCodeAt(4,"UTF-16BE"));
	// Flow speed
	data = SYS.strFromCharCode(0x3, 0, 0x1A, 0, 2);
	if(flowSpd.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
		flowSpd = SYS.Special.FLibSYS.floatMergeWord(data.charCodeAt(2,"UTF-16BE"), data.charCodeAt(4,"UTF-16BE"));
	// Gas P
	data = SYS.strFromCharCode(0x3, 0, 0x1C, 0, 2);
	if(p.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
		p = SYS.Special.FLibSYS.floatMergeWord(data.charCodeAt(2,"UTF-16BE"), data.charCodeAt(4,"UTF-16BE"));
	// Gas T
	data = SYS.strFromCharCode(0x3, 0, 0x1E, 0, 2);
	if(t.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
		t = SYS.Special.FLibSYS.floatMergeWord(data.charCodeAt(2,"UTF-16BE"), data.charCodeAt(4,"UTF-16BE"));
	// Coefficient of the compression
	data = SYS.strFromCharCode(0x3, 0, 0x20, 0, 2);
	if(coefCompr.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
		coefCompr = SYS.Special.FLibSYS.floatMergeWord(data.charCodeAt(2,"UTF-16BE"), data.charCodeAt(4,"UTF-16BE"));
	// Not work time
	data = SYS.strFromCharCode(0x3, 0, 0x22, 0, 1);
	if(notWorkTm.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
		notWorkTm = data.charCodeAt(2);
	// Volume: accumulated work
	data = SYS.strFromCharCode(0x3, 0x1, 0x00, 0, 2);
	if(vAccWork.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
		vAccWork = data.charCodeAt(2,"UTF-32BE");
	// Volume: accumulated resume
	data = SYS.strFromCharCode(0x3, 0x1, 0x02, 0, 2);
	if(vAccResult.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
		vAccResult = data.charCodeAt(2,"UTF-32BE");
	// Flow: work
	data = SYS.strFromCharCode(0x3, 0x1, 0x04, 0, 2);
	if(fWork.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
		fWork = SYS.Special.FLibSYS.floatMergeWord(data.charCodeAt(2,"UTF-16BE"), data.charCodeAt(4,"UTF-16BE"));
	// Flow: resume
	data = SYS.strFromCharCode(0x3, 0x1, 0x06, 0, 2);
	if(fResult.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
		fResult = SYS.Special.FLibSYS.floatMergeWord(data.charCodeAt(2,"UTF-16BE"), data.charCodeAt(4,"UTF-16BE"));

	// Current time
	data = SYS.strFromCharCode(0x3, 0x1, 0x10, 0, 3);
	if(curTm.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
		curTm = (data.charCodeAt(2+5)+2000).toString()+"-"+data.charCodeAt(2+4).toString(10,2)+"-"+data.charCodeAt(2+3).toString(10,2)+"T"+data.charCodeAt(2+2)+":"+data.charCodeAt(2+1).toString(10,2)+":"+data.charCodeAt(2+0).toString(10,2);
	// Archive begin
	data = SYS.strFromCharCode(0x3, 0x1, 0x13, 0, 3);
	if(arhStart.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt()) {
		dtY = data.charCodeAt(2+5)+2000; dtM = data.charCodeAt(2+4); dtD = data.charCodeAt(2+3); dtH = data.charCodeAt(2+2);
		arhStart_ = SYS.mktime(0, 0, dtH, dtD, dtM-1, dtY);
		arhStart = dtY.toString()+"-"+dtM.toString(10,2)+"-"+dtD.toString(10,2)+"T"+dtH+":"+data.charCodeAt(2+1).toString(10,2)+":"+data.charCodeAt(2+0).toString(10,2);
	}
	// Archive last hour
	data = SYS.strFromCharCode(0x3, 0x1, 0x16, 0, 3);
	if(arhLastH.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt()) {
		dtY = data.charCodeAt(2+5)+2000; dtM = data.charCodeAt(2+4); dtD = data.charCodeAt(2+3); dtH = data.charCodeAt(2+2);
		arhs[0].tmLast = SYS.mktime(0, 0, dtH, dtD, dtM-1, dtY);
		arhLastH = dtY.toString()+"-"+dtM.toString(10,2)+"-"+dtD.toString(10,2)+"T"+dtH+":"+data.charCodeAt(2+1).toString(10,2)+":"+data.charCodeAt(2+0).toString(10,2);
	}
	// Archive last day
	data = SYS.strFromCharCode(0x3, 0x1, 0x19, 0, 3);
	if(arhLastD.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt()) {
		dtY = data.charCodeAt(2+5)+2000; dtM = data.charCodeAt(2+4); dtD = data.charCodeAt(2+3); dtH = data.charCodeAt(2+2);
		arhs[1].tmLast = SYS.mktime(0, 0, dtH, dtD, dtM-1, dtY);
		arhLastD = dtY.toString()+"-"+dtM.toString(10,2)+"-"+dtD.toString(10,2)+"T"+dtH+":"+data.charCodeAt(2+1).toString(10,2)+":"+data.charCodeAt(2+0).toString(10,2);
	}
	// Archive last month
	data = SYS.strFromCharCode(0x3, 0x1, 0x1C, 0, 3);
	if(arhLastM.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt()) {
		dtY = data.charCodeAt(2+5)+2000; dtM = data.charCodeAt(2+4); dtD = data.charCodeAt(2+3); dtH = data.charCodeAt(2+2);
		arhs[2].tmLast = SYS.mktime(0, 0, dtH, dtD, dtM-1, dtY);
		arhLastM = dtY.toString()+"-"+dtM.toString(10,2)+"-"+dtD.toString(10,2)+"T"+dtH+":"+data.charCodeAt(2+1).toString(10,2)+":"+data.charCodeAt(2+0).toString(10,2);
	}
	// Count hour
	data = SYS.strFromCharCode(0x3, 0x1, 0x1F, 0, 1);
	if(cntH.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
		cntH = data.charCodeAt(2);

	//Archives array processing
	arhSt = "";
	for(iA = 0; !arhStart_.isEVal() && iA < arhs.length; iA++) {
		aO = arhs[iA];
		if(aO.tmLast.isEVal())	continue;
		arhSt += (arhSt.length?"; ":"")+aO.id + ": ";
		SYS.messDebug("/GB","Archive "+aO.id+"; arh="+aO.arh);
		if(!aO.arh.length)	{ arhSt += tr("Disabled"); continue; }
		if(t_err.toInt())	arhSt += tr("Missed by error - ");

		//Initiate the archive reading current point
		if(!aO.tm)	aO.tm = max(arhStart_, aO.tmLast-aO.cnt*aO.per);
		
		//Requests
		while(!t_err.toInt() && aO.tm <= aO.tmLast && (SYS.time()-tmCur) < arhTmLim) {
			SYS.localtime(aO.tm, 0, 0, dtH, dtD, dtM, dtY);
			SYS.messDebug("/GB","Requesting "+aO.id+" for "+dtY+"-"+(dtM+1)+"-"+dtD+"T"+dtH+":0:0");
			data = SYS.strFromCharCode(aO.func, 0, 0, dtH, dtD, dtM+1, dtY-2000);
			if(!(t_err=req(data)).toInt() && data.length == 33) {
				archSet("vAccResult", aO.tm, aO.arh, data.charCodeAt(7,"UTF-32"));
				archSet("vAccWork", aO.tm, aO.arh, data.charCodeAt(11,"UTF-32"));
				archSet("vResult", aO.tm, aO.arh, 1e-4*data.charCodeAt(15,"UTF-32"));
				archSet("vWork", aO.tm, aO.arh, 1e-4*data.charCodeAt(19,"UTF-32"));
				archSet("p", aO.tm, aO.arh, 1e-1*data.charCodeAt(23,"UTF-16"));
				archSet("t", aO.tm, aO.arh, 1e-2*data.charCodeAt(25,"UTF-16"));
				archSet("notWorkTm", aO.tm, aO.arh, data.charCodeAt(27,"UTF-16"));
				aO.tm += aO.per;
			}
		}

		if(aO.per > 24*3600)	dtForm = "%Y-%m";
		else if(aO.per > 3600)	dtForm = "%Y-%m-%d";
		else if(aO.per > 60)	dtForm = "%Y-%m-%d %H";
		else							dtForm = "%Y-%m-%d %H %M";
		if(aO.tm >= aO.tmLast) arhSt += tr("Trace for current time=%1").replace("%1",SYS.strftime(aO.tm,dtForm));
		else arhSt += tr("Initial reading in time=%1").replace("%1",SYS.strftime(aO.tm,dtForm));
		SYS.cntrReq(SYS.XMLNode("save").setAttr("path",this.nodePath()+"/%2fobj").setAttr("force",1));
	}

	tmPrev = tmCur;
}

if(!t_err.toInt() && aStStr.length)	t_err = "0:"+aStStr;
f_err = t_err+arhSt;','','',1707747551);
INSERT INTO tmplib_DevLib VALUES('AutoPILOT','AutoPILOT PRO Flow Computers','','','The template implements support for AutoPILOT PRO Flow Computers from Thermo Fisher Scientific (http://www.thermofisher.com/).

Communication protocol of the devices is based on ModBus but it implements only nonstandard functions.

Parameters, provided by this protocol, are mixed for static and dynamic, that is attributes for archiving parameters are provided by the template and settings from some table can be specified in the attribute "Read tables set" for dynamic creation the representative attributes. Table 128 also has named parameters. For archiving parameters (8) there are provided hourly and daily archives. Also there are provided alarms and audits archives.

The template works in two modes: initial and tracing. Where the initial mode means first reading all archives from hourly one and switching to the tracing mode after finish the reading, that is reading fresh archiving and the specified tables of the settings.

Status of the template is shown in the "Error (err)" attribute with information about the mode and position of the read data of the archives.

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

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

	return reqO.attr("err");
}

function valSet(attr, data, dOff, dscr) {
	dOff += 4;
	vtp = "";
	val = EVAL;
	if((vlTp=data.charCodeAt(dOff)) == 4) {
		vtp = "string";
		endStr = data.indexOf("\x00", dOff+1);
		if(endStr < 0) return "12:ASCII string have no end symbol";
		val = data.slice(dOff+1, endStr);
		dOff = endStr+1;
	}
	else if(vlTp == 5) {
		vtp = "real,ro";
		val = Special.FLibSYS.floatMergeWord(data.charCodeAt(dOff+3,"UTF-16BE"), data.charCodeAt(dOff+1,"UTF-16BE"));
		dOff += 5;
	}
	else if(vlTp == 6) {
		vtp = "boolean,ro";
		val = data.charCodeAt(dOff+1);
		dOff += 2;
	}
	else if(vlTp == 7) {
		vtp = "int,ro";
		val = data.charCodeAt(dOff+1);
		dOff += 2;
	}
	else if(vlTp == 8) {
		vtp = "int,ro";
		val = data.charCodeAt(dOff+1, "UTF-16BE");
		if(val > 32767) val -= 65536;
		dOff += 3;
	} else return "11:Unknown value type "+vlTp;

	if(dscr.length && vtp.length) this.attrAdd(attr, dscr, vtp);
	this[attr].set(val, 0, 0, true);

	return "0";
}

//Seting value to the archive
function archSet(attr, vTm, vl, arh) {
	SYS.messDebug("/Pilot/"+this.cfg("SHIFR"), "attr="+attr+"; vTm="+SYS.strftime(vTm)+"; arh="+arh+"; vl="+vl);
	atrO = this[attr];
	if(!atrO || atrO.isEVal()) return;
	arguments[attr] = vl;	//Set the current-last value
	//if(vTm > atrO.time())	atrO.set(vl,vTm,0,true);
	if(atrO.arch())	atrO.arch().setVal(vTm*1e6, vl, arh);
}

//Main process
if(f_start) {
	transport_ = transport;
	rdTblsSet_ = "";
	reqCntr = 0;
	tr = EVAL;
	reset = false;
	lstATmH = lstATmD = lstATmALRM = lstATmAUDIT = 0;
	maxAItmsH = 2000, maxAItmsD = 200, maxAItmsALRM = maxAItmsAUDIT = 250;
	cTm = 0;
	iReadTable = 0;
	rdTmALRM = rdTmAUDIT = 0;
}

//Items set changing process
if(f_start || rdTblsSet != rdTblsSet_) {
	rdTblsSet_ = rdTblsSet;

	tables = new Array();

	for(off = 0; (iIt=rdTblsSet.parse(0,"\n",off)).length; ) {
		itW = new Object();
		itW.nm = iIt.parse(0, ":");
		itW.pgN = iIt.parse(1, ":").toInt();
		itW.updPer = iIt.parse(2, ":").toInt();
		itW.subReq = SYS.strEncode(iIt.parse(3,":"),"Bin");
		itW.toRead = true;
		itW.first = true;
		if(itW.nm == "128")
			itW.names = new Array("<Zero>", "Gas Quality Data Descriptor Text", "Gas Quality Data ID", "BTU Content", "Specific Gravity", "Methane Content",
				"Nitrogen Content",	"Carbon Dioxide Content", "Ethane Content", "Propane Content", "Water Content",
				"Hydrogen Sulfide Content",	"Hydrogen Content", "Carbon Monoxide Content", "Oxygen Content", "I-Butane Content",
				"N-Butane Content", "I-Pentane Content", "N-Pentane Content", "C6+ Content", "N-Hexane Content",
				"N-Heptane Content", "N-Octane Content", "N-Nonane Content", "N-Decane Content", "Helium Content",
				"Argon Content", "Air Content", "Neo-Pentane", "Audit Register Offset");
		if(itW.nm.length && itW.pgN > 0 && itW.subReq.length)	tables.push(itW);
	}
}

if(reset) {
	reset = false, cTm = 0, arhLastH = arhLastD = arhLastALRM = arhLastAUDIT = lstATmALRM = lstATmAUDIT = 0;
	for(iIt = 0; iIt < tables.length; iIt++) tables[iIt].toRead = true;
}
if(f_start || f_stop) return;

tErr = "";
prcSt = "";

//Check for the transport change and connect
if(tr.isEVal() || transport != transport_)	{
	if(!transport.length) {
		if(this.cntr().cfg("PROT") != "RTU") {
			tr = EVAL;
			tErr = "1:"+tr("Output transport is empty and the controller object is not ModBus RTU.");
		} else tr = new Object();
	}
	else tr = SYS.Transport[transport.parse(0)]["out_"+transport.parse(1)];
	transport_ = transport;
}
if(tErr.toInt()) ;
else if(tr.isEVal())
	tErr = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
else if(addr < 0 || addr > 255)
	tErr = "2:"+tr("Address ''%1'' out of range [0...255].").replace("%1",addr.toString());
else {
	tmCur = SYS.time();

	if(reqCntr < 0) {
		if(tmCur > abs(reqCntr))	reqCntr = 0;
		else tErr = "10:"+tr("No connection, next try after %1 seconds").replace("%1",(abs(reqCntr)-tmCur).toString());
	}

	//Unified requesting for table data by the function 0x4A
	if(!tErr.toInt()) {
		if(iReadTable < tables.length) {
			itW = tables[iReadTable];
			if(itW.toRead) {
				for(page = 0; page < itW.pgN; page++) {
					data = SYS.strFromCharCode(0x4A, 5, page) + itW.subReq;
					if(tErr.toInt() || (tErr=req(data)).toInt())	break;
					if(data.charCodeAt(0) != 0x4A || data.charCodeAt(1)+2 != data.length)
						tErr = "10:"+tr("Error function %1 response.").replace("%1","0x4A");
					else
						for(dOff = 3; !tErr.toInt() && dOff < data.length; ) {
							qlt = data.charCodeAt(dOff+2);
							pId = data.charCodeAt(dOff+3);
							tErr = valSet("tbl"+itW.nm+"_"+pId, data, dOff, (itW.first?(itW.names.isEVal()?"Table "+itW.nm+" Item "+pId:itW.names[pId]):""));
						}
				}
				if(page >= itW.pgN)	{
					itW.first = false; itW.toRead = false; itW.rdTm = SYS.time();

					SYS.messDebug("/Pilot/"+this.cfg("SHIFR"), "Successfully readed table "+itW.nm);
				}
			} else { itW.toRead = (itW.updPer > 0 && (SYS.time()-itW.rdTm) > itW.updPer); }
		}
		if((iReadTable++) >= tables.length) iReadTable = 0;
	}

	//Requesting the hourly archive
	isCurTrace = (arhLastH >= maxAItmsH && (SYS.time()-lstATmH)/3600 >= 2);
	if(isCurTrace)	SYS.messDebug("/Pilot/"+this.cfg("SHIFR"), "difH="+((SYS.time()-lstATmH)/3600));
	if(arhH.length && (arhLastH < maxAItmsH || isCurTrace)) {
		// Requesting size
		if(!tErr.toInt()) {
			if(isCurTrace)	arhLastH = maxAItmsH - floor((SYS.time()-lstATmH)/3600);
			aSize = 0, itSize = 10*4;
			aOff = 4 + arhLastH*itSize;
			data = SYS.strFromCharCode(0x48, 2, 0xFF, 0xFF, 0xFF, 0);	//Requesting the records count
			if(!(tErr=req(data)).toInt())	aSize = data.charCodeAt(5,"UTF-32BE")&0xFFFFFF;
		}
		// Requesting the data
		if(!tErr.toInt() && (aSize-aOff) >= itSize) {
			data = SYS.strFromCharCode(0x48, 2, (aOff>>16)&0xFF, (aOff>>8)&0xFF, aOff&0xFF, 0xF0);
			if(!(tErr=req(data)).toInt()) {
				mbSz = data.charCodeAt(5);
				for(aOff = 6; (aOff+itSize-6) <= mbSz; aOff += itSize, arhLastH++) {
					lstATmH = data.charCodeAt(aOff,"UTF-32BE") - tmZone*3600;
					SYS.messDebug("/Pilot/"+this.cfg("SHIFR"), "lstATmH="+SYS.strftime(lstATmH));
					archSet("vAcc", lstATmH, Special.FLibSYS.floatMergeWord(data.charCodeAt(aOff+10,"UTF-16BE"), data.charCodeAt(aOff+8,"UTF-16BE")), arhH);
					archSet("dp", lstATmH, Special.FLibSYS.floatMergeWord(data.charCodeAt(aOff+14,"UTF-16BE"), data.charCodeAt(aOff+12,"UTF-16BE")), arhH);
					archSet("p", lstATmH, Special.FLibSYS.floatMergeWord(data.charCodeAt(aOff+18,"UTF-16BE"), data.charCodeAt(aOff+16,"UTF-16BE")), arhH);
					archSet("t", lstATmH, Special.FLibSYS.floatMergeWord(data.charCodeAt(aOff+22,"UTF-16BE"), data.charCodeAt(aOff+20,"UTF-16BE")), arhH);
					archSet("flowTm", lstATmH, Special.FLibSYS.floatMergeWord(data.charCodeAt(aOff+26,"UTF-16BE"), data.charCodeAt(aOff+24,"UTF-16BE")), arhH);
					archSet("grav", lstATmH, Special.FLibSYS.floatMergeWord(data.charCodeAt(aOff+30,"UTF-16BE"), data.charCodeAt(aOff+28,"UTF-16BE")), arhH);
					archSet("C", lstATmH, Special.FLibSYS.floatMergeWord(data.charCodeAt(aOff+34,"UTF-16BE"), data.charCodeAt(aOff+32,"UTF-16BE")), arhH);
					archSet("N", lstATmH, Special.FLibSYS.floatMergeWord(data.charCodeAt(aOff+38,"UTF-16BE"), data.charCodeAt(aOff+36,"UTF-16BE")), arhH);
				}
			}
		}
		prcSt += tr("Hourly archive read item %1 from %2; ").replace("%1",arhLastH.toString()).replace("%2",floor((aSize-4)/itSize).toString());
	}

	//Requesting the daily archive
	isCurTrace = (arhLastD >= maxAItmsD && (SYS.time()-lstATmD)/86400 >= 2);
	if(isCurTrace)	SYS.messDebug("/Pilot/"+this.cfg("SHIFR"), "difD="+((SYS.time()-lstATmD)/86400));
	if(arhD.length && (arhLastD < maxAItmsD || isCurTrace)) {
		// Requesting size
		if(!tErr.toInt()) {
			if(isCurTrace)	arhLastD = maxAItmsD - floor((SYS.time()-lstATmD)/86400);
			aSize = 0, itSize = 10*4;
			aOff = 4 + arhLastD*itSize;
			data = SYS.strFromCharCode(0x48, 1, 0xFF, 0xFF, 0xFF, 0);	//Requesting the records count
			if(!(tErr=req(data)).toInt())	aSize = data.charCodeAt(5,"UTF-32BE")&0xFFFFFF;
		}
		// Requesting the data
		if(!tErr.toInt() && (aSize-aOff) >= itSize) {
			data = SYS.strFromCharCode(0x48, 1, (aOff>>16)&0xFF, (aOff>>8)&0xFF, aOff&0xFF, 0xF0);
			if(!(tErr=req(data)).toInt()) {
				mbSz = data.charCodeAt(5);
				for(aOff = 6; (aOff+itSize-6) <= mbSz; aOff += itSize, arhLastD++) {
					lstATmD = data.charCodeAt(aOff,"UTF-32BE");
					SYS.messDebug("/Pilot/"+this.cfg("SHIFR"), "lstATmD="+SYS.strftime(lstATmD));
					archSet("vAcc", lstATmD, Special.FLibSYS.floatMergeWord(data.charCodeAt(aOff+10,"UTF-16BE"), data.charCodeAt(aOff+8,"UTF-16BE")), arhD);
					archSet("dp", lstATmD, Special.FLibSYS.floatMergeWord(data.charCodeAt(aOff+14,"UTF-16BE"), data.charCodeAt(aOff+12,"UTF-16BE")), arhD);
					archSet("p", lstATmD, Special.FLibSYS.floatMergeWord(data.charCodeAt(aOff+18,"UTF-16BE"), data.charCodeAt(aOff+16,"UTF-16BE")), arhD);
					archSet("t", lstATmD, Special.FLibSYS.floatMergeWord(data.charCodeAt(aOff+22,"UTF-16BE"), data.charCodeAt(aOff+20,"UTF-16BE")), arhD);
					archSet("flowTm", lstATmD, Special.FLibSYS.floatMergeWord(data.charCodeAt(aOff+26,"UTF-16BE"), data.charCodeAt(aOff+24,"UTF-16BE")), arhD);
					archSet("grav", lstATmD, Special.FLibSYS.floatMergeWord(data.charCodeAt(aOff+30,"UTF-16BE"), data.charCodeAt(aOff+28,"UTF-16BE")), arhD);
					archSet("C", lstATmD, Special.FLibSYS.floatMergeWord(data.charCodeAt(aOff+34,"UTF-16BE"), data.charCodeAt(aOff+32,"UTF-16BE")), arhD);
					archSet("N", lstATmD, Special.FLibSYS.floatMergeWord(data.charCodeAt(aOff+38,"UTF-16BE"), data.charCodeAt(aOff+36,"UTF-16BE")), arhD);
				}
			}
		}
		prcSt += tr("Daily archive read item %1 from %2; ").replace("%1",arhLastD.toString()).replace("%2",floor((aSize-4)/itSize).toString());
	}

	//Requesting the ALARMS archive
	if(arhALRM.length &&
		(arhLastALRM < maxAItmsALRM || (tmPoolEv >= 0 && (SYS.time()-rdTmALRM) > tmPoolEv)))
	{
		if(arhLastALRM >= maxAItmsALRM)	arhLastALRM = 0;
		// Requesting size
		if(!tErr.toInt()) {
			aSize = 0, itSize = 5*4;
			aOff = 4 + arhLastALRM*itSize;
			data = SYS.strFromCharCode(0x48, 0x85, 0xFF, 0xFF, 0xFF, 0);	//Requesting the records count
			if(!(tErr=req(data)).toInt())	aSize = data.charCodeAt(5,"UTF-32BE")&0xFFFFFF;
		}
		// Requesting the data
		if(!tErr.toInt() && (aSize-aOff) >= itSize) {
			data = SYS.strFromCharCode(0x48, 0x85, (aOff>>16)&0xFF, (aOff>>8)&0xFF, aOff&0xFF, 0xF0);
			if(!(tErr=req(data)).toInt()) {
				mbSz = data.charCodeAt(5);
				for(aOff = 6; (aOff+itSize-6) <= mbSz; aOff += itSize, arhLastALRM++) {
					if((lstATmALRM_=data.charCodeAt(aOff+4,"UTF-32BE")) < lstATmALRM)	continue;
					alCode = data.charCodeAt(aOff, "UTF-16BE");
					alReg = data.charCodeAt(aOff+2, "UTF-16BE");
					lstATmALRM = lstATmALRM_;
					alPrev = Special.FLibSYS.floatMergeWord(data.charCodeAt(aOff+10,"UTF-16BE"), data.charCodeAt(aOff+8,"UTF-16BE"));
					alCur = Special.FLibSYS.floatMergeWord(data.charCodeAt(aOff+14,"UTF-16BE"), data.charCodeAt(aOff+12,"UTF-16BE"));
					alUnkn = data.charCodeAt(aOff+16, "UTF-16BE");
					SYS.Archive.messPut(lstATmALRM, 0, "alPilot:"+this.cntr().cfg("ID")+"."+this.cfg("SHIFR")+"."+alReg, 4, "Pilot register "+alReg+ ": ALARM "+alPrev+" > "+alCur+" (Unknown=0x"+alUnkn.toString(16)+"): "+alCode, arhALRM);
					SYS.messDebug("/Pilot/"+this.cfg("SHIFR"), "lstATmALRM="+SYS.strftime(lstATmALRM));
				}
			}
		}
		prcSt += tr("ALARM archive read item %1 from %2; ").replace("%1",arhLastALRM.toString()).replace("%2",floor((aSize-4)/itSize).toString());
		if(arhLastALRM >= maxAItmsALRM)	rdTmALRM = SYS.time();
	}

	//Requesting the AUDITS archive
	if(arhAUDIT.length &&
		(arhLastAUDIT < maxAItmsAUDIT || (tmPoolEv >= 0 && (SYS.time()-rdTmAUDIT) > tmPoolEv)))
	{
		if(arhLastAUDIT >= maxAItmsAUDIT)	arhLastAUDIT = 0;
		// Requesting size
		if(!tErr.toInt()) {
			aSize = 0, itSize = 5*4;
			aOff = 4 + arhLastAUDIT*itSize;
			data = SYS.strFromCharCode(0x48, 0x84, 0xFF, 0xFF, 0xFF, 0);	//Requesting the records count
			if(!(tErr=req(data)).toInt())	aSize = data.charCodeAt(5,"UTF-32BE")&0xFFFFFF;
		}
		// Requesting the data
		if(!tErr.toInt() && (aSize-aOff) >= itSize) {
			data = SYS.strFromCharCode(0x48, 0x84, (aOff>>16)&0xFF, (aOff>>8)&0xFF, aOff&0xFF, 0xF0);
			if(!(tErr=req(data)).toInt()) {
				mbSz = data.charCodeAt(5);
				for(aOff = 6; (aOff+itSize-6) <= mbSz; aOff += itSize, arhLastAUDIT++) {
					if((lstATmAUDIT_=data.charCodeAt(aOff+4,"UTF-32BE")) < lstATmAUDIT)	continue;
					alCode = data.charCodeAt(aOff, "UTF-16BE");
					alReg = data.charCodeAt(aOff+2, "UTF-16BE");
					lstATmAUDIT = lstATmAUDIT_;
					alPrev = Special.FLibSYS.floatMergeWord(data.charCodeAt(aOff+10,"UTF-16BE"), data.charCodeAt(aOff+8,"UTF-16BE"));
					alCur = Special.FLibSYS.floatMergeWord(data.charCodeAt(aOff+14,"UTF-16BE"), data.charCodeAt(aOff+12,"UTF-16BE"));
					alUnkn = data.charCodeAt(aOff+16, "UTF-16BE");
					SYS.Archive.messPut(lstATmAUDIT, 0, "audPilot:"+this.cntr().cfg("ID")+"."+this.cfg("SHIFR")+"."+alReg, 4, "Pilot register "+alReg+ ": AUDIT "+alPrev+" > "+alCur+" (Unknown=0x"+alUnkn.toString(16)+"): "+alCode, arhAUDIT);
					SYS.messDebug("/Pilot/"+this.cfg("SHIFR"), "lstATmAUDIT="+SYS.strftime(lstATmAUDIT));
				}
			}
		}
		prcSt += tr("AUDIT archive read item %1 from %2; ").replace("%1",arhLastAUDIT.toString()).replace("%2",floor((aSize-4)/itSize).toString());
		if(arhLastAUDIT >= maxAItmsAUDIT)	rdTmAUDIT = SYS.time();
	}
}

//Error set
if(!tErr.length)	tErr = "0:Requests="+reqCntr;
if(tErr.toInt()) {
	if(!tr.isEVal() && tr.start()) tr.start(false);
	if(f_err != tErr) {
		SYS.messDebug("/Pilot/"+this.cfg("SHIFR"), tr("Error")+": "+tErr);
		reqCntr = -(SYS.time()+tmConTry);

		/*aLs = this.nodeList("a_");
		for(iA = 0; iA < aLs.length; iA++)
			if((aPref=aLs[iA].slice(2,5)) == "tbl")
				this[aLs[iA].slice(2)].set(EVAL_INT, 0, 0, true);

		vAcc = dp = p = t = flowTm = grav = C = N = EVAL_REAL;*/
	}
}
else if(prcSt.length)	tErr += "; "+prcSt;
f_err = tErr;','','',1707748115);
INSERT INTO tmplib_DevLib VALUES('AutoPILOT_old','AutoPILOT OLD Flow Computers','','','The template implements support for AutoPILOT Flow Gas Computers from Thermo Fisher Scientific (http://www.thermofisher.com/) known as old devices of the firm, when new-pro ones are here.

Communication protocol of the devices is completely specific and also works on serial busses with the same CRC algorithm as in ModBus/RTU.

Parameters, provided by this protocol, are all static, that is the representative attributes are provided by the template. For archiving parameters (6) there are provided hourly and daily archives.

The template works in two modes: initial and tracing. Where the initial mode means first reading all archives from hourly one and switching to the tracing mode after finish the reading, that is reading fresh archiving, the instantaneous and calculated data.

Status of the template is shown in the "Error (err)" attribute with information about the mode and position of the read data of the archives.

Author: Roman Savochenko <roman@oscada.org>
Total complexity: 1.2 HD
Ordered by: BLUE STAR GROUP Ltd
Version: 1.0.0
License: GPLv2','','',240,0,'JavaLikeCalc.JavaScript
//Same request to the device
function req(data) {
	reqCntr++;

	if(data == EVAL)	data = "";
	req = SYS.strFromCharCode(0xAA, addr, data.length+5) + data;
	//Calc and append the CRC
	CRC = Special.FLibSYS.CRC(req, 16, 0x8005, -1, true, true, 0);
	req += SYS.strFromCharCode(CRC&0xFF, CRC>>8);
	SYS.messDebug("/PilotOld/"+this.cfg("SHIFR"), tr("Request")+": "+SYS.strDecode(req,"Bin"," "));

	resp = tr.messIO(req);
	//Wait for an acceptance
	while(resp.length && (resp.length < 6 || resp.length < resp.charCodeAt(2)) && (tresp=tr.messIO("")).length) resp += tresp;
	if(resp.length < 6 || (resp.charCodeAt(0)>>4) != 0x5 || resp.length != resp.charCodeAt(2))
		return tr("3:No response or the response is inconsistent. ");
	if(Special.FLibSYS.CRC(resp.slice(0,-2),16,0x8005,-1,true,true,0) != resp.charCodeAt(resp.length-2,"UTF-16LE"))
		return tr("3:Error the response CRC. ");

	SYS.messDebug("/PilotOld/"+this.cfg("SHIFR"), tr("Response")+": "+SYS.strDecode(resp,"Bin"," "));

	data = resp.slice(3,-2);
	if((tVl=resp.charCodeAt(3)) == 0xFF || !(tVl&0x80) || (tVl&0x7F) != (req.charCodeAt(3)&0x7F)) {
		return tr("4:The function is error or inconsistent to the request one. ");
	}
	return "0";
}

function valGetF(data, dOff) {
	return Special.FLibSYS.floatMergeWord(data.charCodeAt(dOff,"UTF-16LE"),data.charCodeAt(dOff+2,"UTF-16LE"));
}

//Seting value to the archive
function archSet(attr, vTm, vl, arh) {
	SYS.messInfo("/PilotOld/"+this.cfg("SHIFR"), "attr="+attr+"; vTm="+SYS.strftime(vTm)+"; arh="+arh+"; vl="+vl);
	atrO = this[attr];
	if(!atrO || atrO.isEVal()) return;
	arguments[attr] = vl;	//Set the current-last value
	//if(vTm > atrO.time())	atrO.set(vl,vTm,0,true);
	if(atrO.arch())	atrO.arch().setVal(vTm*1e6, vl, arh);
}

//Main process
if(f_start) {
	transport_ = transport;
	reqCntr = 0;
	tr = EVAL;
	reset = false;
	lstATmH = lstATmD = lstATmALRM = lstATmAUDIT = 0;
}

if(reset) {
	reset = false, arhLastH = arhLastD = 0;
}
if(f_start || f_stop) return;

tErr = "";
prcSt = "";

//Check for the transport change and connect
if(tr.isEVal() || transport != transport_)	{
	tr = SYS.Transport[transport.parse(0)]["out_"+transport.parse(1)];
	transport_ = transport;
}
if(tr.isEVal())
	tErr = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
else if(addr < 0 || addr > 255)
	tErr = "2:"+tr("Address ''%1'' out of range [0...255].").replace("%1",addr.toString());
else {
	tmCur = SYS.time();
	tmCurY = tmCurM = tmCurD = tmCurH = 0;
	SYS.localtime(tmCur, 0, 0, tmCurH, tmCurD, tmCurM, tmCurY);

	if(reqCntr < 0) {
		if(tmCur > abs(reqCntr))	reqCntr = 0;
		else tErr = "10:"+tr("No connection, next try after %1 seconds").replace("%1",(abs(reqCntr)-tmCur).toString());
	}

	//Read Instantaneous and Calculated Data
	data = SYS.strFromCharCode(4, 1);
	if(!(tErr=req(data)).toInt()) {
		arguments["dp"] = valGetF(data, 2);
		arguments["p"] = valGetF(data, 6);
		arguments["t"] = valGetF(data, 10);
		arguments["sqrRootExt"] = valGetF(data, 14);
		arguments["f"] = valGetF(data, 18);
		arguments["fTotal"] = valGetF(data, 22);
		arguments["eTotal"] = valGetF(data, 26);
		arguments["fAcc"] = valGetF(data, 30);
		arguments["b"] = valGetF(data, 34);
		arguments["supComprF"] = valGetF(data, 38);
		arguments["Fr"] = valGetF(data, 42);
		arguments["E_D"] = valGetF(data, 46);
		arguments["K"] = valGetF(data, 50);
		arguments["Fb"] = valGetF(data, 54);
		arguments["Fg"] = valGetF(data, 58);
		arguments["Fa"] = valGetF(data, 62);
		arguments["Ftf"] = valGetF(data, 66);
		arguments["Fpb"] = valGetF(data, 70);
		arguments["Ftb"] = valGetF(data, 74);
		arguments["extFY"] = valGetF(data, 78);
		arguments["subFY"] = valGetF(data, 82);
		arguments["yestDayTotal"] = valGetF(data, 86);
		dtTm = data.charCodeAt(95).toString(10,2)+"-"+data.charCodeAt(94).toString(10,2)+"-"+data.charCodeAt(96).toString(10,2)+"T"+data.charCodeAt(97).toString(10,2)+":"+data.charCodeAt(98).toString(10,2)+":"+data.charCodeAt(99).toString(10,2);
	}

	if(!tErr.toInt()) {
		data = SYS.strFromCharCode(0x11, 1);
		if(!(tErr=req(data)).toInt()) {
			factFlowSt = data.charCodeAt(2, "UTF-16LE");
			arguments["factFwv"] = valGetF(data, 4);
			arguments["factWatCnt"] = valGetF(data, 8);
			arguments["factWellStr"] = valGetF(data, 12);
			arguments["factGasVeloc"] = valGetF(data, 16);
			arguments["factLocGrav"] = valGetF(data, 20);
			arguments["factCalbGrav"] = valGetF(data, 24);
		}
	}

	//Requesting the hourly archive
	if(!tErr.toInt() && arhH.length && (tmCur-arhLastH) > 60*60) {
		tmLastY = tmLastM = tmLastD = tmLastH = 0;
		SYS.localtime(arhLastH, 0, 0, tmLastH, tmLastD, tmLastM, tmLastY);

		for(sec = 0; !tErr.toInt() && (SYS.time()-tmCur) < arhTmLim /*&& sec < 3*/; sec++) {
			data = SYS.strFromCharCode(0x15, 1, sec, tmLastM+1, tmLastD, max(0,tmLastY-2000), tmLastH, tmCurM+1, tmCurD, max(0,tmCurY-2000), tmCurH);
			if(!(tErr=req(data)).toInt()) {
				nReqs = data.charCodeAt(2);
				szReq = (data.length-4)/nReqs;
				SYS.messInfo("/PilotOld/"+this.cfg("SHIFR"), "nReqs="+nReqs+"; szReq="+szReq);
				for(rOff = 4, iReq = 0; iReq < nReqs; iReq++, rOff += szReq) {
					arhLastH = SYS.mktime(0, data.charCodeAt(rOff+4), data.charCodeAt(rOff+3), data.charCodeAt(rOff+1), data.charCodeAt(rOff+0)-1, , data.charCodeAt(rOff+2)+2000);
					SYS.messInfo("/PilotOld/"+this.cfg("SHIFR"), "req="+iReq+"; date="+SYS.strftime(arhLastH));
					archSet("fTotal", arhLastH, valGetF(data,rOff+5), arhH);
					archSet("eTotal", arhLastH, valGetF(data,rOff+9), arhH);
					archSet("dp", arhLastH, valGetF(data,rOff+13), arhH);
					archSet("p", arhLastH, valGetF(data,rOff+17), arhH);
					archSet("t", arhLastH, valGetF(data,rOff+21), arhH);
					archSet("sqrRootExt", arhLastH, valGetF(data,rOff+25), arhH);
				}
				SYS.cntrReq(SYS.XMLNode("save").setAttr("path",this.nodePath()+"/%2fobj").setAttr("force",1));
				if(data.charCodeAt(3) == 0)	break;
			}
		}
	}
	if(arhH.length) {
		if((tmCur-arhLastH) < 2*60*60) prcSt += tr("Hours: ") + tr("Trace for current time=%1").replace("%1",SYS.strftime(arhLastH,"%Y/%m/%d %H-%M")) + "; ";
		else prcSt += tr("Hours: ") + tr("Initial reading in time=%1").replace("%1",SYS.strftime(arhLastH,"%Y/%m/%d %H-%M")) + "; ";
	}

	//Requesting the daily archive
	if(!tErr.toInt() && arhD.length && (tmCur-arhLastD) > 24*60*60) {
		tmLastY = tmLastM = tmLastD = 0;
		SYS.localtime(arhLastD, 0, 0, 0, tmLastD, tmLastM, tmLastY);

		for(sec = 0; !tErr.toInt() && (SYS.time()-tmCur) < arhTmLim /*&& sec < 3*/; sec++) {
			data = SYS.strFromCharCode(0x14, 1, sec, tmLastM+1, tmLastD, max(0,tmLastY-2000), tmCurM+1, tmCurD, max(0,tmCurY-2000));
			if(!(tErr=req(data)).toInt()) {
				nReqs = data.charCodeAt(2);
				szReq = (data.length-4)/nReqs;
				SYS.messInfo("/PilotOld/"+this.cfg("SHIFR"), "nReqs="+nReqs+"; szReq="+szReq);
				for(rOff = 4, iReq = 0; iReq < nReqs; iReq++, rOff += szReq) {
					arhLastD = SYS.mktime(0, 0, 0, data.charCodeAt(rOff+1), data.charCodeAt(rOff+0)-1, , data.charCodeAt(rOff+2)+2000);
					SYS.messInfo("/PilotOld/"+this.cfg("SHIFR"), "req="+iReq+"; date="+SYS.strftime(arhLastD));
					archSet("fTotal", arhLastD, valGetF(data,rOff+3), arhD);
					archSet("eTotal", arhLastD, valGetF(data,rOff+7), arhD);
					archSet("dp", arhLastD, valGetF(data,rOff+11), arhD);
					archSet("p", arhLastD, valGetF(data,rOff+15), arhD);
					archSet("t", arhLastD, valGetF(data,rOff+19), arhD);
					archSet("sqrRootExt", arhLastD, valGetF(data,rOff+23), arhD);
				}
				SYS.cntrReq(SYS.XMLNode("save").setAttr("path",this.nodePath()+"/%2fobj").setAttr("force",1));
				if(data.charCodeAt(3) == 0)	break;
			}
		}
	}
	if(arhD.length) {
		if((tmCur-arhLastD) < 2*24*60*60) prcSt += tr("Days: ") + tr("Trace for current time=%1").replace("%1",SYS.strftime(arhLastD,"%Y/%m/%d")) + "; ";
		else prcSt += tr("Days: ") + tr("Initial reading in time=%1").replace("%1",SYS.strftime(arhLastD,"%Y/%m/%d")) + "; ";
	}
}

//Error set
if(!tErr.length)	tErr = "0:Requests="+reqCntr;
if(tErr.toInt()) {
	if(!tr.isEVal() && tr.start()) tr.start(false);
	if(f_err != tErr) {
		SYS.messDebug("/PilotOld/"+this.cfg("SHIFR"), tr("Error")+": "+tErr);
		reqCntr = -(SYS.time()+tmConTry);

		/*aLs = this.nodeList("a_");
		for(iA = 0; iA < aLs.length; iA++)
			if((aPref=aLs[iA].slice(2,5)) == "tbl")
				this[aLs[iA].slice(2)].set(EVAL_INT, 0, 0, true);

		vAcc = dp = p = t = flowTm = grav = C = N = EVAL_REAL;*/
	}
}
else if(prcSt.length)	tErr += "; "+prcSt;
f_err = tErr;','','',1707748115);
INSERT INTO tmplib_DevLib VALUES('CorBK','Corrector BK','','','The template implements support for BK Correctors of Gas Flow in most completed volume, that is it is the most complex and advanced template for this consumer.

Parameters, provided by this protocol, are static, that is all their representing attributes are provided by the template. Part of these parameters have hourly (4 parameters), day (6 parameters) and month (4 parameters) history. Some part of the parameters are settings. And the protocol also provides reading of the alarm history. Archives reading enabled only for specified archivers in the proper configuration fields.

The template works in two modes: initial and tracing. Where the initial mode means first reading all archives from hourly one and switching to the tracing mode after finish the reading, that is reading current values and the fresh archiving.

Status of the template is shown in the "Error (err)" attribute with information about the mode and position of the read data of the archives. Due to the communication is slow and archives can be big, status of the read archive data saved at it change, that is you can stop the template or disable PC in whole but the template will continue the archives reading from saved position and not from the begin, which reset you can by the proper boolean attribute.

Author: Roman Savochenko <roman@oscada.org>
Total complexity: 5.2 HD
Ordered by: BLUE STAR GROUP Ltd
Version: 1.0.0
License: GPLv2','','',240,0,'JavaLikeCalc.JavaScript
//Same request to the device
function req(tr, fnc, data, noRespData) {
	if(data == EVAL)	data = "";
	if(noRespData == EVAL)	noRespData = false;
	req = "#" + addr.toString(16) + fnc.toString(16) + data;
	//Calc and append the CRC
	for(CRC = 0x25, iR = 1; iR < req.length; iR++)	CRC = CRC^req.charCodeAt(iR);
	req += CRC.toString(16,2) + "\r";
	SYS.messDebug("/CorrBK/req", tr("Request")+": "+req.slice(0,-1));
	//tm_ = SYS.time(utm_);
	resp = tr.messIO(req);
	//Wait for an acceptance
	while(resp.length && (resp.length < 3 || resp.indexOf("\r") < 0) && (tresp=tr.messIO("")).length) resp += tresp;
	if(resp.length < 3 || (accEnd=resp.indexOf("\r")) < 0)
		return tr("3:No response or the response is incompleted. ");
	if(resp.slice(0,accEnd) != "%"+req.slice(1,3)+"OKEY")
		return tr("4:Not acceptable response ''%1''.").replace("%1",resp.slice(0,accEnd));
	resp = resp.slice(accEnd+1);
	while((resp.length < 6 || resp[resp.length-1] != "\r") && (tresp=tr.messIO("")).length) resp += tresp;
	if(resp.length < 6 || resp[resp.length-1] != "\r")
		return tr("3:No response or the response incompleted. ");
	SYS.messDebug("/CorrBK/req", tr("Response")+": "+resp.slice(0,-1));
	if(noRespData)	{ data = resp.slice(3,-1); return "0"; }
	//Calc and check the CRC
	for(CRC = 0x25, iR = 1; iR < resp.length-3; iR++)	CRC = CRC^resp.charCodeAt(iR);
	if(CRC != resp.slice(-3).toInt(16))	return tr("3:Error the response CRC. ");

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

	return "0";
}

//Parse float
function floatGet(str) {
	X = new Array();
	isZero = true;
	for(iB = 0; iB < 4; iB++) {
		X[iB] = str.slice(iB*2,2+iB*2).toInt(16);
		if(isZero && X[iB])	isZero = false;
	}
	if(isZero)	return 0;
	sign = (X[0] > 127); X[0] = X[0] | 0x80;
	rez = pow(2,X[1]-127) * (X[0]*pow(2,16) + X[3]*pow(2,8) + X[2]) / pow(2,24);
	return sign ? -rez : rez;
}
//Produce float
function floatSet(vl) {
	exp = 0;
	while(vl/pow(2,exp) > 1 && exp < 128)		exp++;
	while(vl/pow(2,exp) < 0.5 && exp > -127)exp--;
	mts = (vl/pow(2,exp))*pow(2,24) + 0.5;
	if(vl >= 0) mts = mts&0x7FFFFF;
	return ((mts>>16)&0xFF).toString(16,2)+(exp+127).toString(16,2)+(mts&0xFFFF).toString(16,4);
}

//Set value to the archive
function archSet(attr, vTm, arh, vl) {
	SYS.messDebug("/CorrBK/archSet","attr="+attr+"; vTm="+SYS.strftime(vTm)+"; arh="+arh+"; vl="+vl);
	atrO = this[attr];
	if(!atrO || atrO.isEVal()) return;
	arguments[attr] = vl;	//Set the current value
	//if(vTm > atrO.time())	atrO.set(vl,vTm,0,true);
	if(atrO.arch())	atrO.arch().setVal(vTm*1e6, vl, arh);
}

//Item of the archive processing:
// read value by the position, validate and write to the atribute''s archive in the archivator''s part
function archItPrc(tr, aO) {
	pos = (aO.initPos < aO.cnt) ? aO.initPos : aO.pos;
	tmCur = SYS.time();
	//  Read and check header of the item
	data = (aO.beg+(pos*aO.itSz)).toString(16,4)+"00000000";
	if((t_err=req(tr,6,data)).toInt())	return t_err;

	if(aO.id == "Alarms") {
		dtY = 2018; dtM = dtM_ = data.slice(0,2).toInt(10); dtD = data.slice(2,4).toInt(10); dtH = data.slice(4,6).toInt(10); dtMin = data.slice(6,8).toInt(10);
		dtTm_ = SYS.mktime(0, dtMin, dtH, dtD, dtM-1, dtY);
		SYS.localtime(tmCur, 0, 0, 0, 0, dtM_, dtY);
		if(dtM > dtM_)	dtY--;

		SYS.messDebug("/CorrBK/archItPrc", aO.id+": pos="+pos+"; dtY="+dtY+"; dtM="+dtM+"; dtD="+dtD+"; dtH="+dtH+"; dtMin="+dtMin+"; dtTm="+dtTm_);

		if(dtM < 1 || dtM > 12 || dtD < 1 || dtD > 31 || floor(dtTm_/aO.per) > floor(tmCur/aO.per))
			return "10:Date or time of the item is wrong";
		SYS.messDebug("/CorrBK/archItPrc", aO.id+": dtTm="+dtTm_+"; aO.tm="+aO.tm+"; aO.pos="+aO.pos);
		//  Mark the item as a candidate to current/last point
		if(dtTm_ > aO.tm) {
			aO.tm = dtTm_;
			if((aO.pos=pos+1) >= aO.cnt) aO.pos = 0;
		}
		aCode = data.slice(8,12).toInt(16);
		data = (aO.beg+(pos*aO.itSz)+6).toString(16,4)+"00000000";
		if((t_err=req(tr,6,data)).toInt())	return t_err;
		aValue = floatGet(data);
		SYS.messDebug("/CorrBK/archItPrc", aO.id+": code="+aCode+"; value="+aValue);
		// For an alarm message
		SYS.Archive.messPut(dtTm_, 0, "alCorBK:"+this.cntr().cfg("ID")+"."+this.cfg("SHIFR"), 3, "Alarm : " + aCode + " : " + aValue, aO.arh);
	}
	else {
		dtY = 2000 + data.slice(0,2).toInt(10); dtM = data.slice(2,4).toInt(10); dtD = data.slice(4,6).toInt(10); dtH = data.slice(6,8).toInt(10);
		dtTm_ = (aO.per >= 24*3600) ? SYS.timegm(0, 0, dtH, dtD, dtM-1, dtY) : SYS.mktime(0, 0, dtH, dtD, dtM-1, dtY);

		SYS.messDebug("/CorrBK/archItPrc", aO.id+": pos="+pos+"; dtY="+dtY+"; dtM="+dtM+"; dtD="+dtD+"; dtH="+dtH+"; dtTm="+dtTm_);

		if(dtM < 1 || dtM > 12 || dtD < 1 || dtD > 31 || floor(dtTm_/aO.per) > floor(tmCur/aO.per) || 
				(aO.initPos < aO.cnt && dtTm_ < (tmCur-aO.cnt*aO.per)))
//				((aO.initPos < aO.cnt && dtTm_ < (tmCur-aO.cnt*aO.per)) || dtTm_ <= aO.tm))
			return "10:Date or time of the item is wrong";
		//  Mark the item as a candidate to current/last point
		SYS.messDebug("/CorrBK/archItPrc", aO.id+": dtTm="+dtTm_+"; aO.tm="+aO.tm+"; aO.pos="+aO.pos);
		if(dtTm_ > aO.tm) {
			aO.tm = dtTm_;
			if((aO.pos=pos+1) >= aO.cnt) aO.pos = 0;
		}
		archSet(aO.attrs[0], dtTm_, aO.arh, floatGet(data.slice(8)));
		for(iA = 1; iA < aO.attrs.length; iA += 2) {
			data = (aO.beg+(pos*aO.itSz)+8*(iA+1)/2).toString(16,4)+"00000000";
			if((t_err=req(tr,6,data)).toInt())	return t_err;
			archSet(aO.attrs[iA], dtTm_, aO.arh, floatGet(data));
			if((iA+1) < aO.attrs.length)
				archSet(aO.attrs[iA+1], dtTm_, aO.arh, floatGet(data.slice(8)));
		}
	}
	return "";
}

//Main process
// Set transport and init it
if(f_start) {
	f_err = "0:"+tr("Waiting for the full call");
	transport_ = transport;
	tr = SYS.Transport.nodeAt(transport, ".");
	devSt = ""; aSt = EVAL_INT; arhSt = "";
	cTm = 0;
	dtTm = EVAL;
	motoHours = EVAL;
	vWork = vAccWork = vResult = vAccResult = p = t = pFact = fWork = fResult = EVAL;
	stN2 = stN2_ = stCO2 = stCO2_ = stQ = stQ_ = stBP = stBP_ = stSubPL = stSubPL_
			= stSubPH = stSubPH_ = stSubT = stSubT_ = stFH = stFH_ = stFL = stFL_
			= stSubFL = stSubFL_ = stSubFH = stSubFH_ = EVAL;
	reset = false;
}

if((f_start && typeof(arhs) != "array") || reset) {
	reset = false;
	//Archives init
	tmCur = SYS.time();
	arhs = new Array();
	a = new Object();
	a.id = "Hours"; a.beg = 0x0136; a.itSz = 20; a.cnt = 1080; a.per = 3600;
	a.arh = arhH; a.attrs = new Array("p", "t", "vWork", "vResult");
	a.pos = 0; a.tm = 0; a.aTm = tmCur; a.initPos = 0;
	arhs.push(a);
	a = new Object();
	a.id = "Days"; a.beg = 0x5596; a.itSz = 28; a.cnt = 100; a.per = 24*3600;
	a.arh = arhD; a.attrs = new Array("p", "t", "vWork", "vResult", "vAccWork", "vAccResult");
	a.pos = 0; a.tm = 0; a.aTm = tmCur; a.initPos = 0;
	arhs.push(a);
	a = new Object();
	a.id = "Months"; a.beg = 0x6086; a.itSz = 20; a.cnt = 25; a.per = 31*24*3600;
	a.arh = arhM; a.attrs = new Array("vWork", "vResult", "vAccWork", "vAccResult");
	a.pos = 0; a.tm = 0; a.aTm = tmCur; a.initPos = 0;
	arhs.push(a);
	a = new Object();
	a.id = "Alarms"; a.beg = 0x627A; a.itSz = 10; a.cnt = 300; a.per = 60;
	a.arh = arhAlrm;
	a.pos = 0; a.tm = 0; a.aTm = tmCur; a.initPos = 0;
	arhs.push(a);
}
if(f_start || f_stop) return;

t_err = "0:";

//Check for the transport change and connect
if(!tr || transport != transport_)	{
	tr = SYS.Transport.nodeAt(transport, ".");
	transport_ = transport;
}
if(!tr)	t_err = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
else if(addr < 0 || addr > 15)
	t_err = "2:"+tr("Address ''%1'' out of range [0...15].").replace("%1",addr.toString());
else {
	tmCur = SYS.time();
	//Settings write
	// Concentration N2
	if(!stN2.isEVal() && stN2 != stN2_ && !t_err.toInt()) {
		data = "6E36" + floatSet(stN2);
		if(!(t_err=req(tr,15,data,true)).toInt() && data == "OKEY")	stN2_ = stN2;
	}
	// Concentration CO2
	if(!stCO2.isEVal() && stCO2 != stCO2_ && !t_err.toInt()) {
		data = "6E3A" + floatSet(stCO2);
		if(!(t_err=req(tr,15,data,true)).toInt() && data == "OKEY")	stCO2_ = stCO2;
	}
	// Gas density
	if(!stQ.isEVal() && stQ != stQ_ && !t_err.toInt()) {
		data = "6E3E" + floatSet(stQ);
		if(!(t_err=req(tr,15,data,true)).toInt() && data == "OKEY")	stQ_ = stQ;
	}
	// Barometric pressure
	if(!stBP.isEVal() && stBP != stBP_ && !t_err.toInt()) {
		data = "6E42" + floatSet(stBP);
		if(!(t_err=req(tr,15,data,true)).toInt() && data == "OKEY")	stBP_ = stBP;
	}
	// Pressure substitution for lower border
	if(!stSubPL.isEVal() && stSubPL != stSubPL_ && !t_err.toInt()) {
		data = "6E46" + floatSet(stSubPL);
		if(!(t_err=req(tr,15,data,true)).toInt() && data == "OKEY")	stSubPL_ = stSubPL;
	}
	// Pressure substitution for high border
	if(!stSubPH.isEVal() && stSubPH != stSubPH_ && !t_err.toInt()) {
		data = "6E4A" + floatSet(stSubPH);
		if(!(t_err=req(tr,15,data,true)).toInt() && data == "OKEY")	stSubPH_ = stSubPH;
	}
	// Temperature substitution
	if(!stSubT.isEVal() && stSubT != stSubT_ && !t_err.toInt()) {
		data = "6E4E" + floatSet(stSubT);
		if(!(t_err=req(tr,15,data,true)).toInt() && data == "OKEY")	stSubT_ = stSubT;
	}
	// Flow maximum
	if(!stFH.isEVal() && stFH != stFH_ && !t_err.toInt()) {
		data = "6E52" + floatSet(stFH);
		if(!(t_err=req(tr,15,data,true)).toInt() && data == "OKEY")	stFH_ = stFH;
	}
	// Flow minimum
	if(!stFL.isEVal() && stFL != stFL_ && !t_err.toInt()) {
		data = "6E56" + floatSet(stFL);
		if(!(t_err=req(tr,15,data,true)).toInt() && data == "OKEY")	stFL_ = stFL;
	}
	// Flow substitution for lower border
	if(!stSubFL.isEVal() && stSubFL != stSubFL_ && !t_err.toInt()) {
		data = "6E5A" + floatSet(stSubFL);
		if(!(t_err=req(tr,15,data,true)).toInt() && data == "OKEY")	stSubFL_ = stSubFL;
	}
	// Flow substitution for high border
	if(!stSubFH.isEVal() && stSubFH != stSubFH_ && !t_err.toInt()) {
		data = "6E5E" + floatSet(stSubFH);
		if(!(t_err=req(tr,15,data,true)).toInt() && data == "OKEY")	stSubFH_ = stSubFH;
	}

	//Settings read
	// Concentration N2 and CO2
	data = "6E3600000000";
	if(!t_err.toInt() && (stN2.isEVal() || stCO2.isEVal()) && !(t_err=req(tr,6,data)).toInt()) {
		stN2 = stN2_ = floatGet(data);
		stCO2 = stCO2_ = floatGet(data.slice(8));
	}
	// Gas density and Barometric pressure
	data = "6E3E00000000";
	if(!t_err.toInt() && (stQ.isEVal() || stBP.isEVal()) && !(t_err=req(tr,6,data)).toInt()) {
		stQ = stQ_ = floatGet(data);
		stBP = stBP_ = floatGet(data.slice(8));
	}
	// Pressure substitution for lower and high border
	data = "6E4600000000";
	if(!t_err.toInt() && (stSubPL.isEVal() || stSubPH.isEVal()) && !(t_err=req(tr,6,data)).toInt()) {
		stSubPL = stSubPL_ = floatGet(data);
		stSubPH = stSubPH_ = floatGet(data.slice(8));
	}
	// Temperature substitution and Flow maximum
	data = "6E4E00000000";
	if(!t_err.toInt() && (stSubT.isEVal() || stFH.isEVal()) && !(t_err=req(tr,6,data)).toInt()) {
		stSubT = stSubT_ = floatGet(data);
		stFH = stFH_ = floatGet(data.slice(8));
	}
	// Flow minimum and Flow substitution for lower border
	data = "6E5600000000";
	if(!t_err.toInt() && (stFL.isEVal() || stSubFL.isEVal()) && !(t_err=req(tr,6,data)).toInt()) {
		stFL = stFL_ = floatGet(data);
		stSubFL = stSubFL_ = floatGet(data.slice(8));
	}
	// Flow substitution for high border
	data = "6E5E00000000";
	if(!t_err.toInt() && stSubFH.isEVal() && !(t_err=req(tr,6,data)).toInt())
		stSubFH = stSubFH_ = floatGet(data);

	//Current values request
	if(curRegul && (tmCur-cTm) > arhTryPer) {
		dtTm = motoHours = vWork = vResult = aSt = t = p = pFact = fWork = fResult = EVAL;
		cTm = tmCur;
	}
	// Date and time
	data = "036A00000000";
	if(dtTm.isEVal() && !t_err.toInt() && !(t_err=req(tr,5,data)).toInt()) {
		dtTm =	data.slice(0,2).toInt(10).toString() + "-" + data.slice(2,4).toInt(10).toString() + "-" + data.slice(4,6).toInt(10).toString() +
					"T" + data.slice(6,8).toInt(10).toString() + ":" + data.slice(8,10).toInt(10).toString();
		dtTm = SYS.strptime(dtTm, "%y-%m-%dT%H:%M").toString() + ": " + dtTm;
	}
	// Moto-hours
	data = "020A00000000";
	if(motoHours.isEVal() && !t_err.toInt() && !(t_err=req(tr,5,data)).toInt())
		motoHours = data.slice(0,4).toInt(16);
	// Volume: work and result
	data = "021000000000";
	if(vWork.isEVal() && !t_err.toInt() && !(t_err=req(tr,5,data)).toInt()) {
		vWork = floatGet(data);
		vResult = floatGet(data.slice(8));
	}
	// Alarms
	data = "022200000000";
	if(aSt.isEVal() && !t_err.toInt() && !(t_err=req(tr,5,data)).toInt()) {
		aSt = data.slice(0,4).toInt(16);
		devSt = aSt.toString()+": ";
		if(aSt&0x01)	devSt += "Er 0300; ";
		if(aSt&0x02)	devSt += "Er 0100; ";
		if(aSt&0x08)	devSt += "Er 0200; ";
		if(aSt&0x10)	devSt += "Er 2000; ";
		if(aSt&0x20)	devSt += "Er 1000; ";
		if(aSt&0x40)	devSt += "Er 3000; ";
		if(aSt&0x80)	devSt += "Er 4000; ";
		if(aSt&0x100)	devSt += "Er 0002; ";
		if(aSt&0x400)	devSt += "Er 0001; ";
		if(aSt&0x1000)devSt += "Er 0010; ";
		if(aSt&0x2000)devSt += "Er 0030; ";
		if(aSt&0x4000)devSt += "Er 0020; ";
		if(aSt&0x8000)devSt += "Er 0003; ";
		//SYS.messInfo("/CorrBK","aSt: "+aSt+"; devSt: "+devSt);
	}
	// Temperature, Pressure
	data = "024C00000000";
	if(t.isEVal() && !t_err.toInt() && !(t_err=req(tr,5,data)).toInt()) {
		t = floatGet(data);
		p = floatGet(data.slice(8));
	}
	// Pressing factor
	data = "025C00000000";
	if(pFact.isEVal() && !t_err.toInt() && !(t_err=req(tr,5,data)).toInt())
		pFact = floatGet(data);
	// Flow: work and resume
	data = "029800000000";
	if(fWork.isEVal() && !t_err.toInt() && !(t_err=req(tr,5,data)).toInt()) {
		fWork = floatGet(data);
		fResult = floatGet(data.slice(8));
	}

	//Archives array processing
	arhSt = "";
	for(iA = 0; iA < arhs.length; iA++) {
		aO = arhs[iA];
		arhSt += (arhSt.length?"; ":"")+aO.id + ": ";
		if(!aO.arh.length)	{ arhSt += tr("Disabled"); continue; }
		if(t_err.toInt())	arhSt += tr("Missed by error - ");
		// Check for the last value
		if(!t_err.toInt()) {
			if(aO.initPos >= aO.cnt && (tmCur-aO.aTm) > arhTryPer)
				while((SYS.time()-tmCur) < arhTmLim) {
					if((t_err1=archItPrc(tr,aO)).toInt() && t_err1.toInt() != 10)	t_err = t_err1;
					else aO.aTm = (iA && (tmCur-arhs[iA-1].aTm) < arhTryPer) ? arhs[iA-1].aTm : tmCur;
					if(t_err1.toInt())	break;
				}
			else while(aO.initPos < aO.cnt && (SYS.time()-tmCur) < arhTmLim) {
				if(aO.initPos < 0)	{ aO.initPos++; break; }
				SYS.messDebug("/CorrBK/archInit", aO.id+": initPos="+aO.initPos+"; pos="+aO.pos);
				if((t_err1=archItPrc(tr,aO)).toInt() && t_err1.toInt() != 10)	{ t_err = t_err1; break; }
				aO.initPos++;
				//  Miss the invalid item and re-check after some cycles, after an hour
				//if(t_err1.toInt() && aO.initPos >= aO.cnt && !aO.tm)	aO.initPos = -arhTryPer*f_frq;	//????
			}
		}
		if(aO.per > 24*3600)	dtForm = "%Y-%m";
		else if(aO.per > 3600)	dtForm = "%Y-%m-%d";
		else if(aO.per > 60)	dtForm = "%Y-%m-%d %H";
		else							dtForm = "%Y-%m-%d %H %M";
		if(aO.initPos >= aO.cnt) arhSt += tr("Trace for current in pos=%1, last=%2")
				.replace("%1",aO.pos.toString()).replace("%2",SYS.strftime(aO.tm,dtForm));
		else arhSt += tr("Initial scan in pos=%1(%4), near pos=%2, time=%3")
				.replace("%1",aO.initPos.toString())
				.replace("%4",aO.cnt.toString())
				.replace("%2",aO.pos.toString())
				.replace("%3",SYS.strftime(aO.tm,dtForm));
		SYS.cntrReq(SYS.XMLNode("save").setAttr("path",this.nodePath()+"/%2fobj").setAttr("force",1));
	}

	tmPrev = tmCur;
}

if(!t_err.toInt())	t_err = "0";
f_err = t_err+arhSt;','','',1707748116);
INSERT INTO tmplib_DevLib VALUES('TDS','Tancy Data','','','The template implements support for Tianxin V1 protocol of Gas Correctors TC220 from GasElectro (http://www.gaselectro.ru/). Communication protocol of the devices is completely specific.

Parameters, provided by this protocol, are all static, that is the representative attributes are provided by the template. For archiving parameters (4) there is provided only the hourly archive.

Author: Roman Savochenko <roman@oscada.org>
Total complexity: 0.95 HD
Ordered by: BLUE STAR GROUP Ltd
Version: 1.0.0
License: GPLv2','','',240,0,'JavaLikeCalc.JavaScript
//Same request to the device
function req(fnc, data) {
	if(data == EVAL)	data = "";

	req = SYS.strFromCharCode(0xCC, addr, fnc) + data;

	//Calc and append the CRC
	for(CRC = 0, iR = 0; iR < req.length; iR++)	CRC += req.charCodeAt(iR);
	req += SYS.strFromCharCode(CRC&0xFF, (CRC>>8)&0xFF, 0xEE);

	SYS.messDebug("/TDS/"+this.cfg("SHIFR"), tr("Request")+": "+SYS.strDecode(req,"Bin"," "));

	resp = tr.messIO(req);
	//Wait for acceptance
	while(resp.length && (resp.length < 8 || resp.length != (resp.charCodeAt(3,"UTF-16LE")+8)) && (tresp=tr.messIO("")).length) resp += tresp;

	if(resp.length < 8 || resp.charCodeAt(0) != 0xCC || resp.charCodeAt(resp.length-1) != 0xEE || resp.length != (resp.charCodeAt(3,"UTF-16LE")+8))
		return tr("3:No response or the response is incompleted. ");
	if(resp.charCodeAt(2) != fnc)
		return tr("4:The response function is improper to the request one.");

	//Calc and check the CRC
	for(CRC = 0, iR = 0; iR < resp.length-3; iR++)	CRC += resp.charCodeAt(iR);
	if((CRC&0xFFFF) != resp.charCodeAt(resp.length-3,"UTF-16LE"))	return tr("3:Error the response CRC. ");

	SYS.messDebug("/TDS/"+this.cfg("SHIFR"), tr("Response")+": "+SYS.strDecode(resp,"Bin"," "));

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

	return "0";
}

//Parse float
function floatGet(str) {
	return ((str.charCodeAt(1)&0x80)?-1:1) * ((str.charCodeAt(0,"UTF-32BE")&0x7FFFFF)/pow(2,23)) * pow(2,str.charCodeAt(0));
}
//Produce float
/*function floatSet(vl) {
	exp = 0;
	while(vl/pow(2,exp) > 1 && exp < 128)		exp++;
	while(vl/pow(2,exp) < 0.5 && exp > -127)exp--;
	mts = (vl/pow(2,exp))*pow(2,24) + 0.5;
	if(vl >= 0) mts = mts&0x7FFFFF;
	return ((mts>>16)&0xFF).toString(16,2)+(exp+127).toString(16,2)+(mts&0xFFFF).toString(16,4);
}*/

//Seting value to the archive
function archSet(attr, vTm, arh, vl) {
	SYS.messDebug("/TDS/archSet","attr="+attr+"; vTm="+SYS.strftime(vTm)+"; arh="+arh+"; vl="+vl);
	atrO = this[attr];
	if(!atrO || atrO.isEVal()) return;
	//arguments[attr] = vl;	//Set the current value
	//if(vTm > atrO.time())	atrO.set(vl,vTm,0,true);
	if(atrO.arch())	atrO.arch().setVal(vTm*1e6, vl, arh);
}

//Main process
// Set transport and init it
if(f_start) {
	f_err = "0";
	transport_ = transport;
	tr = SYS.Transport.nodeAt(transport, ".");
	cTm = 0;
}
if(f_start || f_stop) return;

t_err = "0";
tmCur = SYS.time();

//Check for the transport change and connect
if(!tr || transport != transport_)	{
	tr = SYS.Transport.nodeAt(transport, ".");
	transport_ = transport;
}
if(!tr)	t_err = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
else if(addr < 0 || addr > 255)
	t_err = "2:"+tr("Address ''%1'' out of range [0...255].").replace("%1",addr.toString());
else if((tmCur-cTm) > tryPer) {
	cTm = tmCur;
	// Currents reading
	data = SYS.strFromCharCode(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
	if(!t_err.toInt() && !(t_err=req(0x30,data)).toInt()) {
		dtTm = data.charCodeAt(0,"UTF-16BE").toString(16)+"-"+data.charCodeAt(2).toString(16,2)+"-"+data.charCodeAt(3).toString(16,2)+
			"T"+data.charCodeAt(4).toString(16,2)+":"+data.charCodeAt(5).toString(16,2)+":"+data.charCodeAt(6).toString(16,2);
		fWork = floatGet(data.slice(7,11));
		fResult = floatGet(data.slice(13,17));
		t = floatGet(data.slice(17,21));
		p = floatGet(data.slice(21,25));
		//SYS.messInfo("/TDS/"+this.cfg("SHIFR"), tr("Response")+": "+SYS.strDecode(data,"Bin"," "));
	}

	// Archive reading
	data = SYS.strFromCharCode(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
	if(arhH.length && !t_err.toInt() && !(t_err=req(0x01,data)).toInt())
		for(off = 0; off < data.length; off += 28) {
			tmStmp = SYS.mktime(data.charCodeAt(off+6).toString(16).toInt(),
				data.charCodeAt(off+5).toString(16).toInt(),
				data.charCodeAt(off+4).toString(16).toInt(),
				data.charCodeAt(off+3).toString(16).toInt(),
				data.charCodeAt(off+2).toString(16).toInt()-1,
				data.charCodeAt(off,"UTF-16BE").toString(16).toInt());
			archSet("fWork", tmStmp, arhH, floatGet(data.slice(off+7,off+11)));
			archSet("fResult", tmStmp, arhH, floatGet(data.slice(off+13,off+17)));
			archSet("t", tmStmp, arhH, floatGet(data.slice(off+17,off+21)));
			archSet("p", tmStmp, arhH, floatGet(data.slice(off+21,off+25)));
		}
}

if(!t_err.toInt())	t_err = "0";
f_err = t_err;','','',1707747719);
INSERT INTO tmplib_DevLib VALUES('UltraMag','Ultramag','','','The template implements support for Ultramag Gas Meters of the firm "EPO Signal" LTD.

Communication protocol of the devices is based on ModBus but with support only function 4 (Read Input Registers) and 16 (Preset Multiple Registers). The protocol supports reading archives also, that is why it implemented as a different template.

Parameters, provided by this protocol, are static, that is all their representing attributes are provided by the template. Part of these parameters have hourly (4 parameters), day (6 parameters) and month (4 parameters) history. Archives reading enabled only for specified archivers in the proper configuration fields.

The template works in two modes: initial and tracing. Where the initial mode means first reading all archives from hourly one and switching to the tracing mode after finish the reading, that is reading current values and the fresh archiving.

Status of the template is shown in the "Error (err)" attribute with information about the mode and position of the read data of the archives. Due to the communication is slow and archives can be big, status of the read archive data saved at it change, that is you can stop the template or disable PC in whole but the template will continue the archives reading from saved position and not from the begin, which reset you can by the proper boolean attribute.

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

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

	return reqO.attr("err");
}

//Seting value to the archive
function archSet(attr, vTm, arh, vl) {
	//SYS.messDebug("/UM/archSet","attr="+attr+"; vTm="+SYS.strftime(vTm)+"; arh="+arh+"; vl="+vl);
	atrO = this[attr];
	if(!atrO || atrO.isEVal()) return;
	arguments[attr] = vl;	//Set the current value
	//if(vTm > atrO.time())	atrO.set(vl,vTm,0,true);
	if(atrO.arch())	atrO.arch().setVal(vTm*1e6, vl, arh);
}

//Main process
if(f_start) {
	f_err = "0:"+tr("Waiting for the full call");
	transport_ = transport;
	tr = EVAL;
	arhSt = "";
	cTm = 0;
	devManuf = devNameVer = devSN = curTm = EVAL;
	p = t = coefCor = vWork = vAccWork = vResult = vAccResult = fWork = fResult = EVAL;
	reset = false;
}

if((f_start && typeof(arhs) != "array") || reset) {
	reset = false;
	//Archives init
	tmCur = SYS.time();
	arhs = new Array();
	arhs.tmPrev = 0;
	a = new Object();
	a.id = "Hours"; a.numb = 1; a.cnt = 14400; a.per = 3600; a.arh = arhH; a.pos = -1; a.tm = 0; a.tmTrc = 0;
	arhs.push(a);
	a = new Object();
	a.id = "Days"; a.numb = 2; a.cnt = 1800; a.per = 24*3600; a.arh = arhD; a.pos = -1; a.tm = 0; a.tmTrc = 0;
	arhs.push(a);
	a = new Object();
	a.id = "Months"; a.numb = 3; a.cnt = 2000; a.per = 31*24*3600; a.arh = arhM; a.pos = -1; a.tm = 0; a.tmTrc = 0;
	arhs.push(a);
}
if(f_start || f_stop) return;

t_err = "0:";

//Check for the transport change and connect
if(tr.isEVal() || transport != transport_)	{
	if(!transport.length) {
		if(this.cntr().cfg("PROT") != "RTU")
			t_err = "1:"+tr("Output transport is empty and the controller object is not ModBus RTU.");
		else tr = new Object();
	}
	else tr = SYS.Transport[transport.parse(0)]["out_"+transport.parse(1)];
	transport_ = transport;
}
if(t_err.toInt()) ;
else if(tr.isEVal())
	t_err = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
else if(addr < 0 || addr > 247)
	t_err = "2:"+tr("Address ''%1'' out of range [0...247].").replace("%1",addr.toString());
else {

	tmCur = SYS.time();
	//Settings write
	// Concentration N2
	/*if(!stN2.isEVal() && stN2 != stN2_ && !t_err.toInt()) {
		data = "6E36" + floatSet(stN2);
		if(!(t_err=req(tr,15,data,true)).toInt() && data == "OKEY")	stN2_ = stN2;
	}*/

	//Settings read
	// Concentration N2 and CO2
	/*data = "6E3600000000";
	if(!t_err.toInt() && (stN2.isEVal() || stCO2.isEVal()) && !(t_err=req(tr,6,data)).toInt()) {
		stN2 = stN2_ = floatGet(data);
		stCO2 = stCO2_ = floatGet(data.slice(8));
	}*/

	//Current values request
	if((tmCur-cTm) > arhTryPer) {
		devManuf = devNameVer = devSN = curTm = EVAL;
		p = t = coefCor = vWork = vAccWork = vResult = vAccResult = fWork = fResult = EVAL;
		cTm = tmCur;
	}
	// Manufacturer name
	data = SYS.strFromCharCode(0x4, 0x2, 0x59, 0, 15);
	if(devManuf.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
		devManuf = SYS.strCodeConv(data.slice(2), "CP866", "");
	// Device number
	data = SYS.strFromCharCode(0x4, 0x2, 0x77, 0, 5);
	if(devSN.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
		devSN = SYS.strCodeConv(data.slice(2), "CP866", "");
	// Sensor P SN
	data = SYS.strFromCharCode(0x4, 0x2, 0x81, 0, 5);
	if(sensP_SN.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
		sensP_SN = SYS.strCodeConv(data.slice(2), "CP866", "");
	// Sensor T SN
	data = SYS.strFromCharCode(0x4, 0x2, 0x8B, 0, 5);
	if(sensT_SN.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
		sensT_SN = SYS.strCodeConv(data.slice(2), "CP866", "");
	// Flow: work
	data = SYS.strFromCharCode(0x4, 0, 0xF, 0, 4);
	if(fWork.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
	{ io = Special.FLibSYS.IO(data.slice(2), "", "b"); fWork = io.read("double", 1); }
	// Flow: resume
	data = SYS.strFromCharCode(0x4, 0, 0x17, 0, 4);
	if(fResult.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
	{ io = Special.FLibSYS.IO(data.slice(2), "", "b"); fResult = io.read("double", 1); }
	// Gas P
	data = SYS.strFromCharCode(0x4, 0, 0x1F, 0, 4);
	if(p.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
	{ io = Special.FLibSYS.IO(data.slice(2), "", "b"); p = io.read("double", 1); }
	// Gas T
	data = SYS.strFromCharCode(0x4, 0, 0x27, 0, 4);
	if(t.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
	{ io = Special.FLibSYS.IO(data.slice(2), "", "b"); t = io.read("double", 1); }
	// Coefficient of the correction
	data = SYS.strFromCharCode(0x4, 0, 0x3F, 0, 4);
	if(coefCor.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
	{ io = Special.FLibSYS.IO(data.slice(2), "", "b"); coefCor = io.read("double", 1); }
	// Volume work
	data = SYS.strFromCharCode(0x4, 0, 0x4F, 0, 4);
	if(vWork.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
	{ io = Special.FLibSYS.IO(data.slice(2), "", "b"); vWork = io.read("double", 1); }
	// Volume standard
	data = SYS.strFromCharCode(0x4, 0, 0x47, 0, 4);
	if(vResult.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
	{ io = Special.FLibSYS.IO(data.slice(2), "", "b"); vResult = io.read("double", 1); }
	// Current time
	data = SYS.strFromCharCode(0x4, 0, 0x1, 0, 6);
	if(curTm.isEVal() && !t_err.toInt() && !(t_err=req(data)).toInt())
		curTm = (data.charCodeAt(1+5)*256+data.charCodeAt(1+6)).toString()+"-"+data.charCodeAt(1+4).toString(10,2)+"-"+data.charCodeAt(1+2).toString(10,2)+"T"+data.charCodeAt(1+8)+":"+data.charCodeAt(1+10).toString(10,2)+":"+data.charCodeAt(1+12).toString(10,2);

	//Archives array processing
	arhSt = "";
	for(iA = 0; iA < arhs.length; iA++) {
		aO = arhs[iA];
		arhSt += (arhSt.length?"; ":"")+aO.id + ": ";
		//SYS.messDebug("/GB","Archive "+aO.id+"; arh="+aO.arh);
		if(!aO.arh.length)	{ arhSt += tr("Disabled"); continue; }

		//Initiate the archive reading current point
		if(aO.pos < 0)	aO.pos = aO.cnt;
		if(arhs.tmPrev && (tVl=(tmCur-arhs.tmPrev)/aO.per) > 1)	//compensate the long shut down
			aO.pos = min(aO.cnt, aO.pos+floor(tVl));

		if(t_err.toInt())	arhSt += tr("Missed by error - ");
		else if(aO.pos > 1 || (tmCur-aO.tmTrc) >= arhTryPer) {
			//Set the archive number
			data = SYS.strFromCharCode(0x10, 0x01, 0x2F, 0, 1, 2, 0, aO.numb);
			if(!(t_err=req(data)).toInt()) {
				//Requests
				while(!t_err.toInt() && (SYS.time()-tmCur) < arhTmLim) {
					//Set the item number
					data = SYS.strFromCharCode(0x10, 0x01, 0x31, 0, 1, 2, aO.pos/256, aO.pos%256);
					if(!(t_err=req(data)).toInt())	{
						//Requesting the needed data
						data = SYS.strFromCharCode(0x4, 0x1, 0x41, 0, 18);
						if(!(t_err=req(data)).toInt())	{
							io = Special.FLibSYS.IO(data.slice(2), "", "b");
							dtD = io.read("uint8", 1);
							dtM = io.read("uint8", 1);
							dtY = 2000+io.read("uint8", 1);
							dtH = io.read("uint8", 1);
							if(dtD >= 1 && dtD <= 31 && dtM >= 1 && dtM <= 12 && dtH <= 23) {
								dtTm = (aO.per >= 24*3600) ? SYS.timegm(0, 0, dtH, dtD, dtM-1, dtY) : SYS.mktime(0, 0, dtH, dtD, dtM-1, dtY);
								aO.tm = max(aO.tm, dtTm);
								//SYS.messDebug("/UM","Archive "+aO.id+"; arh="+aO.arh+"; tm="+SYS.strftime(dtTm)+"; dtSrc="+dtD);
								if(aO.numb == 1) {	//hours
									archSet("p", dtTm, aO.arh, io.read("float",1));
									archSet("t", dtTm, aO.arh, io.read("float",1));
									archSet("vAccWork", dtTm, aO.arh, io.read("double",1));
									archSet("vAccResult", dtTm, aO.arh, io.read("double",1));
								}
								else if(aO.numb == 2) {	//days
									archSet("p", dtTm, aO.arh, io.read("float",1));
									archSet("t", dtTm, aO.arh, io.read("float",1));
									archSet("vWork", dtTm, aO.arh, io.read("float",1));
									archSet("vResult", dtTm, aO.arh, io.read("float",1));
									archSet("vAccWork", dtTm, aO.arh, io.read("double",1));
									archSet("vAccResult", dtTm, aO.arh, io.read("double",1));
								}
								else if(aO.numb == 3) {	//months
									archSet("vWork", dtTm, aO.arh, io.read("float",1));
									archSet("vResult", dtTm, aO.arh, io.read("float",1));
									archSet("vAccWork", dtTm, aO.arh, io.read("double",1));
									archSet("vAccResult", dtTm, aO.arh, io.read("double",1));
								}
							}
							if(aO.pos > 1)	aO.pos--;
							else { aO.tmTrc = tmCur; break; }
						}
					}
				}
			}
		}
		if(aO.per > 24*3600)		dtForm = "%Y-%m";
		else if(aO.per > 3600)	dtForm = "%Y-%m-%d";
		else if(aO.per > 60)		dtForm = "%Y-%m-%d %H";
		else									dtForm = "%Y-%m-%d %H %M";
		if(aO.pos == 1) arhSt += tr("Trace for current time=%1").replace("%1",SYS.strftime(aO.tm,dtForm));
		else arhSt += tr("Initial reading in position %1, time=%2").replace("%1",aO.pos.toString()).replace("%2",SYS.strftime(aO.tm,dtForm));
		SYS.cntrReq(SYS.XMLNode("save").setAttr("path",this.nodePath()+"/%2fobj").setAttr("force",1));
	}

	arhs.tmPrev = tmCur;
}

f_err = t_err + arhSt;','','',1707747778);
INSERT INTO tmplib_DevLib VALUES('enronDaniel','Enron, Daniel','','','The template implements support for Enron, Daniel Gas Counters.

Communication protocol of the devices is based on ModBus but with registers size 4 bytes instead standard 2 bytes, that is they cannot be polled by the standard OpenSCADA module DAQ.ModBus (http://oscada.org/wiki/Special:MyLanguage/Modules/ModBus). So, the template implements that protocol variant on the Logical Level (http://oscada.org/wiki/Special:MyLanguage/Documents/DAQ#LogicLev) and with main features of the DAQ.ModBus module, that is allows for free registers specifying. On ground the module later was created the "ModBus base (mbBase) (http://oscada.org/wiki/Libs/Devices#mbBase)" template as a base for further creation related to ModBus templates with standard functions and additional implementing only specific functions.

For numbers need registers go to documentation of vendor the devices! The template provides only reading current values and has not got any archives due to missing such feature in the communication protocol.

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

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

	return reqO.attr("err");
}

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

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

if(f_start || f_stop) return;

tErr = "";

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

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

				/*if(itO1.tp == "u")			this[itO1.id].set(io.read("uint32",1), 0, 0, true);
				else if(itO1.tp == "i")	this[itO1.id].set(io.read("int32",1), 0, 0, true);
				else if(itO1.tp == "s")	this[itO1.id].set(io.read("char",blk[0].sz), 0, 0, true);
				else this[itO1.id].set(io.read("float",1), 0, 0, true);
				if(itO1.wr)	itO1.val = this[itO1.id].get();*/
			}
			blk = new Array(); blk.push(itO);
		}
	}
}

f_err = tErr.length ? tErr : "0";','','',1707747986);
INSERT INTO tmplib_DevLib VALUES('FlowGAS','Flow GAS','','','The template implements support for FlowGas Gas Correctors of the firm "EPO Signal" LTD.

Communication protocol of the devices is based on standard ModBus but with re-implementing function 16 for reading archives. So, the template became the first one used the template "ModBus base (mbBase)" in own ground.

Parameters, provided by this protocol, are mixed for static and dynamic, that is attributes for archiving parameters are provided by the template and extra current ones can be specified in the attribute "Items set" for dynamic creation the representative attributes. For archiving parameters (4) there is provided only the hourly archive.

The template works in two modes: initial and tracing. Where the initial mode means first reading all archives from hourly one and switching to the tracing mode after finish the reading, that is reading current values and the fresh archiving.

Status of the template is shown in the "Error (err)" attribute with information about the mode and position of the read data of the archives. Due to the communication is slow and archives can be big, status of the read archive data saved at it change, that is you can stop the template or disable PC in whole but the template will continue the archives reading from saved position and not from the begin, which reset you can by the proper boolean attribute.

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

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

	return reqO.attr("err");
}

//Seting value to the archive
function archSet(attr, vTm, arh, vl) {
	SYS.messInfo("/FlowGAS/archSet","attr="+attr+"; vTm="+SYS.strftime(vTm)+"; arh="+arh+"; vl="+vl);
	atrO = this[attr];
	if(!atrO || atrO.isEVal()) return;
	arguments[attr] = vl;	//Setting the current value
	//if(vTm > atrO.time())	atrO.set(vl,vTm,0,true);
	if(atrO.arch())	atrO.arch().setVal(vTm*1e6, vl, arh);
}

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

if((f_start && typeof(arhs) != "array") || reset) {
	reset = false;
	//Archives init
	arhs = new Array();
	a = new Object();
	a.id = "Hours"; a.func = 1; a.dtSz = 28; a.pos = 14400; a.per = 3600;
	a.arh = arhH; a.attrs = new Array("p", "t", "vWork-d", "vResult-d");
	arhs.push(a);
	//...
}
if(f_start || f_stop) return;

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

if(f_start || f_stop) return;

tErr = "";

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

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

	//Archives array processing
	tmCur = SYS.time();
	arhSt = "";
	for(iA = 0; iA < arhs.length; iA++) {
		aO = arhs[iA];
		//if(aO.tmLast.isEVal())	continue;
		arhSt += (arhSt.length?"; ":"")+aO.id + ": ";
		SYS.messDebug("/FlowGAS","Archive "+aO.id+"; arh="+aO.arh);
		if(!aO.arh.length)	{ arhSt += tr("Disabled"); continue; }
		if(tErr.toInt())		arhSt += tr("Missed by an error - ");
		else {
			// The archive selection
			PDU = SYS.strFromCharCode(0x10, 0x01, 0x2f, 0, 1, 2, 0, aO.func);
			if((tErr=req(PDU)).toInt())	arhSt += tr("Missed by an error - ");
		}
		while(!tErr.toInt() && (SYS.time()-tmCur) < arhTmLim) {
			SYS.messDebug("/FlowGAS","Requesting "+aO.id+" for "+aO.pos);
			// The need position selection
			PDU = SYS.strFromCharCode(0x10, 0x01, 0x31, 0, 1, 2, aO.pos>>8, aO.pos&0xFF);
			if((tErr=req(PDU)).toInt())	{ arhSt += tr("Missed by an error - "); continue; }
			// The selected data reading
			PDU = SYS.strFromCharCode(4, 0x01, 0x41, 0, aO.dtSz/2);
			if((tErr=req(PDU)).toInt()) { arhSt += tr("Missed by an error - "); continue; }

			io = Special.FLibSYS.IO(PDU, "", "b"); io.pos = 2;
			dtD = io.read("uint8",1), dtM = io.read("uint8",1), dtY = io.read("uint8",1), dtH = io.read("uint8",1);
			aO.tm = SYS.mktime(0, 0, dtH, dtD, dtM-1, 2000+dtY);
			for(iA = 0; iA < aO.attrs.length; iA++)
				archSet(aO.attrs[iA].parse(0,"-"), aO.tm, aO.arh, (aO.attrs[iA].parse(1,"-")=="d") ? io.read("double",1,"b") : io.read("float",1,"b"));
			aO.pos = max(1, aO.pos-1);
		}

		if(aO.per > 24*3600)	dtForm = "%Y-%m";
		else if(aO.per > 3600)	dtForm = "%Y-%m-%d";
		else if(aO.per > 60)	dtForm = "%Y-%m-%d %H";
		else									dtForm = "%Y-%m-%d %H %M";
		if(aO.pos == 1) arhSt += tr("Trace for current time=%1").replace("%1",SYS.strftime(aO.tm,dtForm));
		else arhSt += tr("Initial reading in pos=%1(%2)").replace("%1",aO.pos.toString()).replace("%2",SYS.strftime(aO.tm,dtForm));
		SYS.cntrReq(SYS.XMLNode("save").setAttr("path",this.nodePath()+"/%2fobj").setAttr("force",1));
	}

	// Requesting the data
	/*if(!tErr.toInt()) {
		PDU = SYS.strFromCharCode(0x10, 0x01, 0x2f, 0, 1, 2, 0, 1);
		//SYS.messInfo("/ED","reqPDU="+SYS.strDecode(PDU,"Bin"," "));
		if(!(tErr=req(PDU)).toInt()) {
			stTm = SYS.time();
			for( ; iRec > 0 && (SYS.time()-stTm) < 20; iRec--) {
				PDU = SYS.strFromCharCode(0x10, 0x01, 0x31, 0, 1, 2, iRec>>8, iRec&0xFF);
				if(!(tErr=req(PDU)).toInt()) {
					PDU = SYS.strFromCharCode(4, 0x01, 0x41, 0, 28/2);
					if(!(tErr=req(PDU)).toInt()) {
						io = Special.FLibSYS.IO(PDU, "", "b"); io.pos = 2;
						SYS.messInfo("/FlowGAS",
							"iRec="+iRec+"; "
							"DateTm="+io.read("uint8",1)+"."+io.read("uint8",1)+"."+io.read("uint8",1)+"T"+io.read("uint8",1)+"; "
							"P="+io.read("float",1,"b")+"; "
							"T="+io.read("float",1,"b")+"; "
							"AccWorkF="+io.read("double",1,"b")+"; "
							"AccStdF="+io.read("double",1,"b"));
//	01 04 1C
//10 02 16 17	- 16.02.22 23
//43 4B 10 5C - P
//40 7F 2F F5 - T
//41 0A 5D 88 00 00 00 00 - Overal accumulated work flow
//41 2C 5E 69 8E 6C 20 A1 - Overal accumulated standard flow
//45 01
					}
				}
			}
		}
	}*/
}

if(!tErr.length)	tErr = "0";
if(tErr.toInt()) {
	if(!tr.isEVal() && tr.start()) tr.start(false);
	if(f_err != tErr)
		for(var iDt in dt) {
			itO = dt[iDt];
			if(iDt == "10000")	continue;
			this[itO.id].set(EVAL, 0, 0, true);
		}
}
f_err = tErr+(arhSt.length?": "+arhSt:"");','','',1707747484);
INSERT INTO tmplib_DevLib VALUES('ergomera125','Ergomera 125','','','The template implements support for Ergomera 125 Counters and for the manufacturer own protocol, due to the ModBus based protocol has obvious problems. Communication protocol of the devices is completely specific.

<!!> Even the manufacturer own protocol have had problems on accessible to test devices, but mostly in reading the archives, so the template is not tested properly and it is considered as the initial implementation! The template reads the daily and hourly archives and some set of the instantaneous parameters.

Author: Roman Savochenko <roman@oscada.org>
Total complexity: 0.5 HD
Sponsored by: Vinnica Poultry Farm
Version: 0.5.0
License: GPLv2','','',240,0,'JavaLikeCalc.JavaScript
//Same request to the device
function req(data, cmd, waitSz) {
	if(data == null || cmd == null || waitSz == null)	return tr("2:Empty request. ");
	req = "EM"+cmd+" !125m"+addr.toString(10,4)+data+SYS.strFromCharCode(0x0D,rand(255));
	//Calc and append the CRC
	CRC = Special.FLibSYS.CRC(req, 16, 0x8005);
	req += SYS.strFromCharCode(CRC&0xFF, CRC>>8);

	SYS.messDebug("/EM125/"+this.cfg("SHIFR"), tr("Request")+": "+SYS.strDecode(req,"Bin"," "));

	//Send and receive
	for(resp = tr.messIO(req); resp.length && (!waitSz || resp.length < (waitSz+1)) && (tresp=tr.messIO("")).length; )
		resp += tresp;
	if(!resp.length)	return tr("3:No response. ");
	else if(resp.length < (waitSz+1))	return tr("3:Response isn''t completed. ");
	for(CRC = 0xA5, iCh = 0; iCh < (resp.length-1); iCh++)
		CRC = CRC^resp.charCodeAt(iCh);
	if(CRC != resp.charCodeAt(resp.length-1))
	//if(Special.FLibSYS.CRC(resp.slice(0,-2),16,0x8005) != resp.charCodeAt(resp.length-2,"UTF-16LE"))
		return tr("3:Error the response CRC. ");

	SYS.messDebug("/EM125/"+this.cfg("SHIFR"), tr("Response")+": "+SYS.strDecode(resp,"Bin"," "));

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

	return "0";
}

//Seting value to the archive
function archSet(attr, vTm, vl, arh) {
	SYS.messDebug("/EM125/"+this.cfg("SHIFR"), "attr="+attr+"; vTm="+SYS.strftime(vTm)+"; arh="+arh+"; vl="+vl);
	atrO = this[attr];
	if(!atrO || atrO.isEVal()) return;
	arguments[attr] = vl;	//Set the current-last value
	//if(vTm > atrO.time())	atrO.set(vl,vTm,0,true);
	if(atrO.arch())	atrO.arch().setVal(vTm*1e6, vl, arh);
}

if(f_start) {
	reset = arhSelH = arhSelD = false;
	cTm = 0;
	arhThresH = 60*60;
	arhThresD = 2*24*60*60;
}

if(reset) {
	reset = arhSelH = arhSelD = false, arhLastD = arhLastH = 0;
	cTm = 0;
}

if(f_start || f_stop)	return;

tErr = "";

if(!(tr=SYS.Transport.outAt(transport)) || !tr.start(true))
	tErr = "1:"+tr("Output transport ''%1'' error.").replace("%1",transport);
else if(addr < 0 || addr > 9999)
	tErr = "2:"+tr("Address ''%1'' out of range [0...9999].").replace("%1",addr.toString());
else {
	tmCur = SYS.time();

	//Reading current values
	if((tmCur-cTm) > arhTryPer) {
		devDtTm = EVAL, devDtTm_ = 0;
		G1 = G2 = q1 = q2 = q3 = q4 = m1 = m2 = m3 = m4 = T1 = T2 = T3 = P1 = P2 = P3 = dT1 = dT2 = EVAL;
		Q1 = Q2 = V1 = V2 = V3 = V4 = M1 = M2 = M3 = M4 = EVAL;
		cTm = tmCur;
	}

	data = "";
	if(!devDtTm_ && !(tErr=req(data,"C",450)).toInt()) {
		io = Special.FLibSYS.IO(data); io.pos = 0;
		tmS = io.read("uint8", 1); tmM = io.read("uint8", 1); tmH = io.read("uint8", 1);
		dtD = io.read("uint8", 1); dtM = io.read("uint8", 1); dtY = io.read("uint8", 1);
		devDtTm = SYS.strftime(devDtTm_=SYS.mktime(tmS,tmM,tmH,dtD,dtM-1,2000+dtY));
		io.pos += 10;
		G1 = io.read("float", 1);
		G2 = io.read("float", 1);
		q1 = io.read("float", 1);
		q2 = io.read("float", 1);
		/*q3 =*/ io.read("float", 1);
		/*q4 =*/ io.read("float", 1);
		m1 = io.read("float", 1);
		m2 = io.read("float", 1);
		/*m3 =*/ io.read("float", 1);
		/*m4 =*/ io.read("float", 1);
		T1 = io.read("float", 1);
		T2 = io.read("float", 1);
		T3 = io.read("float", 1);
		P1 = io.read("float", 1);
		P2 = io.read("float", 1);
		P3 = io.read("float", 1);
		/*q1 =*/ io.read("float", 1);
		/*q2 =*/ io.read("float", 1);
		q3 = io.read("float", 1);
		q4 = io.read("float", 1);
		/*m1 =*/ io.read("float", 1);
		/*m2 =*/ io.read("float", 1);
		m3 = io.read("float", 1);
		m4 = io.read("float", 1);
		dT1 = io.read("float", 1);
		dT2 = io.read("float", 1);
		io.pos = 256;
		Q1 = io.read("long", 1);
		Q2 = io.read("long", 1);
		V1 = io.read("long", 1);
		V2 = io.read("long", 1);
		V3 = io.read("long", 1);
		V4 = io.read("long", 1);
		M1 = io.read("long", 1);
		M2 = io.read("long", 1);
		M3 = io.read("long", 1);
		M4 = io.read("long", 1);
		Q1 += io.read("float", 1);
		Q2 += io.read("float", 1);
		V1 += io.read("float", 1);
		V2 += io.read("float", 1);
		V3 += io.read("float", 1);
		V4 += io.read("float", 1);
		M1 += io.read("float", 1);
		M2 += io.read("float", 1);
		M3 += io.read("float", 1);
		M4 += io.read("float", 1);
	}

	//Requesting the hourly archive
	if(devDtTm_ && !arhSelD && arhH.length && !tErr.toInt() && (devDtTm_-arhLastH) > arhThresH) {
		toSave = false;

		if(!arhSelH)
			cmd = "A",	data = " %0 "+(arhLastH?SYS.strftime(arhLastH,"%d.%m.%y"):"1.01.00");
		else cmd = "F", data = "";
		while((SYS.time()-tmCur) < arhTmLim && !(tErr=req(data,cmd,130)).toInt()) {
			io = Special.FLibSYS.IO(data); io.pos = 2;
			tmH = io.read("uint8", 1);
			dtD = io.read("uint8", 1); dtM = io.read("uint8", 1); dtY = io.read("uint8", 1);

			if(tmH <= 24 && (arhLastH_=SYS.mktime(0,0,tmH,dtD,dtM-1,2000+dtY)) > arhLastH) {
				arhLastH = arhLastH_;

				archSet("dQ1", arhLastH, io.read("float", 1), arhH);
				archSet("dQ2", arhLastH, io.read("float", 1), arhH);
				archSet("dV1", arhLastH, io.read("float", 1), arhH);
				archSet("dV2", arhLastH, io.read("float", 1), arhH);
				archSet("dV3", arhLastH, io.read("float", 1), arhH);
				archSet("dV4", arhLastH, io.read("float", 1), arhH);
				archSet("dM1", arhLastH, io.read("float", 1), arhH);
				archSet("dM2", arhLastH, io.read("float", 1), arhH);
				archSet("dM3", arhLastH, io.read("float", 1), arhH);
				archSet("dM4", arhLastH, io.read("float", 1), arhH);
				archSet("dt1_work", arhLastH, io.read("long", 1), arhH);
				archSet("dt2_work", arhLastH, io.read("long", 1), arhH);
				archSet("dt3_work", arhLastH, io.read("long", 1), arhH);
				archSet("dt4_work", arhLastH, io.read("long", 1), arhH);
				archSet("dt1_idle", arhLastH, io.read("long", 1), arhH);
				archSet("dt2_idle", arhLastH, io.read("long", 1), arhH);
				archSet("dt3_idle", arhLastH, io.read("long", 1), arhH);
				archSet("dt4_idle", arhLastH, io.read("long", 1), arhH);
				archSet("dt1_cut", arhLastH, io.read("long", 1), arhH);
				archSet("dt2_cut", arhLastH, io.read("long", 1), arhH);
				archSet("dt1_rev", arhLastH, io.read("long", 1), arhH);
				archSet("dt2_rev", arhLastH, io.read("long", 1), arhH);
				archSet("dt1_up", arhLastH, io.read("long", 1), arhH);
				archSet("dt2_up", arhLastH, io.read("long", 1), arhH);
				archSet("dt_nopow", arhLastH, io.read("long", 1), arhH);
				archSet("dT1", arhLastH, io.read("float", 1), arhH);
				archSet("dT2", arhLastH, io.read("float", 1), arhH);
				archSet("dT3", arhLastH, io.read("float", 1), arhH);
				archSet("dP1", arhLastH, io.read("float", 1), arhH);
				archSet("dP2", arhLastH, io.read("float", 1), arhH);
				archSet("dP3", arhLastH, io.read("float", 1), arhH);

				toSave = true;
			}
			cmd = "F", data = "";
			arhSelH = ((devDtTm_-arhLastH) > arhThresH);
		}
		if(toSave)	SYS.cntrReq(SYS.XMLNode("save").setAttr("path",this.nodePath()+"/%2fobj").setAttr("force",1));
	}
	//Requesting the daily archive
	else if(devDtTm_ && !arhSelH && arhD.length && !tErr.toInt() && (devDtTm_-arhLastD) > arhThresD) {
		toSave = false;

		if(!arhSelD)
			cmd = "A",	data = " %1 "+(arhLastD?SYS.strftime(arhLastD,"%d.%m.%y"):"1.01.00");
		else cmd = "F", data = "";
		while((SYS.time()-tmCur) < arhTmLim && !(tErr=req(data,cmd,130)).toInt()) {
			io = Special.FLibSYS.IO(data); io.pos = 3;
			dtD = io.read("uint8", 1); dtM = io.read("uint8", 1); dtY = io.read("uint8", 1);

			if((arhLastD_=SYS.mktime(0,0,0,dtD,dtM-1,2000+dtY)) > arhLastD) {
				arhLastD = arhLastD_;

				archSet("dQ1", arhLastD, io.read("float", 1), arhD);
				archSet("dQ2", arhLastD, io.read("float", 1), arhD);
				archSet("dV1", arhLastD, io.read("float", 1), arhD);
				archSet("dV2", arhLastD, io.read("float", 1), arhD);
				archSet("dV3", arhLastD, io.read("float", 1), arhD);
				archSet("dV4", arhLastD, io.read("float", 1), arhD);
				archSet("dM1", arhLastD, io.read("float", 1), arhD);
				archSet("dM2", arhLastD, io.read("float", 1), arhD);
				archSet("dM3", arhLastD, io.read("float", 1), arhD);
				archSet("dM4", arhLastD, io.read("float", 1), arhD);
				archSet("dt1_work", arhLastD, io.read("long", 1), arhD);
				archSet("dt2_work", arhLastD, io.read("long", 1), arhD);
				archSet("dt3_work", arhLastD, io.read("long", 1), arhD);
				archSet("dt4_work", arhLastD, io.read("long", 1), arhD);
				archSet("dt1_idle", arhLastD, io.read("long", 1), arhD);
				archSet("dt2_idle", arhLastD, io.read("long", 1), arhD);
				archSet("dt3_idle", arhLastD, io.read("long", 1), arhD);
				archSet("dt4_idle", arhLastD, io.read("long", 1), arhD);
				archSet("dt1_cut", arhLastD, io.read("long", 1), arhD);
				archSet("dt2_cut", arhLastD, io.read("long", 1), arhD);
				archSet("dt1_rev", arhLastD, io.read("long", 1), arhD);
				archSet("dt2_rev", arhLastD, io.read("long", 1), arhD);
				archSet("dt1_up", arhLastD, io.read("long", 1), arhD);
				archSet("dt2_up", arhLastD, io.read("long", 1), arhD);
				archSet("dt_nopow", arhLastD, io.read("long", 1), arhD);
				archSet("dT1", arhLastD, io.read("float", 1), arhD);
				archSet("dT2", arhLastD, io.read("float", 1), arhD);
				archSet("dT3", arhLastD, io.read("float", 1), arhD);
				archSet("dP1", arhLastD, io.read("float", 1), arhD);
				archSet("dP2", arhLastD, io.read("float", 1), arhD);
				archSet("dP3", arhLastD, io.read("float", 1), arhD);

				toSave = true;
			}

			cmd = "F", data = "";
			arhSelD = ((devDtTm_-arhLastD) > arhThresD);
		}

		if(toSave)	SYS.cntrReq(SYS.XMLNode("save").setAttr("path",this.nodePath()+"/%2fobj").setAttr("force",1));
	}
}

//Error set
if(!tErr.length)	tErr = "0";
if(tErr.toInt()) {
	if(!tr.isEVal() && tr.start()) tr.start(false);
	if(f_err != tErr) {
		SYS.messDebug("/EM125/"+this.cfg("SHIFR"), tr("Error")+": "+tErr);

		//G1 = G2 = q1 = q2 = q3 = q4 = m1 = m2 = m3 = m4 = T1 = T2 = T3 = P1 = P2 = P3 = dT1 = dT2 = EVAL;
		//Q1 = Q2 = V1 = V2 = V3 = V4 = M1 = M2 = M3 = M4 = EVAL;
	}
}
prcSt = "";
if(arhD.length) {
	if(arhLastD && (devDtTm_-arhLastD) <= arhThresD)
		prcSt += tr("Days: ") + tr("Trace for current time=%1").replace("%1",SYS.strftime(arhLastD,"%Y-%m-%d")) + "; ";
	else {
		prcSt += tr("Days: ") + tr("Reading in time=%1").replace("%1",SYS.strftime(arhLastD,"%Y-%m-%d")) + "; ";
	}
}
if(arhH.length) {
	if(arhLastH && (devDtTm_-arhLastH) <= arhThresH)
		prcSt += tr("Hours: ") + tr("Trace for current time=%1").replace("%1",SYS.strftime(arhLastH,"%Y-%m-%d %H")) + "; ";
	else
		prcSt += tr("Hours: ") + tr("Reading in time=%1").replace("%1",SYS.strftime(arhLastH,"%Y-%m-%d %H")) + "; ";
}
f_err = tErr + (prcSt.length?((tErr=="0")?": ":" ")+prcSt:"");','','',1740065040);
