====== arctrm для разработчика ====== ===== 1. Назначение ===== ''ArctrmModule'' - модуль архивации температурных значений. Он читает значения датчиков из тегов уже настроенных модулей оборудования, сохраняет периодические срезы в базу Firebird и пишет события изменения состояния связи с устройствами и потери актуальности данных подвесов. Сам модуль не реализует протокол обмена с оборудованием. Доступ к данным выполняется через ''Ref'': каждый ''Sensor'' ссылается на тег вида '':Pdv.T'', ''Podves'' проверяет актуальность данных по '':Pdv.Time'', а ''ArctrmDevice'' контролирует состояние устройства через '':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.Time'', содержит список ''Sensor'', синхронизирует запись в ''PODVES'', сохраняет строку архива с актуальными данными и формирует событие потери актуальности. | | ''Sensor'' | Ссылается на температурный тег, читает целочисленное значение и заменяет отсутствующие или выходящие за диапазон значения специальными кодами. | | ''Duration'' | Разбирает строковый период и выравнивает дату-время на границу периода. | | ''Constants'' | Содержит коды событий, специальные значения датчиков и предел актуальности данных подвеса. | | ''Defaults'' | Содержит значения конфигурации по умолчанию. | | ''Params'' | Пустой класс; в текущей реализации не используется. | | ''ArctrmException'' | Проверяемое исключение для ошибок конфигурации, подготовки и инициализации Arctrm. | Прямое подключение к базе выполняет только ''ArctrmDataService''. Остальная часть модуля работает с объектами предметной модели и теговыми ссылками. ===== 3. Структура объектов ===== Иерархия объектов во время работы: ArctrmModule └── ArctrmDevice └── Podves └── Sensor ''ArctrmModule'' хранит все устройства в списке ''devices''. Список создается в конструкторе и заполняется в ''loadModule()'' вызовом ''ArctrmDevice.load(...)''. ''ArctrmDevice'' создается по одной записи из карты ''promauto.termo5''. Ключ записи становится именем устройства и используется как имя модуля, из которого читаются теги. Устройство создает: * список ''podvess''; * ссылку ''Ref'' на тег ''SYSTEM.ErrorFlag''; * состояние ''lastStatus'', по которому определяется необходимость записи события. ''Podves'' создается по записи внутри конфигурации устройства. Номер подвеса получается из ключа конфигурации через ''entry.getKey().substring(1)'' и ''Integer.parseInt(...)''. Например, ключ ''p3'' дает номер ''3''. Имя подвеса формируется как ''.'', а значение конфигурации сохраняется как описание ''descr''. Кроме списка датчиков подвес создает ссылку ''refTime'' на тег '':Pdv.Time'' и хранит признаки ''dataValid'' и ''needSaveEvent''. ''Sensor'' создается для каждого индекса от ''0'' до ''sensorCount - 1''. Имя тега строится так: String tagname = String.format("Pdv%d.T%d", num, i); Ссылка датчика указывает на '':Pdv.T''. Связь с базой данных строится через ''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''. После подготовки сервиса модуль вызывает ''prepare()'' у каждого устройства. Устройства подготавливают ссылки на ''SYSTEM.ErrorFlag'', а подвесы - ссылки всех датчиков и ''Pdv.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.Time''. - Если рассчитанный ''dt'' отличается от ''lastDt'', сохраняется архивный срез и выполняется очистка старых строк. - Для каждого устройства проверяется состояние связи и при изменении пишется событие в ''EVENTLOG''. - Выполняется ''svc.commit()''. - ''lastDt'' обновляется текущим выровненным временем. ==== Сохранение данных ==== При наступлении нового периода каждое устройство вызывает ''savePodves(...)''. Подвес с актуальными данными собирает значения своих датчиков и передает их в ''svc.saveTemper(dt, id, values)''; в базу пишется одна строка ''TEMPER''. Если ссылка на ''Pdv.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'' ==== Назначение: хранение событий модуля, изменения состояния устройств и потери актуальности данных подвесов. 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) ) ^ Поле ^ Назначение ^ | ''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'' передается имя ''.''. | Очистка ''EVENTLOG'' в коде не реализована. ==== Таблица ''PODVES'' ==== Назначение: справочник настроенных подвесов. 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) ) ^ Поле ^ Назначение ^ | ''ID'' | Первичный ключ, сохраняется в ''Podves.id''. | | ''NAME'' | Уникальное имя подвеса в формате ''.''. | | ''DESCR'' | Описание из конфигурации. | Синхронизация выполняется через Firebird-запрос: update or insert into (name, descr) values (?, ?) matching (name) returning id Удаление строк ''PODVES'', исчезнувших из конфигурации, не реализовано. ==== Таблица ''TEMPER'' ==== Назначение: архив температурных срезов. 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 ) ^ Поле ^ Назначение ^ | ''ID'' | Первичный ключ строки архива. | | ''PODVES_ID'' | Ссылка на ''PODVES.ID''. | | ''DT'' | Выровненная метка времени архивного периода. | | ''T0..T'' | Динамические целочисленные колонки значений датчиков. | Индекс: ''IX_TEMPER_DT'' по полю ''DT''. Колонки датчиков добавляются в ''ArctrmDataService.ensureSensorColumns(sensorCount)'': alter table add T integer Если колонка уже есть, она не создается повторно. ===== 6. Алгоритм архивирования ===== ==== Опрос датчиков ==== На каждом цикле выполнения вызывается цепочка: ArctrmModule.executeModule() -> ArctrmDevice.update() -> Podves.update() -> Podves.checkTimeValid() -> Sensor.update() при актуальных данных Сначала ''Podves.update()'' проверяет ссылку ''Pdv.Time''. Данные считаются актуальными, если ссылка существует, а значение тега находится в диапазоне от ''0'' до ''PODVES_UPDATE_TIME_LIMIT = 600'' включительно. При неактуальных данных ''Sensor.update()'' не вызывается и значения датчиков остаются прежними. Для подвеса с актуальными данными ''Sensor.update()'' работает с теговой ссылкой: ^ Условие ^ Записываемое значение ^ | Тег не найден через ''ref.linkIfNotValid()'' | ''VALUE_NOLINK = 3013'' | | Значение меньше ''-1000'' | ''VALUE_SHORTAGE = 3010'' | | Значение больше ''2000'' | ''VALUE_BROKEN = 3011'' | | Значение в диапазоне | Исходное целое значение тега | Исходное значение, вышедшее за допустимый диапазон, отдельно не сохраняется. ==== Формирование времени ==== Время архива берется от базы данных: LocalDateTime dt = Duration.floorToPeriod(svc.now(), periodMs); ''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 строится один раз при подготовке: insert into (podves_id, dt, T0, T1, ...) values (?, ?, ?, ?, ...) ==== Циклическое обслуживание архива ==== При инициализации ''recordCount'' загружается запросом ''select count(*) from ''. Каждая вставка увеличивает ''uncommitedCount''. Перед фиксацией нового архивного среза рассчитывается число удаляемых строк: deleteCount = max(0, recordCount + uncommitedCount - maxRecordCount) Если ''deleteCount > 0'', выполняется удаление старейших строк по ''ID'': delete from order by id rows После успешного ''commit()'' счетчик обновляется: recordCount = recordCount + uncommitedCount - deleteCount ''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'' | Общий флаг включения модуля. | Обрабатывается базовым классом. | ==== Пример конфигурации ==== 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. В этом примере ''plugin.arctrm'' и ''module.arctrm'' соответствуют схеме загрузки конфигурации: ''ConfigurationYaml.getModuleConf(...)'' ищет ''plugin.'' и внутри него ''module.''. Значения ''database'', ''period'', ''size'', ''schema'' и ''sensorCount'' читаются в ''ArctrmModule.loadModule()''. Секция ''promauto.termo5'' передается в ''ArctrmDevice.load(...)''. Ключи ''mytrm1'' и ''mytrm2'' становятся именами модулей-источников тегов. Ключи ''p0'', ''p5'' и ''p1'' дают номера подвесов ''0'', ''5'' и ''1'', потому что код берет часть ключа после первого символа и разбирает ее как число. При ''sensorCount: 6'' для каждого подвеса создаются ссылки на теги ''Pdv.T0'' ... ''Pdv.T5'', а также ссылка на ''Pdv.Time''. Комментарий ''#604800'' в строке ''size'' является YAML-комментарием и не участвует в значении параметра. Активное значение в примере - ''30''. Пример выше предоставлен как документационный пример и соответствует фактическому разбору конфигурации в коде. Готовый Arctrm-конфиг в просмотренных файлах репозитория не найден. ===== 8. Диагностика ===== ''getInfo()'' возвращает краткую строку состояния. Если модуль выключен: disabled Для включенного модуля формат такой: devices= podves= sensors=< nolinkSensors=> cursize= last= ^ Поле ^ Значение ^ | ''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.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.Time'' и ''SYSTEM.ErrorFlag''. * Требования к эксплуатационной очистке ''EVENTLOG'', удалению старых ''PODVES'' и реакции на внешние изменения базы в коде не определены.