Show pageOld revisionsBacklinksBack to top This page is read only. You can view the source, but not change it. Ask your administrator if you think this is wrong. ====== arctrm для разработчика ====== ===== 1. Назначение ===== ''ArctrmModule'' - модуль архивации температурных значений. Он читает значения датчиков из тегов уже настроенных модулей оборудования, сохраняет периодические срезы в базу Firebird и пишет события изменения состояния связи с устройствами и потери актуальности данных подвесов. Сам модуль не реализует протокол обмена с оборудованием. Доступ к данным выполняется через ''Ref'': каждый ''Sensor'' ссылается на тег вида ''<deviceName>:Pdv<num>.T<sensorIndex>'', ''Podves'' проверяет актуальность данных по ''<deviceName>:Pdv<num>.Time'', а ''ArctrmDevice'' контролирует состояние устройства через ''<deviceName>:SYSTEM.ErrorFlag''. По коду видно, что конфигурация устройств берется из секции ''promauto.termo5''; тип ''promauto.termo5'' создается периферийным плагином как ''PaTermo5Module''. Описание плагина в ''ArctrmPlugin.getPluginDescription()'' - ''termo value archiver''. ===== 2. Архитектура ===== Модуль состоит из жизненного цикла JRoboPLC, объектной модели устройств и сервиса доступа к базе данных. ^ Класс ^ Ответственность ^ | ''ArctrmPlugin'' | Регистрирует плагин с именем ''arctrm'', создает ''ArctrmModule'' и вызывает загрузку конфигурации. | | ''ArctrmModule'' | Управляет загрузкой, подготовкой, инициализацией, циклом выполнения, записью архива, записью событий, ''connected''-тегом, остановом и reload. | | ''ArctrmDataService'' | Инкапсулирует работу с БД: поиск модуля базы, проверку Firebird, загрузку и выполнение скрипта, синхронизацию ''PODVES'', запись ''TEMPER'' и ''EVENTLOG'', удаление старых архивных строк и учет текущего размера архива. | | ''ArctrmDevice'' | Описывает одно настроенное устройство, содержит список ''Podves'', читает ''SYSTEM.ErrorFlag'' и пишет события изменения статуса устройства. | | ''Podves'' | Описывает один подвес устройства, проверяет актуальность данных по тегу ''Pdv<num>.Time'', содержит список ''Sensor'', синхронизирует запись в ''PODVES'', сохраняет строку архива с актуальными данными и формирует событие потери актуальности. | | ''Sensor'' | Ссылается на температурный тег, читает целочисленное значение и заменяет отсутствующие или выходящие за диапазон значения специальными кодами. | | ''Duration'' | Разбирает строковый период и выравнивает дату-время на границу периода. | | ''Constants'' | Содержит коды событий, специальные значения датчиков и предел актуальности данных подвеса. | | ''Defaults'' | Содержит значения конфигурации по умолчанию. | | ''Params'' | Пустой класс; в текущей реализации не используется. | | ''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''. Кроме списка датчиков подвес создает ссылку ''refTime'' на тег ''<deviceName>:Pdv<num>.Time'' и хранит признаки ''dataValid'' и ''needSaveEvent''. ''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)''. Сервис: - Ищет модуль базы данных по имени ''database''. - Проверяет, что найденный модуль является ''FirebirdDatabaseModule''. - Загружает ресурс ''dbscr/dbscr.arctrm.yml''. - Формирует имена таблиц ''EVENTLOG'', ''PODVES'', ''TEMPER'' с учетом схемы. - Генерирует SQL вставки в ''TEMPER'' с колонками ''T0..T<n-1>''. После подготовки сервиса модуль вызывает ''prepare()'' у каждого устройства. Устройства подготавливают ссылки на ''SYSTEM.ErrorFlag'', а подвесы - ссылки всех датчиков и ''Pdv<num>.Time''. В конце подготовки выставляется ''needInit = true'', а ''lastDt'' сбрасывается в ''null''. ==== Инициализация ==== Инициализация выполняется в начале рабочего цикла при наличии подключения к базе и только если ''needInit == true''. ''init()'' выполняет следующие шаги: - ''svc.init(sensorCount)'' выполняет скрипт ''arctrm.init1'', добавляет отсутствующие колонки датчиков в ''TEMPER'', загружает ''recordCount'' и сбрасывает счетчики неподтвержденных операций. - Каждое устройство вызывает ''device.init(svc)'', а каждый подвес синхронизирует себя с таблицей ''PODVES''. - ''lastDt'' загружается запросом последней строки ''TEMPER'' по убыванию ''ID''. - В ''EVENTLOG'' пишется событие ''EVENT_ARCTRM_INIT'' с пустым сообщением. - Выполняется ''svc.commit()''. - ''needInit'' сбрасывается в ''false''. Если в ''TEMPER'' нет строк, ''getLastDt()'' возвращает ''LocalDateTime.MIN''. ==== Цикл выполнения ==== Каждый вызов ''executeModule()'' начинается с проверки ''svc.isConnected()''. Если база недоступна, модуль выключает тег ''connected'', ставит ''needInit = true'' и возвращает ''false''. При доступной базе цикл такой: - Выполняется отложенная инициализация. - Тег ''connected'' устанавливается в ''true''. - Берется серверное время базы через ''svc.now()''. - Время округляется вниз до границы периода через ''Duration.floorToPeriod(...)''. - Все устройства и подвесы обновляют состояние; датчики опрашиваются только у подвесов с актуальным значением ''Pdv<num>.Time''. - Если рассчитанный ''dt'' отличается от ''lastDt'', сохраняется архивный срез и выполняется очистка старых строк. - Для каждого устройства проверяется состояние связи и при изменении пишется событие в ''EVENTLOG''. - Выполняется ''svc.commit()''. - ''lastDt'' обновляется текущим выровненным временем. ==== Сохранение данных ==== При наступлении нового периода каждое устройство вызывает ''savePodves(...)''. Подвес с актуальными данными собирает значения своих датчиков и передает их в ''svc.saveTemper(dt, id, values)''; в базу пишется одна строка ''TEMPER''. Если ссылка на ''Pdv<num>.Time'' не найдена или значение находится вне диапазона ''0..600'', строка для этого подвеса не создается. События состояния устройства сохраняются отдельно через ''svc.saveEvent(...)''. Событие пишется только при изменении статуса относительно ''lastStatus''. Событие ''EVENT_PODVES_UPDATE_STALE'' формируется при первой потере актуальности данных подвеса и сохраняется вызовом ''Podves.save(...)'' при наступлении следующего архивного периода. ==== Останов и 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'' | ''100'' | Записывается при инициализации Arctrm. | | ''EVENT_DEVICE_CONNECTED'' | ''99'' | Записывается при переходе устройства в состояние связи. | | ''EVENT_DEVICE_DISCONNECTED'' | ''1'' | Записывается, если ''SYSTEM.ErrorFlag == true''. | | ''EVENT_DEVICE_NOLINK'' | ''0'' | Записывается, если ссылка на ''SYSTEM.ErrorFlag'' не найдена и предыдущий статус имел другой код. | | ''EVENT_PODVES_UPDATE_STALE'' | ''3'' | Записывается при первой потере актуальности данных подвеса; в ''MESSAGE'' передается имя ''<deviceName>.<podvesNum>''. | Очистка ''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() -> Podves.checkTimeValid() -> Sensor.update() при актуальных данных </code> Сначала ''Podves.update()'' проверяет ссылку ''Pdv<num>.Time''. Данные считаются актуальными, если ссылка существует, а значение тега находится в диапазоне от ''0'' до ''PODVES_UPDATE_TIME_LIMIT = 600'' включительно. При неактуальных данных ''Sensor.update()'' не вызывается и значения датчиков остаются прежними. Для подвеса с актуальными данными ''Sensor.update()'' работает с теговой ссылкой: ^ Условие ^ Записываемое значение ^ | Тег не найден через ''ref.linkIfNotValid()'' | ''VALUE_NOLINK = 3013'' | | Значение меньше ''-1000'' | ''VALUE_SHORTAGE = 3010'' | | Значение больше ''2000'' | ''VALUE_BROKEN = 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''. При записи: * каждое устройство вызывает ''save(...)'' для всех своих подвесов; * каждый подвес с актуальными данными формирует список текущих значений датчиков; * ''ArctrmDataService.saveTemper(...)'' вставляет одну строку ''TEMPER'' для каждого подвеса с актуальными данными; * подвес с неактуальными данными не создает строку ''TEMPER'', но при установленном ''needSaveEvent'' записывает событие ''EVENT_PODVES_UPDATE_STALE''. Формат подготовленного 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> plugin.arctrm: module.arctrm: database: db period: 5s size: 30 #604800 schema: AT sensorCount: 6 promauto.termo5: mytrm1: p0: 231 силос, корпус 2 (гос.резерв) p5: 232 силос, корпус 2. mytrm2: p1: 401 силос, корпус 4. </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'', потому что код берет часть ключа после первого символа и разбирает ее как число. При ''sensorCount: 6'' для каждого подвеса создаются ссылки на теги ''Pdv<num>.T0'' ... ''Pdv<num>.T5'', а также ссылка на ''Pdv<num>.Time''. Комментарий ''#604800'' в строке ''size'' является YAML-комментарием и не участвует в значении параметра. Активное значение в примере - ''30''. Пример выше предоставлен как документационный пример и соответствует фактическому разбору конфигурации в коде. Готовый Arctrm-конфиг в просмотренных файлах репозитория не найден. ===== 8. Диагностика ===== ''getInfo()'' возвращает краткую строку состояния. Если модуль выключен: <code text> disabled </code> Для включенного модуля формат такой: <code text> <NOT CONNECTED! >devices=<n> podves=<n> sensors=<n>< nolinkSensors=<n>> cursize=<n> last=<time|never> </code> ^ Поле ^ Значение ^ | ''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. Обработка ошибок ===== ==== Ошибки базы данных ==== На этапе подготовки ''ArctrmDataService.prepare(...)'' выбрасывает ''ArctrmException'' в случаях: * модуль базы данных не найден; * найденный модуль не является ''FirebirdDatabaseModule''; * ресурс ''dbscr/dbscr.arctrm.yml'' не загружен. ''prepareModule()'' перехватывает ''ArctrmException'', пишет ошибку через ''env.printError(...)'' и возвращает ''false''. На этапе выполнения отсутствие подключения определяется через ''svc.isConnected()''. В этом случае модуль: - выключает ''connected''; - ставит ''needInit = true''; - возвращает ''false''. ==== Ошибки связи с устройствами ==== Состояние устройства определяется по ''SYSTEM.ErrorFlag'': ^ Условие ^ Код события ^ | Ссылка на ''SYSTEM.ErrorFlag'' не найдена | ''EVENT_DEVICE_NOLINK = 0'' | | Ссылка есть, ''ErrorFlag == true'' | ''EVENT_DEVICE_DISCONNECTED = 1'' | | Ссылка есть, ''ErrorFlag == false'' | ''EVENT_DEVICE_CONNECTED = 99'' | Событие пишется только при изменении состояния относительно ''lastStatus''. Поле ''lastStatus'' изначально равно ''0'', поэтому начальное состояние ''EVENT_DEVICE_NOLINK'' не записывается, а начальные состояния ''EVENT_DEVICE_DISCONNECTED'' и ''EVENT_DEVICE_CONNECTED'' записываются. Низкоуровневые ошибки обмена с оборудованием Arctrm не анализирует; они видны только через теги внешних модулей. ==== Неактуальные данные подвеса ==== Ссылка на ''Pdv<num>.Time'' считается неактуальной, если она не найдена либо содержит значение меньше ''0'' или больше ''600''. При переходе ''dataValid'' из ''null'' или ''true'' в ''false'' устанавливается ''needSaveEvent''. До восстановления актуальности датчики не опрашиваются, а строка ''TEMPER'' для подвеса не записывается. При наступлении следующего архивного периода ''Podves.save(...)'' записывает ''EVENT_PODVES_UPDATE_STALE = 3'' с именем подвеса в ''MESSAGE'' и сбрасывает ''needSaveEvent''. Повторное событие возможно только после восстановления актуальности и ее новой потери; отдельное событие восстановления не предусмотрено. ==== Некорректные значения датчиков ==== Значения ниже ''-1000'' заменяются на ''VALUE_SHORTAGE = 3010'', значения выше ''2000'' - на ''VALUE_BROKEN = 3011''. Отдельные события и отдельное логирование для таких значений не реализованы. Если тег датчика не найден, значение становится ''VALUE_NOLINK = 3013''. Такие датчики дополнительно подсчитываются в ''getInfo()''. ==== Неожиданные исключения ==== Все исключения внутри основной части ''executeModule()'' перехватываются общим ''catch (Exception e)''. Модуль: - пишет ошибку через ''env.printError(...)''; - вызывает ''svc.rollback()''; - выключает ''connected''; - устанавливает ''needInit = true''; - возвращает ''true''. ===== 10. Производительность ===== Реализация использует несколько простых оптимизаций. ^ Механизм ^ Эффект ^ | Генерация ''sqlTemperInsert'' один раз в ''prepare()'' | Не строит SQL вставки на каждом архивном цикле. | | ''openPreparedStatement(...)'' в ''saveTemper()'' | Подготовленный ''PreparedStatement'' переиспользуется до ''commit()'' или ''rollback()''. | | Запись только при смене периода | Обычные циклы выполнения без нового периода не пишут строки ''TEMPER''. | | Кэш ''recordCount'' | Не выполняет ''count(*)'' после каждой вставки. | | Очистка только при превышении ''size'' | Удаление старых строк запускается только при необходимости. | | Однократная загрузка списка колонок ''TEMPER'' при init | Позволяет добавить только отсутствующие ''T*''-колонки. | JDBC batch-вставки не используются: при наступлении периода выполняется одна вставка на каждый подвес с актуальными данными. ===== 11. Ограничения ===== ^ Ограничение ^ Основание в реализации ^ | Поддерживается только 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. Возможные улучшения ===== * Добавить тесты для ''Duration'', ''Sensor.update()'', ''getInfo()'', смены статусов устройств и расчета ''recordCount''. * Использовать batch-вставки для ''TEMPER'', если число подвесов большое. * Добавить явные поля качества/статуса вместо кодирования состояний специальными числами в ''T*''. * Реализовать политику очистки ''EVENTLOG''. * Добавить синхронизацию удаления ''PODVES'', если удаленные из конфигурации подвесы должны исчезать из справочника. * Загружать последнюю архивную метку по ''max(DT)'', если важна именно временная непрерывность, а не порядок вставки. * Периодически сверять ''recordCount'' с реальным количеством строк при возможных внешних изменениях базы. * Расширить ''getInfo()'': показывать имя БД, период, лимит архива, последнюю ошибку или время последней успешной записи. ===== Проверка по исходникам ===== Документ сверялся с текущей реализацией: * ''ArctrmModule.java''; * ''ArctrmDataService.java''; * ''ArctrmDevice.java''; * ''Podves.java''; * ''Sensor.java''; * ''Constants.java''; * ''Defaults.java''; * ''Duration.java''; * ''ArctrmPlugin.java''; * ''ArctrmException.java''; * ''Params.java''; * ''src/main/resources/dbscr/dbscr.arctrm.yml''; * ''DatabaseProtoServiceImpl.java''; * ''AbstractModule.java''; * ''Ref.java''; * ''PeripherialPlugin.java''; * ''PaTermo5Module.java''. Места, где код не дает полной информации: * Пример конфигурации предоставлен как документационный; штатный Arctrm-конфиг в просмотренных файлах репозитория не найден. * Бизнес-смысл событий известен только по именам констант. * Низкоуровневое поведение оборудования находится вне Arctrm; модуль видит только теги датчиков, ''Pdv<num>.Time'' и ''SYSTEM.ErrorFlag''. * Требования к эксплуатационной очистке ''EVENTLOG'', удалению старых ''PODVES'' и реакции на внешние изменения базы в коде не определены. doc/jroboplc/modules/arctrm_dev.txt Last modified: 2026/08/01 18:06by denis