.. _external: Внешние программы ================= .. _METLAB: https://dipaul.ru/catalog/element/metlab/ Программа MeterVision достигает максимальной эффективности в сочетание с другими программами, которые способны обрабатывать результаты распознавания машинного зрения. Например, такой программой может быть `METLAB`_ или программа пользователя Для этих целей предусмотрены возможности отправлять результаты распознавания из данной программы во внешние двумя способами (файлы в ``output`` и сервер socket; включение режимов — в :ref:`res`). Также из внешних программ можно управлять некоторыми функциями программы MeterVision двумя способами: 1. Через текстовый файл ``input/command.txt`` в :ref:`catalog`. 2. По **socket** (если включено «Передавать результаты по socket» в :ref:`settings`) — тем же синтаксисом команд, каждая команда на отдельной строке; сервер отвечает ``OK`` / ``ERROR: ...`` (или результатами на команду ``get``). В файл или по socket можно передать сразу несколько команд: каждая команда на отдельной строке. Порядок выполнения — от первой строки к последней. Возможны следующие команды: 1. Закрыть программу MeterVision .. code-block:: exit 2. Сохранить :ref:`screen` * В папку по умолчанию (output/screenshots/ в :ref:`catalog`) с названием файла по умолчанию (дата и время создания) .. code-block:: screenshot * В папку по умолчанию с другим названием .. code-block:: screenshot my_name.png * В указанную папку с другим названием .. code-block:: screenshot path/to/my/dir/my_name.png 3. Открыть файл настроек зон .. code-block:: open path/to/my/dir/settings.mv 4. Зафиксировать фон (только для стрелочных методов) .. code-block:: fix_background n Где n - номер целевой зоны 5. Снять фиксацию фона (только для стрелочных методов) .. code-block:: unfix_background n Где n - номер целевой зоны 6. Зафиксировать центр (только для стрелочных методов) .. code-block:: fix_center n Где n - номер целевой зоны 7. Снять фиксацию центра (только для стрелочных методов) .. code-block:: unfix_center n Где n - номер целевой зоны 8. Забыть статистику зоны (только для стрелочных методов) .. code-block:: forget_stats n Где n - номер целевой зоны 9. Забыть фон (только для стрелочных методов) .. code-block:: forget_background n Где n - номер целевой зоны 10. Переключиться на другую шкалу (только для стрелочных методов) .. code-block:: scale n m Где n - номер целевой зоны, m - номер шкалы, на которую нужно переключиться 11. Переключить режим отображения результата (только для стрелочных методов) * Результат — показания по шкале .. code-block:: result_mode n scale * Результат — угол наклона стрелки (в градусах) .. code-block:: result_mode n angle Где n - номер целевой зоны Пример файла input/command.txt -------------------------------- .. code-block:: screenshot my_name.png unfix_background 1 fix_center 1 scale 1 3 result_mode 1 angle Передача команд и результатов по socket ---------------------------------------- При включённом режиме socket (см. :ref:`res`) внешняя программа подключается к host/port из ``input/socket_config.txt`` и отправляет текстовые строки с переводом строки в конце. Разделитель команд и ответов — перевод строки. За одно подключение можно отправить несколько команд; на каждую команду сервер отвечает одной строкой (для ``get`` — строка с результатами вида ``{1: '24.10', 2: 'AC'}``, для остальных — ``OK`` или ``ERROR: ...``). * ``get`` или ``results`` — получить текущие результаты распознавания (один ответ на запрос; на одном соединении команду можно повторять сколько угодно раз). * Любая команда из списка выше — выполнить действие; ответ ``OK`` или ``ERROR: ...``. Пример последовательности команд: .. code-block:: get fix_center 1 scale 1 2 Пример получения результатов (файл ``examples/socket_results_example.py``). На одном соединении можно многократно вызывать ``get_results()``: .. code-block:: python import socket import time class MeterVisionClient: def __init__(self, host='localhost', port=65432): self.host = host self.port = port self.sock = None self._buffer = '' def connect(self): """Подключиться к серверу MeterVision.""" self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect((self.host, self.port)) self._buffer = '' print(f"Подключено к серверу {self.host}:{self.port}") def get_results(self): """ Запросить текущие результаты. Можно вызывать многократно на одном соединении. Возвращает строку вида "{1: '24.10', 2: 'AC'}". """ if self.sock is None: raise RuntimeError('Сначала вызовите connect()') self.sock.sendall(b'get\n') while '\n' not in self._buffer: chunk = self.sock.recv(4096) if not chunk: raise ConnectionError('Сервер закрыл соединение') self._buffer += chunk.decode('utf-8') line, self._buffer = self._buffer.split('\n', 1) return line def close(self): """Закрыть соединение.""" if self.sock is not None: self.sock.close() self.sock = None print('Соединение закрыто') if __name__ == "__main__": client = MeterVisionClient() try: client.connect() for i in range(5): results = client.get_results() print(f"[{i + 1}] Получено:", results) time.sleep(1) except ConnectionRefusedError as e: print(e) except KeyboardInterrupt: print('Соединение прервано') finally: client.close() Пример отправки команд (файл ``examples/socket_commands_example.py``): .. code-block:: python import socket def send_commands(commands, host='localhost', port=65432): """ Отправляет список команд и печатает ответы сервера. Args: commands: список строк-команд без завершающего \\n host, port: адрес сервера MeterVision """ client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: client_socket.connect((host, port)) print(f"Подключено к серверу {host}:{port}") except ConnectionRefusedError as e: print(e) return try: payload = ''.join(cmd.strip() + '\n' for cmd in commands if cmd.strip()) client_socket.sendall(payload.encode('utf-8')) buffer = '' replies_expected = sum(1 for cmd in commands if cmd.strip()) replies_got = 0 while replies_got < replies_expected: data = client_socket.recv(4096) if not data: break buffer += data.decode('utf-8') while '\n' in buffer: line, buffer = buffer.split('\n', 1) print("Ответ:", line) replies_got += 1 if replies_got >= replies_expected: break except KeyboardInterrupt: print('Соединение прервано') finally: client_socket.close() if __name__ == "__main__": # Пример: запросить результаты и выполнить команды для стрелочной зоны send_commands([ 'get', 'fix_center 1', 'result_mode 1 scale', ]) .. note:: Эти примеры также находятся в ``examples/socket_results_example.py`` и ``examples/socket_commands_example.py`` относительно директории установки программы.