doc:jroboplc:modules:arctrm

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
doc:jroboplc:modules:arctrm [2026/07/22 21:15] – [2. Архитектура] denisdoc:jroboplc:modules:arctrm [2026/08/01 11:25] (current) – [События] denis
Line 1: Line 1:
-====== Модуль Arctrm ======+====== arctrm ======
  
-===== 1. Назначение =====+Модуль ''arctrm'' предназначен для периодического архивирования температурных значений в базе данных Firebird. На текущий момент значения считываются только из тегов модулей ''promauto.termo5''. В дальнейшем возможно расширение поддержки других модулей термометрии.
  
-''ArctrmModule'' - модуль архивации температурных значений. Он читает значения датчиков из тегов уже настроенных модулей оборудования, сохраняет периодические срезы в базу Firebird и пишет события изменения состояния связи с устройствами.+Модуль сохраняет:
  
-Сам модуль не реализует протокол обмена с оборудованием. Доступ к данным выполняется через ''Ref'': каждый ''Sensor'' ссылается на тег вида ''<deviceName>:Pdv<num>.T<sensorIndex>''а ''ArctrmDevice'' контролирует состояние устройства через ''<deviceName>:SYSTEM.ErrorFlag''. По коду видно, что конфигурация устройств берется из секции ''promauto.termo5''; тип ''promauto.termo5'' создается периферийным плагином как ''PaTermo5Module''.+  * температурные срезы по подвескам с актуальными данными; 
 +  * сведения о подвесках; 
 +  * события запуска архива, изменения состояния связи с устройствами и потери актуальности данных подвесок.
  
-Описание плагина в ''ArctrmPlugin.getPluginDescription()'' - ''termo value archiver''.+Сам модуль не выполняет обмен с оборудованием. Данные и состояние связи он получает из тегов других модулей.
  
-===== 2. Архитектура =====+[[doc:jroboplc:modules:arctrm_dev]]
  
-Модуль состоит из жизненного цикла JRoboPLC, объектной модели устройств и сервиса доступа к базе данных.+===== Конфигурация =====
  
-| Класс | Ответственность | +Пример настройки:
-| --- | --- | +
-| ''ArctrmPlugin'' | Регистрирует плагин с именем ''arctrm'', создает ''ArctrmModule'' и вызывает загрузку конфигурации. | +
-| ''ArctrmModule'' | Управляет загрузкой, подготовкой, инициализацией, циклом выполнения, записью архива, записью событий, ''connected''-тегом, остановом и reload. | +
-| ''ArctrmDataService'' | Инкапсулирует работу с БД: поиск модуля базы, проверку Firebird, загрузку и выполнение скрипта, синхронизацию ''PODVES'', запись ''TEMPER'' и ''EVENTLOG'', удаление старых архивных строк и учет текущего размера архива. | +
-| ''ArctrmDevice'' | Описывает одно настроенное устройство, содержит список ''Podves'', читает ''SYSTEM.ErrorFlag'' и пишет события изменения статуса устройства. | +
-| ''Podves'' | Описывает один подвес устройства, содержит список ''Sensor'', синхронизирует запись в ''PODVES'', сохраняет одну строку архива со всеми своими датчиками. | +
-| ''Sensor'' | Ссылается на температурный тег, читает целочисленное значение и заменяет отсутствующие или выходящие за диапазон значения специальными кодами. | +
-| ''Duration'' | Разбирает строковый период и выравнивает дату-время на границу периода. | +
-| ''Constants'' | Содержит коды событий и специальные значения датчиков. | +
-| ''Defaults'' | Содержит значения конфигурации по умолчанию. | +
-| ''ArctrmException''Проверяемое исключение для ошибок подготовки и инициализации Arctrm. | +
- +
-Прямое подключение к базе выполняет только ''ArctrmDataService''. Остальная часть модуля работает с объектами предметной модели и теговыми ссылками. +
- +
-===== 3. Структура объектов ===== +
- +
-Иерархия объектов во время работы: +
- +
-<code text> +
-ArctrmModule +
-└── ArctrmDevice +
-    └── Podves +
-        └── Sensor +
-<code> +
- +
-''ArctrmModule'' хранит все устройства в списке ''devices''. Список создается в конструкторе и заполняется в ''loadModule()'' вызовом ''ArctrmDevice.load(...)''+
- +
-''ArctrmDevice'' создается по одной записи из карты ''promauto.termo5''. Ключ записи становится именем устройства и используется как имя модуля, из которого читаются теги. Устройство создает: +
- +
-- список ''podvess''; +
-- ссылку ''Ref'' на тег ''SYSTEM.ErrorFlag''; +
-- состояние ''lastStatus'', по которому определяется необходимость записи события. +
- +
-''Podves'' создается по записи внутри конфигурации устройства. Номер подвеса получается из ключа конфигурации через ''entry.getKey().substring(1)'' и ''Integer.parseInt(...)''. Например, ключ ''p3'' дает номер ''3''. Имя подвеса формируется как ''<deviceName>.<podvesNum>'', а значение конфигурации сохраняется как описание ''descr''+
- +
-''Sensor'' создается для каждого индекса от ''0'' до ''sensorCount - 1''. Имя тега строится так: +
- +
-<code java> +
-String tagname = String.format("Pdv%d.T%d", num, i); +
-<code> +
- +
-Ссылка датчика указывает на ''<deviceName>:Pdv<num>.T<i>''+
- +
-Связь с базой данных строится через ''PODVES.ID'': после ''syncPodves(...)'' объект ''Podves'' сохраняет идентификатор в поле ''id'', а строки ''TEMPER'' ссылаются на него через ''PODVES_ID''+
- +
-===== 4. Принцип работы ===== +
- +
-==== Загрузка конфигурации ==== +
- +
-''ArctrmModule.loadModule(conf)'' читает параметры модуля, проверяет базовую корректность значений и создает тег ''connected''+
- +
-| Параметр | Куда сохраняется | Проверка | +
-| --- | --- | --- | +
-| ''database'' | ''svc.databaseModuleName'' | Проверяется позже в ''ArctrmDataService.prepare(...)''. | +
-| ''schema'' | ''svc.schema'' | Специальной проверки в ''loadModule()'' нет. | +
-| ''size'' | ''svc.maxRecordCount'' | Должен быть больше ''0''. | +
-| ''period'' | ''periodMs'' | Разбирается через ''Duration.parseMillis(...)''; результат не должен быть ''0''. | +
-| ''sensorCount'' | ''sensorCount'' | Должен быть больше ''0''. | +
-| ''promauto.termo5'' | ''devices'' | Передается в ''ArctrmDevice.load(...)''; ошибки конфигурации приводят к ''false''. | +
- +
-Общие параметры ''enable'', ''debug.logging'', ''func.tags'', ''tag.values'', ''tag.flags'' и флаги модуля обрабатываются в ''AbstractModule.load(...)''. В самом Arctrm явно используется ''enable'': при выключенном модуле ''getInfo()'' возвращает ''disabled'', а унаследованный жизненный цикл пропускает подготовку и выполнение. +
- +
-==== Подготовка базы и ссылок ==== +
- +
-''prepareModule()'' вызывает ''svc.prepare(sensorCount)''. Сервис: +
- +
-1. Ищет модуль базы данных по имени ''database''+
-2. Проверяет, что найденный модуль является ''FirebirdDatabaseModule''+
-3. Загружает ресурс ''dbscr/dbscr.arctrm.yml''+
-4. Формирует имена таблиц ''EVENTLOG'', ''PODVES'', ''TEMPER'' с учетом схемы. +
-5. Генерирует SQL вставки в ''TEMPER'' с колонками ''T0..T<n-1>''+
- +
-После подготовки сервиса модуль вызывает ''prepare()'' у каждого устройства. Устройства подготавливают ссылки на ''SYSTEM.ErrorFlag'', а подвесы - ссылки всех датчиков. +
- +
-В конце подготовки выставляется ''needInit = true'', а ''lastDt'' сбрасывается в ''null''+
- +
-==== Инициализация ==== +
- +
-Инициализация выполняется в начале рабочего цикла при наличии подключения к базе и только если ''needInit == true''+
- +
-''init()'' выполняет следующие шаги: +
- +
-1. ''svc.init(sensorCount)'' выполняет скрипт ''arctrm.init1'', добавляет отсутствующие колонки датчиков в ''TEMPER'', загружает ''recordCount'' и сбрасывает счетчики неподтвержденных операций. +
-2. Каждое устройство вызывает ''device.init(svc)'', а каждый подвес синхронизирует себя с таблицей ''PODVES''+
-3. ''lastDt'' загружается запросом последней строки ''TEMPER'' по убыванию ''ID''+
-4. В ''EVENTLOG'' пишется событие ''EVENT_ARCTRM_INIT'' с пустым сообщением. +
-5. Выполняется ''svc.commit()''+
-6. ''needInit'' сбрасывается в ''false''+
- +
-Если в ''TEMPER'' нет строк, ''getLastDt()'' возвращает ''LocalDateTime.MIN''+
- +
-==== Цикл выполнения ==== +
- +
-Каждый вызов ''executeModule()'' начинается с проверки ''svc.isConnected()''. Если база недоступна, модуль выключает тег ''connected'', ставит ''needInit = true'' и возвращает ''false''+
- +
-При доступной базе цикл такой: +
- +
-1. Выполняется отложенная инициализация. +
-2. Тег ''connected'' устанавливается в ''true''+
-3. Берется серверное время базы через ''svc.now()''+
-4. Время округляется вниз до границы периода через ''Duration.floorToPeriod(...)''+
-5. Все устройства, подвесы и датчики обновляют текущие значения. +
-6. Если рассчитанный ''dt'' отличается от ''lastDt'', сохраняется архивный срез и выполняется очистка старых строк. +
-7. Для каждого устройства проверяется состояние связи и при изменении пишется событие в ''EVENTLOG''+
-8. Выполняется ''svc.commit()''+
-9. ''lastDt'' обновляется текущим выровненным временем. +
- +
-==== Сохранение данных ==== +
- +
-При наступлении нового периода каждое устройство вызывает ''savePodves(...)'', каждый подвес собирает значения своих датчиков и передает их в ''svc.saveTemper(dt, id, values)''. В базу пишется одна строка ''TEMPER'' на один подвес. +
- +
-События состояния устройства сохраняются отдельно через ''svc.saveEvent(...)''. Событие пишется только при изменении статуса относительно ''lastStatus''+
- +
-==== Останов и reload ==== +
- +
-''closedownModule()'' при включенном модуле устанавливает ''connected'' в ''false''. Закрытие соединения с базой в этом методе не выполняется. +
- +
-''reload()'' создает временный ''ArctrmModule'', загружает новую конфигурацию, вызывает ''closedown()'' у текущего экземпляра, копирует унаследованные настройки, заменяет ''svc'', ''devices'', ''periodMs'', ''sensorCount'' и снова вызывает ''prepare()''. Явного переноса старых значений тегов в этом методе нет. +
- +
-===== 5. Структура базы данных ===== +
- +
-Схема создается скриптом ''src/main/resources/dbscr/dbscr.arctrm.yml'', который загружается как ресурс и выполняется под именем ''arctrm.init1''+
- +
-==== Таблица ''EVENTLOG'' ==== +
- +
-Назначение: хранение событий модуля и событий изменения состояния устройств. +
- +
-<code sql> +
-CREATE TABLE {schema}EVENTLOG ( +
-    ID          INTEGER GENERATED BY DEFAULT AS IDENTITY CONSTRAINT {schema}PK_EVENTLOG PRIMARY KEY, +
-    DT          TIMESTAMP, +
-    EVENT_CODE  SMALLINT NOT NULL, +
-    MESSAGE     VARCHAR(128) +
-+
-<code> +
- +
-| Поле | Назначение | +
-| --- | --- | +
-| ''ID'' | Первичный ключ. | +
-| ''DT'' | Время события; при записи берется ''db.getServerDatetime()''. | +
-| ''EVENT_CODE'' | Код события из ''Constants''. | +
-| ''MESSAGE'' | Сообщение; для статуса устройства используется имя устройства. | +
- +
-Индекс: ''IX_EVENTLOG_DT'' по полю ''DT''+
- +
-Коды событий, видимые из кода: +
- +
-| Константа | Значение | Где используется | +
-| --- | ---: | --- | +
-| ''EVENT_ARCTRM_INIT'' | ''0'' | Записывается при инициализации Arctrm. | +
-| ''EVENT_CONNECTED'' | ''1'' | Записывается при переходе устройства в состояние связи. | +
-| ''EVENT_DISCONNECTED'' | ''2'' | Записывается, если ''SYSTEM.ErrorFlag == true''. | +
-| ''EVENT_NOLINK'' | ''3'' | Записывается, если ссылка на ''SYSTEM.ErrorFlag'' не найдена. | +
- +
-Очистка ''EVENTLOG'' в коде не реализована. +
- +
-==== Таблица ''PODVES'' ==== +
- +
-Назначение: справочник настроенных подвесов. +
- +
-<code sql> +
-CREATE TABLE {schema}PODVES ( +
-    ID          INTEGER GENERATED BY DEFAULT AS IDENTITY CONSTRAINT {schema}PK_PODVES PRIMARY KEY, +
-    NAME        VARCHAR(32) NOT NULL CONSTRAINT {schema}UQ_PODVES_NAME UNIQUE, +
-    DESCR       VARCHAR(64) +
-+
-<code> +
- +
-| Поле | Назначение | +
-| --- | --- | +
-| ''ID'' | Первичный ключ, сохраняется в ''Podves.id''. | +
-| ''NAME'' | Уникальное имя подвеса в формате ''<deviceName>.<podvesNum>''. | +
-| ''DESCR'' | Описание из конфигурации. | +
- +
-Синхронизация выполняется через Firebird-запрос: +
- +
-<code sql> +
-update or insert into <PODVES> (name, descr) +
-values (?, ?) +
-matching (name) +
-returning id +
-<code> +
- +
-Удаление строк ''PODVES'', исчезнувших из конфигурации, не реализовано. +
- +
-==== Таблица ''TEMPER'' ==== +
- +
-Назначение: архив температурных срезов. +
- +
-<code sql> +
-CREATE TABLE {schema}TEMPER ( +
-    ID          BIGINT GENERATED BY DEFAULT AS IDENTITY CONSTRAINT {schema}PK_TEMPER PRIMARY KEY, +
-    PODVES_ID   INTEGER CONSTRAINT {schema}FK_TEMPER_PODVES REFERENCES {schema}PODVES ON DELETE CASCADE ON UPDATE CASCADE, +
-    DT          TIMESTAMP +
-+
-<code> +
- +
-| Поле | Назначение | +
-| --- | --- | +
-| ''ID'' | Первичный ключ строки архива. | +
-| ''PODVES_ID'' | Ссылка на ''PODVES.ID''. | +
-| ''DT'' | Выровненная метка времени архивного периода. | +
-| ''T0..T<n-1>'' | Динамические целочисленные колонки значений датчиков. | +
- +
-Индекс: ''IX_TEMPER_DT'' по полю ''DT''+
- +
-Колонки датчиков добавляются в ''ArctrmDataService.ensureSensorColumns(sensorCount)'': +
- +
-<code sql> +
-alter table <TEMPER> add T<i> integer +
-<code> +
- +
-Если колонка уже есть, она не создается повторно. +
- +
-===== 6. Алгоритм архивирования ===== +
- +
-==== Опрос датчиков ==== +
- +
-На каждом цикле выполнения вызывается цепочка: +
- +
-<code text> +
-ArctrmModule.executeModule() +
-  -> ArctrmDevice.update() +
-     -> Podves.update() +
-        -> Sensor.update() +
-<code> +
- +
-''Sensor.update()'' работает только с теговой ссылкой: +
- +
-| Условие | Записываемое значение | +
-| --- | ---: | +
-| Тег не найден через ''ref.linkIfNotValid()'' | ''VALUE_NOLINK = 3000''+
-| Значение меньше ''-1000'' | ''VALUE_BROKEN = 3010''+
-| Значение больше ''2000'' | ''VALUE_SHORTAGE = 3011''+
-| Значение в диапазоне | Исходное целое значение тега | +
- +
-Исходное значение, вышедшее за допустимый диапазон, отдельно не сохраняется. +
- +
-==== Формирование времени ==== +
- +
-Время архива берется от базы данных: +
- +
-<code java> +
-LocalDateTime dt = Duration.floorToPeriod(svc.now(), periodMs); +
-<code> +
- +
-''svc.now()'' возвращает ''db.getServerDatetime()''. JVM-время для архивной метки не используется. +
- +
-''Duration.floorToPeriod(...)'' считает миллисекунды от ''1970-01-01T00:00'' и округляет вниз до границы периода. +
- +
-==== Период архива ==== +
- +
-Строковый период разбирается в ''Duration.parseMillis(...)''+
- +
-| Суффикс | Единица | +
-| --- | --- | +
-| ''ms'' | миллисекунды | +
-| ''s'' | секунды | +
-| ''m'' | минуты | +
-| ''h'' | часы | +
-| ''d'' | дни | +
-| нет суффикса | миллисекунды | +
- +
-''null'', пустая строка, отрицательное значение, неверное число и переполнение приводят к ''IllegalArgumentException''. Значение ''0'' дополнительно запрещено в ''ArctrmModule.loadModule()''+
- +
-==== Запись архива ==== +
- +
-Архивные строки пишутся только если рассчитанный ''dt'' отличается от ''lastDt''. При записи: +
- +
-- каждое устройство сохраняет все свои подвесы; +
-- каждый подвес формирует список текущих значений датчиков; +
-- ''ArctrmDataService.saveTemper(...)'' вставляет одну строку ''TEMPER''+
- +
-Формат подготовленного SQL строится один раз при подготовке: +
- +
-<code sql> +
-insert into <TEMPER> (podves_id, dt, T0, T1, ...) +
-values (?, ?, ?, ?, ...) +
-<code> +
- +
-==== Циклическое обслуживание архива ==== +
- +
-При инициализации ''recordCount'' загружается запросом ''select count(*) from <TEMPER>''. Каждая вставка увеличивает ''uncommitedCount''+
- +
-Перед фиксацией нового архивного среза рассчитывается число удаляемых строк: +
- +
-<code text> +
-deleteCount = max(0, recordCount + uncommitedCount - maxRecordCount) +
-<code> +
- +
-Если ''deleteCount > 0'', выполняется удаление старейших строк по ''ID'': +
- +
-<code sql> +
-delete from <TEMPER> order by id rows <deleteCount> +
-<code> +
- +
-После успешного ''commit()'' счетчик обновляется: +
- +
-<code text> +
-recordCount = recordCount + uncommitedCount - deleteCount +
-<code> +
- +
-''rollback()'' сбрасывает ''uncommitedCount'' и ''deleteCount'', но не пересчитывает ''recordCount'' из базы. +
- +
-===== 7. Конфигурация ===== +
- +
-Параметры Arctrm, читаемые из ''loadModule()'': +
- +
-| Параметр | Значение по умолчанию | Назначение | Проверка | +
-| --- | --- | --- | --- | +
-| ''database'' | ''db'' | Имя модуля базы данных. | В ''prepare()'' модуль должен существовать и быть ''FirebirdDatabaseModule''. | +
-| ''schema'' | ''AT'' | Схема/префикс объектов БД. | Отдельной проверки нет. | +
-| ''size'' | ''1_000_000'' | Максимальное количество строк в ''TEMPER''. | Должно быть больше ''0''. | +
-| ''period'' | ''1h'' | Период архивирования. | Должен корректно разобраться и быть больше ''0''. | +
-| ''sensorCount'' | ''6'' | Количество датчиков на каждый подвес. | Должно быть больше ''0''. | +
-| ''promauto.termo5'' | нет в ''Defaults'' | Карта устройств и подвесов. | Ошибки разбора приводят к отказу загрузки. | +
-| ''enable'' | ''true'' в ''AbstractModule'' | Общий флаг включения модуля. | Обрабатывается базовым классом. | +
- +
-==== Пример конфигурации ====+
  
 <code yaml> <code yaml>
 plugin.arctrm: plugin.arctrm:
   module.arctrm:   module.arctrm:
-    database:   db +    database: db 
-    period:     5s +    schema: AT 
-    size:       30 #604800 +    period: 5s 
-    schema:        AT +    size: 1000000 
-    sensorCount:   6+    sensorCount: 6
  
     promauto.termo5:     promauto.termo5:
       mytrm1:       mytrm1:
-        p0: 231 силос, корпус 2 (гос.резерв) +        p0: 231 силос, корпус 2 
-        p5: 232 силос, корпус 2.+        p5: 232 силос, корпус 2
  
       mytrm2:       mytrm2:
-        p1: 401 силос, корпус 4. +        p1: 401 силос, корпус 4 
-<code>+</code>
  
-В этом примере ''plugin.arctrm'' и ''module.arctrm'' соответствуют схеме загрузки конфигурации: ''ConfigurationYaml.getModuleConf(...)'' ищет ''plugin.<pluginName>'' и внутри него ''module.<moduleName>''. Значения ''database'', ''period'', ''size'', ''schema'' и ''sensorCount'' читаются в ''ArctrmModule.loadModule()''.+==== Параметры ====
  
-Секция ''promauto.termo5'' передается в ''ArctrmDevice.load(...)''. Ключи ''mytrm1'' и ''mytrm2'' становятся именами модулей-источников теговКлючи ''p0''''p5'' и ''p1'' дают номера подвесов ''0'', ''5'' и ''1'', потому что код берет часть ключа после первого символа и разбирает ее как числоПри ''sensorCount6'' для каждого подвеса создаются ссылки на теги ''Pdv<num>.T0'' ... ''Pdv<num>.T5''.+^ Параметр ^ По умолчанию ^ Описание ^ 
 +''database'' ''db'' | Имя модуля базы данных. Модуль должен использовать Firebird
 +''schema'' ''AT'' | Префикс таблиц базы данных. | 
 +''period'' | ''1h'' | Период сохранения архива. Поддерживаются суффиксы ''ms'', ''s''''m'', ''h'', ''d''. | 
 +| ''size'' | ''1000000'' | Максимальное количество строк в таблице ''TEMPER''
 +''sensorCount'' | ''6'' | Количество датчиков в каждой подвеске. | 
 +| ''promauto.termo5'' | — | Список модулей-источников и настроенных подвесок|
  
-Комментарий ''#604800'' в строке ''size'' является YAML-комментарием и не участвует в значении параметра. Активное значение в примере - ''30''.+Значения ''size''''period'' и ''sensorCount'' должны быть больше нуля.
  
-Пример выше предоставлен как документационный пример и соответствует фактическому разбору конфигурации в коде. Готовый Arctrm-конфиг в просмотренных файлах репозитория не найден.+==== Настройка устройств ====
  
-===== 8Диагностика =====+Ключ первого уровня в секции ''promauto.termo5'' является именем модуля-источника тегов. Ключ подвески состоит из произвольного первого символа и числового номера. Например, из ключа ''p5'' модуль получает номер подвеса ''5''.
  
-''getInfo()'' возвращает краткую строку состояния. +Для каждой подвески создаются ссылки на теги:
- +
-Если модуль выключен:+
  
 <code text> <code text>
-disabled +<deviceName>:Pdv<podvesNum>.T0 
-<code>+<deviceName>:Pdv<podvesNum>.T1 
 +... 
 +<deviceName>:Pdv<podvesNum>.T<sensorCount-1> 
 +</code>
  
-Для включенного модуля формат такой:+Актуальность данных подвески определяется по тегу:
  
 <code text> <code text>
-<NOT CONNECTED! >devices=<n> podves=<n> sensors=<n>< nolinkSensors=<n>> cursize=<n> last=<time|never+<deviceName>:Pdv<podvesNum>.Time 
-<code>+</code>
  
-| Поле | Значение +Данные считаются актуальными, если ссылка на тег существует, а его значение находится в диапазоне от ''0'' до ''600'' включительно.
-| --- | --- | +
-| ''NOT CONNECTED!'' | Добавляется через ''ANSI.redBold(...)'', если ''tagConnected.getBool() == false''. | +
-| ''devices'' | Количество объектов в ''devices''. | +
-| ''podves'' | Сумма ''device.podvess.size()'' по всем устройствам. | +
-| ''sensors'' | Сумма ''podves.sensors.size()'' по всем подвесам. | +
-| ''nolinkSensors'' | Показывается только если есть датчики со значением ''VALUE_NOLINK''; фрагмент подсвечивается ''ANSI.redBold(...)''. | +
-| ''cursize'' | Текущий кэшированный размер ''TEMPER'', возвращаемый ''svc.getRecordCount()''. | +
-| ''last'' | ''never'', если ''lastDt == null'' или ''LocalDateTime.MIN''; иначе дата в формате ''yyyy-MM-dd HH:mm:ss''|+
  
-Имя модуля базы данных, период архива и максимальный размер архива в текущем ''getInfo()'' не выводятся.+Состояние связи с устройством определяется по тегу:
  
-===== 9Обработка ошибок =====+<code text> 
 +<deviceName>:SYSTEM.ErrorFlag 
 +</code>
  
-==== Ошибки базы данных ====+===== Принцип работы =====
  
-На этапе подготовки ''ArctrmDataService.prepare(...)'' выбрасывает ''ArctrmException'' в случаях:+После подключения к базе модуль автоматически:
  
-модуль базы данных не найден+  создаёт необходимые таблицы и индексы
-найденный модуль не является ''FirebirdDatabaseModule''; +  - добавляет отсутствующие колонки ''T0...Tn'' в таблицу ''TEMPER''; 
-- ресурс ''dbscr/dbscr.arctrm.yml'' не загружен.+  синхронизирует справочник подвесок; 
 +  - загружает дату последней архивной записи; 
 +  - записывает событие запуска архива.
  
-''prepareModule()'' перехватывает ''ArctrmException''пишет ошибку через ''env.printError(...)'' и возвращает ''false''.+На каждом рабочем цикле модуль сначала проверяет ''Pdv<podvesNum>.Time'' каждой подвески. Значения датчиков считываются только для подвесок с актуальными данными. Время архивного среза берётся с сервера базы данных и округляется вниз до границы периода ''period''.
  
-На этапе выполнения отсутствие подключения определяется через ''svc.isConnected()''. В этом случае модуль:+Если наступил новый период, в ''TEMPER'' записывается по одной строке для каждой подвески с актуальными данными. Для подвески с отсутствующим тегом ''Time'' или значением вне диапазона ''0...600'' строка не создаётся. При превышении лимита ''size'' удаляются строки с наименьшими значениями ''ID''.
  
-1. выключает ''connected''; +===== База данных =====
-2. ставит ''needInit true''; +
-3. возвращает ''false''.+
  
-==== Ошибки связи с устройствами ====+^ Таблица ^ Назначение ^ 
 +| ''PODVES'' | Справочник подвесок: имя и описание. | 
 +| ''TEMPER'' | Архив температурных срезов. Содержит ''PODVES_ID'', ''DT'' и динамические колонки ''T0...Tn''. | 
 +| ''EVENTLOG'' | Журнал запуска архива, изменения состояния связи с устройствами и потери актуальности данных подвесок. |
  
-Состояние устройства определяется по ''SYSTEM.ErrorFlag'':+Основные связи:
  
-| Условие | Код события | +<code text> 
---- | --- | +PODVES.ID <TEMPER.PODVES_ID 
-| Ссылка на ''SYSTEM.ErrorFlag'' не найдена | ''EVENT_NOLINK''+</code>
-| Ссылка есть, ''ErrorFlag == true'' | ''EVENT_DISCONNECTED'' | +
-| Ссылка есть, ''ErrorFlag == false'' | ''EVENT_CONNECTED'' |+
  
-Событие пишется только при изменении состояния относительно ''lastStatus''. Низкоуровневые ошибки обмена с оборудованием Arctrm не анализирует; они видны только через теги внешних модулей.+Одна строка ''TEMPER'' содержит значения всех датчиков одной подвески за один архивный период.
  
-==== Некорректные значения датчиков ====+===== Значения датчиков =====
  
-Значения ниже ''-1000'' заменяются на ''VALUE_BROKEN'', значения выше ''2000'' - на ''VALUE_SHORTAGE''. Отдельные события и отдельное логирование для таких значений не реализованы.+Обычные значения записываются без изменения. Ошибочные состояния сохраняются в тех же колонках ''T0...Tn'' как специальные числа.
  
-Если тег датчика не найден, значение становится ''VALUE_NOLINK''Такие датчики дополнительно подсчитываются в ''getInfo()''.+^ Значение ^ Состояние 
 +| ''3010'' | Значение ниже допустимого диапазона — короткое замыкание. | 
 +| ''3011'' | Значение выше допустимого диапазона — обрыв. | 
 +''3013'' Тег датчика не найден — ''NOLINK''|
  
-==== Неожиданные исключения ====+Допустимый диапазон обычного значения: от ''-1000'' до ''2000'' включительно.
  
-Все исключения внутри основной части ''executeModule()'' перехватываются общим ''catch (Exception e)''. Модуль:+===== События =====
  
-1. пишет ошибку через ''env.printError(...)''+^ Код ^ Событие 
-2. вызывает ''svc.rollback()''; +''0'' | Ссылка на ''SYSTEM.ErrorFlag'' не найдена
-3. выключает ''connected''+| ''1'' | Связь с устройством потеряна: ''SYSTEM.ErrorFlag = true''. | 
-4. устанавливает ''needInit = true''+| ''3'' | Данные подвески неактуальны: ссылка на ''Pdv<podvesNum>.Time'' не найдена или значение находится вне диапазона ''0...600''
-5. возвращает ''true''.+| ''99'' | Связь с устройством установлена. | 
 +''100'' | Инициализация архивации|
  
-===== 10. Производительность =====+Для событий состояния устройства в поле ''MESSAGE'' записывается имя устройства, для события неактуальных данных — имя подвески в формате ''<deviceName>.<podvesNum>'', для события инициализации — пустая строка.
  
-Реализация использует несколько простых оптимизаций.+Событие состояния устройства записывается только при изменении его состояния. Событие неактуальных данных формируется при первой потере актуальности, записывается при наступлении следующего архивного периода и не повторяется до восстановления актуальности и её следующей потери.
  
-| Механизм | Эффект | +===== Теги модуля =====
-| --- | --- | +
-| Генерация ''sqlTemperInsert'' один раз в ''prepare()'' | Не строит SQL вставки на каждом архивном цикле. | +
-| ''openPreparedStatement(...)'' в ''saveTemper()'' | Подготовленный ''PreparedStatement'' переиспользуется до ''commit()'' или ''rollback()''. | +
-| Запись только при смене периода | Обычные циклы выполнения без нового периода не пишут строки ''TEMPER''. | +
-| Кэш ''recordCount'' | Не выполняет ''count(*)'' после каждой вставки. | +
-| Очистка только при превышении ''size'' | Удаление старых строк запускается только при необходимости. | +
-| Однократная загрузка списка колонок ''TEMPER'' при init | Позволяет добавить только отсутствующие ''T*''-колонки. |+
  
-JDBC batch-вставки не используются: при наступлении периода выполняется одна вставка на каждый подвес.+^ Тег ^ Тип ^ Описание 
 +| ''connected'' | boolean | Установлен, когда модуль подключён к базе и рабочий цикл выполняется без ошибки|
  
-===== 11. Ограничения =====+===== Диагностика =====
  
-| Ограничение | Основание в реализации | +Метод ''getInfo()'' возвращает краткую строку состояниянапример:
-| --- | --- | +
-| Поддерживается только Firebird. | ''ArctrmDataService.prepare(...)'' требует ''FirebirdDatabaseModule''. | +
-| ''sensorCount'' общий для всего модуля. | Один параметр используется для всех ''Podves''. | +
-| Удаление отсутствующих в конфигурации подвесов не выполняется. | Есть ''update or insert'', но нет удаления из ''PODVES''. | +
-| ''EVENTLOG'' не очищается. | В коде нет метода очистки этой таблицы. | +
-| Очистка ''TEMPER'' идет по ''ID'', а не по ''DT''. | SQL: ''delete ... order by id rows ...''. | +
-| Последняя дата архива ищется по максимальному ''ID'', не по максимальному ''DT''. | SQL: ''select first 1 dt ... order by id desc''. | +
-| ''recordCount'' может устареть при внешних изменениях ''TEMPER''. | После init счетчик обновляется только внутренними commit/rollback. | +
-| Значение ''Sensor.value'' до первого опроса равно Java-значению по умолчанию ''0''. | Поле ''int value'' не инициализируется явно. | +
-| Нет отдельного признака качества значения в ''TEMPER''. | Спецсостояния записываются числами в ''T*''. | +
-| Штатный Arctrm-конфиг в просмотренных файлах не найден. | Пример в этом документе является документационным и проверен по коду загрузки. | +
-| Назначение кодов событий описано только именами констант. | Дополнительной документации в коде нет. |+
  
-===== 12. Возможные улучшения ===== +<code text> 
- +devices=2 podves=3 sensors=18 cursize=30 last=2026-07-23 00:16:50 
-Добавить тесты для ''Duration'', ''Sensor.update()'', ''getInfo()'', смены статусов устройств и расчета ''recordCount''+</code>
-Использовать batch-вставки для ''TEMPER'', если число подвесов большое. +
-- Добавить явные поля качества/статуса вместо кодирования состояний специальными числами в ''T*''+
-- Реализовать политику очистки ''EVENTLOG''+
-- Добавить синхронизацию удаления ''PODVES'', если удаленные из конфигурации подвесы должны исчезать из справочника. +
-- Загружать последнюю архивную метку по ''max(DT)'', если важна именно временная непрерывность, а не порядок вставки. +
-- Периодически сверять ''recordCount'' с реальным количеством строк при возможных внешних изменениях базы. +
-- Расширить ''getInfo()'': показывать имя БД, период, лимит архива, последнюю ошибку или время последней успешной записи. +
- +
-===== Проверка по исходникам =====+
  
-Документ сверялся с текущей реализацией:+^ Поле ^ Описание ^ 
 +| ''devices'' | Количество настроенных устройств. | 
 +| ''podves'' | Общее количество подвесок. | 
 +| ''sensors'' | Общее количество датчиков. | 
 +| ''nolinkSensors'' | Количество датчиков без ссылки; выводится только при ненулевом значении. | 
 +| ''cursize'' | Текущий размер таблицы ''TEMPER'', учтённый модулем. | 
 +| ''last'' | Дата и время последнего архивного периода или ''never''. |
  
-''ArctrmModule.java''+При отсутствии подключения в начало строки добавляется ''NOT CONNECTED!''Если модуль выключен, возвращается ''disabled''.
-- ''ArctrmDataService.java''+
-- ''ArctrmDevice.java''; +
-- ''Podves.java''; +
-- ''Sensor.java''; +
-- ''Constants.java''; +
-- ''Defaults.java''; +
-- ''Duration.java''; +
-- ''ArctrmPlugin.java''; +
-- ''ArctrmException.java''; +
-- ''Context.java''; +
-- ''src/main/resources/dbscr/dbscr.arctrm.yml''; +
-- ''DatabaseProtoServiceImpl.java''; +
-- ''AbstractModule.java''; +
-- ''Ref.java''; +
-- ''PeripherialPlugin.java''; +
-- ''PaTermo5Module.java''.+
  
-Места, где код не дает полной информации:+===== Ограничения =====
  
-Пример конфигурации предоставлен как документационный; штатный Arctrm-конфиг в просмотренных файлах репозитория не найден. +  * Поддерживается только Firebird
-- Бизнес-смысл событий известен только по именам констант+  * Значение ''sensorCount'' является общим для всех подвесок. 
-- Низкоуровневое поведение оборудования находится вне Arctrm; модуль видит только теги и ''SYSTEM.ErrorFlag''+  * Таблица ''EVENTLOG'' автоматически не очищается. 
-- Требования к эксплуатационной очистке ''EVENTLOG'', удалению старых ''PODVES'' и реакции на внешние изменения базы в коде не определены.+  * Удаление архивных строк выполняется по ''ID'', а не по дате ''DT''.
  • doc/jroboplc/modules/arctrm.1784744120.txt.gz
  • Last modified: 2026/07/22 21:15
  • by denis