AnyMath 文档

开发自定义设备支持包

本指南描述了一个场景,其中:

  1. 基于Python的自定义支持包在本地计算机上运行,您需要从 AnyMath .

  2. 还假设该支持包将在模型中使用。 AnyMath 还有他从街区打来的电话 AnyMath 功能.

  3. 这个任务可以用 恩吉。综合服务.

设备支持包的结构

最小设备模板放在目录中 设备:

my_extension/
  devices/
    mydevice/
      __init__.py
      mydevice.py
支持包(设备)必须位于 设备/<设备文件夹>/.

命名方案:

devices/debug_device/           — папка устройства
├── __init__.py                 — from .debug_device import DebugDevice
└── debug_device.py             — class DebugDevice(BaseDevice):
                                      def ping(...) -> str:

Julia: EngeeDeviceManager.Devices.DEBUGDEVICE
Блок:  using .EngeeDeviceManager.Devices.DEBUGDEVICE
       dev = DEBUGDEVICE.DebugDevice()

文件夹名称 debug_device,类名 [医]DebugDevice 和Julia模块的名称 [医]DEBUGDEVICE 受大小写转换规则约束: 蛇类,蛇类骆驼箱 (Python类)→ UPPER_CASE (Julia是一个模块)。

设备类(Python)

强制性要求:

  1. 继承自 N.基地,基地.

  2. 所有参数类型的完整注释和公共方法的返回值。

  3. 打算从块调用的方法不应该以 _ -这些方法将无法从脚本和块中获得。

设备类的示例:

from devices.base_device import BaseDevice
from main_logger import MainLogger


class MyDeviceLogger(MainLogger):
    pass


logger = MyDeviceLogger()


class MyDevice(BaseDevice):
    def __init__(self) -> None:
        pass

    def connect(self, host: str, port: int) -> bool:
        logger.info("Connect {}:{}", host, port)
        return True

    def read_value(self, channel: int) -> float:
        return 12.34

请注意,从内置类继承时 主博客 可以为自定义支持包实现多语言日志记录。 例如:

from collections import defaultdict
from typing import Any

from main_logger import MainLogger


class MyDeviceLogger(MainLogger):
    def __init__(self) -> None:
        self.tr: Any = defaultdict(dict)

        self.tr = {
            "Connect {}:{}": {
                "ru": "Подключение к {}:{}",
            },
            "Read failed: {}": {
                "ru": "Ошибка чтения: {}",
            },
        }

用户设备 添加至*Engee。综合服务*, 同步AnyMath 之后它可以在脚本和块中使用。

您可以清楚地看到上面描述的用于开发自己的支持包的机制是如何在实践中应用于使用社区示例的真实硬件的。: 为Engee开发硬件支持包。整合

从Engee功能块调用

在 *通用代码*从连接开始 Engee.集成,如在标准 .nglib 街区 硬件,其源代码是开放的。

package_dir = "/internal_persistent_vol/support_packages/locations/Engee-Device-Manager/EngeeDeviceManager.jl"
include("$(package_dir)/src/EngeeDeviceManager.jl")

using .EngeeDeviceManager
using .EngeeDeviceManager.Devices.MYDEVICE

然后使用通常的方法调用。:

mutable struct Block <: AbstractCausalComponent
    dev::MYDEVICE.MyDevice
    function Block()
        d = MYDEVICE.MyDevice()
        d.connect(host, Int(port))
        new(d)
    end
end

function (c::Block)(t::Real, channel)
    return c.dev.read_value(Int(channel))
end

这里 梅迪维斯 -这是与类名对应的用户支持包模块的名称 梅迪维斯 大写。

一个包含参数、初始化和完成的块的完整示例

一个典型的设备 UDP 接收器*用途 *通用代码 三个功能:

package_dir = "/internal_persistent_vol/support_packages/locations/Engee-Device-Manager/EngeeDeviceManager.jl"
include("$(package_dir)/src/EngeeDeviceManager.jl")

using .EngeeDeviceManager
using .EngeeDeviceManager.Devices.SOCKET

mutable struct Block <: AbstractCausalComponent
    socket::SOCKET.Socket
    function Block()
        socket = SOCKET.Socket("AF_INET", "SOCK_DGRAM")
        SOCKET.bind(socket, ip, port)
        new(socket)
    end
end

function (c::Block)(t::Real)
    response = SOCKET.receive(c.socket, buf_size)
    if response === nothing
        return 0, zeros(UInt8, buf_size)
    elseif length(response.data) == 0
        return 0, zeros(UInt8, buf_size)
    end
    return length(response.data), resize!(response.data, buf_size)
end

function terminate!(c::Block)
    SOCKET.close(c.socket)
    return nothing
end

分析结构:

  1. 构造函数 座()-模拟运行时调用一次。 在其中创建设备的实例并调用初始化方法(绑定, 打开, 连接 等等)。 块参数(知识产权, 港口)取自掩码,可作为构造函数参数使用。

  2. 功能 (c::Block)(t::Real)-在模拟的每一步调用。 这是块的主体。 这里 t -当前时间,嗯 c.领域 -访问结构的字段。 返回的值对应于块的输出端口的顺序。

  3. 功能 终止!(c::座)-在模拟结束时调用。 它释放了资源:关闭套接字,文件和连接。 她的挑战是可选的。

块参数*通过掩码设置(假面价值.nglib). 在 *通用代码 从面具上用他们的名字来称呼他们: 知识产权, 港口, buf_大小, 采样时间.

以相同的方式启用自定义支持包。 一开始 通用代码 请务必指定模块的路径:

package_dir = "/internal_persistent_vol/support_packages/locations/Engee-Device-Manager/EngeeDeviceManager.jl"
include("$(package_dir)/src/EngeeDeviceManager.jl")

using .EngeeDeviceManager
using .EngeeDeviceManager.Devices.MYDEVICE

mutable struct Block <: AbstractCausalComponent
    dev::MYDEVICE.MyDevice
    function Block()
        d = MYDEVICE.MyDevice()
        d.connect(host, port)
        new(d)
    end
end

function (c::Block)(t::Real)
    return c.dev.read_value(channel)
end

function terminate!(c::Block)
    # закрытие соединения, если необходимо
    return nothing
end

常见问题

  1. 块显示消息 未找到方法.

    原因:方法是私有的(_…)或者没有完整的类型注释。

  2. 自定义支持包不会导入到 通用代码 .

    原因:同步失败 syncExtensions(). 执行 恩吉。clear_all() 在命令行上 AnyMath .

  3. 模块中的函数签名已更改,但函数的工作方式相同。

    原因:在 AnyMath 旧版本的支持包仍然存在。 重复一遍 syncExtensions(). 如果函数尚未更新,请运行 恩吉。clear_all() 在命令行上 AnyMath .