@vention/machine-logic-ui-sdk

中文文档
0.47.6发布于 2026/11/03MITTypeScriptES Module
中文使用文档
文档目录5 节
  1. MQTT上的实时数据
  2. 输入主题
  3. 原始客户端
  4. 与应用程序的后端对话
  5. 触摸屏和控制中心

@vention / machine-logic-ui-sdk

用于MachineLogic应用程序的自定义HMI的浏览器助手,应用程序中的React应用程序 customui/ 挂件和控制中心在iframe中加载的文件夹。它为HMI提供了来自机器的MQTT代理的实时数据、执行引擎和应用程序自己的后端进程的url、用于触摸挂件的屏幕上键盘,以及关闭HMI或将控制中心移动到另一个页面的消息。将它安装在应用程序的确切版本上 customui/package.json:

npm install @vention/machine-logic-ui-sdk@<pinned-version>

人机界面需要什么?

  • 反应17或19。包的 peerDependencies 接受 react ^17.0.2 || ^19.3.0。SDK针对两个版本进行类型检查 typecheck-react19 目标);它的单元测试运行在React 17上。
  • @mui/material 和 @emotion/react。包条目导入 tss-react/mui (用 VirtualKeyboard),因此导入任何导出都会将其拉入。
  • 一个处理CSS导入的打包器,比如Vite。条目导入 react-simple-keyboard/build/css/index.css.

在平台-产品单线程中,也可以通过 @ventionco/machine-logic-ui-sdk 通过TypeScript路径别名。应用程序安装已发布的 @vention/machine-logic-ui-sdk.

每个导出的参数、prop和返回类型都在包的类型声明中,您的编辑器可以使用这些类型声明 tsc 阅读。在那里寻找签名;这个自述文件只涵盖了类型不能显示的行为。

MQTT上的实时数据

将应用程序包装一次 MqttProvider。它打开一个MQTT-over-websocket连接 useMqttSubscription 呼叫下面它共享,并重新连接自己。

  • version 默认为 "v2" 并选择哪个代理地址派生,通过 getMqttWsUrl。在边缘,两者都使用未加密的websocket: "v2" 连接到 192.168.5.2:9001 和 "v3" 连接端口 9001 在服务HMI的主机上。在云框架中,两者使用相同的 wss:// URL: wss://<the HMI's host><mqttPath> 当主机页面给出 mqttPath 查询以。开头的参数 /,否则从iframe URL派生的URL。
  • url 自己设置代理websocket URL。如果设置了, version 和URL检测被跳过。例如,将其用于独立开发 ws://localhost:9001.

从 projects/machine-code/apps/ai-operator/frontend/src/main.tsx修剪:

import { StrictMode } from "react"
import ReactDOM from "react-dom"
import { MqttProvider } from "@vention/machine-logic-ui-sdk"

import App from "./app/app"

ReactDOM.render(
  <StrictMode>
    <MqttProvider version="v3">
      <App />
    </MqttProvider>
  </StrictMode>,
  document.getElementById("root")
)

对于iframe之外的开发,您可以自己传递代理,例如使用 VITE_MQTT_WS_URL=ws://localhost:9001。如果不设置,提供者将返回到检测:

<MqttProvider version="v3" url={import.meta.env.VITE_MQTT_WS_URL}>
  <App />
</MqttProvider>

阅读一个主题 useMqttSubscription。它返回关于该主题的最新消息,或者 undefined 直到第一个人到来。对每个主题进行一次代理订阅,因此只有第一个读取主题的组件才能获得保留的消息;稍后为同一主题挂载的组件将返回 undefined 直到下次出版。

  • topic 必须是一个准确的主题。钩子将每个传入主题与此字符串进行比较 ===,所以通配符过滤器如 operation/bins/+ 订阅代理,但从不更新返回值。使用 MqttClient 通配符。
  • unsubscribeOnUnmount,默认关闭,在组件卸载时在代理上取消订阅。代理订阅是跨提供者为每个主题共享的,因此这也会为仍在读取该主题的任何其他组件停止该主题。每当连接状态改变时,清理也会运行,并且钩子在连接后再次订阅。
  • 每个有效载荷都通过 JSON.parse。在HMI读取的主题上发布JSON;无效JSON的有效负载会在消息处理程序中抛出。type参数是一个断言,而不是运行时检查。
  • 没有订阅,直到提供者的 connectionStatus 是 "connected"。在重新连接之后,钩子再次订阅。

从 projects/machine-code/apps/ai-operator/frontend/src/app/contexts/operation-context.tsx修剪:

import { useEffect, useState } from "react"
import { useMqttSubscription } from "@vention/machine-logic-ui-sdk"

interface BinStatus {
  cardState: "normal" | "missing"
  pickedCount?: number
  averageCycleTime?: number
}

export const OperationStatus = () => {
  const [isOperationRunning, setIsOperationRunning] = useState(false)

  const operationActive = useMqttSubscription<boolean>({
    topic: "operation/operationActive",
    unsubscribeOnUnmount: true,
  })

  const bin1Status = useMqttSubscription<BinStatus>({
    topic: "operation/bins/1",
    unsubscribeOnUnmount: true,
  })

  useEffect(
    function handleOperationActiveFromMqtt() {
      if (operationActive === undefined) return
      setIsOperationRunning(operationActive)
    },
    [operationActive]
  )

  return (
    <p>
      {isOperationRunning ? "Running" : "Stopped"}, bin 1 picked {bin1Status?.pickedCount ?? 0}
    </p>
  )
}

mqttContext 是提供程序填充的React上下文。阅读它 useContext(mqttContext):

  • client:供应商的 mqtt.js MqttClient (不是这个包裹 MqttClient 类),或 null 在第一次连接之前。用它来发布。
  • connectionStatus:提供者的连接状态。
  • subscribedTopics, addSubscribedTopic, removeSubscribedTopic:供应商的记账 useMqttSubscription。应用程序不需要它们。
import { useContext } from "react"
import { mqttContext } from "@vention/machine-logic-ui-sdk"

export const StopButton = () => {
  const { client, connectionStatus } = useContext(mqttContext)

  return (
    <button disabled={connectionStatus !== "connected"} onClick={() => client?.publish("operation/command", JSON.stringify({ action: "stop" }))}>
      Stop
    </button>
  )
}

输入主题

这个 MqttPayloadByTopic Type在主题映射中查找主题的有效负载类型。将应用程序的主题声明为该映射:

import { MqttPayloadByTopic, useMqttSubscription } from "@vention/machine-logic-ui-sdk"

type OperationTopics = {
  "operation/operationActive": boolean
  "operation/bins/1": { cardState: "normal" | "missing"; pickedCount?: number }
}

type BinStatus = MqttPayloadByTopic<OperationTopics, "operation/bins/1">

export function useOperationTopic<Topic extends keyof OperationTopics & string>(topic: Topic) {
  return useMqttSubscription<MqttPayloadByTopic<OperationTopics, Topic>>({ topic, unsubscribeOnUnmount: true })
}

// useOperationTopic("operation/operationActive") returns boolean | undefined

原始客户端

MqttClient 是mqtt.js的一个小包装器,用于React之外的代码或通配符订阅。

  • 构造函数立即连接。它的可选回调在每个成功的连接上运行,包括在重新连接之后。
  • connect 是构造函数调用的。再次调用它会打开第二个连接;应用程序不需要这样做。
  • subscribe(topic, regex, callback) 订阅 topic 在代理(MQTT通配符) + 和 # 是允许的)和呼叫 callback 对于主题匹配的每个传入消息 regex.
  • unsubscribe(topic, regex, callback) 移除那个回调,并取消订阅 topic 在代理上,一旦没有剩余的回调使用相同的 regex.
  • publish 发布字符串,除非传递,否则不保留 retain。序列化对象 JSON.stringify 你自己。
  • 一、 Callback 以字符串形式接收主题和原始有效负载;负载没有被解析。

在第一个连接打开之前进行的调用将排队,并在第一个连接打开后发送。关闭后,客户端在重新连接之前被断开约一秒钟;在这段时间里 publish 无声地落下 subscribe 永远不会到达代理,并且一旦重新连接,客户端不会重新发送在旧连接上进行的代理订阅。对于长期使用的React HMI,首选 MqttProvider 和 useMqttSubscription,重新连接后重新订阅。

import { Callback, MqttClient, getMqttWsUrl } from "@vention/machine-logic-ui-sdk"

const BIN_TOPIC_FILTER = "operation/bins/+"
const BIN_TOPIC_PATTERN = /^operation\/bins\/\d+$/

const onBinStatus: Callback = (topic, message) => {
  const status = JSON.parse(message)
  console.log(topic, status.pickedCount)
}

export async function watchBins(): Promise<MqttClient> {
  const client = new MqttClient(await getMqttWsUrl("v3"), () => console.log("MQTT connected"))
  client.subscribe(BIN_TOPIC_FILTER, BIN_TOPIC_PATTERN, onBinStatus)
  client.publish("operation/command", JSON.stringify({ action: "start" }))
  return client
}

// Later: client.unsubscribe(BIN_TOPIC_FILTER, BIN_TOPIC_PATTERN, onBinStatus)

URL助手:

  • connectToMqttAndGetClient 构建一个客户端 await getMqttWsUrl(),也就是 "v2" 代理地址。它不需要争论;为 "v3" 构造 new MqttClient(await getMqttWsUrl("v3")) 代替。
  • getMqttWsUrl 返回代理websocket URL version,默认为 "v2"。当 isOnEdge() 为true,则返回一个未加密的websocket URL: 192.168.5.2:9001 用于 "v2" 和端口 9001 的当前主机名 "v3"。否则,当HMI的URL有 mqttPath 以开头的查询参数 / 当SDK加载时,由主机页面设置,它返回 wss://<the HMI's host><mqttPath>。否则它会返回 getMqttWsUrlGivenUiIframeUrl(window.location.href).
  • getMqttWsUrlGivenUiIframeUrl 从远程iframe URL派生云代理URL。它将URL保持到并包括 digital-twin 路径段、开关 https: 来 wss:,然后追加后面的路径段 passthrough 和 /mqtt: wss://<host>/.../digital-twin/<token>/mqtt.

与应用程序的后端对话

它们返回要传递的普通字符串 fetch 或RPC传输,例如 createConnectTransport({ baseUrl }) 从 @connectrpc/connect-web.

  • getExecutionEngineHttpUrl 返回执行引擎的基础URL: http://<current hostname>:3100 在边上,否则一切都在 window.location.href 之前 /machineCodeUi (如果该段不存在,则为整个URL)。
  • getMachineCodeProcessHttpUrl 接受进程名并返回 <execution engine URL>/v1/machineCode/services/<processName>,执行引擎的一个应用程序进程的代理。
  • getMachineCodeApiBaseUrl 返回 getMachineCodeProcessHttpUrl(processName) 紧随其后的是 /api.
import { getMachineCodeApiBaseUrl } from "@vention/machine-logic-ui-sdk"

export async function fetchStatus(): Promise<unknown> {
  const response = await fetch(`${getMachineCodeApiBaseUrl("my-backend")}/status`)
  return response.json()
}

"my-backend" 表示注册应用程序后端进程的名称,和 /status 对于后端服务的路由。

触摸屏和控制中心

VirtualKeyboard 是一个没有道具的屏幕键盘组件。渲染一次,靠近布局的根。它监听文档中任何输入的焦点,并在150毫秒后显示出来;当用户按下提交键,或者在键盘和任何输入之外点击时,它隐藏起来。它在隐藏的时候什么也不呈现。可见时,它是 position: sticky 面板,视窗高度的45%,在其父面板的底部,它将聚焦的输入滚动到视图中。

  • 文本布局包括 text, password, email, search, url, month, datetime-local, time, <textarea>; 数字布局包括 tel, date, week。其他输入类型没有键盘。
  • type="number" 不支持:键盘记录警告并将输入更改为 type="tel"。使用 type="tel" 用于数字字段。
  • 值通过本机值设置器写入,后面跟着 input 事件,所以React onChange 处理程序。一个 change 如果值改变,则在模糊时分派事件。

isTouchScreenDevice 当 "ontouchstart" in window 或者 navigator.maxTouchPoints > 0。把键盘关在上面:

import { ReactNode } from "react"
import { VirtualKeyboard, isTouchScreenDevice } from "@vention/machine-logic-ui-sdk"

export const AppLayout = ({ children }: { children: ReactNode }) => (
  <div style={{ display: "flex", flexDirection: "column", height: "100vh", overflow: "auto" }}>
    <main style={{ flexGrow: 1 }}>{children}</main>
    {isTouchScreenDevice() ? <VirtualKeyboard /> : null}
  </div>
)

isOnEdge 当 window.location.hostname 开始于 192.168 或者 localhost这意味着人机界面是通过机器的网络提供的,而不是通过云。上面的URL帮助器在它的基础上进行分支。一个开发服务器 localhost 这也是为什么独立开发会过时的原因 MqttProvider 一个显式的 url.

HMI在iframe中运行。这些帖子给 window.parent 主机页要执行的操作:

  • closeCustomUi 的帖子 "closeCustomUi",要求主机关闭HMI。
  • navigateControlCenter 的帖子 "navigateToRemoteSupport" 用于 "remoteSupport" 或者 "navigateToControlCenterHomepage" 用于 "controlCenterHomepage",将主机移至远程支持页面或控制中心首页。
import { closeCustomUi, navigateControlCenter } from "@vention/machine-logic-ui-sdk"

export const ExitBar = () => (
  <nav>
    <button onClick={() => navigateControlCenter("remoteSupport")}>Get help</button>
    <button onClick={closeCustomUi}>Exit</button>
  </nav>
)

在框架之外, window.parent 是页面本身,因此这些消息不会到达任何地方,除非页面侦听它们。

版本与发布记录

@vention/machine-logic-ui-sdk 共发布 230 个版本,首次发布于 2024/08/27;过去 12 个月发布了 186 个版本。

版本发布日期距今
0.47.6latest2026/11/03今天
0.47.52026/10/313 天前
0.47.42026/10/304 天前
0.47.32026/10/295 天前
0.47.22026/10/295 天前
0.47.12026/10/277 天前

其他发布通道:dev → 0.0.0-dev.4527.a3d343ca7。

关于 @vention/machine-logic-ui-sdk 的常见问题

如何安装 @vention/machine-logic-ui-sdk?

在项目目录运行 npm install @vention/machine-logic-ui-sdk,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(^17.0.2 || ^19.3.0)、@mui/material(^5.16.7)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@vention/machine-logic-ui-sdk 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@vention/machine-logic-ui-sdk 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@vention/machine-logic-ui-sdk 可以商用吗?

@vention/machine-logic-ui-sdk 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@vention/machine-logic-ui-sdk 还在维护吗?

最新版本 0.47.6 发布于 2026/11/03(今天);至今共发布 230 个版本,其中过去 12 个月发布了 186 个;首次发布于 2024/08/27。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。