Differences
This shows you the differences between two versions of the page.
| Next revision | Previous revision | ||
| doc:jroboplc:modules:arctrm [2026/07/22 20:56] – created denis | doc:jroboplc:modules:arctrm [2026/08/01 11:25] (current) – [События] denis | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | = Arctrm Module | + | ====== arctrm ====== |
| - | == 1. Purpose == | + | Модуль '' |
| - | The `arctrm` plugin provides the `ArctrmModule`, | + | Модуль сохраняет: |
| - | The module does not implement the hardware protocol itself. It reads values through `Ref` objects that point to tags exposed by other modules. In the inspected code, configuration is read from the `promauto.termo5` section, and the referenced hardware type `promauto.termo5` is created by the peripheral plugin as `PaTermo5Module`. | + | * температурные срезы по подвескам с актуальными данными; |
| + | * сведения о подвесках; | ||
| + | * события запуска архива, изменения состояния связи с устройствами и потери актуальности данных подвесок. | ||
| - | The plugin description returned by `ArctrmPlugin.getPluginDescription()` is `termo value archiver`. | + | Сам модуль не выполняет обмен с оборудованием. Данные и состояние связи он получает из тегов других модулей. |
| - | == 2. High-level Architecture == | + | [[doc: |
| - | The module is split into a small runtime layer, an object hierarchy for configured devices, and a database service. | + | ===== Конфигурация ===== |
| - | `ArctrmPlugin` registers the plugin name `arctrm` and creates `ArctrmModule` instances. It delegates module loading to `ArctrmModule.load(conf)`. | + | Пример настройки: |
| - | `ArctrmModule` owns the module | + | <code yaml> |
| + | plugin.arctrm: | ||
| + | | ||
| + | | ||
| + | schema: AT | ||
| + | period: 5s | ||
| + | size: 1000000 | ||
| + | sensorCount: | ||
| - | `ArctrmDataService` extends `DatabaseProtoServiceImpl` and contains all direct database interaction. It resolves the configured database module, verifies that it is a `FirebirdDatabaseModule`, loads and executes the Arctrm database script, builds schema-qualified table names, creates missing sensor columns, synchronizes podves metadata, writes temperature rows and event rows, deletes old archive rows, and tracks the current archive record count. | + | promauto.termo5: |
| + | mytrm1: | ||
| + | p0: 231 силос, корпус 2 | ||
| + | p5: 232 силос, корпус 2 | ||
| - | `ArctrmDevice` represents one configured termo device. It owns a list of `Podves` objects and a reference to the device tag `SYSTEM.ErrorFlag`. It delegates value collection to its podves list and writes an event when its connection status changes. | + | mytrm2: |
| + | p1: 401 силос, корпус 4 | ||
| + | </ | ||
| - | `Podves` represents one podves channel group under a device. It owns a list of `Sensor` objects, synchronizes itself with the `PODVES` table, and writes one `TEMPER` archive row for all its sensor values. | + | ==== Параметры ==== |
| - | `Sensor` represents one temperature tag reference. It links to a tag such as `Pdv0.T0`, reads the integer value, and normalizes missing or out-of-range values to module constants. | + | ^ Параметр ^ По умолчанию ^ Описание ^ |
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| - | `Duration` parses | + | Значения '' |
| - | `Constants` defines event codes and special sensor values. | + | ==== Настройка устройств ==== |
| - | `Defaults` defines default configuration values. | + | Ключ первого уровня в секции '' |
| - | `ArctrmException` is a checked exception used for module-specific preparation and initialization failures. | + | Для каждой подвески создаются ссылки на теги: |
| - | `Context` currently contains no fields or methods and is not used by the implementation. | + | <code text> |
| - | + | < | |
| - | == 3. Object Model == | + | < |
| - | + | ... | |
| - | The runtime object hierarchy is: | + | < |
| - | + | ||
| - | <code> | + | |
| - | ArctrmModule | + | |
| - | +-- ArctrmDevice | + | |
| - | +-- Podves | + | |
| - | +-- Sensor | + | |
| </ | </ | ||
| - | `ArctrmModule` owns all configured devices in the `devices` list. It creates this list during construction and fills it in `loadModule()` by calling `ArctrmDevice.load(...)`. | + | Актуальность данных подвески определяется по тегу: |
| - | `ArctrmDevice` is created from one entry in the `promauto.termo5` configuration map. The entry key becomes the device name. The device creates one `Ref` to `<deviceName>: | + | <code text> |
| - | + | < | |
| - | `Podves` names are built as `< | + | |
| - | + | ||
| - | `Sensor` instances are created inside each podves. For each sensor index from `0` to `sensorCount - 1`, `Podves` creates a tag name `Pdv< | + | |
| - | + | ||
| - | The database model mirrors part of this object model: | + | |
| - | + | ||
| - | < | + | |
| - | PODVES | + | |
| - | +-- TEMPER | + | |
| </ | </ | ||
| - | Each `Podves` stores its database identifier in `Podves.id` after `ArctrmDataService.syncPodves(...)` returns the `PODVES.ID` value. Temperature archive rows in `TEMPER` refer to this identifier through `TEMPER.PODVES_ID`. | + | Данные считаются актуальными, если ссылка на тег существует, а его значение находится в диапазоне от '' |
| - | + | ||
| - | == 4. Execution Flow == | + | |
| - | + | ||
| - | === Loading === | + | |
| - | + | ||
| - | `ArctrmModule.loadModule(conf)` reads module-specific configuration: | + | |
| - | + | ||
| - | - `database` into `svc.databaseModuleName`; | + | |
| - | - `schema` into `svc.schema`; | + | |
| - | - `size` into `svc.maxRecordCount`; | + | |
| - | - `period`, parsed by `Duration.parseMillis(...)` into `periodMs`; | + | |
| - | - `sensorCount` into `sensorCount`; | + | |
| - | - `promauto.termo5`, passed to `ArctrmDevice.load(...)`. | + | |
| - | + | ||
| - | `size` must be greater than zero. `period` must parse successfully and must not result in `0`. `sensorCount` must be greater than zero. Any exception while parsing the period or loading devices is logged through `env.printError(...)`, | + | |
| - | + | ||
| - | After successful configuration loading, the module creates a boolean tag named `connected` with initial value `false`. | + | |
| - | + | ||
| - | The inherited `AbstractModule.load(...)` also reads common module settings such as `enable`, `debug.logging`, | + | |
| - | + | ||
| - | === Preparation === | + | |
| - | + | ||
| - | `prepareModule()` calls `svc.prepare(sensorCount)`. The service resolves the configured database module, requires it to be a Firebird database module, loads `dbscr/ | + | |
| - | + | ||
| - | After the service is prepared, each `ArctrmDevice` prepares its podves and its `SYSTEM.ErrorFlag` reference. Each `Podves` prepares all of its sensor references. `prepareModule()` then sets `needInit = true` and resets `lastDt` to `null`. | + | |
| - | + | ||
| - | If `svc.prepare(...)` throws `ArctrmException`, | + | |
| - | + | ||
| - | === Initialization === | + | |
| - | + | ||
| - | `executeModule()` calls `init()` at the beginning of a connected execution cycle. `init()` returns immediately if `needInit` is `false`. | + | |
| - | + | ||
| - | When initialization is required, `init()`: | + | |
| - | + | ||
| - | 1. Executes the database initialization script through `svc.init(sensorCount)`. | + | |
| - | 2. Synchronizes all podves through `device.init(svc)`. | + | |
| - | 3. Loads the last saved timestamp from `TEMPER` into `lastDt` through `svc.getLastDt()`. | + | |
| - | 4. Writes an initialization event with `Constants.EVENT_ARCTRM_INIT` and an empty message. | + | |
| - | 5. Commits the database transaction. | + | |
| - | 6. Sets `needInit = false`. | + | |
| - | + | ||
| - | If no row exists in `TEMPER`, `svc.getLastDt()` returns `LocalDateTime.MIN`. | + | |
| - | + | ||
| - | === Execute Cycle === | + | |
| - | + | ||
| - | Each `executeModule()` cycle starts by checking `svc.isConnected()`. If the database is not connected, the module sets the `connected` tag off, sets `needInit = true`, and returns `false`. | + | |
| - | + | ||
| - | When the database is connected, the module: | + | |
| - | + | ||
| - | 1. Runs `init()` if needed. | + | |
| - | 2. Sets the `connected` tag on. | + | |
| - | 3. Gets the database server datetime from `svc.now()`. | + | |
| - | 4. Floors that timestamp to the configured period with `Duration.floorToPeriod(...)`. | + | |
| - | 5. Updates every configured device, podves, and sensor. | + | |
| - | 6. If the floored timestamp differs from `lastDt`, writes one temperature row per podves and runs archive cleanup. | + | |
| - | 7. Writes device status events for devices whose status changed. | + | |
| - | 8. Commits the transaction. | + | |
| - | 9. Assigns the floored timestamp to `lastDt`. | + | |
| - | + | ||
| - | All device and sensor communication in this flow is tag based. `Sensor.update()` calls `ref.linkIfNotValid()` and `ref.getInt()`; | + | |
| - | + | ||
| - | === Exceptions During Execution === | + | |
| - | + | ||
| - | Any exception thrown inside the connected execution block is caught by `executeModule()`. The module logs the exception, rolls back the data service, turns the `connected` tag off, sets `needInit = true`, and then returns `true`. | + | |
| - | + | ||
| - | === Closedown === | + | |
| - | + | ||
| - | `closedownModule()` returns immediately when `enable` is `false`. Otherwise it sets the `connected` tag off and returns `true`. It does not close the database connection directly. | + | |
| - | + | ||
| - | === Reload === | + | |
| - | + | ||
| - | `reload()` creates a temporary `ArctrmModule`, | + | |
| - | + | ||
| - | The implementation does not copy old runtime values from the previous `connected` tag. It also does not explicitly move tags between tag tables during reload. | + | |
| - | + | ||
| - | == 5. Database Schema == | + | |
| - | + | ||
| - | The schema is defined in `src/ | + | |
| - | + | ||
| - | === EVENTLOG === | + | |
| - | + | ||
| - | Purpose: stores module and device status events. | + | |
| - | + | ||
| - | Fields: | + | |
| - | + | ||
| - | - `ID INTEGER GENERATED BY DEFAULT AS IDENTITY`: primary key. | + | |
| - | - `DT TIMESTAMP`: event timestamp. | + | |
| - | - `EVENT_CODE SMALLINT NOT NULL`: event code from `Constants`. | + | |
| - | - `MESSAGE VARCHAR(128)`: | + | |
| - | + | ||
| - | Indexes: | + | |
| - | + | ||
| - | - `IX_EVENTLOG_DT` on `DT`. | + | |
| - | + | ||
| - | Writes: | + | |
| - | + | ||
| - | - `ArctrmModule.init()` writes `EVENT_ARCTRM_INIT` with an empty message. | + | |
| - | - `ArctrmDevice.saveStatus()` writes `EVENT_CONNECTED`, | + | |
| - | + | ||
| - | The code does not implement retention cleanup for `EVENTLOG`. | + | |
| - | + | ||
| - | === PODVES === | + | |
| - | + | ||
| - | Purpose: stores the configured podves names and descriptions. | + | |
| - | + | ||
| - | Fields: | + | |
| - | + | ||
| - | - `ID INTEGER GENERATED BY DEFAULT AS IDENTITY`: primary key. | + | |
| - | - `NAME VARCHAR(32) NOT NULL`: unique podves name, built as `< | + | |
| - | - `DESCR VARCHAR(64)`: | + | |
| - | + | ||
| - | Constraints: | + | |
| - | + | ||
| - | - Unique constraint on `NAME`. | + | |
| - | + | ||
| - | Writes: | + | |
| - | + | ||
| - | - `ArctrmDataService.syncPodves(...)` uses Firebird `update or insert ... matching (name) returning id` to insert or update the row and return `ID`. | + | |
| - | + | ||
| - | The code does not delete `PODVES` rows that disappear from configuration. | + | |
| - | + | ||
| - | === TEMPER === | + | |
| - | + | ||
| - | Purpose: stores periodic temperature snapshots. | + | |
| - | + | ||
| - | Fields from the database script: | + | |
| - | + | ||
| - | - `ID BIGINT GENERATED BY DEFAULT AS IDENTITY`: primary key. | + | |
| - | - `PODVES_ID INTEGER`: foreign key to `PODVES`. | + | |
| - | - `DT TIMESTAMP`: archive timestamp. | + | |
| - | + | ||
| - | Dynamic fields: | + | |
| - | + | ||
| - | - `T0`, `T1`, ..., `T< | + | |
| - | - Each dynamic field is created as `INTEGER`. | + | |
| - | + | ||
| - | Relationships: | + | |
| - | + | ||
| - | - `TEMPER.PODVES_ID` references `PODVES.ID` with `ON DELETE CASCADE ON UPDATE CASCADE`. | + | |
| - | + | ||
| - | Indexes: | + | |
| - | + | ||
| - | - `IX_TEMPER_DT` on `DT`. | + | |
| - | + | ||
| - | Writes: | + | |
| - | + | ||
| - | - `ArctrmDataService.saveTemper(...)` inserts one row per podves with `PODVES_ID`, | + | |
| - | + | ||
| - | Cleanup: | + | |
| - | + | ||
| - | - `ArctrmDataService.deleteOldRecords()` deletes oldest rows by `ID` when the cached record count plus pending inserts exceeds `maxRecordCount`. | + | |
| - | + | ||
| - | == 6. Archive Algorithm == | + | |
| - | + | ||
| - | === Sensor Collection === | + | |
| - | + | ||
| - | Each execution cycle calls `device.update()`, | + | |
| - | + | ||
| - | `Sensor.update()` behavior: | + | |
| - | + | ||
| - | - If the referenced tag cannot be linked, `value` becomes `Constants.VALUE_NOLINK` (`3000`). | + | |
| - | - If the referenced integer value is below `Sensor.VALUE_LIMIT_LOW` (`-1000`), `value` becomes `Constants.VALUE_BROKEN` (`3010`). | + | |
| - | - If the referenced integer value is above `Sensor.VALUE_LIMIT_HIGH` (`2000`), `value` becomes `Constants.VALUE_SHORTAGE` (`3011`). | + | |
| - | - Otherwise, the referenced integer value is stored unchanged. | + | |
| - | + | ||
| - | The implementation does not preserve the raw sensor value when it is outside the allowed range. | + | |
| - | + | ||
| - | === Timestamp Generation === | + | |
| - | + | ||
| - | The module uses the database server time, not JVM local time, for archive timestamps. `ArctrmDataService.now()` returns `db.getServerDatetime()`. | + | |
| - | + | ||
| - | The timestamp is aligned by `Duration.floorToPeriod(dateTime, | + | |
| - | + | ||
| - | Supported period suffixes are: | + | |
| - | + | ||
| - | - `ms`: milliseconds; | + | |
| - | - `s`: seconds; | + | |
| - | - `m`: minutes; | + | |
| - | - `h`: hours; | + | |
| - | - `d`: days; | + | |
| - | - no suffix: milliseconds. | + | |
| - | + | ||
| - | Negative periods, empty periods, null periods, invalid numeric values, and arithmetic overflow are rejected by `Duration.parseMillis(...)`. `ArctrmModule.loadModule(...)` additionally rejects a parsed period of `0`. | + | |
| - | + | ||
| - | === Write Timing === | + | |
| - | + | ||
| - | Temperature rows are written only when the current floored timestamp differs from `lastDt`. | + | |
| - | + | ||
| - | During initialization, | + | |
| - | + | ||
| - | When a write is due, each device writes all of its podves. Each podves writes exactly one row containing all current sensor values. | + | |
| - | + | ||
| - | === Archive Maintenance === | + | |
| - | + | ||
| - | `ArctrmDataService.init(...)` initializes `recordCount` with `select count(*) from < | + | |
| - | Every temperature insert increments `uncommitedCount`. Before commit, `deleteOldRecords()` computes: | + | Состояние связи с устройством определяется по тегу: |
| - | < | + | < |
| - | deleteCount = max(0, recordCount + uncommitedCount - maxRecordCount) | + | < |
| </ | </ | ||
| - | If `deleteCount` is greater than zero, the service executes: | + | ===== Принцип работы ===== |
| - | < | + | После подключения к базе модуль автоматически: |
| - | delete from < | + | |
| - | </ | + | |
| - | After commit, the cached count is updated as: | + | - создаёт необходимые таблицы и индексы; |
| + | - добавляет отсутствующие колонки '' | ||
| + | - синхронизирует справочник подвесок; | ||
| + | - загружает дату последней архивной записи; | ||
| + | - записывает событие запуска архива. | ||
| - | <code> | + | На каждом рабочем цикле модуль сначала проверяет '' |
| - | recordCount = recordCount + uncommitedCount - deleteCount | + | |
| - | </code> | + | |
| - | Then `uncommitedCount` and `deleteCount` are reset to zero. Rollback also resets `uncommitedCount` and `deleteCount`, but it does not recompute `recordCount`. | + | Если наступил новый период, в '' |
| - | == 7. Runtime Information | + | ===== База данных ===== |
| - | `ArctrmModule.getInfo()` returns runtime summary text. | + | ^ Таблица ^ Назначение ^ |
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| - | If the module is disabled, it returns: | + | Основные связи: |
| - | < | + | < |
| - | disabled | + | PODVES.ID <- TEMPER.PODVES_ID |
| </ | </ | ||
| - | For enabled modules, the format is: | + | Одна строка '' |
| - | < | + | ===== Значения датчиков ===== |
| - | < | + | |
| - | </ | + | |
| - | Fields: | + | Обычные значения записываются без изменения. Ошибочные состояния сохраняются в тех же колонках '' |
| - | - `NOT CONNECTED!`: | + | ^ Значение ^ Состояние ^ |
| - | - `devices`: current size of the `devices` list. | + | | '' |
| - | - `podves`: sum of `device.podvess.size()` across all devices. | + | | '' |
| - | - `sensors`: sum of `podves.sensors.size()` across all podves. | + | | '' |
| - | - `nolinkSensors`: | + | |
| - | - `cursize`: current cached `TEMPER` record count from `svc.getRecordCount()`. | + | |
| - | - `last`: `never` when `lastDt` is `null` or `LocalDateTime.MIN`; | + | |
| - | The current implementation does not include the database module name in `getInfo()`. | + | Допустимый диапазон обычного значения: |
| - | == 8. Configuration | + | ===== События ===== |
| - | The module reads the following Arctrm-specific parameters in `ArctrmModule.loadModule(...)`. | + | ^ Код ^ Событие ^ |
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| + | | '' | ||
| - | === database === | + | Для событий состояния устройства в поле '' |
| - | Database module name. | + | Событие состояния устройства записывается только при изменении его состояния. Событие неактуальных данных формируется при первой потере актуальности, |
| - | Default: `db`. | + | ===== Теги модуля ===== |
| - | Stored in `svc.databaseModuleName`. During preparation it must resolve to a module implementing `Database`, and it must be an instance of `FirebirdDatabaseModule`. | + | ^ Тег ^ Тип ^ Описание ^ |
| + | | '' | ||
| - | === schema | + | ===== Диагностика ===== |
| - | Database schema prefix used by the database script and by table-name generation. | + | Метод '' |
| - | Default: `AT`. | + | <code text> |
| - | + | devices=2 podves=3 sensors=18 cursize=30 last=2026-07-23 00:16:50 | |
| - | Stored in `svc.schema` and passed to the database script as `schema=<value>`. | + | |
| - | + | ||
| - | === size === | + | |
| - | + | ||
| - | Maximum number of records retained in `TEMPER`. | + | |
| - | + | ||
| - | Default: `1_000_000`. | + | |
| - | + | ||
| - | The value must be greater than zero. It is stored in `svc.maxRecordCount`. | + | |
| - | + | ||
| - | === period === | + | |
| - | + | ||
| - | Archive period. | + | |
| - | + | ||
| - | Default: `1h`. | + | |
| - | + | ||
| - | The value is parsed by `Duration.parseMillis(...)`. Supported suffixes are `ms`, `s`, `m`, `h`, and `d`; values without suffix are interpreted as milliseconds. The parsed value must be greater than zero. | + | |
| - | + | ||
| - | === sensorCount === | + | |
| - | + | ||
| - | Number of temperature | + | |
| - | + | ||
| - | Default: `6`. | + | |
| - | + | ||
| - | The value must be greater than zero. It controls how many `Sensor` objects are created for every podves and how many dynamic `T*` columns are required in `TEMPER`. | + | |
| - | + | ||
| - | === promauto.termo5 === | + | |
| - | + | ||
| - | Device configuration map consumed by `ArctrmDevice.load(...)`. | + | |
| - | + | ||
| - | Expected shape from implementation: | + | |
| - | + | ||
| - | < | + | |
| - | promauto.termo5: | + | |
| - | < | + | |
| - | p0: < | + | |
| - | p1: < | + | |
| </ | </ | ||
| - | The device map key is used as the referenced module name. Each podves key must have at least two characters and must contain a numeric suffix after the first character, because the code parses `entry.getKey().substring(1)` as an integer. The code does not require the first character to be `p`; it only ignores the first character and parses the rest. | + | ^ Поле ^ Описание ^ |
| - | + | | '' | |
| - | The repository does not contain an Arctrm-specific sample configuration, | + | | '' |
| - | + | | '' | |
| - | === enable === | + | | '' |
| - | + | | '' | |
| - | This is inherited from `AbstractModule`, | + | | '' |
| - | + | ||
| - | Default: `true`. | + | |
| - | + | ||
| - | When `enable` is false, inherited lifecycle methods skip preparation, | + | |
| - | + | ||
| - | == 9. Error Handling == | + | |
| - | + | ||
| - | === Database Connection Failures === | + | |
| - | + | ||
| - | During preparation: | + | |
| - | + | ||
| - | - missing configured database module causes `ArctrmException(" | + | |
| - | - non-Firebird database causes `ArctrmException(" | + | |
| - | - database script load failure causes `ArctrmException(" | + | |
| - | + | ||
| - | `prepareModule()` catches `ArctrmException`, | + | |
| - | + | ||
| - | During execution: | + | |
| - | + | ||
| - | - if `svc.isConnected()` is false, the module turns the `connected` tag off, sets `needInit = true`, and returns `false`; | + | |
| - | - `getInfo()` shows `NOT CONNECTED!` when the module's `connected` tag is false. | + | |
| - | + | ||
| - | === Communication Failures === | + | |
| - | + | ||
| - | Arctrm detects device communication state through the referenced `SYSTEM.ErrorFlag` tag: | + | |
| - | + | ||
| - | - if the reference cannot be linked, device status is `EVENT_NOLINK`; | + | |
| - | - if the tag is linked and `refErrorFlag.getBool()` is true, status is `EVENT_DISCONNECTED`; | + | |
| - | - otherwise status is `EVENT_CONNECTED`. | + | |
| - | + | ||
| - | `ArctrmDevice.saveStatus(...)` writes an event only when this status differs from `lastStatus`. | + | |
| - | + | ||
| - | The module does not communicate with hardware directly and does not inspect serial-port errors. Those details belong to the referenced peripheral modules. | + | |
| - | + | ||
| - | === Invalid Sensor Values === | + | |
| - | + | ||
| - | `Sensor.update()` treats values below `-1000` as `VALUE_BROKEN` (`3010`) and values above `2000` as `VALUE_SHORTAGE` (`3011`). These constants are archived in the same `TEMPER.T*` fields as normal sensor values. | + | |
| - | + | ||
| - | The implementation does not log invalid sensor values and does not write separate events for them. | + | |
| - | + | ||
| - | === NOLINK Sensors === | + | |
| - | + | ||
| - | If a sensor reference cannot be linked, `Sensor.update()` stores `VALUE_NOLINK` (`3000`). `getInfo()` counts sensors whose current value equals this constant and displays `nolinkSensors=<n>` only when the count is greater than zero. | + | |
| - | + | ||
| - | === Unexpected Exceptions === | + | |
| - | + | ||
| - | Unexpected exceptions during the connected execution block are caught by `executeModule()`. The module logs the exception, rolls back through `svc.rollback()`, | + | |
| - | + | ||
| - | == 10. Performance Considerations == | + | |
| - | + | ||
| - | The implementation contains several simple optimizations: | + | |
| - | + | ||
| - | - The temperature insert SQL is generated once in `ArctrmDataService.prepare(...)` based on `sensorCount`. | + | |
| - | - `saveTemper(...)` uses a prepared statement through `openPreparedStatement(...)`; the inherited service reuses it until commit or rollback closes it. | + | |
| - | - Archive rows are written only when the period-aligned timestamp changes, not on every module execution cycle. | + | |
| - | - `recordCount` is cached after initialization and updated on commit instead of running `count(*)` after every insert. | + | |
| - | - Old records are deleted only when the cached count plus pending inserts exceeds `maxRecordCount`. | + | |
| - | - Existing `TEMPER` column names are loaded once during initialization before missing sensor columns are added. | + | |
| - | + | ||
| - | The implementation does not use JDBC batch inserts. It executes one insert per podves when an archive write is due. | + | |
| - | + | ||
| - | == 11. Known Limitations == | + | |
| - | + | ||
| - | - Only Firebird databases are supported; `ArctrmDataService.prepare(...)` rejects any database module that is not a `FirebirdDatabaseModule`. | + | |
| - | - `sensorCount` is global for the whole module. Different podves cannot have different sensor counts. | + | |
| - | - `PODVES` rows are inserted or updated, but rows removed from configuration are not deleted by Arctrm. | + | |
| - | - `EVENTLOG` has no cleanup logic. | + | |
| - | - `TEMPER` cleanup is based on ascending `ID`, not on `DT`. | + | |
| - | - The last saved timestamp is loaded from the row with the highest `ID`, not from the maximum `DT`. | + | |
| - | - The cached `recordCount` can become stale if another process modifies `TEMPER` after initialization. | + | |
| - | - `Sensor.value` defaults to Java's default integer value `0` until the first `Sensor.update()` call. | + | |
| - | - `ArctrmDevice.lastStatus` defaults to `0`, which is also `EVENT_ARCTRM_INIT`; | + | |
| - | - The module creates a `connected` tag with `tagtable.createBool(...)`; | + | |
| - | - No Arctrm-specific automated tests or sample Arctrm module configuration were found in the inspected source tree. | + | |
| - | - The business meaning of event codes is limited to the constant names in `Constants`; | + | |
| - | + | ||
| - | == 12. Possible Improvements == | + | |
| - | + | ||
| - | - Add unit tests for `Duration`, `Sensor.update()`, | + | |
| - | - Add an Arctrm sample configuration that shows the expected `promauto.termo5` structure. | + | |
| - | - Use JDBC batch inserts for archive writes when many podves are configured. | + | |
| - | - Add explicit quality/ | + | |
| - | - Add retention or cleanup for `EVENTLOG`. | + | |
| - | - Add synchronization cleanup for `PODVES` rows no longer present in configuration, | + | |
| - | - Load the last archive timestamp by maximum `DT` if archive continuity should be timestamp-based rather than insertion-order based. | + | |
| - | - Periodically reconcile cached `recordCount` with the database if external writers or manual maintenance are expected. | + | |
| - | - Expose more runtime diagnostics, | + | |
| - | + | ||
| - | == Source Coverage Review == | + | |
| - | + | ||
| - | The documentation above was checked against the current implementation of: | + | |
| - | - `ArctrmModule.java` | + | При отсутствии подключения в начало строки добавляется '' |
| - | - `ArctrmDataService.java` | + | |
| - | - `ArctrmDevice.java` | + | |
| - | - `Podves.java` | + | |
| - | - `Sensor.java` | + | |
| - | - `Constants.java` | + | |
| - | - `Defaults.java` | + | |
| - | - `Duration.java` | + | |
| - | - `ArctrmPlugin.java` | + | |
| - | - `ArctrmException.java` | + | |
| - | - `Context.java` | + | |
| - | - `src/ | + | |
| - | - directly referenced behavior in `DatabaseProtoServiceImpl`, | + | |
| - | Sections with intentionally stated unknowns or limited evidence: | + | ===== Ограничения ===== |
| - | - Configuration: | + | * Поддерживается только Firebird. |
| - | - Purpose: the code describes the plugin as `termo value archiver`; any broader product or operator-facing purpose is not documented in source. | + | * Значение '' |
| - | - Database Schema: field business semantics beyond names, types, constraints, | + | * Таблица '' |
| - | - Error Handling: hardware/ | + | * Удаление архивных строк выполняется по '' |
| - | - Known Limitations: | + | |