Разработка нового коннектора
----------------------------
.. 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: