From 40d80ef55ea25521052fb9c5665774e76817a67f Mon Sep 17 00:00:00 2001 From: zavolo Date: Wed, 14 Jan 2026 21:33:17 +0300 Subject: [PATCH] Initial commit --- LICENSE | 21 + README.md | 55 + example/example.py | 54 + main.py | 272 +++++ owen/__init__.py | 0 owen/__pycache__/__init__.cpython-314.pyc | Bin 0 -> 130 bytes owen/__pycache__/client.cpython-314.pyc | Bin 0 -> 15617 bytes owen/__pycache__/converter.cpython-314.pyc | Bin 0 -> 13779 bytes owen/__pycache__/device.cpython-314.pyc | Bin 0 -> 89480 bytes owen/__pycache__/protocol.cpython-314.pyc | Bin 0 -> 12072 bytes owen/client.py | 209 ++++ owen/converter.py | 229 ++++ owen/device.py | 1244 ++++++++++++++++++++ owen/protocol.py | 151 +++ setup.py | 27 + test/test_client.py | 153 +++ test/test_protocol.py | 158 +++ 17 files changed, 2573 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 example/example.py create mode 100644 main.py create mode 100644 owen/__init__.py create mode 100644 owen/__pycache__/__init__.cpython-314.pyc create mode 100644 owen/__pycache__/client.cpython-314.pyc create mode 100644 owen/__pycache__/converter.cpython-314.pyc create mode 100644 owen/__pycache__/device.cpython-314.pyc create mode 100644 owen/__pycache__/protocol.cpython-314.pyc create mode 100644 owen/client.py create mode 100644 owen/converter.py create mode 100644 owen/device.py create mode 100644 owen/protocol.py create mode 100644 setup.py create mode 100644 test/test_client.py create mode 100644 test/test_protocol.py diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2c4f673 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 RAA80 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8a3cd76 --- /dev/null +++ b/README.md @@ -0,0 +1,55 @@ +# python-owen + +Библиотека для работы с приборами фирмы [Овен] по протоколам Modbus и ОВЕН + +## 📋 Поддерживаемые модели + +| Модель | Модель | Модель | +| :--------: | :--------: | :--------: | +| [ТРМ101] | [ТРМ151] | [ТРМ210] | +| [ТРМ136] | [ТРМ200] | [ТРМ212] | +| [ТРМ138] | [ТРМ201] | [ТРМ251] | +| [ТРМ148] | [ТРМ202] | [2ТРМ1] | +| [ПР100] | | | + +> Возможна поддержка других моделей путем добавления настроек в файл `owen/device.py` +> Не со всеми моделями проверена работа библиотеки + +## ⚠️ Важная информация + +### Требования к версии pymodbus + +**Для корректной работы python-owen необходимо установить старую версию библиотеки pymodbus:** + +```bash +pip3 install pymodbus==2.5.3 +``` + +⚠️ **При использовании более новых версий pymodbus библиотека работать не будет!** + +### Особенности Овен ПР100 + +**Овен ПР100 не поддерживает протокол ОВЕН — только Modbus!** + +## 📦 О проекте + +Это форк репозитория: https://github.com/RAA80/python-owen + +--- + +## 📚 Ссылки на документацию приборов + +[Овен]: https://owen.ru +[ТРМ101]: https://owen.ru/product/trm101 +[ТРМ136]: https://owen.ru/product/trm136 +[ТРМ138]: https://owen.ru/product/trm138 +[ТРМ148]: https://owen.ru/product/trm148 +[ТРМ151]: https://owen.ru/product/trm151 +[ТРМ200]: https://owen.ru/product/trm200 +[ТРМ201]: https://owen.ru/product/trm201 +[ТРМ202]: https://owen.ru/product/trm202 +[ТРМ210]: https://owen.ru/product/trm210 +[ТРМ212]: https://owen.ru/product/trm212 +[ТРМ251]: https://owen.ru/product/trm251 +[2ТРМ1]: https://owen.ru/product/2trm1 +[ПР100]: https://owen.ru/product/pr100 \ No newline at end of file diff --git a/example/example.py b/example/example.py new file mode 100644 index 0000000..6c2807c --- /dev/null +++ b/example/example.py @@ -0,0 +1,54 @@ +#! /usr/bin/env python3 + +"""Пример использования библиотеки.""" + +import logging + +from pymodbus.client.sync import ModbusSerialClient +from serial import Serial + +from owen.client import OwenModbusClient, OwenSerialClient +from owen.device import TRM201 + +logging.basicConfig(level=logging.INFO) + + +if __name__ == "__main__": + transport = Serial(port="COM5", + baudrate=115200, + stopbits=1, + parity="N", + bytesize=8, + timeout=0.1) + trm = OwenSerialClient(transport=transport, device=TRM201, unit=1, addr_len_8=True) # длина адреса в битах: True=8, False=11 + + # transport = ModbusSerialClient(method="rtu", + # port="COM5", + # baudrate=115200, + # stopbits=2, + # parity="N", + # bytesize=8, # "rtu": bytesize=8, "ascii": bytesize=7 + # timeout=0.1, + # retry_on_empty=True) + # trm = OwenModbusClient(transport=transport, device=TRM201, unit=1) + + print(trm) + + """ !!! + Если параметр не использует индекс, т.е. index=None, + либо прибор одноканальный и у параметра index=0, + то индекс указывать необязательно + + Для многоканальных приборов (например ТРМ202) и протокола Modbus названия + параметров (например IN.T1, IN.T2) для совместимости с протоколом ОВЕН + преобразуются в: + IN.T1 --> name="IN.T", index=0 + IN.T2 --> name="IN.T", index=1 + """ + + name = "SP" # Остальные названия параметров в файле 'device.py' для конкретного устройства + value = trm.get_param(name=name, index=0) + print(f"{name} = {value}") + + result = trm.set_param(name=name, index=0, value=value) + print(f"{name} = {result}") diff --git a/main.py b/main.py new file mode 100644 index 0000000..cd07a1a --- /dev/null +++ b/main.py @@ -0,0 +1,272 @@ +#! /usr/bin/env python3 +"""Пример работы с программируемым логическим реле Овен ПР100""" +import logging +import time +import sys +from datetime import datetime +from pymodbus.client.sync import ModbusSerialClient +from pymodbus.exceptions import ModbusException +from owen.client import OwenModbusClient +from owen.device import PR100 + +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + +def read_digital_inputs(pr100: OwenModbusClient, num_inputs: int = 4): + """Чтение состояния дискретных входов""" + logger.info("Чтение дискретных входов") + try: + # Читаем все входы одним регистром + di_value = pr100.get_param(name="DI") + logger.info(f"Регистр дискретных входов: {di_value} (0x{di_value:04X})") + # Декодируем отдельные входы + for i in range(num_inputs): + state = (di_value >> i) & 1 + logger.info(f" DI{i+1}: {'ВКЛ' if state else 'ВЫКЛ'}") + return True + except Exception as e: + logger.error(f"Ошибка при чтении дискретных входов: {e}") + return False + +def read_analog_inputs(pr100: OwenModbusClient): + """Чтение аналоговых входов""" + logger.info("Чтение аналоговых входов") + success_count = 0 + for i in range(1, 5): + try: + # Читаем как float + value_float = pr100.get_param(name=f"AI{i}") + logger.info(f"AI{i} (float): {value_float:.3f}") + # Читаем как int с учетом dp + value_int = pr100.get_param(name=f"AI{i}.INT") + dp = pr100.get_param(name=f"AI{i}.DP") + logger.info(f"AI{i} (int): {value_int}, точность: {dp} знаков") + success_count += 1 + except Exception as e: + logger.error(f"Ошибка при чтении AI{i}: {e}") + return success_count > 0 + +def control_outputs(pr100: OwenModbusClient, outputs: dict): + """ + Управление дискретными выходами + + Args: + pr100: Экземпляр клиента ПР100 + outputs: Словарь {номер_выхода: состояние}, например {1: True, 2: False} + """ + logger.info("Управление дискретными выходами") + try: + # Читаем текущее состояние + current_value = pr100.get_param(name="DO") + logger.info(f"Текущее состояние выходов: 0x{current_value:04X}") + # Формируем новое значение + new_value = current_value + for output_num, state in outputs.items(): + bit_pos = output_num - 1 + if state: + new_value |= (1 << bit_pos) # Установить бит + else: + new_value &= ~(1 << bit_pos) # Сбросить бит + # Записываем новое состояние + logger.info(f"Запись нового состояния: 0x{new_value:04X}") + pr100.set_param(name="DO", value=new_value) + # Показываем, что изменилось + for output_num, state in outputs.items(): + logger.info(f" Q{output_num}: {'ВКЛ' if state else 'ВЫКЛ'}") + return True + except Exception as e: + logger.error(f"Ошибка при управлении выходами: {e}") + return False + +def read_system_time(pr100: OwenModbusClient): + """Чтение системного времени ПР100""" + logger.info("Чтение системного времени") + try: + sec = pr100.get_param(name="TIME.SEC") + min_val = pr100.get_param(name="TIME.MIN") + hour = pr100.get_param(name="TIME.HOUR") + day = pr100.get_param(name="TIME.DAY") + month = pr100.get_param(name="TIME.MONTH") + year = pr100.get_param(name="TIME.YEAR") + dow = pr100.get_param(name="TIME.DOW") + days_of_week = ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"] + logger.info(f"Дата: {day:02d}.{month:02d}.20{year:02d}") + logger.info(f"Время: {hour:02d}:{min_val:02d}:{sec:02d}") + logger.info(f"День недели: {days_of_week[dow]}") + return True + except Exception as e: + logger.error(f"Ошибка при чтении системного времени: {e}") + return False + +def set_system_time(pr100: OwenModbusClient): + """Установка системного времени ПР100 на текущее""" + logger.info("Установка системного времени") + try: + now = datetime.now() + pr100.set_param(name="TIME.SEC", value=now.second) + pr100.set_param(name="TIME.MIN", value=now.minute) + pr100.set_param(name="TIME.HOUR", value=now.hour) + pr100.set_param(name="TIME.DAY", value=now.day) + pr100.set_param(name="TIME.MONTH", value=now.month) + pr100.set_param(name="TIME.YEAR", value=now.year % 100) + logger.info(f"Время установлено: {now.strftime('%d.%m.%Y %H:%M:%S')}") + return True + except Exception as e: + logger.error(f"Ошибка при установке времени: {e}") + return False + +def monitoring_loop(pr100: OwenModbusClient, duration: int = 10): + """ + Цикл мониторинга входов и выходов + + Args: + pr100: Экземпляр клиента ПР100 + duration: Длительность мониторинга в секундах + """ + logger.info(f"Запуск мониторинга на {duration} секунд") + start_time = time.time() + error_count = 0 + max_errors = 5 + try: + while time.time() - start_time < duration: + try: + # Читаем входы + di = pr100.get_param(name="DI") + ai1 = pr100.get_param(name="AI1") + do = pr100.get_param(name="DO") + logger.info(f"DI: 0x{di:04X}, AI1: {ai1:.2f}, DO: 0x{do:04X}") + error_count = 0 # Сброс счетчика при успешном чтении + except Exception as e: + error_count += 1 + logger.error(f"Ошибка чтения (#{error_count}): {e}") + if error_count >= max_errors: + logger.error(f"Достигнуто максимальное количество ошибок ({max_errors}). Остановка мониторинга.") + return False + time.sleep(1) + return True + except KeyboardInterrupt: + logger.info("Мониторинг прерван пользователем") + return True + +def test_connection(transport: ModbusSerialClient, unit_id: int) -> bool: + """ + Проверка соединения с устройством + + Args: + transport: Клиент Modbus + unit_id: Адрес устройства + + Returns: + True если соединение успешно, False в противном случае + """ + try: + if not transport.connect(): + logger.error("Не удалось установить соединение с портом") + return False + # Пробуем прочитать регистр для проверки связи + result = transport.read_holding_registers(address=0, count=1, unit=unit_id) + if result.isError(): + logger.error(f"Устройство не отвечает на адресе {unit_id}") + return False + logger.info("Соединение установлено успешно") + return True + except Exception as e: + logger.error(f"Ошибка при проверке соединения: {e}") + return False + +def main(): + logger.info("=" * 60) + logger.info("Начало работы с Овен ПР100") + logger.info("=" * 60) + # Параметры подключения + PORT = "COM6" + BAUDRATE = 115200 + UNIT_ID = 1 # Адрес устройства Modbus + transport = None + try: + logger.info(f"Настройка подключения: {PORT}, {BAUDRATE} бод, адрес {UNIT_ID}") + # Создание клиента Modbus + transport = ModbusSerialClient( + method="rtu", + port=PORT, + baudrate=BAUDRATE, + stopbits=1, + parity="N", + bytesize=8, + timeout=0.5, + retry_on_empty=True + ) + # Проверка соединения + if not test_connection(transport, UNIT_ID): + logger.error("=" * 60) + logger.error("КРИТИЧЕСКАЯ ОШИБКА: Не удалось подключиться к устройству") + logger.error(f"Проверьте:") + logger.error(f" 1. Правильность имени порта ({PORT})") + logger.error(f" 2. Подключение кабеля") + logger.error(f" 3. Питание устройства") + logger.error(f" 4. Адрес устройства ({UNIT_ID})") + logger.error(f" 5. Скорость обмена ({BAUDRATE})") + logger.error("=" * 60) + return 1 + # Создание клиента ПР100 + pr100 = OwenModbusClient(transport=transport, device=PR100, unit=UNIT_ID) + logger.info("-" * 60) + # Чтение дискретных входов + read_digital_inputs(pr100, num_inputs=4) + logger.info("-" * 60) + # Чтение аналоговых входов + read_analog_inputs(pr100) + logger.info("-" * 60) + # Чтение системного времени + read_system_time(pr100) + logger.info("-" * 60) + # Установка системного времени (закомментировано по умолчанию) + # if input("Установить текущее время? (y/N): ").lower() == 'y': + # set_system_time(pr100) + # logger.info("-" * 60) + # Управление выходами + # ВНИМАНИЕ: Записывается только когда переключатель в положении СТОП! + logger.warning("ВНИМАНИЕ: Управление выходами работает только в режиме СТОП!") + control_outputs(pr100, {1: True, 2: False, 3: True}) + logger.info("-" * 60) + # Запуск мониторинга (закомментировано по умолчанию) + # monitoring_loop(pr100, duration=30) + logger.info("=" * 60) + logger.info("Работа завершена успешно") + logger.info("=" * 60) + return 0 + except FileNotFoundError as e: + logger.error("=" * 60) + logger.error(f"ОШИБКА: Порт {PORT} не найден") + logger.error("Доступные порты можно посмотреть с помощью команды:") + logger.error(" python -m serial.tools.list_ports") + logger.error("=" * 60) + return 1 + except ModbusException as e: + logger.error("=" * 60) + logger.error(f"ОШИБКА Modbus: {e}") + logger.error("=" * 60) + return 1 + except KeyboardInterrupt: + logger.info("=" * 60) + logger.info("Работа прервана пользователем") + logger.info("=" * 60) + return 0 + except Exception as e: + logger.error("=" * 60) + logger.error(f"НЕОЖИДАННАЯ ОШИБКА: {e}", exc_info=True) + logger.error("=" * 60) + return 1 + finally: + if transport is not None: + try: + transport.close() + logger.info("Соединение закрыто") + except Exception as e: + logger.error(f"Ошибка при закрытии соединения: {e}") + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/owen/__init__.py b/owen/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/owen/__pycache__/__init__.cpython-314.pyc b/owen/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..72e392cb7977a9e9cb0d02cddae3bde0104e04b8 GIT binary patch literal 130 zcmdPq_I|p@<2{{|u76C7)~+Q&3rw zk)NlVU!Iy51193*GxIV_;^XxSDsOSv#eDOe_X5J~1;gG8QodSpe1L B8g~Ey literal 0 HcmV?d00001 diff --git a/owen/__pycache__/client.cpython-314.pyc b/owen/__pycache__/client.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..158f822fcf8cab9736dac8f43a07e36758b1e74e GIT binary patch literal 15617 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 literal 0 HcmV?d00001 diff --git a/owen/__pycache__/device.cpython-314.pyc b/owen/__pycache__/device.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a79a774648bddb7d11d3804c8b32d6578ba0c308 GIT binary patch literal 89480 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 HyuAMlOj~)8 literal 0 HcmV?d00001 diff --git a/owen/__pycache__/protocol.cpython-314.pyc b/owen/__pycache__/protocol.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..254f3c1ac8cdeef7f8ee9745c7e6fff4b26fa9fb GIT binary patch literal 12072 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 YpE_cU!P6zUG%gBx#WR~8&*5D2|5Y11*8l(j literal 0 HcmV?d00001 diff --git a/owen/client.py b/owen/client.py new file mode 100644 index 0000000..021757d --- /dev/null +++ b/owen/client.py @@ -0,0 +1,209 @@ +#! /usr/bin/env python3 + +"""Реализация класса клиента для управления контроллером ОВЕН.""" + +from __future__ import annotations + +from operator import mul, truediv +from typing import TYPE_CHECKING, Callable + +from pymodbus.constants import Endian +from pymodbus.payload import BinaryPayloadBuilder, BinaryPayloadDecoder + +from owen.protocol import Owen, OwenError + +if TYPE_CHECKING: + from pymodbus.client.sync import ModbusSerialClient + from pymodbus.pdu import ModbusResponse + from serial import Serial + + from owen.device import MODBUS_PARAMS, OWEN_DEVICE, OWEN_PARAMS + + +class ClientMixin: + """Класс-примесь клиента.""" + + @staticmethod + def check_index(name: str, dev: OWEN_PARAMS | MODBUS_PARAMS, + index: int | None) -> int | None: + """Проверка индекса.""" + + if not index: + index = None if None in dev["index"] else 0 + if index not in dev["index"]: + msg = f"'{name}' does not support index '{index}'" + raise OwenError(msg) + + return index + + @staticmethod + def check_value(name: str, dev: OWEN_PARAMS | MODBUS_PARAMS, + value: float | str | None) -> None: + """Проверка данных.""" + + if all([value is None, dev["min"] is not None, dev["max"] is not None]) or \ + all([value is not None, dev["min"] == dev["max"] is None or + value < dev["min"] or value > dev["max"]]): # type: ignore + msg = f"An '{name}' value of '{value}' is out of range" + raise OwenError(msg) + + +class OwenSerialClient(ClientMixin): + """Класс клиента для взаимодействия с устройством по протоколу ОВЕН.""" + + def __init__(self, transport: Serial, device: OWEN_DEVICE, unit: int, *, + addr_len_8: bool = True) -> None: + """Инициализация класса клиента с указанными параметрами.""" + + self.socket = transport + self.unit = unit + self.device = device + self.addr_len_8 = addr_len_8 + + self._owen = Owen(self.unit, addr_len_8) + + def __repr__(self) -> str: + """Строковое представление объекта.""" + + return (f"{type(self).__name__}(transport={self.socket}, unit={self.unit}, " + f"addr_len_8={self.addr_len_8})") + + def __del__(self) -> None: + """Закрытие соединения с устройством при удалении объекта.""" + + if self.socket: + self.socket.close() + + def bus_exchange(self, packet: bytes) -> bytes: + """Обмен по интерфейсу.""" + + self.socket.reset_input_buffer() + self.socket.reset_output_buffer() + + self.socket.write(packet) + return self.socket.read_until(b"\r") + + def send_message(self, flag: int, name: str, index: int | None, + data: bytes = b"") -> bytes: + """Подготовка данных для обмена.""" + + packet = self._owen.make_packet(flag, name, index, data) + answer = self.bus_exchange(packet) + return self._owen.parse_response(packet, answer) + + def get_param(self, name: str, index: int | None = None) -> float | str: + """Чтение данных из устройства.""" + + dev = self.device["Owen"][name.upper()] + index = self.check_index(name, dev, index) + result = self.send_message(1, name, index) + return self._owen.unpack_value(dev["type"], result, index) + + def set_param(self, name: str, index: int | None = None, + value: float | str | None = None) -> bool: + """Запись данных в устройство.""" + + dev = self.device["Owen"][name.upper()] + index = self.check_index(name, dev, index) + self.check_value(name, dev, value) + data = self._owen.pack_value(dev["type"], value) + return bool(self.send_message(0, name, index, data)) + + +class OwenModbusClient(ClientMixin): + """Класс клиента для взаимодействия с устройством по протоколу Modbus.""" + + def __init__(self, transport: ModbusSerialClient, device: OWEN_DEVICE, + unit: int) -> None: + """Инициализация класса клиента с указанными параметрами.""" + + self.socket = transport + self.unit = unit + self.device = device + + self.socket.connect() + + def __repr__(self) -> str: + """Строковое представление объекта.""" + + return f"{type(self).__name__}(transport={self.socket}, unit={self.unit})" + + def __del__(self) -> None: + """Закрытие соединения с устройством при удалении объекта.""" + + if self.socket: + self.socket.close() + + @staticmethod + def check_error(retcode: ModbusResponse) -> bool: + """Проверка возвращаемого значения на ошибку.""" + + if retcode.isError(): + raise OwenError(retcode) + return True + + def _read(self, dev: MODBUS_PARAMS, index: int | None) -> float | str: + """Чтение данных из регистра Modbus.""" + + count = {"U16": 1, "I16": 1, "U32": 2, "I32": 2, "F32": 2, "STR": 4, + }[dev["type"]] + result = self.socket.read_holding_registers(address=dev["index"][index], + count=count, + unit=self.unit) + self.check_error(result) + decoder = BinaryPayloadDecoder.fromRegisters(result.registers, Endian.Big) + + return {"U16": decoder.decode_16bit_uint, + "I16": decoder.decode_16bit_int, + "U32": decoder.decode_32bit_uint, + "I32": decoder.decode_32bit_int, + "F32": decoder.decode_32bit_float, + "STR": lambda: decoder.decode_string(8), + }[dev["type"]]() + + def modify_value(self, func: Callable[[float, float], float], dev: MODBUS_PARAMS, + index: int | None, value: float) -> float: + """Преобразование значения к нужной точности.""" + + if dev["dp"]: + dp_dev = self.device["Modbus"][dev["dp"]] + dp = self._read(dp_dev, index) + value = func(value, 10.0**int(dp)) + + prec = dev["precision"] + return func(value, 10.0**prec) if prec else value + + def get_param(self, name: str, index: int | None = None) -> float | str: + """Чтение данных из устройства.""" + + dev = self.device["Modbus"][name.upper()] + index = self.check_index(name, dev, index) + value = self._read(dev, index) + return self.modify_value(truediv, dev, index, value) + + def set_param(self, name: str, index: int | None = None, + value: float | str | None = None) -> bool: + """Запись данных в устройство.""" + + dev = self.device["Modbus"][name.upper()] + index = self.check_index(name, dev, index) + self.check_value(name, dev, value) + value = self.modify_value(mul, dev, index, value) + + builder = BinaryPayloadBuilder(None, Endian.Big) + {"U16": lambda value: builder.add_16bit_uint(int(value)), + "I16": lambda value: builder.add_16bit_int(int(value)), + "U32": lambda value: builder.add_32bit_uint(int(value)), + "I32": lambda value: builder.add_32bit_int(int(value)), + "F32": lambda value: builder.add_32bit_float(float(value)), + "STR": lambda value: builder.add_string(str(value)), + }[dev["type"]](value) + + result = self.socket.write_registers(address=dev["index"][index], + values=builder.build(), + skip_encode=True, + unit=self.unit) + return self.check_error(result) + + +__all__ = ["OwenModbusClient", "OwenSerialClient"] diff --git a/owen/converter.py b/owen/converter.py new file mode 100644 index 0000000..18fb6ee --- /dev/null +++ b/owen/converter.py @@ -0,0 +1,229 @@ +#! /usr/bin/env python3 + +"""Функции для упаковки и распаковки разных типов данных.""" + +from binascii import hexlify, unhexlify +from decimal import Decimal +from struct import pack, unpack + + +def pack_str(value: str) -> bytes: + """Упаковка данных типа STR.""" + + return bytes(value[::-1], encoding="cp1251") + + +def unpack_str(value: bytes, index: int) -> str: + """Распаковка данных типа STR.""" + + return bytes(value[::-1]).decode("cp1251") + + +def pack_u24(value: tuple[int, int]) -> bytes: + """Упаковка данных типа U24.""" + + return pack(">BH", *value)[:3] + + +def unpack_u24(value: bytes, index: int) -> tuple[int, int]: + """Распаковка данных типа U24.""" + + return unpack(">BH", value[:3]) + + +def pack_i8(value: int) -> bytes: + """Упаковка данных типа I8.""" + + return pack(">b", value)[:1] + + +def unpack_i8(value: bytes, index: int) -> int: + """Распаковка данных типа I8.""" + + return unpack(">b", value[:1])[0] + + +def pack_u8(value: int) -> bytes: + """Упаковка данных типа U8.""" + + return pack(">B", value)[:1] + + +def unpack_u8(value: bytes, index: int) -> int: + """Распаковка данных типа U8.""" + + return unpack(">B", value[:1])[0] + + +def pack_i16(value: int) -> bytes: + """Упаковка данных типа I16.""" + + return pack(">h", value)[:2] + + +def unpack_i16(value: bytes, index: int) -> int: + """Распаковка данных типа I16.""" + + return unpack(">h", value[:2])[0] + + +def pack_u16(value: int) -> bytes: + """Упаковка данных типа U16.""" + + return pack(">H", value)[:2] + + +def unpack_u16(value: bytes, index: int) -> int: + """Распаковка данных типа U16.""" + + return unpack(">H", value[:2])[0] + + +def pack_f24(value: float) -> bytes: + """Упаковка данных типа F24.""" + + return pack(">f", value)[:3] + + +def unpack_f24(value: bytes, index: int) -> float: + """Распаковка данных типа F24.""" + + return unpack(">f", value[:3] + b"\x00")[0] + + +def pack_f32(value: float) -> bytes: + """Упаковка данных типа F32.""" + + return pack(">f", value)[:4] + + +def unpack_f32(value: bytes, index: int) -> float: + """Распаковка данных типа F32.""" + + return unpack(">f", value[:4])[0] + + +def pack_f32t(value: tuple[float, int]) -> bytes: + """Упаковка данных типа F32+T.""" + + return pack(">fH", *value)[:6] + + +def unpack_f32t(value: bytes, index: int) -> tuple[float, int]: + """Распаковка данных типа F32+T.""" + + return unpack(">fH", value[:6]) + + +def pack_i32(value: int) -> bytes: + """Упаковка данных типа I32.""" + + return pack(">i", value)[:4] + + +def unpack_i32(value: bytes, index: int) -> int: + """Распаковка данных типа I32.""" + + return unpack(">i", value[:4])[0] + + +def pack_u32(value: int) -> bytes: + """Упаковка данных типа U32.""" + + return pack(">I", value)[:4] + + +def unpack_u32(value: bytes, index: int) -> int: + """Распаковка данных типа U32.""" + + return unpack(">I", value[:4])[0] + + +def pack_sdot(value: float) -> bytes: + """Упаковка данных типа STORED_DOT.""" + + sign, digits, exponent = Decimal(str(value)).as_tuple() + mantissa = int("".join(map(str, digits))) + + frmt, size, chunk = {mantissa < 16: (">B", 4, slice(1)), + mantissa >= 4096: (">I", 20, slice(1, 4)), + }.get(True, (">H", 12, slice(2))) + + bin_str = f"{sign:1b}{abs(exponent):03b}{mantissa:0{size}b}" + return pack(frmt, int(bin_str, 2))[chunk] + + +def unpack_sdot(value: bytes, index: int) -> float: + """Распаковка данных типа STORED_DOT.""" + + if index is not None: + value = value[:-2] + + data = int.from_bytes(value, "big") + shift = len(value) * 8 - 4 + + sign = data >> (shift + 3) & 1 + exponent = data >> shift & 7 + mantissa = data & (2 ** shift - 1) + + return (-1) ** sign * 10 ** (-exponent) * mantissa + + +def _encode_dot_u(value: float) -> bytes: + """Упаковка данных типа DEC_DOTi.""" + + value = int(value) + length = len(str(value)) + length += length % 2 + hexstr = f"{value:0{length}d}" + + return unhexlify(hexstr) + + +def pack_dot0(value: float) -> bytes: + """Упаковка данных типа DEC_DOT0.""" + + return _encode_dot_u(value) + + +def pack_dot3(value: float) -> bytes: + """Упаковка данных типа DEC_DOT3.""" + + return _encode_dot_u(value * 1000) + + +def _decode_dot_u(value: bytes, index: int) -> int: + """Распаковка данных типа DEC_DOTi.""" + + if index is not None: + value = value[:-2] + + return int(hexlify(value).decode()) + + +def unpack_dot0(value: bytes, index: int) -> int: + """Распаковка данных типа DEC_DOT0.""" + + return _decode_dot_u(value, index) + + +def unpack_dot3(value: bytes, index: int) -> float: + """Распаковка данных типа DEC_DOT3.""" + + return _decode_dot_u(value, index) / 1000.0 + + +OWEN_TYPE = {"F32+T": {"pack": pack_f32t, "unpack": unpack_f32t}, + "SDOT": {"pack": pack_sdot, "unpack": unpack_sdot}, + "DOT0": {"pack": pack_dot0, "unpack": unpack_dot0}, + "DOT3": {"pack": pack_dot3, "unpack": unpack_dot3}, + "F32": {"pack": pack_f32, "unpack": unpack_f32}, + "F24": {"pack": pack_f24, "unpack": unpack_f24}, + "U16": {"pack": pack_u16, "unpack": unpack_u16}, + "I16": {"pack": pack_i16, "unpack": unpack_i16}, + "U32": {"pack": pack_u32, "unpack": unpack_u32}, + "I32": {"pack": pack_i32, "unpack": unpack_i32}, + "U8": {"pack": pack_u8, "unpack": unpack_u8}, + "I8": {"pack": pack_i8, "unpack": unpack_i8}, + "U24": {"pack": pack_u24, "unpack": unpack_u24}, # для N.err + "STR": {"pack": pack_str, "unpack": unpack_str}} diff --git a/owen/device.py b/owen/device.py new file mode 100644 index 0000000..1980420 --- /dev/null +++ b/owen/device.py @@ -0,0 +1,1244 @@ +#! /usr/bin/env python3 + + +"""Данный файл содержит настройки приборов.""" + +# Назначение полей таблиц настроек: +# 1. Тип протокола Owen +# * Название параметра (в верхнем регистре) +# - Тип параметра +# - Словарь поддерживаемых индексов (None - индекса нет; 0,1 и т.д) + адрес индекса +# - Минимальное значение параметра +# - Максимальное значение параметра +# 2. Тип протокола Modbus +# * Название параметра (в верхнем регистре) +# - Тип параметра +# - Словарь поддерживаемых индексов (None - индекса нет; 0,1 и т.д) + адрес Modbus +# - Минимальное значение параметра +# - Максимальное значение параметра +# - Признак зависимости значения от другого параметра: название параметра или None +# - Кол-во знаков после запятой + +# Поддерживаемые типы данных: +# I8, U8 - signed and unsigned char (1 byte) +# I16, U16 - signed and unsigned short (2 bytes) +# I32, U32 - signed and unsigned int (4 bytes) +# U24 - for N.ERR (3 bytes) +# F24, F32 - float (3 or 4 bytes) +# F32+T - float + time modificator (6 bytes) +# STR - string (8 bytes) +# SDOT - STORED_DOT +# DOT0, DOT3 - DEC_DOT0, DEC_DOT3 + +from __future__ import annotations + +from typing import TypedDict + + +class OWEN_PARAMS(TypedDict): + type: str + index: dict[int | None, int | None] + min: int | float | None + max: int | float | None + + +class MODBUS_PARAMS(TypedDict): + type: str + index: dict[int | None, int] + min: int | float | None + max: int | float | None + dp: str | None + precision: int + + +class OWEN_DEVICE(TypedDict): + Owen: dict[str, OWEN_PARAMS] + Modbus: dict[str, MODBUS_PARAMS] + + +# Таблица настроек измерителя-ПИД-регулятора ТРМ101 +TRM101: OWEN_DEVICE = { + "Owen": {"PV": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "SP": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "R-S": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "AT": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "O": {"type": "F24", "index": {None: None}, "min": 0, "max": 100}, + "IN-T": {"type": "U8", "index": {None: None}, "min": 1, "max": 26}, + "DPT": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "DP": {"type": "U8", "index": {None: None}, "min": 0, "max": 3}, + "IN-L": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "IN-H": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "SL-L": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "SL-H": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "SH": {"type": "F24", "index": {None: None}, "min": -500, "max": 500}, + "KU": {"type": "F24", "index": {None: None}, "min": 0.500, "max": 2.000}, + "INF": {"type": "F24", "index": {None: None}, "min": 0, "max": 999}, + "FB": {"type": "F24", "index": {None: None}, "min": 0, "max": 9999}, + "AN-L": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "AN-H": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "EV-1": {"type": "U8", "index": {None: None}, "min": 0, "max": 2}, + "ALT": {"type": "U8", "index": {None: None}, "min": 0, "max": 11}, + "AL-D": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "AL-H": {"type": "F24", "index": {None: None}, "min": 0, "max": 9999}, + "OREU": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "CP": {"type": "U8", "index": {None: None}, "min": 1, "max": 250}, + "VSP": {"type": "F24", "index": {None: None}, "min": 0, "max": 9999}, + "CNTL": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "HYST": {"type": "F24", "index": {None: None}, "min": 0, "max": 9999}, + "ONST": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "ONER": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "RAMP": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "P": {"type": "F24", "index": {None: None}, "min": 0.001, "max": 9999}, + "I": {"type": "F24", "index": {None: None}, "min": 0, "max": 3999}, + "D": {"type": "F24", "index": {None: None}, "min": 0, "max": 3999}, + "DB": {"type": "F24", "index": {None: None}, "min": 0, "max": 200}, + "OL-L": {"type": "F24", "index": {None: None}, "min": 0, "max": 100}, + "OL-H": {"type": "F24", "index": {None: None}, "min": 0, "max": 100}, + "ORL": {"type": "F24", "index": {None: None}, "min": 0.2, "max": 100}, + "MVER": {"type": "F24", "index": {None: None}, "min": 0, "max": 100}, + "MVST": {"type": "F24", "index": {None: None}, "min": 0, "max": 100}, + "MDST": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "LBA": {"type": "U16", "index": {None: None}, "min": 0, "max": 9999}, + "LBAB": {"type": "F24", "index": {None: None}, "min": 0, "max": 9999}, + "ADDR": {"type": "U16", "index": {None: None}, "min": 0, "max": 2047}, + "RSDL": {"type": "U8", "index": {None: None}, "min": 1, "max": 45}, + "A.LEN": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "BPS": {"type": "U8", "index": {None: None}, "min": 0, "max": 8}, + "LEN": {"type": "U8", "index": {None: None}, "min": 1, "max": 1}, + "PRTY": {"type": "U8", "index": {None: None}, "min": 0, "max": 0}, + "SBIT": {"type": "U8", "index": {None: None}, "min": 0, "max": 0}, + "VER": {"type": "STR", "index": {None: None}, "min": None, "max": None}, + "DEV": {"type": "STR", "index": {None: None}, "min": None, "max": None}, + "APLY": {"type": "U8", "index": {None: None}, "min": None, "max": None}, + "INIT": {"type": "U8", "index": {None: None}, "min": None, "max": None}, + "N.ERR": {"type": "U24", "index": {None: None}, "min": 0, "max": 255}, + "ATTR": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "O-ED": {"type": "F24", "index": {None: None}, "min": 0, "max": 100}, + "O.": {"type": "F24", "index": {None: None}, "min": 0, "max": 100}, + "EDPT": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + }, +} + +# Таблица настроек измерителя-регулятора шестиканального ТРМ136 +TRM136: OWEN_DEVICE = { + "Owen": {"IND.T": {"type": "DOT0", "index": {None: None}, "min": 1, "max": 600}, + "IND.R": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 60}, + "IND.A": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "AL.DR": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 6}, + "AL.HD": {"type": "DOT0", "index": {None: None}, "min": 1, "max": 600}, + "AL.ST": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "CJ-.C": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "SYST": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "BL.AR": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "IN.FD": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 15}, + "PRT": {"type": "DOT0", "index": {None: None}, "min": 1, "max": 6}, + "IN.SH": {"type": "SDOT", "index": {None: None}, "min": -99.9, "max": 999.9}, + "IN.SL": {"type": "DOT3", "index": {None: None}, "min": 0.9, "max": 1.1}, + "IN-T": {"type": "U8", "index": {None: None}, "min": 0, "max": 21}, + "IN.FG": {"type": "SDOT", "index": {None: None}, "min": 0, "max": 100}, + "AIN.L": {"type": "SDOT", "index": {None: None}, "min": -999, "max": 9999}, + "AIN.H": {"type": "SDOT", "index": {None: None}, "min": -999, "max": 9999}, + "C.SP": {"type": "SDOT", "index": {None: None}, "min": -999, "max": 9999}, + "HYST": {"type": "SDOT", "index": {None: None}, "min": 1, "max": 9999}, + "C.SP.O": {"type": "SDOT", "index": {None: None}, "min": 0, "max": 9999}, + "HT.ON": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 9000}, + "HT.OF": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 9000}, + "DL.ON": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 3600}, + "DL.OF": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 3600}, + "BL.ST": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "DP_": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 3}, + "ER.ST": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "AL.T": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 5}, + "AO.L": {"type": "SDOT", "index": {None: None}, "min": -999, "max": 9999}, + "AO.H": {"type": "SDOT", "index": {None: None}, "min": -999, "max": 9999}, + "C.DR": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 6}, + "C.LBT": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 9000}, + "AL.OU": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "C.LBA": {"type": "SDOT", "index": {None: None}, "min": 1, "max": 100}, + "C.IN": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 18}, + "BPS": {"type": "U8", "index": {None: None}, "min": 0, "max": 8}, + "LEN": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "PRTY": {"type": "U8", "index": {None: None}, "min": 0, "max": 2}, + "SBIT": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "A.LEN": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "ADDR": {"type": "U16", "index": {None: None}, "min": 0, "max": 2040}, + "N.FLT": {"type": "U8", "index": {None: None}, "min": 0, "max": 8}, + "DATA": {"type": "U8", "index": {None: None}, "min": 0, "max": 7}, + "T.INC": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "CHAR": {"type": "STR", "index": {None: None}, "min": None, "max": None}, + "SOUR": {"type": "U16", "index": {None: None}, "min": 0, "max": 2047}, + "READ": {"type": "F32+T", "index": {None: None}, "min": -2**127, "max": 2**127}, + "DR.DG": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 1}, + "N.ERR": {"type": "U24", "index": {None: None}, "min": 0, "max": 255}, + }, + "Modbus": {"READ": {"type": "F32", "index": {0: 0x0003, 1: 0x0008, 2: 0x000D, 3: 0x0012, 4: 0x0017, 5: 0x001C}, "min": -999, "max": 9999, "dp": None, "precision": 0}, + "R.CAL": {"type": "F32", "index": {0: 0x0043, 1: 0x0048, 2: 0x004D, 3: 0x0052, 4: 0x0057, 5: 0x005C}, "min": -999, "max": 9999, "dp": None, "precision": 0}, + "R.CIN": {"type": "U16", "index": {0: 0x0000, 1: 0x0001, 2: 0x0002, 3: 0x0003, 4: 0x0004, 5: 0x0005}, "min": -999, "max": 9999, "dp": None, "precision": 0}, + "C.SP": {"type": "U16", "index": {0: 0x0011, 1: 0x0015, 2: 0x0019, 3: 0x001D, 4: 0x0021, 5: 0x0025}, "min": -999, "max": 9999, "dp": None, "precision": 0}, + "C.SP.S": {"type": "U16", "index": {0: 0x0013, 1: 0x0017, 2: 0x001B, 3: 0x001F, 4: 0x0023, 5: 0x0027}, "min": -999, "max": 9999, "dp": None, "precision": 0}, + "HYST": {"type": "U16", "index": {0: 0x0031, 1: 0x0033, 2: 0x0035, 3: 0x0037, 4: 0x0039, 5: 0x003B}, "min": 0.001, "max": 9999, "dp": None, "precision": 0}, + # "": {"type": "U16", "index": {0: 0x0000, 1: 0x0001, 2: 0x0002, 3: 0x0003, 4: 0x0004, 5: 0x0005}, "min": -999, "max": 9999, "dp": None, "precision": 0}, # Состояние ВУ - название параметра неизвестно + }, +} + +# Таблица настроек измерителя-регулятора восьмиканального ТРМ138 +TRM138: OWEN_DEVICE = { + "Owen": {"IND.T": {"type": "DOT0", "index": {None: None}, "min": 1, "max": 600}, + "IND.R": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 60}, + "IND.A": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "AL.DR": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 8}, + "AL.HD": {"type": "DOT0", "index": {None: None}, "min": 1, "max": 600}, + "AL.ST": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "CJ-.C": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "SYST": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "BL.AR": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "IN.FD": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 15}, + "PRT": {"type": "DOT0", "index": {None: None}, "min": 1, "max": 8}, + "IN.SH": {"type": "SDOT", "index": {None: None}, "min": -99.9, "max": 999.9}, + "IN.SL": {"type": "DOT3", "index": {None: None}, "min": 0.9, "max": 1.1}, + "IN-T": {"type": "U8", "index": {None: None}, "min": 0, "max": 21}, + "IN.FG": {"type": "SDOT", "index": {None: None}, "min": 0, "max": 100}, + "AIN.L": {"type": "SDOT", "index": {None: None}, "min": -999, "max": 9999}, + "AIN.H": {"type": "SDOT", "index": {None: None}, "min": -999, "max": 9999}, + "C.SP": {"type": "SDOT", "index": {None: None}, "min": -999, "max": 9999}, + "HYST": {"type": "SDOT", "index": {None: None}, "min": 1, "max": 9999}, + "C.SP.O": {"type": "SDOT", "index": {None: None}, "min": 0, "max": 9999}, + "HT.ON": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 9000}, + "HT.OF": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 9000}, + "DL.ON": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 3600}, + "DL.OF": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 3600}, + "BL.ST": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "DP_": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 3}, + "ER.ST": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "AL.T": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 5}, + "AO.L": {"type": "SDOT", "index": {None: None}, "min": -999, "max": 9999}, + "AO.H": {"type": "SDOT", "index": {None: None}, "min": -999, "max": 9999}, + "C.DR": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 8}, + "C.LBT": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 9000}, + "AL.OU": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "C.LBA": {"type": "SDOT", "index": {None: None}, "min": 1, "max": 100}, + "C.IN": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 19}, + "BPS": {"type": "U8", "index": {None: None}, "min": 0, "max": 8}, + "LEN": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "PRTY": {"type": "U8", "index": {None: None}, "min": 0, "max": 2}, + "SBIT": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "A.LEN": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "ADDR": {"type": "U16", "index": {None: None}, "min": 0, "max": 2040}, + "N.FLT": {"type": "U8", "index": {None: None}, "min": 0, "max": 8}, + "DATA": {"type": "U8", "index": {None: None}, "min": 0, "max": 7}, + "T.INC": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "CHAR": {"type": "STR", "index": {None: None}, "min": None, "max": None}, + "SOUR": {"type": "U16", "index": {None: None}, "min": 0, "max": 2047}, + "READ": {"type": "F32+T", "index": {None: None}, "min": -2**127, "max": 2**127}, + "DR.DG": {"type": "DOT0", "index": {None: None}, "min": 0, "max": 1}, + "N.ERR": {"type": "U24", "index": {None: None}, "min": 0, "max": 255}, + }, + "Modbus": {"READ": {"type": "F32", "index": {0: 0x0003, 1: 0x0008, 2: 0x000D, 3: 0x0012, 4: 0x0017, 5: 0x001C, 6: 0x0021, 7: 0x0026}, "min": -999, "max": 9999, "dp": None, "precision": 0}, + "R.CAL": {"type": "F32", "index": {0: 0x0043, 1: 0x0048, 2: 0x004D, 3: 0x0052, 4: 0x0057, 5: 0x005C, 6: 0x0061, 7: 0x0066}, "min": -999, "max": 9999, "dp": None, "precision": 0}, + "R.CIN": {"type": "U16", "index": {0: 0x0000, 1: 0x0001, 2: 0x0002, 3: 0x0003, 4: 0x0004, 5: 0x0005, 6: 0x0006, 7: 0x0007}, "min": -999, "max": 9999, "dp": None, "precision": 0}, + "C.SP": {"type": "U16", "index": {0: 0x0011, 1: 0x0015, 2: 0x0019, 3: 0x001D, 4: 0x0021, 5: 0x0025, 6: 0x0029, 7: 0x002D}, "min": -999, "max": 9999, "dp": None, "precision": 0}, + "C.SP.S": {"type": "U16", "index": {0: 0x0013, 1: 0x0017, 2: 0x001B, 3: 0x001F, 4: 0x0023, 5: 0x0027, 6: 0x002B, 7: 0x002F}, "min": -999, "max": 9999, "dp": None, "precision": 0}, + "HYST": {"type": "U16", "index": {0: 0x0031, 1: 0x0033, 2: 0x0035, 3: 0x0037, 4: 0x0039, 5: 0x003B, 6: 0x003D, 7: 0x003F}, "min": 0.001, "max": 9999, "dp": None, "precision": 0}, + "C.DR": {"type": "U16", "index": {0: 0x0041, 1: 0x0042, 2: 0x0043, 3: 0x0044, 4: 0x0045, 5: 0x0046, 6: 0x0047, 7: 0x0048}, "min": 0, "max": 8, "dp": None, "precision": 0}, + # "": {"type": "U16", "index": {0: 0x0051, 1: 0x0052, 2: 0x0053, 3: 0x0054, 4: 0x0055, 5: 0x0056, 6: 0x0057, 7: 0x0058}, "min": -999, "max": 9999, "dp": None, "precision": 0}, # Значение ЦАП ВУ - название параметра неизвестно + # "": {"type": "U16", "index": {0: 0x0000, 1: 0x0001, 2: 0x0002, 3: 0x0003, 4: 0x0004, 5: 0x0005, 6: 0x0006, 7: 0x0007}, "min": -999, "max": 9999, "dp": None, "precision": 0}, # Состояние ВУ - название параметра неизвестно + }, +} + +# Таблица настроек измерителя регулятора микропроцессорного ТРМ148 +TRM148: OWEN_DEVICE = { + "Owen": {"READ": {"type": "F32+T", "index": {None: None}, "min": -2**127, "max": 2**127}, + "R.CAL": {"type": "F32+T", "index": {None: None}, "min": -2**127, "max": 2**127}, + "CAL.T": {"type": "U8", "index": {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7}, "min": 0, "max": 9}, + "SP.LU": {"type": "SDOT", "index": {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7}, "min": -9999, "max": 9999}, + "P.-SP": {"type": "U8", "index": {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7}, "min": 0, "max": 1}, + "VER": {"type": "STR", "index": {None: None}, "min": None, "max": None}, + "DEV": {"type": "STR", "index": {None: None}, "min": None, "max": None}, + "MOD.V": {"type": "U8", "index": {None: None}, "min": 0, "max": 255}, + }, + "Modbus": {"R.ST": {"type": "U16", "index": {0: 0x0060, 1: 0x0061, 2: 0x0062, 3: 0x0063, 4: 0x0064, 5: 0x0065, 6: 0x0066, 7: 0x0067}, "min": 0, "max": 2, "dp": None, "precision": 0}, + # "": {"type": "F32", "index": {0: 0x0000, 1: 0x0002, 2: 0x0004, 3: 0x0006, 4: 0x0008, 5: 0x000A, 6: 0x000C, 7: 0x000E}, "min": -999, "max": 9999, "dp": None, "precision": 0}, # Измеритель - название параметра неизвестно + # "": {"type": "F32", "index": {0: 0x0020, 1: 0x0022, 2: 0x0024, 3: 0x0026, 4: 0x0028, 5: 0x002A, 6: 0x002C, 7: 0x002E}, "min": -999, "max": 9999, "dp": None, "precision": 0}, # Вычислитель - название параметра неизвестно + # "": {"type": "F32", "index": {0: 0x0040, 1: 0x0042, 2: 0x0044, 3: 0x0046, 4: 0x0048, 5: 0x004A, 6: 0x004C, 7: 0x004E}, "min": -999, "max": 9999, "dp": None, "precision": 0}, # Состояние ВУ - название параметра неизвестно + # "": {"type": "F32", "index": {0: 0x0080, 1: 0x0082, 2: 0x0084, 3: 0x0086, 4: 0x0088, 5: 0x008A, 6: 0x008C, 7: 0x008E}, "min": -999, "max": 9999, "dp": None, "precision": 0}, # Уставка рассчетная - название параметра неизвестно + "IN.T": {"type": "U16", "index": {0: 0x0100, 1: 0x0101, 2: 0x0102, 3: 0x0103, 4: 0x0104, 5: 0x0105, 6: 0x0106, 7: 0x0107}, "min": 1, "max": 41, "dp": None, "precision": 0}, + "IN.FD": {"type": "U16", "index": {0: 0x0110, 1: 0x0111, 2: 0x0112, 3: 0x0113, 4: 0x0114, 5: 0x0115, 6: 0x0116, 7: 0x0117}, "min": 0, "max": 1800, "dp": None, "precision": 0}, + "IN.FG": {"type": "F32", "index": {0: 0x0120, 1: 0x0122, 2: 0x0124, 3: 0x0126, 4: 0x0128, 5: 0x012A, 6: 0x012C, 7: 0x012E}, "min": 0, "max": 9999, "dp": None, "precision": 0}, + "ITRL": {"type": "F32", "index": {0: 0x0130, 1: 0x0132, 2: 0x0134, 3: 0x0136, 4: 0x0138, 5: 0x013A, 6: 0x013C, 7: 0x013E}, "min": 0, "max": 30, "dp": None, "precision": 0}, + "IN.SH": {"type": "F32", "index": {0: 0x0140, 1: 0x0142, 2: 0x0144, 3: 0x0146, 4: 0x0148, 5: 0x014A, 6: 0x014C, 7: 0x014E}, "min": -999, "max": 9999, "dp": None, "precision": 0}, + "IN.SL": {"type": "F32", "index": {0: 0x0150, 1: 0x0152, 2: 0x0154, 3: 0x0156, 4: 0x0158, 5: 0x015A, 6: 0x015C, 7: 0x015E}, "min": 0.9, "max": 1.1, "dp": None, "precision": 0}, + "AIN.L": {"type": "F32", "index": {0: 0x0160, 1: 0x0162, 2: 0x0164, 3: 0x0166, 4: 0x0168, 5: 0x016A, 6: 0x016C, 7: 0x016E}, "min": -999, "max": 9999, "dp": None, "precision": 0}, + "AIN.H": {"type": "F32", "index": {0: 0x0170, 1: 0x0172, 2: 0x0174, 3: 0x0176, 4: 0x0178, 5: 0x017A, 6: 0x017C, 7: 0x017E}, "min": -999, "max": 9999, "dp": None, "precision": 0}, + "A.IST": {"type": "F32", "index": {0: 0x0200, 1: 0x0202, 2: 0x0204, 3: 0x0206, 4: 0x0208, 5: 0x020A, 6: 0x020C, 7: 0x020E}, "min": -999, "max": 9999, "dp": None, "precision": 0}, + "REG.T": {"type": "U16", "index": {0: 0x0300, 1: 0x0301, 2: 0x0302, 3: 0x0303, 4: 0x0304, 5: 0x0305, 6: 0x0306, 7: 0x0307}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "LBA": {"type": "U16", "index": {0: 0x0310, 1: 0x0311, 2: 0x0312, 3: 0x0313, 4: 0x0314, 5: 0x0315, 6: 0x0316, 7: 0x0317}, "min": 1, "max": 2, "dp": None, "precision": 0}, + "PB": {"type": "F32", "index": {0: 0x0320, 1: 0x0322, 2: 0x0324, 3: 0x0326, 4: 0x0328, 5: 0x032A, 6: 0x032C, 7: 0x032E}, "min": 0.001, "max": 9999, "dp": None, "precision": 0}, + "TI": {"type": "U16", "index": {0: 0x0330, 1: 0x0331, 2: 0x0332, 3: 0x0333, 4: 0x0334, 5: 0x0335, 6: 0x0336, 7: 0x0337}, "min": 0, "max": 65535, "dp": None, "precision": 0}, + "TD.TI": {"type": "F32", "index": {0: 0x0340, 1: 0x0342, 2: 0x0344, 3: 0x0346, 4: 0x0348, 5: 0x034A, 6: 0x034C, 7: 0x034E}, "min": 0, "max": 0.3, "dp": None, "precision": 0}, + "I.UPR": {"type": "F32", "index": {0: 0x0350, 1: 0x0352, 2: 0x0354, 3: 0x0356, 4: 0x0358, 5: 0x035A, 6: 0x035C, 7: 0x035E}, "min": -100, "max": 100, "dp": None, "precision": 0}, + "I.MIN": {"type": "F32", "index": {0: 0x0360, 1: 0x0362, 2: 0x0364, 3: 0x0366, 4: 0x0368, 5: 0x036A, 6: 0x036C, 7: 0x036E}, "min": -100, "max": 100, "dp": None, "precision": 0}, + "HYS.C": {"type": "F32", "index": {0: 0x0370, 1: 0x0372, 2: 0x0374, 3: 0x0376, 4: 0x0378, 5: 0x037A, 6: 0x037C, 7: 0x037E}, "min": 0, "max": 9999, "dp": None, "precision": 0}, + "P.RES": {"type": "F32", "index": {0: 0x0400, 1: 0x0402, 2: 0x0404, 3: 0x0406, 4: 0x0408, 5: 0x040A, 6: 0x040C, 7: 0x040E}, "min": 0, "max": 100, "dp": None, "precision": 0}, + "P.UPR": {"type": "F32", "index": {0: 0x0410, 1: 0x0412, 2: 0x0414, 3: 0x0416, 4: 0x0418, 5: 0x041A, 6: 0x041C, 7: 0x041E}, "min": -100, "max": 100, "dp": None, "precision": 0}, + "P.MIN": {"type": "F32", "index": {0: 0x0420, 1: 0x0422, 2: 0x0424, 3: 0x0426, 4: 0x0428, 5: 0x042A, 6: 0x042C, 7: 0x042E}, "min": -100, "max": 100, "dp": None, "precision": 0}, + "DLP": {"type": "U16", "index": {0: 0x0500, 1: 0x0501, 2: 0x0502, 3: 0x0503, 4: 0x0504, 5: 0x0505, 6: 0x0506, 7: 0x0507}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "DB.F": {"type": "F32", "index": {0: 0x0510, 1: 0x0512, 2: 0x0514, 3: 0x0516, 4: 0x0518, 5: 0x051A, 6: 0x051C, 7: 0x051E}, "min": 0.05, "max": 10, "dp": None, "precision": 0}, + "T.STP": {"type": "U16", "index": {0: 0x0520, 1: 0x0521, 2: 0x0522, 3: 0x0523, 4: 0x0524, 5: 0x0525, 6: 0x0526, 7: 0x0527}, "min": 1, "max": 60, "dp": None, "precision": 0}, + "TP.L": {"type": "F32", "index": {0: 0x0530, 1: 0x0532, 2: 0x0534, 3: 0x0536, 4: 0x0538, 5: 0x053A, 6: 0x053C, 7: 0x053E}, "min": 0.1, "max": 10, "dp": None, "precision": 0}, + "TP.H": {"type": "U16", "index": {0: 0x0540, 1: 0x0541, 2: 0x0542, 3: 0x0543, 4: 0x0544, 5: 0x0545, 6: 0x0546, 7: 0x0547}, "min": 1, "max": 900, "dp": None, "precision": 0}, + "TFP": {"type": "F32", "index": {0: 0x0550, 1: 0x0552, 2: 0x0554, 3: 0x0556, 4: 0x0558, 5: 0x055A, 6: 0x055C, 7: 0x055E}, "min": 0.1, "max": 10, "dp": None, "precision": 0}, + "LSP": {"type": "F32", "index": {0: 0x0560, 1: 0x0562, 2: 0x0564, 3: 0x0566, 4: 0x0568, 5: 0x056A, 6: 0x056C, 7: 0x056E}, "min": 0, "max": 100, "dp": None, "precision": 0}, + "THP": {"type": "U16", "index": {0: 0x0600, 1: 0x0601, 2: 0x0602, 3: 0x0603, 4: 0x0604, 5: 0x0605, 6: 0x0606, 7: 0x0607}, "min": 1, "max": 81, "dp": None, "precision": 0}, + "T.L": {"type": "F32", "index": {0: 0x0610, 1: 0x0612, 2: 0x0614, 3: 0x0616, 4: 0x0618, 5: 0x061A, 6: 0x061C, 7: 0x061E}, "min": 0.05, "max": 0.5, "dp": None, "precision": 0}, + "AO.L": {"type": "F32", "index": {0: 0x0620, 1: 0x0622, 2: 0x0624, 3: 0x0626, 4: 0x0628, 5: 0x062A, 6: 0x062C, 7: 0x062E}, "min": -999, "max": 9999, "dp": None, "precision": 0}, + "AO.H": {"type": "F32", "index": {0: 0x0630, 1: 0x0632, 2: 0x0634, 3: 0x0636, 4: 0x0638, 5: 0x063A, 6: 0x063C, 7: 0x063E}, "min": -999, "max": 9999, "dp": None, "precision": 0}, + "ER.ST": {"type": "U16", "index": {0: 0x0640, 1: 0x0641, 2: 0x0642, 3: 0x0643, 4: 0x0644, 5: 0x0645, 6: 0x0646, 7: 0x0647}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "D.LBA": {"type": "F32", "index": {0: 0x0650, 1: 0x0652, 2: 0x0654, 3: 0x0656, 4: 0x0658, 5: 0x065A, 6: 0x065C, 7: 0x065E}, "min": 0.001, "max": 9999, "dp": None, "precision": 0}, + "T.LBA": {"type": "U16", "index": {0: 0x0660, 1: 0x0661, 2: 0x0662, 3: 0x0663, 4: 0x0664, 5: 0x0665, 6: 0x0666, 7: 0x0667}, "min": 1, "max": 600, "dp": None, "precision": 0}, + "BPS": {"type": "U16", "index": {None: 0x1000}, "min": 0, "max": 8, "dp": None, "precision": 0}, + "A.LEN": {"type": "U16", "index": {None: 0x1004}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "ADDR": {"type": "U16", "index": {None: 0x1005}, "min": 0, "max": 255, "dp": None, "precision": 0}, + "RS.DL": {"type": "U16", "index": {None: 0x1006}, "min": 0, "max": 50, "dp": None, "precision": 0}, + "LEN": {"type": "U16", "index": {None: 0x1001}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "PRTY": {"type": "U16", "index": {None: 0x1002}, "min": 0, "max": 0, "dp": None, "precision": 0}, + "SBIT": {"type": "U16", "index": {None: 0x1003}, "min": 0, "max": 1, "dp": None, "precision": 0}, + # "": {"type": "U16", "index": {None: 0x1010}, "min": 0, "max": 0, "dp": None, "precision": 0}, # Команда смены сетевых настроек - название параметра неизвестно + "OR.SP": {"type": "U16", "index": {0: 0x0700, 1: 0x0701, 2: 0x0702, 3: 0x0703, 4: 0x0704, 5: 0x0705, 6: 0x0706, 7: 0x0707, 8: 0x0708, 9: 0x0709, 10: 0x070A, 11: 0x070B, 12: 0x070C, 13: 0x070D, 14: 0x070E, 15: 0x070F, 16: 0x0710, 17: 0x0711, 18: 0x0712, 19: 0x0713, 20: 0x0714, 21: 0x0715, 22: 0x0716, 23: 0x0717}, "min": -999, "max": 9999, "dp": None, "precision": 0}, + "LF.LU": {"type": "F32", "index": {0: 0x0720, 1: 0x0722, 2: 0x0724, 3: 0x0726, 4: 0x0728, 5: 0x072A, 6: 0x072C, 7: 0x072E, 8: 0x0730, 9: 0x0732, 10: 0x0734, 11: 0x0736, 12: 0x0738, 13: 0x073A, 14: 0x073C, 15: 0x073E, 16: 0x0740, 17: 0x0742, 18: 0x0744, 19: 0x0746, 20: 0x0748, 21: 0x074A, 22: 0x074C, 23: 0x074E}, "min": 0, "max": 9999, "dp": None, "precision": 0}, + "SP.LU": {"type": "F32", "index": {0: 0x0750, 1: 0x0752, 2: 0x0754, 3: 0x0756, 4: 0x0758, 5: 0x075A, 6: 0x075C, 7: 0x075E, 8: 0x0760, 9: 0x0762, 10: 0x0764, 11: 0x0766, 12: 0x0768, 13: 0x076A, 14: 0x076C, 15: 0x076E, 16: 0x0770, 17: 0x0772, 18: 0x0774, 19: 0x0776, 20: 0x0778, 21: 0x077A, 22: 0x077C, 23: 0x077E}, "min": -9999, "max": 9999, "dp": None, "precision": 0}, + "B.CH.L": {"type": "F32", "index": {0: 0x0780, 1: 0x0782, 2: 0x0784, 3: 0x0786, 4: 0x0788, 5: 0x078A, 6: 0x078C, 7: 0x078E, 8: 0x0790, 9: 0x0792, 10: 0x0794, 11: 0x0796, 12: 0x0798, 13: 0x079A, 14: 0x079C, 15: 0x079E, 16: 0x07A0, 17: 0x07A2, 18: 0x07A4, 19: 0x07A6, 20: 0x07A8, 21: 0x07AA, 22: 0x07AC, 23: 0x07AE}, "min": -9999, "max": 9999, "dp": None, "precision": 0}, + "B.CH.H": {"type": "F32", "index": {0: 0x07B0, 1: 0x07B2, 2: 0x07B4, 3: 0x07B6, 4: 0x07B8, 5: 0x07BA, 6: 0x07BC, 7: 0x07BE, 8: 0x07C0, 9: 0x07C2, 10: 0x07C4, 11: 0x07C6, 12: 0x07C8, 13: 0x07CA, 14: 0x07CC, 15: 0x07CE, 16: 0x07D0, 17: 0x07D2, 18: 0x07D4, 19: 0x07D6, 20: 0x07D8, 21: 0x07DA, 22: 0x07DC, 23: 0x07DE}, "min": -9999, "max": 9999, "dp": None, "precision": 0}, + "ABSC": {"type": "F32", "index": {0: 0x0800, 1: 0x0802, 2: 0x0804, 3: 0x0806, 4: 0x0808, 5: 0x080A, 6: 0x080C, 7: 0x080E, 8: 0x0810, 9: 0x0812, 10: 0x0814, 11: 0x0816, 12: 0x0818, 13: 0x081A, 14: 0x081C, 15: 0x081E, 16: 0x0820, 17: 0x0822, 18: 0x0824, 19: 0x0826}, "min": -999, "max": 9999, "dp": None, "precision": 0}, + "ORDN": {"type": "F32", "index": {0: 0x0900, 1: 0x0902, 2: 0x0904, 3: 0x0906, 4: 0x0908, 5: 0x090A, 6: 0x090C, 7: 0x090E, 8: 0x0910, 9: 0x0912, 10: 0x0914, 11: 0x0916, 12: 0x0918, 13: 0x091A, 14: 0x091C, 15: 0x091E, 16: 0x0920, 17: 0x0922, 18: 0x0924, 19: 0x0926}, "min": -16383, "max": 16383, "dp": None, "precision": 0}, + }, +} + +# Таблица настроек измерителя-регулятора ТРМ151 +TRM151: OWEN_DEVICE = { + "Owen": {"READ": {"type": "F32+T", "index": {None: None}, "min": -9999, "max": 9999}, + "R.CAL": {"type": "F32+T", "index": {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7}, "min": -9999, "max": 9999}, + "RD.RG": {"type": "F32", "index": {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7}, "min": 0, "max": 1}, + "R.OUT": {"type": "F32", "index": {0: 0, 1: 1}, "min": -1, "max": 1}, + "SET.P": {"type": "F32", "index": {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7}, "min": -999, "max": 9999}, + "R.KEY": {"type": "U16", "index": {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7}, "min": 0, "max": 1}, + "R.PRG": {"type": "U16", "index": {None: None}, "min": 0, "max": 1}, + "R.STP": {"type": "U16", "index": {None: None}, "min": 0, "max": 1}, + "R.ST": {"type": "U16", "index": {None: None}, "min": 0, "max": 1}, + "R.S": {"type": "U16", "index": {None: None}, "min": 0, "max": 2}, + }, +} + +# Таблица настроек измерителя двухканального ТРМ200 +TRM200: OWEN_DEVICE = { + "Owen": {"PV": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "LUPV": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "IN.T": {"type": "U8", "index": {0: 0, 1: 1}, "min": 1, "max": 26}, + "DPT": {"type": "U8", "index": {0: 0, 1: 1}, "min": 0, "max": 1}, + "DP": {"type": "U8", "index": {0: 0, 1: 1}, "min": 0, "max": 3}, + "IN.L": {"type": "F24", "index": {0: 0, 1: 1}, "min": -1999, "max": 9999}, + "IN.H": {"type": "F24", "index": {0: 0, 1: 1}, "min": -1999, "max": 9999}, + "SQR": {"type": "U8", "index": {0: 0, 1: 1}, "min": 0, "max": 1}, + "ILU": {"type": "U8", "index": {0: 0, 1: 1}, "min": 0, "max": 2}, + "SH": {"type": "F24", "index": {0: 0, 1: 1}, "min": -500, "max": 500}, + "KU": {"type": "F24", "index": {0: 0, 1: 1}, "min": 0.500, "max": 2.000}, + "FB": {"type": "F24", "index": {0: 0, 1: 1}, "min": 0, "max": 9999}, + "INF": {"type": "F24", "index": {0: 0, 1: 1}, "min": 0, "max": 999}, + "REST": {"type": "U8", "index": {None: None}, "min": 5, "max": 100}, + "PROT": {"type": "U8", "index": {None: None}, "min": 0, "max": 2}, + "BPS": {"type": "U8", "index": {None: None}, "min": 0, "max": 8}, + "A.LEN": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "ADDR": {"type": "U16", "index": {None: None}, "min": 0, "max": 2047}, + "RSDL": {"type": "U8", "index": {None: None}, "min": 1, "max": 45}, + "LEN": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "PRTY": {"type": "U8", "index": {None: None}, "min": 0, "max": 0}, + "SBIT": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "VER": {"type": "STR", "index": {None: None}, "min": None, "max": None}, + "DEV": {"type": "STR", "index": {None: None}, "min": None, "max": None}, + "PRTL": {"type": "U8", "index": {None: None}, "min": None, "max": None}, + "APLY": {"type": "U8", "index": {None: None}, "min": None, "max": None}, + "INIT": {"type": "U8", "index": {None: None}, "min": None, "max": None}, + "N.ERR": {"type": "U24", "index": {None: None}, "min": 0, "max": 255}, + "ATTR": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "OAPT": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "WTPT": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "EDPT": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + }, + "Modbus": {"STAT": {"type": "U16", "index": {None: 0x0000}, "min": 0, "max": 65535, "dp": None, "precision": 0}, + "PV": {"type": "F32", "index": {0: 0x1009, 1: 0x100B}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "LUPV": {"type": "F32", "index": {0: 0x100D, 1: 0x100F}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "DEV": {"type": "STR", "index": {None: 0x1000}, "min": None, "max": None, "dp": None, "precision": 0}, + "VER": {"type": "STR", "index": {None: 0x1004}, "min": None, "max": None, "dp": None, "precision": 0}, + "PROT": {"type": "U16", "index": {None: 0x0100}, "min": 0, "max": 2, "dp": None, "precision": 0}, + "BPS": {"type": "U16", "index": {None: 0x0101}, "min": 0, "max": 8, "dp": None, "precision": 0}, + "A.LEN": {"type": "U16", "index": {None: 0x0102}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "ADDR": {"type": "U16", "index": {None: 0x0103}, "min": 0, "max": 255, "dp": None, "precision": 0}, + "RSDL": {"type": "U16", "index": {None: 0x0104}, "min": 0, "max": 45, "dp": None, "precision": 0}, + "LEN": {"type": "U16", "index": {None: 0x0105}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "PRTY": {"type": "U16", "index": {None: 0x0106}, "min": 0, "max": 0, "dp": None, "precision": 0}, + "SBIT": {"type": "U16", "index": {None: 0x0107}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "N.ERR": {"type": "U16", "index": {None: 0x0108}, "min": 0, "max": 255, "dp": None, "precision": 0}, + "PRTL": {"type": "U16", "index": {None: 0x0109}, "min": 1, "max": 1, "dp": None, "precision": 0}, + "APLY": {"type": "U16", "index": {None: 0x010A}, "min": 1, "max": 1, "dp": None, "precision": 0}, + "INIT": {"type": "U16", "index": {None: 0x010B}, "min": 1, "max": 1, "dp": None, "precision": 0}, + "IN.T": {"type": "U16", "index": {0: 0x0200, 1: 0x020B}, "min": 1, "max": 26, "dp": None, "precision": 0}, + "DPT": {"type": "U16", "index": {0: 0x0201, 1: 0x020C}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "DP": {"type": "U16", "index": {0: 0x0202, 1: 0x020D}, "min": 0, "max": 3, "dp": None, "precision": 0}, + "IN.L": {"type": "I16", "index": {0: 0x0203, 1: 0x020E}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "IN.H": {"type": "I16", "index": {0: 0x0204, 1: 0x020F}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "SH": {"type": "I16", "index": {0: 0x0205, 1: 0x0210}, "min": -500, "max": 500, "dp": "DP", "precision": 0}, + "KU": {"type": "U16", "index": {0: 0x0206, 1: 0x0211}, "min": 0.5, "max": 2.0, "dp": None, "precision": 3}, + "FB": {"type": "U16", "index": {0: 0x0207, 1: 0x0212}, "min": 0, "max": 9999, "dp": "DP", "precision": 0}, + "INF": {"type": "U16", "index": {0: 0x0208, 1: 0x0213}, "min": 0, "max": 999, "dp": None, "precision": 0}, + "SQR": {"type": "U16", "index": {0: 0x0209, 1: 0x0214}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "ILU": {"type": "U16", "index": {0: 0x020A, 1: 0x0215}, "min": 0, "max": 2, "dp": None, "precision": 0}, + "REST": {"type": "U16", "index": {None: 0x0300}, "min": 5, "max": 100, "dp": None, "precision": 0}, + "OAPT": {"type": "U16", "index": {None: 0x0700}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "WTPT": {"type": "U16", "index": {None: 0x0701}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "EDPT": {"type": "U16", "index": {None: 0x0702}, "min": 0, "max": 1, "dp": None, "precision": 0}, + }, +} + +# Таблица настроек измерителя-регулятора одноканального ТРМ201 +TRM201: OWEN_DEVICE = { + "Owen": {"PV": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "SP": {"type": "F24", "index": {0: 0}, "min": -1999, "max": 9999}, + "IN.T": {"type": "U8", "index": {0: 0}, "min": 1, "max": 26}, + "DPT": {"type": "U8", "index": {0: 0}, "min": 0, "max": 1}, + "DP": {"type": "U8", "index": {0: 0}, "min": 0, "max": 3}, + "IN.L": {"type": "F24", "index": {0: 0}, "min": -1999, "max": 9999}, + "IN.H": {"type": "F24", "index": {0: 0}, "min": -1999, "max": 9999}, + "SQR": {"type": "U8", "index": {0: 0}, "min": 0, "max": 1}, + "SH": {"type": "F24", "index": {0: 0}, "min": -500, "max": 500}, + "KU": {"type": "F24", "index": {0: 0}, "min": 0.500, "max": 2.000}, + "FB": {"type": "F24", "index": {0: 0}, "min": 0, "max": 9999}, + "INF": {"type": "F24", "index": {0: 0}, "min": 0, "max": 999}, + "REST": {"type": "U8", "index": {None: None}, "min": 5, "max": 100}, + "SL.L": {"type": "F24", "index": {0: 0}, "min": -1999, "max": 9999}, + "SL.H": {"type": "F24", "index": {0: 0}, "min": -1999, "max": 9999}, + "CMP": {"type": "U8", "index": {0: 0}, "min": 0, "max": 4}, + "HYS": {"type": "F24", "index": {0: 0}, "min": 0, "max": 9999}, + "DON": {"type": "U8", "index": {0: 0}, "min": 0, "max": 250}, + "DOF": {"type": "U8", "index": {0: 0}, "min": 0, "max": 250}, + "TON": {"type": "U8", "index": {0: 0}, "min": 0, "max": 250}, + "TOF": {"type": "U8", "index": {0: 0}, "min": 0, "max": 250}, + "DAC": {"type": "U8", "index": {0: 0}, "min": 0, "max": 1}, + "CTL": {"type": "U8", "index": {0: 0}, "min": 0, "max": 1}, + "XP": {"type": "F24", "index": {0: 0}, "min": 0.002, "max": 9999}, + "AN.L": {"type": "F24", "index": {0: 0}, "min": -1999, "max": 9999}, + "AN.H": {"type": "F24", "index": {0: 0}, "min": -1999, "max": 9999}, + "OER": {"type": "U8", "index": {0: 0}, "min": 0, "max": 1}, + "PROT": {"type": "U8", "index": {None: None}, "min": 0, "max": 2}, + "BPS": {"type": "U8", "index": {None: None}, "min": 0, "max": 8}, + "A.LEN": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "ADDR": {"type": "U16", "index": {None: None}, "min": 0, "max": 2047}, + "RSDL": {"type": "U8", "index": {None: None}, "min": 1, "max": 45}, + "LEN": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "PRTY": {"type": "U8", "index": {None: None}, "min": 0, "max": 0}, + "SBIT": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "VER": {"type": "STR", "index": {None: None}, "min": None, "max": None}, + "DEV": {"type": "STR", "index": {None: None}, "min": None, "max": None}, + "PRTL": {"type": "U8", "index": {None: None}, "min": None, "max": None}, + "APLY": {"type": "U8", "index": {None: None}, "min": None, "max": None}, + "INIT": {"type": "U8", "index": {None: None}, "min": None, "max": None}, + "N.ERR": {"type": "U24", "index": {None: None}, "min": 0, "max": 255}, + "ATTR": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "R-L": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "R.OUT": {"type": "F24", "index": {None: None}, "min": 0, "max": 1}, + "OAPT": {"type": "U8", "index": {None: None}, "min": 0, "max": 2}, + "WTPT": {"type": "U8", "index": {None: None}, "min": 0, "max": 2}, + "EDPT": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + }, + "Modbus": {"STAT": {"type": "U16", "index": {None: 0x0000}, "min": 0, "max": 65535, "dp": None, "precision": 0}, + "PV": {"type": "F32", "index": {None: 0x1009}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "SP": {"type": "I16", "index": {None: 0x0002}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "R-L": {"type": "U16", "index": {None: 0x0003}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "R.OUT": {"type": "U16", "index": {None: 0x0004}, "min": 0, "max": 1, "dp": None, "precision": 3}, + "DEV": {"type": "STR", "index": {None: 0x1000}, "min": None, "max": None, "dp": None, "precision": 0}, + "VER": {"type": "STR", "index": {None: 0x1004}, "min": None, "max": None, "dp": None, "precision": 0}, + "PROT": {"type": "U16", "index": {None: 0x0100}, "min": 0, "max": 2, "dp": None, "precision": 0}, + "BPS": {"type": "U16", "index": {None: 0x0101}, "min": 0, "max": 8, "dp": None, "precision": 0}, + "A.LEN": {"type": "U16", "index": {None: 0x0102}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "ADDR": {"type": "U16", "index": {None: 0x0103}, "min": 0, "max": 255, "dp": None, "precision": 0}, + "RSDL": {"type": "U16", "index": {None: 0x0104}, "min": 0, "max": 45, "dp": None, "precision": 0}, + "LEN": {"type": "U16", "index": {None: 0x0105}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "PRTY": {"type": "U16", "index": {None: 0x0106}, "min": 0, "max": 0, "dp": None, "precision": 0}, + "SBIT": {"type": "U16", "index": {None: 0x0107}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "N.ERR": {"type": "U16", "index": {None: 0x0108}, "min": 0, "max": 255, "dp": None, "precision": 0}, + "PRTL": {"type": "U16", "index": {None: 0x0109}, "min": 1, "max": 1, "dp": None, "precision": 0}, + "APLY": {"type": "U16", "index": {None: 0x010A}, "min": 1, "max": 1, "dp": None, "precision": 0}, + "INIT": {"type": "U16", "index": {None: 0x010B}, "min": 1, "max": 1, "dp": None, "precision": 0}, + "IN.T": {"type": "U16", "index": {None: 0x0200}, "min": 1, "max": 26, "dp": None, "precision": 0}, + "DPT": {"type": "U16", "index": {None: 0x0201}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "DP": {"type": "U16", "index": {None: 0x0202}, "min": 0, "max": 3, "dp": None, "precision": 0}, + "IN.L": {"type": "I16", "index": {None: 0x0203}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "IN.H": {"type": "I16", "index": {None: 0x0204}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "SH": {"type": "I16", "index": {None: 0x0205}, "min": -500, "max": 500, "dp": "DP", "precision": 0}, + "KU": {"type": "U16", "index": {None: 0x0206}, "min": 0.5, "max": 2.00, "dp": None, "precision": 3}, + "FB": {"type": "U16", "index": {None: 0x0207}, "min": 0, "max": 9999, "dp": "DP", "precision": 0}, + "INF": {"type": "U16", "index": {None: 0x0208}, "min": 0, "max": 999, "dp": None, "precision": 0}, + "SQR": {"type": "U16", "index": {None: 0x0209}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "REST": {"type": "U16", "index": {None: 0x0300}, "min": 5, "max": 100, "dp": None, "precision": 0}, + "SL.L": {"type": "I16", "index": {None: 0x0400}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "SL.H": {"type": "I16", "index": {None: 0x0401}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "CMP": {"type": "U16", "index": {None: 0x0402}, "min": 0, "max": 4, "dp": None, "precision": 0}, + "HYS": {"type": "U16", "index": {None: 0x0403}, "min": 0, "max": 9999, "dp": "DP", "precision": 0}, + "DON": {"type": "U16", "index": {None: 0x0404}, "min": 0, "max": 250, "dp": None, "precision": 0}, + "DOF": {"type": "U16", "index": {None: 0x0405}, "min": 0, "max": 250, "dp": None, "precision": 0}, + "TON": {"type": "U16", "index": {None: 0x0406}, "min": 0, "max": 250, "dp": None, "precision": 0}, + "TOF": {"type": "U16", "index": {None: 0x0407}, "min": 0, "max": 250, "dp": None, "precision": 0}, + "OER": {"type": "U16", "index": {None: 0x0408}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "DAC": {"type": "U16", "index": {None: 0x0409}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "AN.L": {"type": "I16", "index": {None: 0x040A}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "AN.H": {"type": "I16", "index": {None: 0x040B}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "CTL": {"type": "U16", "index": {None: 0x040C}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "XP": {"type": "U16", "index": {None: 0x040D}, "min": 0.002, "max": 9999, "dp": "DP", "precision": 0}, + "OAPT": {"type": "U16", "index": {None: 0x0700}, "min": 0, "max": 2, "dp": None, "precision": 0}, + "WTPT": {"type": "U16", "index": {None: 0x0701}, "min": 0, "max": 2, "dp": None, "precision": 0}, + "EDPT": {"type": "U16", "index": {None: 0x0702}, "min": 0, "max": 1, "dp": None, "precision": 0}, + }, +} + +# Таблица настроек измерителя-регулятора двухканального ТРМ202 +TRM202: OWEN_DEVICE = { + "Owen": {"PV": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "LUPV": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "SP": {"type": "F24", "index": {0: 0, 1: 1}, "min": -1999, "max": 9999}, + "IN.T": {"type": "U8", "index": {0: 0, 1: 1}, "min": 1, "max": 26}, + "DPT": {"type": "U8", "index": {0: 0, 1: 1}, "min": 0, "max": 1}, + "DP": {"type": "U8", "index": {0: 0, 1: 1}, "min": 0, "max": 3}, + "IN.L": {"type": "F24", "index": {0: 0, 1: 1}, "min": -1999, "max": 9999}, + "IN.H": {"type": "F24", "index": {0: 0, 1: 1}, "min": -1999, "max": 9999}, + "SQR": {"type": "U8", "index": {0: 0, 1: 1}, "min": 0, "max": 1}, + "ILU": {"type": "U8", "index": {0: 0, 1: 1}, "min": 0, "max": 2}, + "SH": {"type": "F24", "index": {0: 0, 1: 1}, "min": -500, "max": 500}, + "KU": {"type": "F24", "index": {0: 0, 1: 1}, "min": 0.500, "max": 2.000}, + "FB": {"type": "F24", "index": {0: 0, 1: 1}, "min": 0, "max": 9999}, + "INF": {"type": "F24", "index": {0: 0, 1: 1}, "min": 0, "max": 999}, + "DISP": {"type": "U8", "index": {None: None}, "min": 0, "max": 2}, + "REST": {"type": "U8", "index": {None: None}, "min": 5, "max": 100}, + "SL.L": {"type": "F24", "index": {0: 0, 1: 1}, "min": -1999, "max": 9999}, + "SL.H": {"type": "F24", "index": {0: 0, 1: 1}, "min": -1999, "max": 9999}, + "CMP": {"type": "U8", "index": {0: 0, 1: 1}, "min": 0, "max": 4}, + "HYS": {"type": "F24", "index": {0: 0, 1: 1}, "min": 0, "max": 9999}, + "DON": {"type": "U8", "index": {0: 0, 1: 1}, "min": 0, "max": 250}, + "DOF": {"type": "U8", "index": {0: 0, 1: 1}, "min": 0, "max": 250}, + "TON": {"type": "U8", "index": {0: 0, 1: 1}, "min": 0, "max": 250}, + "TOF": {"type": "U8", "index": {0: 0, 1: 1}, "min": 0, "max": 250}, + "DAC": {"type": "U8", "index": {0: 0, 1: 1}, "min": 0, "max": 1}, + "CTL": {"type": "U8", "index": {0: 0, 1: 1}, "min": 0, "max": 1}, + "XP": {"type": "F24", "index": {0: 0, 1: 1}, "min": 0.002, "max": 9999}, + "AN.L": {"type": "F24", "index": {0: 0, 1: 1}, "min": -1999, "max": 9999}, + "AN.H": {"type": "F24", "index": {0: 0, 1: 1}, "min": -1999, "max": 9999}, + "OER": {"type": "U8", "index": {0: 0, 1: 1}, "min": 0, "max": 1}, + "PROT": {"type": "U8", "index": {None: None}, "min": 0, "max": 2}, + "BPS": {"type": "U8", "index": {None: None}, "min": 0, "max": 8}, + "A.LEN": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "ADDR": {"type": "U16", "index": {None: None}, "min": 0, "max": 2047}, + "RSDL": {"type": "U8", "index": {None: None}, "min": 1, "max": 45}, + "LEN": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "PRTY": {"type": "U8", "index": {None: None}, "min": 0, "max": 0}, + "SBIT": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "VER": {"type": "STR", "index": {None: None}, "min": None, "max": None}, + "DEV": {"type": "STR", "index": {None: None}, "min": None, "max": None}, + "PRTL": {"type": "U8", "index": {None: None}, "min": None, "max": None}, + "APLY": {"type": "U8", "index": {None: None}, "min": None, "max": None}, + "INIT": {"type": "U8", "index": {None: None}, "min": None, "max": None}, + "N.ERR": {"type": "U24", "index": {None: None}, "min": 0, "max": 255}, + "ATTR": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "R-L": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "R.OUT": {"type": "F24", "index": {None: None}, "min": 0, "max": 1}, + "OAPT": {"type": "U8", "index": {None: None}, "min": 0, "max": 2}, + "WTPT": {"type": "U8", "index": {None: None}, "min": 0, "max": 3}, + "EDPT": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + }, + "Modbus": {"STAT": {"type": "U16", "index": {None: 0x0000}, "min": 0, "max": 65535, "dp": None, "precision": 0}, + "PV": {"type": "F32", "index": {0: 0x1009, 1: 0x100B}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "LUPV": {"type": "F32", "index": {0: 0x100D, 1: 0x100F}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "SP": {"type": "I16", "index": {0: 0x0005, 1: 0x0006}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "R-L": {"type": "U16", "index": {0: 0x0007, 1: 0x0008}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "R.OUT": {"type": "U16", "index": {0: 0x0009, 1: 0x000A}, "min": 0, "max": 1, "dp": None, "precision": 3}, + "DEV": {"type": "STR", "index": {None: 0x1000}, "min": None, "max": None, "dp": None, "precision": 0}, + "VER": {"type": "STR", "index": {None: 0x1004}, "min": None, "max": None, "dp": None, "precision": 0}, + "PROT": {"type": "U16", "index": {None: 0x0100}, "min": 0, "max": 2, "dp": None, "precision": 0}, + "BPS": {"type": "U16", "index": {None: 0x0101}, "min": 0, "max": 8, "dp": None, "precision": 0}, + "A.LEN": {"type": "U16", "index": {None: 0x0102}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "ADDR": {"type": "U16", "index": {None: 0x0103}, "min": 0, "max": 255, "dp": None, "precision": 0}, + "RSDL": {"type": "U16", "index": {None: 0x0104}, "min": 0, "max": 45, "dp": None, "precision": 0}, + "LEN": {"type": "U16", "index": {None: 0x0105}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "PRTY": {"type": "U16", "index": {None: 0x0106}, "min": 0, "max": 0, "dp": None, "precision": 0}, + "SBIT": {"type": "U16", "index": {None: 0x0107}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "N.ERR": {"type": "U16", "index": {None: 0x0108}, "min": 0, "max": 255, "dp": None, "precision": 0}, + "PRTL": {"type": "U16", "index": {None: 0x0109}, "min": 1, "max": 1, "dp": None, "precision": 0}, + "APLY": {"type": "U16", "index": {None: 0x010A}, "min": 1, "max": 1, "dp": None, "precision": 0}, + "INIT": {"type": "U16", "index": {None: 0x010B}, "min": 1, "max": 1, "dp": None, "precision": 0}, + "IN.T": {"type": "U16", "index": {0: 0x0200, 1: 0x020B}, "min": 1, "max": 26, "dp": None, "precision": 0}, + "DPT": {"type": "U16", "index": {0: 0x0201, 1: 0x020C}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "DP": {"type": "U16", "index": {0: 0x0202, 1: 0x020D}, "min": 0, "max": 3, "dp": None, "precision": 0}, + "IN.L": {"type": "I16", "index": {0: 0x0203, 1: 0x020E}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "IN.H": {"type": "I16", "index": {0: 0x0204, 1: 0x020F}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "SH": {"type": "I16", "index": {0: 0x0205, 1: 0x0210}, "min": -500, "max": 500, "dp": "DP", "precision": 0}, + "KU": {"type": "U16", "index": {0: 0x0206, 1: 0x0211}, "min": 0.50, "max": 2.00, "dp": None, "precision": 3}, + "FB": {"type": "U16", "index": {0: 0x0207, 1: 0x0212}, "min": 0, "max": 9999, "dp": "DP", "precision": 0}, + "INF": {"type": "U16", "index": {0: 0x0208, 1: 0x0213}, "min": 0, "max": 999, "dp": None, "precision": 0}, + "SQR": {"type": "U16", "index": {0: 0x0209, 1: 0x0214}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "ILU": {"type": "U16", "index": {0: 0x020A, 1: 0x0215}, "min": 0, "max": 2, "dp": None, "precision": 0}, + "REST": {"type": "U16", "index": {None: 0x0300}, "min": 5, "max": 100, "dp": None, "precision": 0}, + "DISP": {"type": "U16", "index": {None: 0x0301}, "min": 0, "max": 2, "dp": None, "precision": 0}, + "SL.L": {"type": "I16", "index": {0: 0x0400, 1: 0x040E}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "SL.H": {"type": "I16", "index": {0: 0x0401, 1: 0x040F}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "CMP": {"type": "U16", "index": {0: 0x0402, 1: 0x0410}, "min": 0, "max": 4, "dp": None, "precision": 0}, + "HYS": {"type": "U16", "index": {0: 0x0403, 1: 0x0411}, "min": 0, "max": 9999, "dp": "DP", "precision": 0}, + "DON": {"type": "U16", "index": {0: 0x0404, 1: 0x0412}, "min": 0, "max": 250, "dp": None, "precision": 0}, + "DOF": {"type": "U16", "index": {0: 0x0405, 1: 0x0413}, "min": 0, "max": 250, "dp": None, "precision": 0}, + "TON": {"type": "U16", "index": {0: 0x0406, 1: 0x0414}, "min": 0, "max": 250, "dp": None, "precision": 0}, + "TOF": {"type": "U16", "index": {0: 0x0407, 1: 0x0415}, "min": 0, "max": 250, "dp": None, "precision": 0}, + "OER": {"type": "U16", "index": {0: 0x0408, 1: 0x0416}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "DAC": {"type": "U16", "index": {0: 0x0409, 1: 0x0417}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "AN.L": {"type": "I16", "index": {0: 0x040A, 1: 0x0418}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "AN.H": {"type": "I16", "index": {0: 0x040B, 1: 0x0419}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "CTL": {"type": "U16", "index": {0: 0x040C, 1: 0x041A}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "XP": {"type": "U16", "index": {0: 0x040D, 1: 0x041B}, "min": 0.002, "max": 9999, "dp": "DP", "precision": 0}, + "OAPT": {"type": "U16", "index": {None: 0x0700}, "min": 0, "max": 2, "dp": None, "precision": 0}, + "WTPT": {"type": "U16", "index": {None: 0x0701}, "min": 0, "max": 3, "dp": None, "precision": 0}, + "EDPT": {"type": "U16", "index": {None: 0x0702}, "min": 0, "max": 1, "dp": None, "precision": 0}, + }, +} + +# Таблица настроек измерителя-ПИД-регулятора ТРМ210 +TRM210: OWEN_DEVICE = { + "Owen": {"PV": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "SP": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "R-S": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "AT": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "O": {"type": "F24", "index": {None: None}, "min": 0, "max": 100}, + "IN-T": {"type": "U8", "index": {None: None}, "min": 1, "max": 26}, + "DPT": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "DP": {"type": "U8", "index": {None: None}, "min": 0, "max": 3}, + "IN-L": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "IN-H": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "SL-L": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "SL-H": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "SH": {"type": "F24", "index": {None: None}, "min": -500, "max": 500}, + "KU": {"type": "F24", "index": {None: None}, "min": 0.500, "max": 2.000}, + "INF": {"type": "F24", "index": {None: None}, "min": 0, "max": 999}, + "FB": {"type": "F24", "index": {None: None}, "min": 0, "max": 9999}, + "AN-L": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "AN-H": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "EV-1": {"type": "U8", "index": {None: None}, "min": 0, "max": 2}, + "ALT": {"type": "U8", "index": {None: None}, "min": 0, "max": 11}, + "AL-D": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "AL-H": {"type": "F24", "index": {None: None}, "min": 0, "max": 9999}, + "OREU": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "CP": {"type": "U8", "index": {None: None}, "min": 1, "max": 250}, + "VSP": {"type": "F24", "index": {None: None}, "min": 0, "max": 9999}, + "CNTL": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "HYST": {"type": "F24", "index": {None: None}, "min": 0, "max": 9999}, + "ONST": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "ONER": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "RAMP": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "P": {"type": "F24", "index": {None: None}, "min": 0.001, "max": 9999}, + "I": {"type": "F24", "index": {None: None}, "min": 0, "max": 3999}, + "D": {"type": "F24", "index": {None: None}, "min": 0, "max": 3999}, + "DB": {"type": "F24", "index": {None: None}, "min": 0, "max": 200}, + "OL-L": {"type": "F24", "index": {None: None}, "min": 0, "max": 100}, + "OL-H": {"type": "F24", "index": {None: None}, "min": 0, "max": 100}, + "ORL": {"type": "F24", "index": {None: None}, "min": 0.2, "max": 100}, + "MVER": {"type": "F24", "index": {None: None}, "min": 0, "max": 100}, + "MVST": {"type": "F24", "index": {None: None}, "min": 0, "max": 100}, + "MDST": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "LBA": {"type": "U16", "index": {None: None}, "min": 0, "max": 9999}, + "LBAB": {"type": "F24", "index": {None: None}, "min": 0, "max": 9999}, + "PROT": {"type": "U8", "index": {None: None}, "min": 0, "max": 2}, + "ADDR": {"type": "U16", "index": {None: None}, "min": 0, "max": 2047}, + "RSDL": {"type": "U8", "index": {None: None}, "min": 1, "max": 45}, + "A.LEN": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "BPS": {"type": "U8", "index": {None: None}, "min": 0, "max": 8}, + "LEN": {"type": "U8", "index": {None: None}, "min": 1, "max": 1}, + "PRTY": {"type": "U8", "index": {None: None}, "min": 0, "max": 0}, + "SBIT": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "VER": {"type": "STR", "index": {None: None}, "min": None, "max": None}, + "DEV": {"type": "STR", "index": {None: None}, "min": None, "max": None}, + "PRTL": {"type": "U8", "index": {None: None}, "min": None, "max": None}, + "APLY": {"type": "U8", "index": {None: None}, "min": None, "max": None}, + "INIT": {"type": "U8", "index": {None: None}, "min": None, "max": None}, + "N.ERR": {"type": "U24", "index": {None: None}, "min": 0, "max": 255}, + "ATTR": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "R-L": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "R.OUT": {"type": "F24", "index": {None: None}, "min": 0, "max": 1}, + "EDPT": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + }, + "Modbus": {"STAT": {"type": "U16", "index": {None: 0x0000}, "min": 0, "max": 65535, "dp": None, "precision": 0}, + "PV": {"type": "F32", "index": {None: 0x1009}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "SP": {"type": "I16", "index": {None: 0x0002}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "SET.P": {"type": "F32", "index": {None: 0x100D}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "O": {"type": "F32", "index": {None: 0x100F}, "min": 0, "max": 100, "dp": None, "precision": 0}, + "R-L": {"type": "U16", "index": {None: 0x0005}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "R.OUT": {"type": "U16", "index": {None: 0x0006}, "min": 0, "max": 1, "dp": None, "precision": 3}, + "R-S": {"type": "U16", "index": {None: 0x0007}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "AT": {"type": "U16", "index": {None: 0x0008}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "DEV": {"type": "STR", "index": {None: 0x1000}, "min": None, "max": None, "dp": None, "precision": 0}, + "VER": {"type": "STR", "index": {None: 0x1004}, "min": None, "max": None, "dp": None, "precision": 0}, + "PROT": {"type": "U16", "index": {None: 0x0100}, "min": 0, "max": 2, "dp": None, "precision": 0}, + "BPS": {"type": "U16", "index": {None: 0x0101}, "min": 0, "max": 8, "dp": None, "precision": 0}, + "A.LEN": {"type": "U16", "index": {None: 0x0102}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "ADDR": {"type": "U16", "index": {None: 0x0103}, "min": 1, "max": 247, "dp": None, "precision": 0}, + "RSDL": {"type": "U16", "index": {None: 0x0104}, "min": 0, "max": 45, "dp": None, "precision": 0}, + "LEN": {"type": "U16", "index": {None: 0x0105}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "PRTY": {"type": "U16", "index": {None: 0x0106}, "min": 0, "max": 0, "dp": None, "precision": 0}, + "SBIT": {"type": "U16", "index": {None: 0x0107}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "N.ERR": {"type": "U16", "index": {None: 0x0108}, "min": 0, "max": 255, "dp": None, "precision": 0}, + "PRTL": {"type": "U16", "index": {None: 0x0109}, "min": 1, "max": 1, "dp": None, "precision": 0}, + "APLY": {"type": "U16", "index": {None: 0x010A}, "min": 1, "max": 1, "dp": None, "precision": 0}, + "INIT": {"type": "U16", "index": {None: 0x010B}, "min": 1, "max": 1, "dp": None, "precision": 0}, + "IN-T": {"type": "U16", "index": {None: 0x0200}, "min": 1, "max": 26, "dp": None, "precision": 0}, + "DPT": {"type": "U16", "index": {None: 0x0201}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "DP": {"type": "U16", "index": {None: 0x0202}, "min": 0, "max": 3, "dp": None, "precision": 0}, + "IN-L": {"type": "I16", "index": {None: 0x0203}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "IN-H": {"type": "I16", "index": {None: 0x0204}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "SH": {"type": "I16", "index": {None: 0x0205}, "min": -500, "max": 500, "dp": "DP", "precision": 0}, + "KU": {"type": "U16", "index": {None: 0x0206}, "min": 0.5, "max": 2.0, "dp": None, "precision": 3}, + "FB": {"type": "U16", "index": {None: 0x0207}, "min": 0, "max": 9999, "dp": "DP", "precision": 0}, + "INF": {"type": "U16", "index": {None: 0x0208}, "min": 0, "max": 999, "dp": None, "precision": 0}, + "SL-L": {"type": "I16", "index": {None: 0x0300}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "SL-H": {"type": "I16", "index": {None: 0x0301}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "OREU": {"type": "U16", "index": {None: 0x0302}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "CNTL": {"type": "U16", "index": {None: 0x0303}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "CP": {"type": "U16", "index": {None: 0x0304}, "min": 1, "max": 250, "dp": None, "precision": 0}, + "RAMP": {"type": "U16", "index": {None: 0x0305}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "P": {"type": "U16", "index": {None: 0x0306}, "min": 1, "max": 9999, "dp": "DP", "precision": 0}, + "I": {"type": "U16", "index": {None: 0x0307}, "min": 0, "max": 3999, "dp": None, "precision": 0}, + "D": {"type": "U16", "index": {None: 0x0308}, "min": 0, "max": 3999, "dp": None, "precision": 0}, + "DB": {"type": "U16", "index": {None: 0x0309}, "min": 0, "max": 200, "dp": "DP", "precision": 0}, + "VSP": {"type": "U16", "index": {None: 0x030A}, "min": 0, "max": 9999, "dp": "DP", "precision": 0}, + "OL-L": {"type": "U16", "index": {None: 0x030B}, "min": 0, "max": 100, "dp": None, "precision": 0}, + "OL-H": {"type": "U16", "index": {None: 0x030C}, "min": 0, "max": 100, "dp": None, "precision": 0}, + "ORL": {"type": "U16", "index": {None: 0x030D}, "min": 0.2, "max": 100, "dp": None, "precision": 1}, + "MVER": {"type": "U16", "index": {None: 0x030E}, "min": 0, "max": 100, "dp": None, "precision": 0}, + "MDST": {"type": "U16", "index": {None: 0x030F}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "MVST": {"type": "U16", "index": {None: 0x0310}, "min": 0, "max": 100, "dp": None, "precision": 0}, + "HYST": {"type": "U16", "index": {None: 0x0311}, "min": 0, "max": 9999, "dp": "DP", "precision": 0}, + "ONST": {"type": "U16", "index": {None: 0x0312}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "ONER": {"type": "U16", "index": {None: 0x0313}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "EV-1": {"type": "U16", "index": {None: 0x0400}, "min": 0, "max": 2, "dp": None, "precision": 0}, + "LBA": {"type": "U16", "index": {None: 0x0401}, "min": 0, "max": 9999, "dp": None, "precision": 0}, + "LBAB": {"type": "U16", "index": {None: 0x0402}, "min": 0, "max": 9999, "dp": "DP", "precision": 0}, + "ALT": {"type": "U16", "index": {None: 0x0403}, "min": 0, "max": 11, "dp": None, "precision": 0}, + "AL-D": {"type": "U16", "index": {None: 0x0404}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "AL-H": {"type": "U16", "index": {None: 0x0405}, "min": 0, "max": 9999, "dp": "DP", "precision": 0}, + "AN-L": {"type": "I16", "index": {None: 0x0406}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "AN-H": {"type": "I16", "index": {None: 0x0407}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + }, +} + +# Таблица настроек измерителя-ПИД-регулятора ТРМ212 +TRM212: OWEN_DEVICE = { + "Owen": {"PV": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "LUPV": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "SP": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "SET.P": {"type": "F24", "index": {None: None}, "min": -1999, "max": 9999}, + "R-S": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "AT": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "O": {"type": "F24", "index": {None: None}, "min": 0, "max": 100}, + "IN.T": {"type": "U8", "index": {0: 0, 1: 1}, "min": 1, "max": 26}, + "DPT": {"type": "U8", "index": {0: 0, 1: 1}, "min": 0, "max": 1}, + "DP": {"type": "U8", "index": {0: 0, 1: 1}, "min": 0, "max": 3}, + "IN.L": {"type": "F24", "index": {0: 0, 1: 1}, "min": -1999, "max": 9999}, + "IN.H": {"type": "F24", "index": {0: 0, 1: 1}, "min": -1999, "max": 9999}, + "SQR": {"type": "U8", "index": {0: 0, 1: 1}, "min": 0, "max": 2}, + "SH": {"type": "F24", "index": {0: 0, 1: 1}, "min": -500, "max": 500}, + "KU": {"type": "F24", "index": {0: 0, 1: 1}, "min": 0.500, "max": 2.000}, + "FB": {"type": "F24", "index": {0: 0, 1: 1}, "min": 0, "max": 9999}, + "INF": {"type": "F24", "index": {0: 0, 1: 1}, "min": 0, "max": 9999}, + "INP2": {"type": "U8", "index": {None: None}, "min": 0, "max": 4}, + "CALC": {"type": "U8", "index": {None: None}, "min": 0, "max": 34}, + "KPV1": {"type": "F24", "index": {None: None}, "min": -19.99, "max": 99.99}, + "KPV2": {"type": "F24", "index": {None: None}, "min": -19.99, "max": 99.99}, + "SL-L": {"type": "F24", "index": {None: None}, "min": -1999, "max": 3000}, + "SL-H": {"type": "F24", "index": {None: None}, "min": -1999, "max": 3000}, + "OREU": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "RAMP": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "PV0": {"type": "F24", "index": {None: None}, "min": -100, "max": 2000}, + "P": {"type": "F24", "index": {None: None}, "min": 0.001, "max": 9999}, + "I": {"type": "F24", "index": {None: None}, "min": 0, "max": 3999}, + "D": {"type": "F24", "index": {None: None}, "min": 0, "max": 3999}, + "KA": {"type": "F24", "index": {None: None}, "min": 0, "max": 9999}, + "DB": {"type": "F24", "index": {None: None}, "min": 0, "max": 200}, + "VSP": {"type": "F24", "index": {None: None}, "min": 0, "max": 9999}, + "OL-L": {"type": "F24", "index": {None: None}, "min": 0, "max": 100}, + "OL-H": {"type": "F24", "index": {None: None}, "min": 0, "max": 100}, + "MVER": {"type": "F24", "index": {None: None}, "min": 0, "max": 100}, + "MVST": {"type": "F24", "index": {None: None}, "min": 0, "max": 100}, + "MDST": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "LBA": {"type": "U16", "index": {None: None}, "min": 0, "max": 9999}, + "LBAB": {"type": "F24", "index": {None: None}, "min": 0, "max": 9999}, + "ALT": {"type": "U8", "index": {None: None}, "min": 0, "max": 14}, + "AL-D": {"type": "F24", "index": {None: None}, "min": -1999, "max": 3000}, + "AL-H": {"type": "F24", "index": {None: None}, "min": 0, "max": 3000}, + "V.MOT": {"type": "F24", "index": {None: None}, "min": 5, "max": 999}, + "V.DB": {"type": "F24", "index": {None: None}, "min": 0, "max": 9999}, + "V.GAP": {"type": "F24", "index": {None: None}, "min": 0, "max": 10}, + "V.REV": {"type": "F24", "index": {None: None}, "min": 0, "max": 10}, + "V.TOF": {"type": "U8", "index": {None: None}, "min": 0, "max": 10}, + "DIS1": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "DIS2": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "DIS3": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "DIS4": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "DIS5": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "RET": {"type": "U8", "index": {None: None}, "min": 5, "max": 100}, + "NODE": {"type": "U8", "index": {None: None}, "min": 1, "max": 10}, + "X": {"type": "F24", "index": {1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, 10: 10}, "min": -1999, "max": 3000}, + "Y": {"type": "F24", "index": {1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, 10: 10}, "min": -1999, "max": 3000}, + "PROT": {"type": "U8", "index": {None: None}, "min": 0, "max": 2}, + "BPS": {"type": "U8", "index": {None: None}, "min": 0, "max": 8}, + "A.LEN": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "ADDR": {"type": "U16", "index": {None: None}, "min": 0, "max": 2047}, + "RSDL": {"type": "U8", "index": {None: None}, "min": 1, "max": 45}, + "LEN": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "PRTY": {"type": "U8", "index": {None: None}, "min": 0, "max": 0}, + "SBIT": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "VER": {"type": "STR", "index": {None: None}, "min": None, "max": None}, + "DEV": {"type": "STR", "index": {None: None}, "min": None, "max": None}, + "PRTL": {"type": "U8", "index": {None: None}, "min": None, "max": None}, + "APLY": {"type": "U8", "index": {None: None}, "min": None, "max": None}, + "INIT": {"type": "U8", "index": {None: None}, "min": None, "max": None}, + "N.ERR": {"type": "U24", "index": {None: None}, "min": 0, "max": 255}, + "ATTR": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "R-L": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "R.OUT": {"type": "F24", "index": {None: None}, "min": -1, "max": 1}, + "OAPT": {"type": "U8", "index": {None: None}, "min": 0, "max": 2}, + "WTPT": {"type": "U8", "index": {None: None}, "min": 0, "max": 3}, + "EDPT": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + }, + "Modbus": {"STAT": {"type": "U16", "index": {None: 0x0000}, "min": 0, "max": 65535, "dp": None, "precision": 0}, + "PV": {"type": "F32", "index": {0: 0x1009, 1: 0x100B}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "LUPV": {"type": "F32", "index": {None: 0x100D}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "SP": {"type": "I16", "index": {None: 0x0004}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "SET.P": {"type": "F32", "index": {None: 0x1011}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "O": {"type": "F32", "index": {None: 0x1013}, "min": 0, "max": 100, "dp": None, "precision": 0}, + "R-L": {"type": "U16", "index": {None: 0x0007}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "R.OUT": {"type": "U16", "index": {None: 0x0008}, "min": -1.0, "max": 1.0, "dp": None, "precision": 3}, + "R-S": {"type": "U16", "index": {None: 0x0009}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "AT": {"type": "U16", "index": {None: 0x000A}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "DEV": {"type": "STR", "index": {None: 0x1000}, "min": None, "max": None, "dp": None, "precision": 0}, + "VER": {"type": "STR", "index": {None: 0x1004}, "min": None, "max": None, "dp": None, "precision": 0}, + "PROT": {"type": "U16", "index": {None: 0x0100}, "min": 0, "max": 2, "dp": None, "precision": 0}, + "BPS": {"type": "U16", "index": {None: 0x0101}, "min": 0, "max": 8, "dp": None, "precision": 0}, + "A.LEN": {"type": "U16", "index": {None: 0x0102}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "ADDR": {"type": "U16", "index": {None: 0x0103}, "min": 1, "max": 247, "dp": None, "precision": 0}, + "RSDL": {"type": "U16", "index": {None: 0x0104}, "min": 0, "max": 45, "dp": None, "precision": 0}, + "LEN": {"type": "U16", "index": {None: 0x0105}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "PRTY": {"type": "U16", "index": {None: 0x0106}, "min": 0, "max": 0, "dp": None, "precision": 0}, + "SBIT": {"type": "U16", "index": {None: 0x0107}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "N.ERR": {"type": "U16", "index": {None: 0x0108}, "min": 0, "max": 255, "dp": None, "precision": 0}, + "PRTL": {"type": "U16", "index": {None: 0x0109}, "min": 1, "max": 1, "dp": None, "precision": 0}, + "APLY": {"type": "U16", "index": {None: 0x010A}, "min": 1, "max": 1, "dp": None, "precision": 0}, + "INIT": {"type": "U16", "index": {None: 0x010B}, "min": 1, "max": 1, "dp": None, "precision": 0}, + "IN.T": {"type": "U16", "index": {0: 0x0200, 1: 0x020A}, "min": 1, "max": 26, "dp": None, "precision": 0}, + "DPT": {"type": "U16", "index": {0: 0x0201, 1: 0x020B}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "DP": {"type": "U16", "index": {0: 0x0202, 1: 0x020C}, "min": 0, "max": 3, "dp": None, "precision": 0}, + "IN.L": {"type": "I16", "index": {0: 0x0203, 1: 0x020D}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "IN.H": {"type": "I16", "index": {0: 0x0204, 1: 0x020E}, "min": -1999, "max": 9999, "dp": "DP", "precision": 0}, + "SH": {"type": "I16", "index": {0: 0x0205, 1: 0x020F}, "min": -500, "max": 500, "dp": "DP", "precision": 0}, + "KU": {"type": "U16", "index": {0: 0x0206, 1: 0x0210}, "min": 0.5, "max": 2.0, "dp": None, "precision": 3}, + "FB": {"type": "U16", "index": {0: 0x0207, 1: 0x0211}, "min": 0, "max": 9999, "dp": "DP", "precision": 0}, + "INF": {"type": "U16", "index": {0: 0x0208, 1: 0x0212}, "min": 0, "max": 999, "dp": None, "precision": 0}, + "SQR": {"type": "U16", "index": {0: 0x0209, 1: 0x0213}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "INP2": {"type": "U16", "index": {None: 0x0300}, "min": 0, "max": 4, "dp": None, "precision": 0}, + "CALC": {"type": "U16", "index": {None: 0x0301}, "min": 0, "max": 3, "dp": None, "precision": 0}, + "KPV1": {"type": "I16", "index": {None: 0x0302}, "min": -19.99, "max": 99.99, "dp": None, "precision": 2}, + "KPV2": {"type": "I16", "index": {None: 0x0303}, "min": -19.99, "max": 99.99, "dp": None, "precision": 2}, + "SL-L": {"type": "I16", "index": {None: 0x0304}, "min": -1999, "max": 3000, "dp": "DP", "precision": 0}, + "SL-H": {"type": "I16", "index": {None: 0x0305}, "min": -1999, "max": 3000, "dp": "DP", "precision": 0}, + "OREU": {"type": "U16", "index": {None: 0x0306}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "PV0": {"type": "I16", "index": {None: 0x0307}, "min": -100, "max": 2000, "dp": None, "precision": 0}, + "RAMP": {"type": "U16", "index": {None: 0x0308}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "P": {"type": "U16", "index": {None: 0x0309}, "min": 1, "max": 9999, "dp": "DP", "precision": 0}, + "I": {"type": "U16", "index": {None: 0x030A}, "min": 0, "max": 3999, "dp": None, "precision": 0}, + "D": {"type": "U16", "index": {None: 0x030B}, "min": 0, "max": 3999, "dp": None, "precision": 0}, + "DB": {"type": "U16", "index": {None: 0x030C}, "min": 0, "max": 200, "dp": "DP", "precision": 0}, + "VSP": {"type": "U16", "index": {None: 0x030D}, "min": 0, "max": 9999, "dp": "DP", "precision": 0}, + "OL-L": {"type": "U16", "index": {None: 0x030E}, "min": 0, "max": 100, "dp": None, "precision": 0}, + "OL-H": {"type": "U16", "index": {None: 0x030F}, "min": 0, "max": 100, "dp": None, "precision": 0}, + "LBA": {"type": "U16", "index": {None: 0x0310}, "min": 0, "max": 9999, "dp": None, "precision": 0}, + "LBAB": {"type": "U16", "index": {None: 0x0311}, "min": 0, "max": 9999, "dp": "DP", "precision": 0}, + "MVER": {"type": "U16", "index": {None: 0x0312}, "min": 0, "max": 100, "dp": None, "precision": 0}, + "MVST": {"type": "U16", "index": {None: 0x0313}, "min": 0, "max": 100, "dp": None, "precision": 0}, + "MDST": {"type": "U16", "index": {None: 0x0314}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "ALT": {"type": "U16", "index": {None: 0x0315}, "min": 0, "max": 14, "dp": None, "precision": 0}, + "AL-D": {"type": "U16", "index": {None: 0x0316}, "min": -1999, "max": 3000, "dp": "DP", "precision": 0}, + "AL-H": {"type": "U16", "index": {None: 0x0317}, "min": 0, "max": 3000, "dp": "DP", "precision": 0}, + "V.MOT": {"type": "U16", "index": {None: 0x0400}, "min": 5, "max": 999, "dp": None, "precision": 0}, + "V.DB": {"type": "U16", "index": {None: 0x0401}, "min": 0, "max": 9999, "dp": None, "precision": 0}, + "V.GAP": {"type": "U16", "index": {None: 0x0402}, "min": 0, "max": 10, "dp": None, "precision": 1}, + "V.REV": {"type": "U16", "index": {None: 0x0403}, "min": 0, "max": 10, "dp": None, "precision": 1}, + "V.TOF": {"type": "U16", "index": {None: 0x0404}, "min": 0, "max": 10, "dp": None, "precision": 0}, + "RET": {"type": "U16", "index": {None: 0x0500}, "min": 5, "max": 100, "dp": None, "precision": 0}, + "DIS1": {"type": "U16", "index": {None: 0x0501}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "DIS2": {"type": "U16", "index": {None: 0x0502}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "DIS3": {"type": "U16", "index": {None: 0x0503}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "DIS4": {"type": "U16", "index": {None: 0x0504}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "DIS5": {"type": "U16", "index": {None: 0x0505}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "NODE": {"type": "U16", "index": {None: 0x0600}, "min": 1, "max": 10, "dp": None, "precision": 0}, + "X": {"type": "I16", "index": {1: 0x0601, 2: 0x0603, 3: 0x0605, 4: 0x0607, 5: 0x0609, 6: 0x060B, 7: 0x060D, 8: 0x060F, 9: 0x0611, 10: 0x0613}, "min": -1999, "max": 3000, "dp": "DP", "precision": 0}, + "Y": {"type": "I16", "index": {1: 0x0602, 2: 0x0604, 3: 0x0606, 4: 0x0608, 5: 0x060A, 6: 0x060C, 7: 0x060E, 8: 0x0610, 9: 0x0612, 10: 0x0614}, "min": -1999, "max": 3000, "dp": "DP", "precision": 0}, + "OAPT": {"type": "U16", "index": {None: 0x0700}, "min": 0, "max": 2, "dp": None, "precision": 0}, + "WTPT": {"type": "U16", "index": {None: 0x0701}, "min": 0, "max": 4, "dp": None, "precision": 0}, + "EDPT": {"type": "U16", "index": {None: 0x0702}, "min": 0, "max": 1, "dp": None, "precision": 0}, + }, +} + +# Таблица настроек измерителя-регулятора ТРМ251 +TRM251: OWEN_DEVICE = { + "Owen": {"VER": {"type": "STR", "index": {None: None}, "min": None, "max": None}, + "DEV": {"type": "STR", "index": {None: None}, "min": None, "max": None}, + "CJ-.C": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "IN.RE": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "IN-T": {"type": "U8", "index": {0: 0, 1: 1}, "min": 0, "max": 36}, + "IN.FD": {"type": "U16", "index": {0: 0, 1: 1}, "min": 0, "max": 1800}, + "IN.FG": {"type": "U16", "index": {0: 0, 1: 1}, "min": 0, "max": 9999}, + "ITRL": {"type": "U16", "index": {0: 0, 1: 1}, "min": 0.3, "max": 30}, + "IN.SH": {"type": "SDOT", "index": {0: 0, 1: 1}, "min": -999, "max": 9999}, + "IN.SL": {"type": "U16", "index": {0: 0, 1: 1}, "min": 0.9, "max": 1.1}, + "AIN.L": {"type": "SDOT", "index": {0: 0, 1: 1}, "min": -999, "max": 9999}, + "AIN.H": {"type": "SDOT", "index": {0: 0, 1: 1}, "min": -999, "max": 9999}, + "REG.T": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "PB": {"type": "SDOT", "index": {None: None}, "min": 0.001, "max": 9999}, + "TI": {"type": "U16", "index": {None: None}, "min": 0, "max": 1092}, + "TD.TI": {"type": "U16", "index": {None: None}, "min": 0, "max": 0.3}, + "I.UPR": {"type": "I16", "index": {None: None}, "min": -100, "max": 100}, + "I.MIN": {"type": "I16", "index": {None: None}, "min": -100, "max": 100}, + "P.NOM": {"type": "U16", "index": {None: None}, "min": 0, "max": 100}, + "P.UPR": {"type": "U16", "index": {None: None}, "min": 0, "max": 100}, + "P.MIN": {"type": "U16", "index": {None: None}, "min": 0, "max": 100}, + "P.STP": {"type": "U16", "index": {None: None}, "min": 0, "max": 100}, + "P.RES": {"type": "U16", "index": {None: None}, "min": 0, "max": 1000}, + "HYS.C": {"type": "SDOT", "index": {None: None}, "min": 0, "max": 9999}, + "DEL": {"type": "U8", "index": {None: None}, "min": 0, "max": 200}, + "HOLD": {"type": "U8", "index": {None: None}, "min": 0, "max": 200}, + "YO": {"type": "SDOT", "index": {None: None}, "min": -9999, "max": 9999}, + "YDOP": {"type": "SDOT", "index": {None: None}, "min": 0, "max": 999}, + "POU": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "THP": {"type": "U8", "index": {None: None}, "min": 1, "max": 81}, + "T.L": {"type": "F24", "index": {None: None}, "min": 0.05, "max": 0.5}, + "RG.ON": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "AO.L": {"type": "SDOT", "index": {None: None}, "min": -999, "max": 9999}, + "AO.H": {"type": "SDOT", "index": {None: None}, "min": -999, "max": 9999}, + "SP": {"type": "SDOT", "index": {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, 10: 10, 11: 11, 12: 12, 13: 13, 14: 14}, "min": -999, "max": 9999}, + "T.RS": {"type": "U16", "index": {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, 10: 10, 11: 11, 12: 12, 13: 13, 14: 14}, "min": 0, "max": 65520}, + "T.STB": {"type": "U16", "index": {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, 10: 10, 11: 11, 12: 12, 13: 13, 14: 14}, "min": 0, "max": 65520}, + "SIG.T": {"type": "U8", "index": {0: 0, 1: 1, 2: 2}, "min": 0, "max": 1}, + "S.H": {"type": "SDOT", "index": {0: 0, 1: 1, 2: 2}, "min": 0, "max": 9999}, + "S.L": {"type": "SDOT", "index": {0: 0, 1: 1, 2: 2}, "min": 0, "max": 9999}, + "LBA": {"type": "U8", "index": {0: 0, 1: 1, 2: 2}, "min": 0, "max": 1}, + "D.LBA": {"type": "SDOT", "index": {0: 0, 1: 1, 2: 2}, "min": 0.001, "max": 9999}, + "T.LBA": {"type": "U16", "index": {0: 0, 1: 1, 2: 2}, "min": 0.001, "max": 9999}, + "BPS": {"type": "U8", "index": {None: None}, "min": 0, "max": 8}, + "LEN": {"type": "U8", "index": {None: None}, "min": 7, "max": 8}, + "PRTY": {"type": "U8", "index": {None: None}, "min": 0, "max": 2}, + "SBIT": {"type": "U8", "index": {None: None}, "min": 1, "max": 2}, + "A.LEN": {"type": "U8", "index": {None: None}, "min": 8, "max": 11}, + "ADDR": {"type": "U16", "index": {None: None}, "min": 0, "max": 2040}, + "PROT": {"type": "U8", "index": {None: None}, "min": 0, "max": 2}, + "DOT": {"type": "U8", "index": {None: None}, "min": 0, "max": 3}, + "RS.DL": {"type": "U8", "index": {None: None}, "min": 0, "max": 50}, + "BEHV": {"type": "U8", "index": {None: None}, "min": 0, "max": 3}, + "T.SCL": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "NET.S": {"type": "U8", "index": {None: None}, "min": 0, "max": 1}, + "READ": {"type": "F32+T", "index": {None: None}, "min": -999, "max": 9999}, + "R.OUT": {"type": "F32", "index": {None: None}, "min": 0, "max": 1}, + "R.SIG": {"type": "U16", "index": {None: None}, "min": 0, "max": 1}, + "RD.RG": {"type": "F32", "index": {None: None}, "min": 0, "max": 1}, + "R.ST": {"type": "U16", "index": {None: None}, "min": 0, "max": 7}, + "R.PRG": {"type": "U16", "index": {None: None}, "min": 1, "max": 3}, + "R.STP": {"type": "U16", "index": {None: None}, "min": 1, "max": 5}, + "SET.P": {"type": "F32", "index": {None: None}, "min": -999, "max": 9999}, + "R-S": {"type": "U16", "index": {None: None}, "min": 0, "max": 1}, + }, + "Modbus": {"DOT": {"type": "I16", "index": {0: 0x0000, 1: 0x0006}, "min": 0, "max": 3, "dp": None, "precision": 0}, + "READ": {"type": "F32", "index": {0: 0x0004, 1: 0x000A}, "min": -999, "max": 9999, "dp": None, "precision": 0}, + "R.OUT": {"type": "I16", "index": {None: 0x000C}, "min": 0, "max": 1000, "dp": None, "precision": 0}, + "SET.P": {"type": "I16", "index": {None: 0x000D}, "min": -999, "max": 9999, "dp": None, "precision": 0}, + "R.SIG": {"type": "I16", "index": {None: 0x000E}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "R.PRG": {"type": "I16", "index": {None: 0x000F}, "min": 0, "max": 3, "dp": None, "precision": 0}, + "R.STP": {"type": "I16", "index": {None: 0x0010}, "min": 1, "max": 5, "dp": None, "precision": 0}, + "R.ST": {"type": "I16", "index": {None: 0x0011}, "min": 0, "max": 7, "dp": None, "precision": 0}, + "T.SCL": {"type": "I16", "index": {None: 0x0100}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "SP": {"type": "I16", "index": {0: 0x0101, 1: 0x0105, 2: 0x0109, 3: 0x010D, 4: 0x0111, 5: 0x0115, 6: 0x0119, 7: 0x011D, 8: 0x0121, 9: 0x0125, 10: 0x0129, 11: 0x012D, 12: 0x0131, 13: 0x0135, 14: 0x0139}, "min": -999, "max": 9999, "dp": None, "precision": 0}, + "T.RS": {"type": "I16", "index": {0: 0x0103, 1: 0x0107, 2: 0x010B, 3: 0x010F, 4: 0x0113, 5: 0x0117, 6: 0x011B, 7: 0x011F, 8: 0x0123, 9: 0x0127, 10: 0x012B, 11: 0x012F, 12: 0x0133, 13: 0x0137, 14: 0x013B}, "min": 0, "max": 1092, "dp": None, "precision": 0}, + "T.STB": {"type": "I16", "index": {0: 0x0104, 1: 0x0108, 2: 0x010C, 3: 0x0110, 4: 0x0114, 5: 0x0118, 6: 0x011C, 7: 0x0120, 8: 0x0124, 9: 0x0128, 10: 0x012C, 11: 0x0130, 12: 0x0134, 13: 0x0138, 14: 0x013C}, "min": 0, "max": 1092, "dp": None, "precision": 0}, + "S.H": {"type": "I16", "index": {0: 0x0140, 1: 0x0144, 2: 0x0148}, "min": 0, "max": 9999, "dp": None, "precision": 0}, + "S.L": {"type": "I16", "index": {0: 0x0142, 1: 0x0146, 2: 0x014A}, "min": 0, "max": 9999, "dp": None, "precision": 0}, + "R-S": {"type": "I16", "index": {None: 0x0050}, "min": 0, "max": 1, "dp": None, "precision": 0}, + }, +} + +# Таблица настроек измерителя-регулятора микропроцессорного двухканального 2ТРМ1 +_2TRM1: OWEN_DEVICE = { + "Modbus": {"STAT": {"type": "U16", "index": {None: 0x1008}, "min": 0, "max": 65535, "dp": None, "precision": 0}, + "DEV": {"type": "STR", "index": {None: 0x1000}, "min": None, "max": None, "dp": None, "precision": 0}, + "VER": {"type": "STR", "index": {None: 0x1004}, "min": None, "max": None, "dp": None, "precision": 0}, + "PV": {"type": "F32", "index": {0: 0x1009, 1: 0x100B}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "FUN": {"type": "F32", "index": {0: 0x100D, 1: 0x100F}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "SP": {"type": "F32", "index": {0: 0x1011, 1: 0x1013}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "OUT.P": {"type": "F32", "index": {0: 0x1015, 1: 0x1017}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "CTRL": {"type": "U16", "index": {None: 0x1019}, "min": 0, "max": 2, "dp": None, "precision": 0}, + "RESET": {"type": "U16", "index": {None: 0x101A}, "min": 1, "max": 1, "dp": None, "precision": 0}, + "TYPE": {"type": "U16", "index": {0: 0x0004, 1: 0x0104}, "min": 0, "max": 42, "dp": None, "precision": 0}, + "FIL.B": {"type": "F32", "index": {0: 0x0005, 1: 0x0105}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "FIL.T": {"type": "U16", "index": {0: 0x0007, 1: 0x0107}, "min": 0, "max": 999, "dp": None, "precision": 0}, + "DPT": {"type": "U16", "index": {0: 0x0008, 1: 0x0108}, "min": 0, "max": 4, "dp": None, "precision": 0}, + "IND.L": {"type": "F32", "index": {0: 0x0009, 1: 0x0109}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "IND.H": {"type": "F32", "index": {0: 0x000B, 1: 0x010B}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "CF": {"type": "F32", "index": {0: 0x000E, 1: 0x0010}, "min": -100, "max": 100, "dp": None, "precision": 0}, + "DIN.T": {"type": "U16", "index": {0: 0x0012, 1: 0x0112}, "min": 0, "max": 30, "dp": None, "precision": 0}, + "DIN.D": {"type": "F32", "index": {0: 0x0013, 1: 0x0113}, "min": 0.2, "max": 9999, "dp": None, "precision": 0}, + "BARR": {"type": "U16", "index": {0: 0x0015, 1: 0x0115}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "COR1.POINT": {"type": "F32", "index": {0: 0x0016, 1: 0x0116}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "COR1.OFFSET": {"type": "F32", "index": {0: 0x0018, 1: 0x0118}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "COR1.CLR": {"type": "U16", "index": {0: 0x001A, 1: 0x011A}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "COR2.POINT": {"type": "F32", "index": {0: 0x001B, 1: 0x011B}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "COR2.OFFSET": {"type": "F32", "index": {0: 0x001D, 1: 0x011D}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "COR2.CLR": {"type": "U16", "index": {0: 0x001F, 1: 0x011F}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "COR3.POINT": {"type": "F32", "index": {0: 0x0020, 1: 0x0120}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "COR3.OFFSET": {"type": "F32", "index": {0: 0x0022, 1: 0x0122}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "COR3.CLR": {"type": "U16", "index": {0: 0x0024, 1: 0x0124}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "SP.LO": {"type": "F32", "index": {0: 0x0202, 1: 0x0302}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "SP.HI": {"type": "F32", "index": {0: 0x0204, 1: 0x0304}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "LBA.T": {"type": "U16", "index": {0: 0x0208, 1: 0x0308}, "min": 0, "max": 9999, "dp": None, "precision": 0}, + "LBA.B": {"type": "F32", "index": {0: 0x0209, 1: 0x0309}, "min": 0, "max": 9999, "dp": None, "precision": 0}, + "LOG.D": {"type": "U16", "index": {0: 0x0220, 1: 0x0320}, "min": 0, "max": 3, "dp": None, "precision": 0}, + "HYST": {"type": "F32", "index": {0: 0x0221, 1: 0x0321}, "min": 0, "max": 9999, "dp": None, "precision": 0}, + "D.ON": {"type": "U16", "index": {0: 0x0223, 1: 0x0323}, "min": 0, "max": 250, "dp": None, "precision": 0}, + "D.OFF": {"type": "U16", "index": {0: 0x0224, 1: 0x0324}, "min": 0, "max": 250, "dp": None, "precision": 0}, + "H.ON": {"type": "U16", "index": {0: 0x0225, 1: 0x0325}, "min": 0, "max": 250, "dp": None, "precision": 0}, + "H.OFF": {"type": "U16", "index": {0: 0x0226, 1: 0x0326}, "min": 0, "max": 250, "dp": None, "precision": 0}, + "CNT.P": {"type": "U16", "index": {0: 0x0227, 1: 0x0327}, "min": 1, "max": 250, "dp": None, "precision": 0}, + "ERR.D": {"type": "U16", "index": {0: 0x0228, 1: 0x0328}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "STP.D": {"type": "U16", "index": {0: 0x0229, 1: 0x0329}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "A.TYP": {"type": "U16", "index": {0: 0x0240, 1: 0x0340}, "min": 0, "max": 8, "dp": None, "precision": 0}, + "A.BND": {"type": "F32", "index": {0: 0x0241, 1: 0x0341}, "min": 0, "max": 9999, "dp": None, "precision": 0}, + "A.HYS": {"type": "F32", "index": {0: 0x0243, 1: 0x0343}, "min": 0, "max": 9999, "dp": None, "precision": 0}, + "F.BLC": {"type": "U16", "index": {0: 0x0245, 1: 0x0345}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "LOG.A": {"type": "U16", "index": {0: 0x0260, 1: 0x0360}, "min": 0, "max": 3, "dp": None, "precision": 0}, + "XP": {"type": "F32", "index": {0: 0x0261, 1: 0x0361}, "min": 0, "max": 9999, "dp": None, "precision": 0}, # в документации HYST, но HYST уже есть + "OUT.L": {"type": "F32", "index": {0: 0x0263, 1: 0x0363}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "OUT.H": {"type": "F32", "index": {0: 0x0265, 1: 0x0365}, "min": -1999, "max": 9999, "dp": None, "precision": 0}, + "ERR.A": {"type": "U16", "index": {0: 0x0267, 1: 0x0367}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "STP.A": {"type": "U16", "index": {0: 0x0268, 1: 0x0368}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "SCR": {"type": "U16", "index": {0: 0x0400, 1: 0x0401, 2: 0x0402, 3: 0x0403, 4: 0x0404, 5: 0x0405}, "min": 0, "max": 16, "dp": None, "precision": 0}, + "OUT.S": {"type": "U16", "index": {None: 0x0406}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "RET.T": {"type": "U16", "index": {None: 0x0407}, "min": 0, "max": 4, "dp": None, "precision": 0}, + "CHG.T": {"type": "U16", "index": {None: 0x0408}, "min": 0, "max": 5, "dp": None, "precision": 0}, + "PROT": {"type": "U16", "index": {None: 0x0500}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "ADDR": {"type": "U16", "index": {None: 0x0501}, "min": 1, "max": 247, "dp": None, "precision": 0}, + "BAUD": {"type": "U16", "index": {None: 0x0502}, "min": 0, "max": 8, "dp": None, "precision": 0}, + "DPS": {"type": "U16", "index": {None: 0x0503}, "min": 0, "max": 11, "dp": None, "precision": 0}, + "IDLE": {"type": "U16", "index": {None: 0x0504}, "min": 0, "max": 20, "dp": None, "precision": 0}, + "B.ORD": {"type": "U16", "index": {None: 0x0505}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "APLY": {"type": "U16", "index": {None: 0x0506}, "min": 0, "max": 1, "dp": None, "precision": 0}, + "GRF.N": {"type": "U16", "index": {None: 0x0600}, "min": 0, "max": 10, "dp": None, "precision": 0}, + "IN.": {"type": "F32", "index": {0: 0x0601, 1: 0x0605, 2: 0x0609, 3: 0x060D, 4: 0x0611, 5: 0x0615, 6: 0x0619, 7: 0x061D, 8: 0x0621, 9: 0x0625}, "min": 0, "max": 10, "dp": None, "precision": 0}, + "SP.": {"type": "F32", "index": {0: 0x0603, 1: 0x0607, 2: 0x060B, 3: 0x060F, 4: 0x0613, 5: 0x0617, 6: 0x061B, 7: 0x061F, 8: 0x0623, 9: 0x0627}, "min": 0, "max": 10, "dp": None, "precision": 0}, + "PASS": {"type": "U16", "index": {None: 0x0800}, "min": 0, "max": 9999, "dp": None, "precision": 0}, + "PRT.E": {"type": "U16", "index": {None: 0x0801}, "min": 0, "max": 3, "dp": None, "precision": 0}, + "ATR.E": {"type": "U16", "index": {None: 0x0802}, "min": 0, "max": 2, "dp": None, "precision": 0}, + "CJS.E": {"type": "U16", "index": {None: 0x0803}, "min": 0, "max": 1, "dp": None, "precision": 0}, + }, +} + +# Таблица настроек программируемого логического реле ПР100 +PR100: OWEN_DEVICE = { + "Owen": {}, # Протокол OWEN не поддерживается для ПР100 + "Modbus": { + # Дискретные входы (для всех модификаций) + "DI": { + "type": "U16", + "index": {None: 0x0100}, # Все входы в одном регистре как битовая маска + "min": 0, + "max": 4095, # Максимум 12 входов (0xFFF) + "dp": None, + "precision": 0 + }, + # Универсальные входы (аналоговые) - float32 + "AI1": { + "type": "F32", + "index": {None: 0x0B00}, + "min": -999, + "max": 9999, + "dp": None, + "precision": 0 + }, + "AI2": { + "type": "F32", + "index": {None: 0x0B02}, + "min": -999, + "max": 9999, + "dp": None, + "precision": 0 + }, + "AI3": { + "type": "F32", + "index": {None: 0x0B04}, + "min": -999, + "max": 9999, + "dp": None, + "precision": 0 + }, + "AI4": { + "type": "F32", + "index": {None: 0x0B06}, + "min": -999, + "max": 9999, + "dp": None, + "precision": 0 + }, + # Универсальные входы - целое число (результат × 10^dp) + "AI1.INT": { + "type": "I16", + "index": {None: 0x0B80}, + "min": -1999, + "max": 9999, + "dp": "AI1.DP", + "precision": 0 + }, + "AI2.INT": { + "type": "I16", + "index": {None: 0x0B81}, + "min": -1999, + "max": 9999, + "dp": "AI2.DP", + "precision": 0 + }, + "AI3.INT": { + "type": "I16", + "index": {None: 0x0B82}, + "min": -1999, + "max": 9999, + "dp": "AI3.DP", + "precision": 0 + }, + "AI4.INT": { + "type": "I16", + "index": {None: 0x0B83}, + "min": -1999, + "max": 9999, + "dp": "AI4.DP", + "precision": 0 + }, + # Смещение десятичной точки + "AI1.DP": { + "type": "I16", + "index": {None: 0x0BC0}, + "min": 0, + "max": 3, + "dp": None, + "precision": 0 + }, + "AI2.DP": { + "type": "I16", + "index": {None: 0x0BC1}, + "min": 0, + "max": 3, + "dp": None, + "precision": 0 + }, + "AI3.DP": { + "type": "I16", + "index": {None: 0x0BC2}, + "min": 0, + "max": 3, + "dp": None, + "precision": 0 + }, + "AI4.DP": { + "type": "I16", + "index": {None: 0x0BC3}, + "min": 0, + "max": 3, + "dp": None, + "precision": 0 + }, + # Дискретные выходы (записываемые) + "DO": { + "type": "U16", + "index": {None: 0x0000}, # Все выходы в одном регистре + "min": 0, + "max": 1023, # Максимум 10 выходов (Q1-Q8, F1-F2) + "dp": None, + "precision": 0 + }, + # Системное время + "TIME.SEC": { + "type": "I16", + "index": {None: 0x0400}, + "min": 0, + "max": 59, + "dp": None, + "precision": 0 + }, + "TIME.MIN": { + "type": "I16", + "index": {None: 0x0401}, + "min": 0, + "max": 59, + "dp": None, + "precision": 0 + }, + "TIME.HOUR": { + "type": "I16", + "index": {None: 0x0402}, + "min": 0, + "max": 23, + "dp": None, + "precision": 0 + }, + "TIME.DAY": { + "type": "I16", + "index": {None: 0x0403}, + "min": 1, + "max": 31, + "dp": None, + "precision": 0 + }, + "TIME.MONTH": { + "type": "I16", + "index": {None: 0x0404}, + "min": 1, + "max": 12, + "dp": None, + "precision": 0 + }, + "TIME.YEAR": { + "type": "I16", + "index": {None: 0x0405}, + "min": 0, + "max": 99, + "dp": None, + "precision": 0 + }, + "TIME.DOW": { + "type": "I16", + "index": {None: 0x0406}, + "min": 0, + "max": 6, + "dp": None, + "precision": 0 + }, + "TIME.WOM": { + "type": "I16", + "index": {None: 0x0407}, + "min": 0, + "max": 5, + "dp": None, + "precision": 0 + }, + "TIME.WOY": { + "type": "I16", + "index": {None: 0x0408}, + "min": 0, + "max": 53, + "dp": None, + "precision": 0 + }, + } +} \ No newline at end of file diff --git a/owen/protocol.py b/owen/protocol.py new file mode 100644 index 0000000..4747b36 --- /dev/null +++ b/owen/protocol.py @@ -0,0 +1,151 @@ +#! /usr/bin/env python3 + +"""Реализация протокола взаимодействия ОВЕН.""" + +from __future__ import annotations + +import logging +from functools import reduce +from struct import error, unpack + +from owen.converter import OWEN_TYPE + +_logger = logging.getLogger(__name__) +_logger.addHandler(logging.NullHandler()) + + +HEADER = ord("#") +FOOTER = ord("\r") +OWEN_ASCII = {"0": 0, "1": 2, "2": 4, "3": 6, "4": 8, + "5": 10, "6": 12, "7": 14, "8": 16, "9": 18, + "A": 20, "B": 22, "C": 24, "D": 26, "E": 28, + "F": 30, "G": 32, "H": 34, "I": 36, "J": 38, + "K": 40, "L": 42, "M": 44, "N": 46, "O": 48, + "P": 50, "Q": 52, "R": 54, "S": 56, "T": 58, + "U": 60, "V": 62, "W": 64, "X": 66, "Y": 68, + "Z": 70, "-": 72, "_": 74, "/": 76, " ": 78} + + +class OwenError(Exception): + pass + + +class Owen: + """Класс, описывающий протокол ОВЕН.""" + + def __init__(self, unit: int, addr_len_8: bool) -> None: + """Инициализация класса клиента с указанными параметрами.""" + + self.unit = unit + self.addr_len_8 = addr_len_8 + + @staticmethod + def fast_calc(value: int, crc: int, bits: int) -> int: + """Вычисление значения полинома.""" + + return reduce(lambda crc, i: crc << 1 & 0xFFFF ^ (0x8F57 + if (value << i ^ crc >> 8) & 0x80 else 0), range(bits), crc) + + def owen_crc16(self, packet: tuple[int, ...]) -> int: + """Вычисление контрольной суммы.""" + + return reduce(lambda crc, val: self.fast_calc(val, crc, 8), packet, 0) + + def owen_hash(self, packet: tuple[int, ...]) -> int: + """Вычисление hash-функции.""" + + return reduce(lambda crc, val: self.fast_calc(val << 1, crc, 7), packet, 0) + + @staticmethod + def name2code(name: str) -> tuple[int, ...]: + """Преобразование локального идентификатора в числовой код.""" + + code: list[int] = reduce(lambda x, ch: [*x[:-1], x[-1] + 1] if ch == "." + else [*x, OWEN_ASCII[ch]], name.upper(), []) + return (*code, *[OWEN_ASCII[" "]] * (4 - len(code))) + + @staticmethod + def encode_frame(frame: tuple[int, ...]) -> bytes: + """Преобразование пакета из числового вида в строковый.""" + + chars = ([71 + (num >> 4), 71 + (num & 0xF)] for num in frame) + return bytes([HEADER, *sum(chars, []), FOOTER]) + + @staticmethod + def decode_frame(frame: bytes) -> tuple[int, ...]: + """Преобразование пакета из строкового вида в числовой.""" + + pairs = zip(*[iter(frame[1:-1])] * 2) + return tuple((i - 71 << 4) + (j - 71 & 0xF) for i, j in pairs) + + @staticmethod + def pack_value(frmt: str, value: float | str | None) -> bytes: + """Упаковка данных заданного формата.""" + + return b"" if value is None else OWEN_TYPE[frmt]["pack"](value) + + @staticmethod + def unpack_value(frmt: str, value: bytes, index: int | None) -> float | str: + """Распаковка данных заданного формата.""" + + try: + return OWEN_TYPE[frmt]["unpack"](value, index) + except error: + errcode = OWEN_TYPE["U8"]["unpack"](value, index) + msg = f"Device error={errcode:02X}" + raise OwenError(msg) from None + + def make_packet(self, flag: int, name: str, index: int | None, + data: bytes) -> bytes: + """Формирование пакета для записи.""" + + addr0, addr1 = (self.unit & 0xFF, 0) if self.addr_len_8 else \ + (self.unit >> 3 & 0xFF, (self.unit & 0x07) << 5) + if index is not None: + data = bytes([*data, *index.to_bytes(2, "big")]) + + cmd = self.owen_hash(self.name2code(name)) + frame = (addr0, addr1 | flag << 4 | len(data), *cmd.to_bytes(2, "big"), *data) + crc = self.owen_crc16(frame) + packet = self.encode_frame((*frame, *crc.to_bytes(2, "big"))) + + _logger.debug("Send param: address=%d, flag=%d, size=%d, cmd=%04X, " + "index=%s, data=%s, crc=%04X", self.unit, flag, len(data), + cmd, index, tuple(data), crc) + _logger.debug("Send frame: %r, size=%d", packet, len(packet)) + + return packet + + def parse_response(self, packet: bytes, answer: bytes) -> bytes: + """Расшифровка прочитанного пакета.""" + + _logger.debug("Recv frame: %r, size=%d", answer, len(answer)) + + if not answer or answer[0] != HEADER or answer[-1] != FOOTER: + msg = "Invalid message format" + raise OwenError(msg) + + frame = self.decode_frame(answer) + + address = frame[0] if self.addr_len_8 else frame[0] << 3 | frame[1] >> 5 + flag = frame[1] >> 4 & 1 + size = frame[1] & 0xF + cmd, *data, crc = unpack(f">H{size}BH", bytes(frame[2:])) + + _logger.debug("Recv param: address=%d, flag=%d, size=%d, cmd=%04X, data=%s, " + "crc=%04X", address, flag, size, cmd, tuple(data), crc) + + if self.owen_crc16(frame[:-2]) != crc: + msg = "Checksum error" + raise OwenError(msg) + if address != self.unit: + msg = "Sender and receiver addresses mismatch" + raise OwenError(msg) + if packet[7:9] != answer[7:9]: # hash mismatch + msg = "Network error={:02X}, hash={:02X}{:02X}".format(*data) + raise OwenError(msg) + + return bytes(data) + + +__all__ = ["Owen"] diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..46f930b --- /dev/null +++ b/setup.py @@ -0,0 +1,27 @@ +#! /usr/bin/env python3 + +from setuptools import setup + +setup(name="python-owen", + version="0.4.2", + description="OWEN controllers library", + url="https://github.com/RAA80/python-owen", + author="Alexey Ryadno", + author_email="aryadno@mail.ru", + license="MIT", + packages=["owen"], + install_requires=["pymodbus < 3", "pyserial >= 3.4"], + platforms=["Linux", "Windows"], + classifiers=["Development Status :: 4 - Beta", + "Intended Audience :: Science/Research", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: Microsoft :: Windows", + "Operating System :: POSIX :: Linux", + "Operating System :: POSIX", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + ], + ) diff --git a/test/test_client.py b/test/test_client.py new file mode 100644 index 0000000..a89b290 --- /dev/null +++ b/test/test_client.py @@ -0,0 +1,153 @@ +#! /usr/bin/env python3 + +import unittest +from unittest.mock import MagicMock, patch + +from pymodbus.client.sync import ModbusSerialClient +from pymodbus.pdu import ModbusResponse +from pymodbus.register_write_message import WriteMultipleRegistersResponse +from serial import Serial + +from owen.client import ClientMixin, OwenError, OwenModbusClient, OwenSerialClient +from owen.device import TRM201 + + +class FakeOwenSerialClient(OwenSerialClient): + + def bus_exchange(self, packet: bytes) -> bytes: + return {b"#GHHGTMOHHRTO\r": b"#GHGMTMOHJHJGJISSTGTIPLKK\r", # чтение параметра "DEV" тип "STR" + b"#GHHGHUTIKGJI\r": b"#GHGHHUTIGGJKGK\r", # чтение параметра "A.LEN" тип "U8" без индекса + b"#GHHIRJURGGGGHQIV\r": b"#GHGJRJURGHGGGGQROU\r", # чтение параметра "DP" тип "U8" с индексом + b"#GHHGPVMIJIMK\r": b"#GHGIPVMIGGGHNHIR\r", # чтение параметра "ADDR" тип "U16" без индекса + b"#GHHGROTVJNPQ\r": b"#GHGJROTVKIQJIOOJKN\r", # чтение параметра "PV" тип "F24" без индекса + b"#GHHIUSIGGGGGTJIT\r": b"#GHGLUSIGKKJROGGGGGPVUS\r", # чтение параметра "SL.H" тип "F24" с индексом + b"#GHHGGIJJRIQN\r": b"#GHGJGIJJKNRKMLLNJK\r", # чтение параметра "N.ERR" тип "U24" + + b"#GHGHHUTIGGJKGK\r": b"#GHGHHUTIGGJKGK\r", # запись параметра "A.LEN" тип "U8" без индекса + b"#GHGJQLQRGHGGGGPNOJ\r": b"#GHGJQLQRGHGGGGPNOJ\r", # запись параметра "CMP" тип "U8" с индексом + b"#GHGIPVMIGGGHNHIR\r": b"#GHGIPVMIGGGHNHIR\r", # запись параметра "ADDR" тип "U16" без индекса + b"#GHGJPPKMGGGGGGQMGJ\r": b"#GHGJPPKMGGGGGGQMGJ\r", # запись параметра "R.OUT" тип "F24" без индекса + b"#GHGLUSIGKKJROGGGGGPVUS\r": b"#GHGLUSIGKKJROGGGGGPVUS\r", # запись параметра "SL.H" тип "F24" с индексом + b"#GHGGGGUPJSUL\r": b"", # запись параметра "INIT" тип "U8" без индекса + }[packet] + + +class TestClientMixin(unittest.TestCase): + """The unittest for ClientMixin.""" + + def setUp(self) -> None: + self.client = ClientMixin() + + def tearDown(self) -> None: + del self.client + + def test_check_index(self) -> None: + name = "A.LEN" + dev = TRM201["Owen"][name] + + # correct index + self.assertEqual(None, self.client.check_index(name=name, dev=dev, index=None)) + # invalid index + self.assertRaises(OwenError, lambda: self.client.check_index(name=name, dev=dev, index=1)) + + def test_check_value(self) -> None: + name = "DEV" + dev = TRM201["Owen"][name] + + # correct value + self.assertIsNone(self.client.check_value(name=name, dev=dev, value=None)) + # invalid value + self.assertRaises(OwenError, lambda: self.client.check_value(name=name, dev=dev, value=1)) + + name = "SP" + dev = TRM201["Owen"][name] + + # correct value + self.assertTrue(10.0, self.client.check_value(name=name, dev=dev, value=10.0)) + # invalid value (> max) + self.assertRaises(OwenError, lambda: self.client.check_value(name=name, dev=dev, value=10000)) + # invalid value (< min) + self.assertRaises(OwenError, lambda: self.client.check_value(name=name, dev=dev, value=-10000)) + # invalid value + self.assertRaises(OwenError, lambda: self.client.check_value(name=name, dev=dev, value=None)) + + +class TestOwenSerialClient(unittest.TestCase): + """The unittest for OwenSerialClient.""" + + @patch("serial.Serial", autospec=True) + def setUp(self, mock_serial: Serial) -> None: + self.client = FakeOwenSerialClient(transport=mock_serial, device=TRM201, + unit=1, addr_len_8=True) + + def tearDown(self) -> None: + del self.client + + def test_get_param(self) -> None: + # correct index + self.assertEqual(0, self.client.get_param(name="A.LEN", index=None)) + self.assertEqual("ТРМ201", self.client.get_param(name="DEV", index=None)) + self.assertEqual(1, self.client.get_param(name="DP", index=0)) + self.assertEqual(1, self.client.get_param(name="ADDR", index=None)) + self.assertEqual(81.578125, self.client.get_param(name="PV", index=0)) + self.assertEqual(750.0, self.client.get_param(name="SL.H", index=0)) + self.assertEqual((71, 46181), self.client.get_param(name="N.ERR", index=None)) + # invalid index + self.assertRaises(OwenError, lambda: self.client.get_param(name="A.LEN", index=2)) + + def test_set_param(self) -> None: + # correct index and value + self.assertTrue(self.client.set_param(name="A.LEN", index=None, value=0)) + self.assertTrue(self.client.set_param(name="CMP", index=0, value=1)) + self.assertTrue(self.client.set_param(name="ADDR", index=None, value=1)) + self.assertTrue(self.client.set_param(name="R.OUT", index=None, value=0.0)) + self.assertTrue(self.client.set_param(name="SL.H", index=0, value=750.0)) + self.assertRaises(OwenError, lambda: self.client.set_param(name="INIT", index=None, value=None)) + # invalid index + self.assertRaises(OwenError, lambda: self.client.set_param(name="A.LEN", index=2, value=0)) + # invalid value + self.assertRaises(OwenError, lambda: self.client.set_param(name="A.LEN", index=None, value=2)) + self.assertRaises(OwenError, lambda: self.client.set_param(name="INIT", index=None, value=1)) + + +class TestOwenModbusClient(unittest.TestCase): + """The unittest for OwenModbusClient.""" + + @patch("pymodbus.client.sync.ModbusSerialClient", autospec=True) + def setUp(self, mock_modbus: ModbusSerialClient) -> None: + self.client = OwenModbusClient(transport=mock_modbus, device=TRM201, unit=1) + + def tearDown(self) -> None: + del self.client + + def test_check_error(self) -> None: + err = MagicMock(ModbusResponse) + + err.isError.return_value = False + self.assertTrue(self.client.check_error(retcode=err)) + + err.isError.return_value = True + self.assertRaises(OwenError, lambda: self.client.check_error(retcode=err)) + + def test_set_param(self) -> None: + # correct index and value + value = 20.0 + self.client.modify_value = MagicMock(return_value=value) + self.client.socket.write_registers = MagicMock(return_value=WriteMultipleRegistersResponse(1, 2)) + self.assertTrue(self.client.set_param(name="SP", index=0, value=value)) + # invalid index + self.assertRaises(OwenError, lambda: self.client.set_param(name="SP", index=2, value=value)) + # invalid value + self.assertRaises(OwenError, lambda: self.client.set_param(name="SP", index=0, value=None)) + + def test_get_param(self) -> None: + # correct index + self.client._read = MagicMock(return_value=bytearray([0x1, 0x3, 0x2, 0x0, 0x1, 0x79, 0x84])) + self.client.modify_value = MagicMock(return_value=20.0) + self.assertEqual(20.0, self.client.get_param(name="SP", index=0)) + # invalid index + self.assertRaises(OwenError, lambda: self.client.get_param(name="SP", index=2)) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/test_protocol.py b/test/test_protocol.py new file mode 100644 index 0000000..6dcaddf --- /dev/null +++ b/test/test_protocol.py @@ -0,0 +1,158 @@ +#! /usr/bin/env python3 + +import unittest + +from owen.protocol import Owen, OwenError + + +class TestOwenProtocol(unittest.TestCase): + """The unittest for Owen protocol.""" + + def setUp(self) -> None: + self.trm = Owen(unit=1, addr_len_8=True) + self.trm11 = Owen(unit=400, addr_len_8=False) + + def tearDown(self) -> None: + del self.trm + del self.trm11 + + def test_fast_calc(self) -> None: + self.assertEqual(20158, self.trm.fast_calc(84, 159, 7)) + self.assertEqual(5565, self.trm.fast_calc(18, 36695, 8)) + self.assertEqual(53661, self.trm.fast_calc(71, 34988, 8)) + self.assertEqual(60031, self.trm.fast_calc(72, 0, 7)) + self.assertEqual(64238, self.trm.fast_calc(156, 23651, 7)) + self.assertIsInstance(self.trm.fast_calc(156, 23651, 7), int) + + def test_owen_crc16(self) -> None: + self.assertEqual(16434, self.trm.owen_crc16((1, 16, 30, 210))) + self.assertEqual(44267, self.trm.owen_crc16((1, 18, 200, 128, 0, 0))) + self.assertEqual(23007, self.trm.owen_crc16((1, 5, 225, 125, 195, 71, 230, 0, 0))) + self.assertEqual(40940, self.trm.owen_crc16((1, 5, 236, 32, 68, 59, 128, 0, 0))) + self.assertEqual(59803, self.trm.owen_crc16((1, 8, 45, 91, 52, 48, 48, 48, 46, 51, 48, 86))) + self.assertEqual(15584, self.trm.owen_crc16((1, 16, 232, 196))) + self.assertEqual(38212, self.trm.owen_crc16((1, 6, 214, 129, 49, 48, 50, 204, 208, 210))) + self.assertIsInstance(self.trm.owen_crc16((1, 6, 214, 129, 49, 48, 50, 204, 208, 210)), int) + + def test_owen_hash(self) -> None: + self.assertEqual(7890, self.trm.owen_hash((21, 42, 28, 46))) + self.assertEqual(60448, self.trm.owen_hash((56, 43, 34, 78))) + self.assertEqual(47327, self.trm.owen_hash((50, 62, 78, 78))) + self.assertEqual(39238, self.trm.owen_hash((55, 48, 60, 58))) + self.assertEqual(13800, self.trm.owen_hash((48, 78, 78, 78))) + self.assertEqual(46941, self.trm.owen_hash((25, 56, 51, 48))) + self.assertEqual(64104, self.trm.owen_hash((24, 38, 73, 24))) + self.assertEqual(11410, self.trm.owen_hash((28, 62, 72, 2))) + self.assertEqual(233, self.trm.owen_hash((36, 46, 36, 58))) + self.assertIsInstance(self.trm.owen_hash((36, 46, 36, 58)), int) + + def test_name2code(self) -> None: + self.assertEqual((21, 42, 28, 46), self.trm.name2code("A.LEN")) + self.assertEqual((56, 43, 34, 78), self.trm.name2code("SL.H")) + self.assertEqual((50, 62, 78, 78), self.trm.name2code("PV")) + self.assertEqual((55, 48, 60, 58), self.trm.name2code("R.OUT")) + self.assertEqual((48, 78, 78, 78), self.trm.name2code("O")) + self.assertEqual((25, 56, 51, 48), self.trm.name2code("C.SP.O")) + self.assertEqual((24, 38, 73, 24), self.trm.name2code("CJ-.C")) + self.assertEqual((28, 62, 72, 2), self.trm.name2code("EV-1")) + self.assertEqual((36, 46, 36, 58), self.trm.name2code("INIT")) + self.assertIsInstance(self.trm.name2code("INIT"), tuple) + + def test_encode_frame(self) -> None: + self.assertEqual(b"#GHHGHUTIKGJI\r", self.trm.encode_frame((1, 16, 30, 210, 64, 50))) + self.assertEqual(b"#GHGHHUTIGGJKGK\r", self.trm.encode_frame((1, 1, 30, 210, 0, 52, 4))) + self.assertEqual(b"#GHHISOOGGGGGQSUR\r", self.trm.encode_frame((1, 18, 200, 128, 0, 0, 172, 235))) + self.assertEqual(b"#GHGJSOOGGGGGGGUQRK\r", self.trm.encode_frame((1, 3, 200, 128, 0, 0, 0, 234, 180))) + self.assertEqual(b"#GHHIPHGNGGGGKKPV\r", self.trm.encode_frame((1, 18, 145, 7, 0, 0, 68, 159))) + self.assertEqual(b"#GHGLPHGNKHSOGGGGGGJOMV\r", self.trm.encode_frame((1, 5, 145, 7, 65, 200, 0, 0, 0, 56, 111))) + self.assertEqual(b"#GHHGHIGJUIMK\r", self.trm.encode_frame((1, 16, 18, 3, 226, 100))) + self.assertEqual(b"#GHGHHIGJGGIHHO\r", self.trm.encode_frame((1, 1, 18, 3, 0, 33, 24))) + self.assertIsInstance(self.trm.encode_frame((1, 1, 18, 3, 0, 33, 24)), bytes) + + def test_decode_frame(self) -> None: + self.assertEqual((1, 1, 30, 210, 0, 52, 4), self.trm.decode_frame(b"#GHGHHUTIGGJKGK\r")) + self.assertEqual((1, 3, 200, 128, 0, 0, 0, 234, 180), self.trm.decode_frame(b"#GHGJSOOGGGGGGGUQRK\r")) + self.assertEqual((1, 5, 57, 243, 0, 0, 0, 0, 0, 11, 51), self.trm.decode_frame(b"#GHGLJPVJGGGGGGGGGGGRJJ\r")) + self.assertEqual((1, 5, 225, 125, 195, 71, 230, 0, 0, 89, 223), self.trm.decode_frame(b"#GHGLUHNTSJKNUMGGGGLPTV\r")) + self.assertEqual((1, 8, 45, 91, 52, 48, 48, 48, 46, 51, 48, 86, 233, 155), self.trm.decode_frame(b"#GHGOITLRJKJGJGJGIUJJJGLMUPPR\r")) + self.assertEqual((1, 3, 180, 101, 0, 0, 0, 9, 1), self.trm.decode_frame(b"#GHGJRKMLGGGGGGGPGH\r")) + self.assertEqual((1, 3, 2, 51, 71, 180, 101, 87, 52), self.trm.decode_frame(b"#GHGJGIJJKNRKMLLNJK\r")) + self.assertEqual((1, 3, 2, 51, 71, 100, 234, 99, 78), self.trm.decode_frame(b"#GHGJGIJJKNMKUQMJKU\r")) + self.assertEqual((1, 1, 30, 37, 20, 126, 6), self.trm.decode_frame(b"#GHGHHUILHKNUGM\r")) + self.assertIsInstance(self.trm.decode_frame(b"#GHGHHUILHKNUGM\r"), tuple) + + def test_pack_value(self) -> None: + self.assertEqual(bytes([194, 71, 255, 167, 15, 225]), self.trm.pack_value("F32+T", (-49.99966049194336, 4065))) + self.assertEqual(bytes([66, 246, 233, 223]), self.trm.pack_value("F32", 123.45678)) + self.assertEqual(bytes([164, 14]), self.trm.pack_value("SDOT", -10.38)) + self.assertEqual(bytes([29, 172]), self.trm.pack_value("SDOT", 350.0)) + self.assertEqual(bytes([16, 16, 4]), self.trm.pack_value("SDOT", 410.0)) + self.assertEqual(bytes([16]), self.trm.pack_value("SDOT", 0.0)) + self.assertEqual(bytes([0]), self.trm.pack_value("DOT0", 0)) + self.assertEqual(bytes([153]), self.trm.pack_value("DOT0", 99)) + self.assertEqual(bytes([3, 4]), self.trm.pack_value("DOT0", 304)) + self.assertEqual(bytes([9, 135, 101, 67, 33]), self.trm.pack_value("DOT0", 987654321)) + self.assertEqual(bytes([66, 246, 233]), self.trm.pack_value("F24", 123.45678)) + self.assertEqual(bytes([4, 210]), self.trm.pack_value("U16", 1234)) + self.assertEqual(bytes([251, 46]), self.trm.pack_value("I16", -1234)) + self.assertEqual(bytes([12]), self.trm.pack_value("U8", 12)) + self.assertEqual(bytes([244]), self.trm.pack_value("I8", -12)) + self.assertEqual(bytes([50, 48, 50, 204, 208, 210]), self.trm.pack_value("STR", "ТРМ202")) + self.assertEqual(b"", self.trm.pack_value("U8", None)) # if empty buffer + self.assertIsInstance(self.trm.pack_value("I8", -12), bytes) + + def test_unpack_value(self) -> None: + self.assertEqual((-49.99966049194336, 4065), self.trm.unpack_value("F32+T", bytes([194, 71, 255, 167, 15, 225]), None)) + self.assertEqual(123.45677947998047, self.trm.unpack_value("F32", bytes([66, 246, 233, 223]), None)) + self.assertEqual(350.0, self.trm.unpack_value("SDOT", bytes([29, 172, 0, 0]), 0)) + self.assertEqual(410.0, self.trm.unpack_value("SDOT", bytes([16, 16, 4, 0, 0]), 0)) + self.assertEqual(350.0, self.trm.unpack_value("SDOT", bytes([29, 172]), None)) + self.assertEqual(410.0, self.trm.unpack_value("SDOT", bytes([16, 16, 4]), None)) + self.assertEqual(0.0, self.trm.unpack_value("SDOT", bytes([16, 0, 0]), 0)) + self.assertEqual(0.0, self.trm.unpack_value("SDOT", bytes([16]), None)) + self.assertEqual(0, self.trm.unpack_value("DOT0", bytes([0]), None)) + self.assertEqual(99, self.trm.unpack_value("DOT0", bytes([153]), None)) + self.assertEqual(304, self.trm.unpack_value("DOT0", bytes([3, 4]), None)) + self.assertEqual(304, self.trm.unpack_value("DOT0", bytes([3, 4, 0, 0]), 0)) + self.assertEqual(987654321, self.trm.unpack_value("DOT0", bytes([9, 135, 101, 67, 33]), None)) + self.assertEqual(123.455078125, self.trm.unpack_value("F24", bytes([66, 246, 233]), None)) + self.assertEqual((71, 46059), self.trm.unpack_value("U24", bytes([71, 179, 235]), None)) + self.assertEqual(1234, self.trm.unpack_value("U16", bytes([4, 210]), None)) + self.assertEqual(-1234, self.trm.unpack_value("I16", bytes([251, 46]), None)) + self.assertEqual(12, self.trm.unpack_value("U8", bytes([12]), None)) + self.assertEqual(-12, self.trm.unpack_value("I8", bytes([244]), None)) + self.assertEqual("ТРМ202", self.trm.unpack_value("STR", bytes([50, 48, 50, 204, 208, 210]), None)) + self.assertRaises(OwenError, lambda: self.trm.unpack_value("F32", bytes([253]), None)) # if error code + + def test_make_packet(self) -> None: + self.assertEqual(b"#GHHGHUTIKGJI\r", self.trm.make_packet(1, "A.LEN", None, b"")) + self.assertEqual(b"#GHHISOOGGGGGQSUR\r", self.trm.make_packet(1, "DON", 0, b"")) + self.assertEqual(b"#GHGLJPVJGGGGGGGGGGGRJJ\r", self.trm.make_packet(0, "FB", 0, bytes([0, 0, 0]))) + self.assertEqual(b"#GHGLUHNTSJKNUMGGGGLPTV\r", self.trm.make_packet(0, "SL.L", 0, bytes([195, 71, 230]))) + self.assertEqual(b"#GHGHRNIUGGMJSQ\r", self.trm.make_packet(0, "SBIT", None, bytes([0]))) + self.assertEqual(b"#GHGLPHGNKHSOGGGGGGJOMV\r", self.trm.make_packet(0, "SP", 0, bytes([65, 200, 0]))) + self.assertEqual(b"#GHGHRNMGGORMUL\r", self.trm.make_packet(0, "BPS", None, bytes([8]))) + + self.assertEqual(b"#JIHIPHGNGGGGJHVJ\r", self.trm11.make_packet(1, "SP", 0, b"")) + self.assertEqual(b"#JIGLPHGNKHRHPQGGGGUMHO\r", self.trm11.make_packet(0, "SP", 0, bytes([65, 177, 154]))) + self.assertIsInstance(self.trm11.make_packet(0, "SP", 0, bytes([65, 177, 154])), bytes) + + def test_parse_response(self) -> None: + self.assertEqual(bytes([0]), self.trm.parse_response(b"#GHHGHUTIKGJI\r", b"#GHGHHUTIGGJKGK\r")) + self.assertEqual(bytes([0, 0, 0]), self.trm.parse_response(b"#GHHISOOGGGGGQSUR\r", b"#GHGJSOOGGGGGGGUQRK\r")) + self.assertEqual(bytes([195, 71, 230, 0, 0]), self.trm.parse_response(b"#GHHIUHNTGGGGPULL\r", b"#GHGLUHNTSJKNUMGGGGLPTV\r")) + self.assertEqual(bytes([52, 48, 48, 48, 46, 51, 48, 86]), self.trm.parse_response(b"#GHHGITLRRKVN\r", b"#GHGOITLRJKJGJGJGIUJJJGLMUPPR\r")) + self.assertEqual(bytes([71, 180, 101]), self.trm.parse_response(b"#GHHGGIJJRIQN\r", b"#GHGJGIJJKNRKMLLNJK\r")) + self.assertEqual(bytes([100]), self.trm.parse_response(b"#GHHGJONIJKMN\r", b"#GHGHJONIMKKIMP\r")) + self.assertRaises(OwenError, lambda: self.trm.parse_response(b"#GHHGHUTIKGJI\r", b"")) # if empty message + self.assertRaises(OwenError, lambda: self.trm.parse_response(b"#GHHGHUTIKGJI\r", b"GHHGHUTIKGJI\r")) # if first byte not '#' + self.assertRaises(OwenError, lambda: self.trm.parse_response(b"#GHHGHUTIKGJI\r", b"#GHHGHUTIKGJI")) # if last byte not '\r' + self.assertRaises(OwenError, lambda: self.trm.parse_response(b"#GHHINNRQGGGGRUIR\r", b"#GHGJGIJJKNNNRQPUSV\r")) # if error code + self.assertRaises(OwenError, lambda: self.trm.parse_response(b"#GHHIUHNTGGGGPULL\r", b"#GHGLUHNTSJKNUMGGGGLPTD\r")) # if checksum error + self.assertRaises(OwenError, lambda: self.trm.parse_response(b"#GHHGROTVJNPQ\r", b"#IJKJGIJJJHKOKNIJTO\r")) # if addresses mismatch + self.assertIsInstance(self.trm.parse_response(b"#GHHGJONIJKMN\r", b"#GHGHJONIMKKIMP\r"), bytes) + self.assertIsInstance(self.trm.parse_response(b"#GHGLUHNTJVOGGGGGGGQGIG\r", b"#GHGLUHNTJVOGGGGGGGQGIG\r"), bytes) + + +if __name__ == "__main__": + unittest.main()