外部程序

MeterVision 程序在与其他能够处理*机器视觉识别结果*的程序结合使用时可达到最佳效果。 例如,这类程序可以是 METLAB 或用户自编的软件。

为此,程序提供了两种方式将*识别结果*发送到外部程序 (output 中的文件与 socket 服务器;在 识别结果 中启用相应模式)。

同时,也可以通过外部程序以两种方式控制 MeterVision 的某些功能:

  1. 通过 程序目录 下的文本文件 input/command.txt。

  2. 通过 socket(需在 设置 中启用「通过 socket 传输结果」)——使用相同的命令语法; 每条命令单独一行;服务器回复 OK / ERROR: ...``(对 ``get 则返回结果)。

可在文件或 socket 中一次发送多条命令:每条命令单独占一行。 命令按从第一行到最后一行的顺序执行。

可使用的命令如下:

  1. 关闭 MeterVision 程序

exit
  1. 保存 截图

  • 保存到默认文件夹(output/screenshots/ 在 程序目录 中),文件名使用默认名称(创建日期和时间)

screenshot
  • 保存到默认文件夹,但使用自定义文件名

screenshot my_name.png
  • 保存到指定文件夹,并使用自定义文件名

screenshot path/to/my/dir/my_name.png
  1. 打开区域设置文件

open path/to/my/dir/settings.mv
  1. 固定背景(仅限指针方法)

fix_background n

其中 n 为目标区域编号

  1. 取消背景固定(仅限指针方法)

unfix_background n

其中 n 为目标区域编号

  1. 固定中心(仅限指针方法)

fix_center n

其中 n 为目标区域编号

  1. 取消中心固定(仅限指针方法)

unfix_center n

其中 n 为目标区域编号

  1. 忘记区域统计(仅限指针方法)

forget_stats n

其中 n 为目标区域编号

  1. 忘记背景(仅限指针方法)

forget_background n

其中 n 为目标区域编号

  1. 切换到其他刻度(仅限指针方法)

scale n m

其中 n 为目标区域编号,m 为要切换到的刻度编号

  1. 切换结果显示模式(仅限指针方法)

  • 结果为刻度读数

result_mode n scale
  • 结果为指针倾斜角(度)

result_mode n angle

其中 n 为目标区域编号

input/command.txt 文件示例

screenshot my_name.png
unfix_background 1
fix_center 1
scale 1 3
result_mode 1 angle

通过 socket 发送命令与获取结果

启用 socket 模式后(见 识别结果),外部程序连接到 input/socket_config.txt 中的 host/port, 并发送以换行结尾的文本行。

命令与应答以换行符分隔。一次连接可发送多条命令;服务器对每条命令回复一行 (get 的回复形如 {1: '24.10', 2: 'AC'},其他命令回复 OK 或 ERROR: ...)。

  • get 或 results — 获取当前识别结果(一次请求对应一次回复; 可在同一连接上重复调用任意次数)。

  • 上表中的任意命令 — 执行操作;回复 OK 或 ERROR: ...。

命令序列示例:

get
fix_center 1
scale 1 2

获取结果示例(examples/socket_results_example.py)。 可在同一连接上多次调用 get_results():

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):

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',
    ])

备注

上述示例也位于程序安装目录下的 examples/socket_results_example.py 与 examples/socket_commands_example.py。