Разработка нового коннектора ---------------------------- .. attention:: В документации используется термин "привязка": тег "привязан" к коннектору. Это означает, что коннектор должен поставлять данные для этого тега. .. note:: Предположим, что создан проект для нового коннектора и установлен пакет ``prs_connector_core``. Самый простой коннектор для протокола InduX ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ В качестве примера напишем самый простой коннектор для некоторого выдуманного протокола ``InduX``. Допустим, протокол не предполагает подписки на изменения значения параметров, и данные клиенту необходимо читать самому с определённой частотой. Кроме того, для того, чтобы не возиться с низкоуровневым разбором байтов, предположим, что у нас есть пакет ``py_indux`` для чтения данных с устройств, работающих по протоколу ``InduX``. Предварительная работа """""""""""""""""""""" Перед разработкой коннектора необходимо определить: #. Способ связи с устройством, работающим по этому протоколу и, соответственно, какая информация необходима коннектору, чтобы он мог связаться с источником данных. #. Как получать данные для определённого параметра (тега) из источника. .. admonition:: Пример для протокола Modbus RTU :class: dropdown Для связи с устройством необходимо знать: * порт, на котором "висит" устройство; * скорость передачи данных; * количество бит в байте; * проверка чётности; * количество стоп-бит; * таймаут Таким образом, коннектору для связи с устройством необходимо передать всю эту информацию примерно в такой структуре: .. code-block:: python { "port": "/dev/ttyUSB0", "baudrate": 115200, "bytesize": 8, "parity": "N", "stopbits": 1, "timeout": 3 } Для чтения данных какого-либо параметра необходимо знать: * тип регистров * номер стартового регистра * количество регистров для чтения * тип хранимого значения * номер устройства Таким образом, каждый тег, привязанный к коннектору, должен содержать примерно такую структуру: .. code-block:: python { "regType": "H", # H - holding, D - discrete, C - coils, I - input "address": 6, # адрес регистра для чтения "count": 2, # количество регистров, которые необходимо прочитать "slave": 1, # номер устройства "dataType": "FLOAT32", # тип хранимого значения "frequency": 5 # частота (в секундах), с которой необходимо читать значение тега } Предположим, что к коннектору привязан только один тег. Тогда, в конечном итоге, при старте коннектор должен получить полную конфигурацию от платформы примерно такого вида: .. code-block:: python { "action": "prsConnector.full_configuration", "data": { "prsActive": True, # коннектор активен "prsEntityTypeCode": None, # не обращаем внимания "prsJsonConfigString": { "source": { # как подключаться к источнику данных "port": "/dev/ttyUSB0", "baudrate": 115200, "bytesize": 8, "parity": "N", "stopbits": 1, "timeout": 3 }, "log": { # конфигурация журнала логов "level": "INFO", "fileName": "logs/prs_connector.log", "maxBytes": 10485760, "backupCount": 10 } }, "tags": { # список тегов, привязанных к коннектору "ac620c9e-8fd1-103f-9850-bf1c6e0c417e": { # идентификатор тега "prsActive": True, # флаг активности тега "prsJsonConfigString": { "source": { # способ получения данных тега из источника "regType": "H", "address": 6, "count": 2, "slave": 1, "dataType": "FLOAT32", "frequency": 5 }, "maxDev": 0.5, # минимальное значимое отклонение значения "JSONata": "$abs($)>0?1:0" # предположим, что в источнике данных # по указанному адресу хранится мгновенное значение # тока # а наш тег - это флаг того, работает устройство или нет # соответственно, если ток > 0, то устройство работает # записанное в этом ключе JSONata выражение # будет делать то, что нам надо: # возращать 1, если ток > 0 и 0 в противном случае }, "prsValueTypeCode": 0 # тип значений тега: # 0 = int } } } } Допустим, протокол ``InduX`` очень прост: #. Устройство подключается через порт USB и имя подключаемого порта - ``/dev/ttyUSB0``. #. Для чтения текущего значения какого-либо параметра из устройства достаточно указать его имя. .. attention:: Для упрощения кода примем, что теги читаются из источника с одной, общей для всех частотой, поэтому эта частота указывается в конфигурации всего коннектора, а не для каждого тега отдельно. Таким образом, полная конфигурация, которая будет приходить коннектору, примет примерно такой вид: .. code-block:: python { "action": "prsConnector.full_configuration", "data": { "prsActive": True, # коннектор активен "prsEntityTypeCode": None, # не обращаем внимания "prsJsonConfigString": { "source": { # как подключаться к источнику данных "port": "/dev/ttyUSB0", "baudrate": 115200, "bytesize": 8, "parity": "N", "stopbits": 1, "timeout": 3, "frequency": 1 # частота чтения данных из источника }, "log": { # конфигурация журнала логов "level": "INFO", "fileName": "logs/prs_connector.log", "maxBytes": 10485760, "backupCount": 10 } }, "tags": { # Список тегов, привязанных к коннектору. "ac620c9e-8fd1-103f-9850-bf1c6e0c417e": { # Идентификатор тега. # Допустим, тег - это ток, # причём источник отдаёт данные в виде # целого числа, в миллиамперах. # Модель же, реализованная в платформе, предполагает, # что ток - вещественное числои и измеряется в амперах. # Соответственно, прочитанное значение нам нужно # разделить на 1000. "prsActive": True, # флаг активности тега "prsJsonConfigString": { "source": { # способ получения данных тега из источника "name": "current" }, "maxDev": 0.1, # минимальное значимое отклонение - 0.1 А "JSONata": "$/1000" # выражение преобразует миллиамперы в амперы }, "prsValueTypeCode": 0 # тип значений тега: # 1 = float } } } } Код коннектора """""""""""""" .. literalinclude:: prs_indux_connector.py :language: python :linenos: