///
import {
MasterController,
AgvId,
ClientOptions,
Topic,
Order,
State,
Headerless,
InstantActions,
BlockingType,
Connection,
Factsheet,
} from "vda-5050-lib";
import { v4 as uuidv4 } from "npm:uuid";
import { createWorkerEventHelper } from "./worker_event_helper.ts";
// 创建事件助手
const eventHelper = createWorkerEventHelper("vdaWorker");
console.log("VDA 5050 Worker initialized");
// 预注册设备列表(后续主程序可动态更新)
const preRegisteredDevices: AgvId[] = [
];
// 用于保存所有设备状态及动态更新设备列表
const currentDevices: Map<
string,
{ agvId: AgvId; lastSeen: number; isOnline: boolean; state?: State }
> = new Map();
// 内部方法:生成设备唯一 key
function getDeviceKey(agvId: AgvId) {
return `${agvId.manufacturer}-${agvId.serialNumber}`;
}
// 先将预注册设备保存到 currentDevices(初始状态为离线)
preRegisteredDevices.forEach((device) => {
const key = getDeviceKey(device);
currentDevices.set(key, {
agvId: device,
lastSeen: 0,
isOnline: false,
});
});
// 用于保存已经订阅状态的设备,避免重复订阅
const subscribedDevices: Set = new Set();
// 全局保存 MasterController 实例
let masterController: MasterController | null = null;
// 从初始化参数读取的接口名和制造商
let interfaceNameValue = "oagv";
let manufacturerValue = "gateway";
// 标记 Master Controller 是否启动完成
let clientStarted = false;
/**
* 当收到 init 消息后,从启动参数中提取 MQTT broker 地址,从而构造客户端选项并启动控制器
*/
function initializeControllerWithOptions(brokerUrl: string, iValue: string) {
// Validate input parameters
if (!brokerUrl || !iValue) {
console.error("❌ Invalid initialization parameters:");
console.error("brokerUrl:", brokerUrl);
console.error("interfaceName:", iValue);
return;
}
// Reset previous state
clientStarted = false;
if (masterController) {
try {
masterController.stop().catch(() => {});
} catch (e) {
// Ignore stop errors
}
masterController = null;
}
const clientOptions: ClientOptions = {
interfaceName: iValue,
transport: {
// 使用启动参数传入的 MQTT server 地址
brokerUrl: brokerUrl,
heartbeat:5,
reconnectPeriod:1000,
connectTimeout:5000,
},
vdaVersion: "2.0.0",
};
console.log(`🚀 正在初始化VDA 5050 Master Controller...`);
console.log(`📡 MQTT Broker: ${brokerUrl}`);
console.log(`🏷️ Interface: ${iValue}`);
console.log(`🏭 Manufacturer: ${manufacturerValue}`);
// Test MQTT broker connectivity
console.log(`🔍 Testing MQTT broker connectivity...`);
try {
const url = new URL(brokerUrl);
console.log(`📍 Broker host: ${url.hostname}, port: ${url.port || 1883}`);
} catch (urlError) {
console.error("❌ Invalid broker URL format:", urlError);
return;
}
try {
masterController = new MasterController(clientOptions, {});
console.log("📦 MasterController instance created successfully");
} catch (error) {
console.error("❌ Failed to create MasterController instance:", error);
return;
}
// Add timeout to prevent hanging
Promise.race([
masterController.start(),
new Promise((_, reject) =>
setTimeout(() => reject(new Error("Master controller start timeout after 30 seconds")), 30000)
)
])
.then(() => {
clientStarted = true;
console.log("✅ VDA 5050 master controller started successfully");
self.postMessage({ type: "started" });
// 跟踪所有AGV连接状态
masterController!.trackAgvs((trackAgvId, connectionState, timestamp) => {
const key = getDeviceKey(trackAgvId);
// 如果设备不在设备列表中,则不予上线
// console.log("->", key, connectionState);
// if (!currentDevices.has(key)) {
// console.warn(
// `收到未知设备 ${trackAgvId.manufacturer}/${trackAgvId.serialNumber} 的状态消息,忽略上线。`
// );
// return;
// }
const ts = Number(timestamp);
const lastSeen = isNaN(ts) ? Date.now() : ts;
// 自动添加新设备到currentDevices,无需预先配置
let record = currentDevices.get(key);
if (!record) {
// 动态添加新发现的设备
record = { agvId: trackAgvId, lastSeen: 0, isOnline: false };
currentDevices.set(key, record);
console.log(`🆕 自动添加新设备: ${trackAgvId.manufacturer}/${trackAgvId.serialNumber}`);
}
const wasOffline = !record.isOnline;
// 无条件置为 ONLINE 并更新最后更新时间
currentDevices.set(key, { ...record, lastSeen, isOnline: true });
// 发送上线状态更新信息
self.postMessage({
type: "connectionState",
data: { agvId: trackAgvId, state: "ONLINE", timestamp: lastSeen },
});
if (wasOffline) {
// console.log(
// `设备 ${trackAgvId.manufacturer}/${trackAgvId.serialNumber} 新上线`
// );
// 额外通知一次"新上线"
self.postMessage({
type: "connectionState",
data: { agvId: trackAgvId, state: "ONLINE", timestamp: lastSeen },
});
}
// 若设备首次出现,则订阅其状态更新
if (!subscribedDevices.has(key)) {
subscribedDevices.add(key);
try {
// 使用 trackAgvId 本身作为 AgvId 订阅
masterController!.subscribe(
Topic.State,
{ manufacturer: manufacturerValue, serialNumber: trackAgvId.serialNumber },
(state: State) => {
const subKey = getDeviceKey(trackAgvId);
// 自动添加新设备,无需预先配置
let existing = currentDevices.get(subKey);
if (!existing) {
existing = { agvId: trackAgvId, lastSeen: 0, isOnline: false };
currentDevices.set(subKey, existing);
console.log(`🆕 状态订阅中自动添加新设备: ${trackAgvId.manufacturer}/${trackAgvId.serialNumber}`);
}
const wasOfflineInSub = !existing.isOnline;
currentDevices.set(subKey, {
...existing,
lastSeen: Date.now(),
state,
isOnline: true,
});
if (wasOfflineInSub) {
// console.log(
// `设备 ${trackAgvId.manufacturer}/${trackAgvId.serialNumber} 新上线(订阅)`
// );
self.postMessage({
type: "connectionState",
data: {
agvId: trackAgvId,
state: "ONLINE",
timestamp: Date.now(),
},
});
}
self.postMessage({
type: "stateUpdate",
data: {
agvId: trackAgvId,
state: state,
timestamp: Date.now(),
},
});
}
);
// Subscribe to Factsheet topic
masterController!.subscribe(
Topic.Factsheet,
{ manufacturer: manufacturerValue, serialNumber: trackAgvId.serialNumber },
(factsheet: Factsheet) => {
// console.log("收到 factsheet 消息", factsheet);
self.postMessage({
type: "factsheet",
data: { agvId: trackAgvId, factsheet, timestamp: Date.now() },
});
}
);
// Subscribe to Connection topic
masterController!.subscribe(
Topic.Connection,
{ manufacturer: manufacturerValue, serialNumber: trackAgvId.serialNumber },
(connection: Connection) => {
self.postMessage({
type: "deviceDiscovered",
data: { agvId: trackAgvId, connection, timestamp: Date.now() },
});
}
);
} catch (error) {
console.error(`Failed to subscribe to tracked device ${trackAgvId.manufacturer}/${trackAgvId.serialNumber}:`, error);
// 移除已添加的订阅标记,以便重试
subscribedDevices.delete(key);
}
}
});
// 定时检测设备状态,超时则标记为离线
const stateUpdateCycle = 5000;
const offlineThreshold = stateUpdateCycle * 3;
setInterval(() => {
const now = Date.now();
currentDevices.forEach((device, key) => {
// console.log(
// `设备 ${device.agvId.manufacturer}/${device.agvId.serialNumber} - lastSeen: ${device.lastSeen}, isOnline: ${device.isOnline}`
// );
if (now - device.lastSeen > offlineThreshold && device.isOnline) {
device.isOnline = false;
currentDevices.set(key, device);
// console.log(
// `设备 ${device.agvId.manufacturer}/${device.agvId.serialNumber} 超过 ${offlineThreshold} 毫秒未更新,标记为下线`
// );
}
self.postMessage({
type: "connectionState",
data: { agvId: device.agvId, state: device.isOnline ? "ONLINE" : "OFFLINE", timestamp: now },
});
});
}, stateUpdateCycle);
})
.catch((error) => {
console.error("❌ Failed to start VDA 5050 master controller:");
console.error("Error details:", error);
console.error("Error message:", error?.message || "Unknown error");
console.error("Error stack:", error?.stack || "No stack trace");
// Reset client state
clientStarted = false;
masterController = null;
// Schedule retry after delay
console.log("🔄 Scheduling retry in 10 seconds...");
setTimeout(() => {
console.log("🔄 Retrying VDA 5050 master controller initialization...");
initializeControllerWithOptions(brokerUrl, iValue);
eventHelper.dispatchEvent("reconnect-all", {
reason: "grpc-stream-failed",
retryCount: 5,
timestamp: Date.now()
});
self.postMessage({ type: "reconnect-all" });
}, 10000);
});
}
/**
* 辅助函数:
* 当客户端尚未启动时,延迟一段时间后重试订阅
*/
function subscribeWithRetry(device: AgvId, retryCount = 0) {
const key = getDeviceKey(device);
// console.log("订阅设备->", key);
// 自动添加新设备,无需预先配置
if (!currentDevices.has(key)) {
currentDevices.set(key, { agvId: device, lastSeen: 0, isOnline: false });
console.log(`🆕 订阅时自动添加新设备: ${device.manufacturer}/${device.serialNumber}`);
}
if (!clientStarted) {
if (retryCount < 50000) {
// console.warn("Client not started, retry subscribing after delay...", device);
setTimeout(() => {
subscribeWithRetry(device, retryCount + 1);
}, 3000);
} else {
console.error("订阅失败:超过最大重试次数", device);
}
return;
}
if (!subscribedDevices.has(key)) {
subscribedDevices.add(key);
try {
// 双重检查客户端状态
if (!clientStarted || !masterController) {
subscribedDevices.delete(key);
setTimeout(() => {
subscribeWithRetry(device, retryCount + 1);
}, 3000);
return;
}
// Subscribe to State topic
masterController!.subscribe(
Topic.State,
{ manufacturer: manufacturerValue, serialNumber: device.serialNumber },
(state: State) => {
const subKey = getDeviceKey(device);
// 自动添加新设备,无需预先配置
let existing = currentDevices.get(subKey);
if (!existing) {
existing = { agvId: device, lastSeen: 0, isOnline: false };
currentDevices.set(subKey, existing);
console.log(`🆕 状态更新中自动添加新设备: ${device.manufacturer}/${device.serialNumber}`);
}
const wasOfflineInSub = !existing.isOnline;
currentDevices.set(subKey, {
...existing,
lastSeen: Date.now(),
state,
isOnline: true,
});
if (wasOfflineInSub) {
self.postMessage({
type: "connectionState",
data: { agvId: device, state: "ONLINE", timestamp: Date.now() },
});
}
self.postMessage({
type: "stateUpdate",
data: { agvId: device, state, timestamp: Date.now() },
});
}
);
// Subscribe to Factsheet topic
masterController!.subscribe(
Topic.Factsheet,
{ manufacturer: manufacturerValue, serialNumber: device.serialNumber },
(factsheet: Factsheet) => {
// console.log("收到 factsheet 消息", factsheet);
self.postMessage({
type: "factsheet",
data: { agvId: device, factsheet, timestamp: Date.now() },
});
}
);
// Subscribe to Connection topic
masterController!.subscribe(
Topic.Connection,
{ manufacturer: manufacturerValue, serialNumber: device.serialNumber },
(connection: Connection) => {
self.postMessage({
type: "deviceDiscovered",
data: { agvId: device, connection, timestamp: Date.now() },
});
}
);
} catch (error) {
console.error(`Failed to subscribe to device ${device.manufacturer}/${device.serialNumber}:`, error);
// 移除已添加的订阅标记,以便重试
subscribedDevices.delete(key);
// 如果客户端未启动,重新调度重试
if (!clientStarted) {
setTimeout(() => {
subscribeWithRetry(device, 0);
}, 5000);
}
}
}
}
// 处理来自主线程的消息
self.onmessage = async (event) => {
const message = event.data;
// console.log("Received message from main thread:", message);
// 如果收到 init 消息,从启动参数中传入 MQTT server 地址
if (message.type === "init") {
// 从主线程传入初始化参数
// data: {
// brokerUrl: config.mqtt.brokerUrl,
// interfaceName: config.interfaceName,
// manufacturer: config.manufacturer,
// instanceId: config.instanceId
// },
console.log("event", message);
const { brokerUrl, interfaceName, manufacturer, instanceId } = message.data;
console.log(`init params → brokerUrl: ${brokerUrl}, interfaceName: ${interfaceName}, manufacturer: ${manufacturer}, instanceId: ${instanceId}`);
manufacturerValue = instanceId;
interfaceNameValue = interfaceName;
initializeControllerWithOptions(brokerUrl, interfaceNameValue);
}
// 处理单个设备移除
if (message.type === "removeDevice") {
const { manufacturer, serialNumber } = message.data;
const deviceKey = getDeviceKey({ manufacturer, serialNumber });
if (currentDevices.has(deviceKey)) {
// 移除设备
currentDevices.delete(deviceKey);
subscribedDevices.delete(deviceKey);
console.log(`🗑️ 已移除设备: ${manufacturer}/${serialNumber}`);
// 通知主线程设备已移除
self.postMessage({
type: "deviceRemoved",
data: {
manufacturer,
serialNumber,
deviceKey,
remainingDevices: currentDevices.size
}
});
} else {
console.log(`⚠️ 设备 ${manufacturer}/${serialNumber} 不存在,无法移除`);
}
}
// 动态更新设备列表(主程序从配置文件发送更新消息)
if (message.type === "updateDeviceList") {
const newDeviceList: AgvId[] = message.data;
console.log(`🔄 VDA Worker 收到设备列表更新,包含 ${newDeviceList.length} 个设备`);
// 构造新设备的 Key 集合
const newKeys = new Set(newDeviceList.map(getDeviceKey));
let addedCount = 0;
let updatedCount = 0;
// 遍历新设备列表,新增或更新记录,并进行状态订阅
newDeviceList.forEach((device) => {
const key = getDeviceKey(device);
if (!currentDevices.has(key)) {
currentDevices.set(key, { agvId: device, lastSeen: 0, isOnline: false });
console.log(`➕ 新增设备: ${device.manufacturer}/${device.serialNumber}`);
addedCount++;
} else {
// 更新 agvId 信息(如果有必要)
const record = currentDevices.get(key)!;
record.agvId = device;
currentDevices.set(key, record);
console.log(`🔄 更新设备: ${device.manufacturer}/${device.serialNumber}`);
updatedCount++;
}
// 根据设备列表订阅对应的状态更新(重试订阅,直到客户端启动)
subscribeWithRetry(device);
});
// 对于 currentDevices 中存在但不在最新列表中的设备,置为离线
let removedCount = 0;
currentDevices.forEach((device, key) => {
if (!newKeys.has(key)) {
device.isOnline = false;
currentDevices.set(key, device);
console.log(`⏸️ 设备离线: ${device.agvId.manufacturer}/${device.agvId.serialNumber}`);
removedCount++;
}
});
console.log(`✅ 设备列表更新完成: 新增 ${addedCount}, 更新 ${updatedCount}, 离线 ${removedCount}`);
console.log(`📊 当前管理设备总数: ${currentDevices.size}`);
// 通知主线程最新设备列表情况
self.postMessage({
type: "deviceListUpdated",
data: {
total: currentDevices.size,
added: addedCount,
updated: updatedCount,
removed: removedCount,
devices: Array.from(currentDevices.values()).map(d => ({
manufacturer: d.agvId.manufacturer,
serialNumber: d.agvId.serialNumber,
isOnline: d.isOnline
}))
}
});
}
if (message.type === "orderForwarded") {
// Check if client is started before processing orders
if (!clientStarted || !masterController) {
console.warn("❌ VDA client not started yet, ignoring order request");
return;
}
// console.log("收到 AGV 订单消息1", message);
// 解构出 agvId 串号和 order 对象
const { agvId: agvSerial, order: msg } = message.data as { agvId: string; order: any };
// Rebuild order payload as Headerless
const order: Headerless = {
orderId: msg.orderId,
orderUpdateId: msg.orderUpdateId || 0,
nodes: msg.nodes.map((node: any, idx: number) => ({
nodeId: node.nodeId,
sequenceId: idx * 2,
released: node.released ?? true,
nodePosition: node.nodePosition,
actions: node.actions || []
})),
edges: []
};
if (msg.nodes.length > 1) {
for (let i = 0; i < msg.nodes.length - 1; i++) {
order.edges.push({
edgeId: msg.edges?.[i]?.edgeId || `edge${i}to${i + 1}`,
sequenceId: i * 2 + 1,
startNodeId: msg.nodes[i].nodeId,
endNodeId: msg.nodes[i + 1].nodeId,
released: msg.edges?.[i]?.released ?? true,
actions: msg.edges?.[i]?.actions || []
});
}
}
let devId: any = undefined;
// console.log("检查设备", currentDevices, msg.agvId);
currentDevices.forEach((device, key) => {
// console.log("检查设备", device, key, device.agvId.serialNumber, agvSerial);
if (device.agvId.serialNumber === agvSerial) {
devId = device.agvId;
}
});
if (devId) {
// console.log("收到 AGV 订单消息2", devId);
try {
// console.log("---->",{ manufacturer: manufacturerValue, serialNumber: devId.serialNumber }, order);
await masterController!.assignOrder({ manufacturer: manufacturerValue, serialNumber: devId.serialNumber }, order, {
onOrderProcessed: (err, canceled, active, ctx) => {
if (err) {
console.error("Order 被拒绝", err);
} else if (canceled) {
console.log("Order 被取消", ctx.order);
} else if (active) {
console.log("Order 正在执行", ctx.order);
} else {
console.log("Order 完成", ctx.order);
}
},
onNodeTraversed: (node, nextEdge, nextNode, ctx) => {
console.log("节点遍历完成", node);
},
onEdgeTraversing: (edge, start, end, stateChanges, count, ctx) => {
console.log("开始路径", edge);
},
onEdgeTraversed: (edge, start, end, ctx) => {
console.log("路径遍历完成", edge);
},
onActionStateChanged: (actionState, error) => {
console.log("Action 状态变化", actionState, error || "");
}
});
} catch (err) {
console.error("assignOrder 异常", err);
}
} // end if(agvId)
} // end if(message.type === "orderForwarded")
// 2) 收到 InstantActions 转发
if (message.type === "instantActionsForwarded") {
// Check if client is started before processing instant actions
if (!clientStarted || !masterController) {
console.warn("❌ VDA client not started yet, ignoring instant actions request");
return;
}
// console.log("收到 AGV 即时动作消息", message);
const msg: any = message.data;
// const actions: InstantActions = msg.actions;
const { agvId, actions } = msg as {
agvId: string;
actions: Array<{
actionType: string; /* …其它可能的字段… */
actionParameters: Array<{ key: string; value: string }>;
actionDescription: string;
actionId: string;
blockingType: string;
}>;
};
// console.log("收到 AGV 即时动作消息", agvId, actions);
let devId: any = undefined;
currentDevices.forEach((device, key) => {
if (device.agvId.serialNumber === msg.agvId) {
devId = device.agvId;
}
});
if (devId) {
// console.log("收到 AGV 即时动作消息", msg);
try {
const headerless: Headerless = {
actions: actions.map(a => ({
actionType: a.actionType, // 必填
actionId: a.actionId, // 必填
blockingType: a.blockingType === "HARD" ? BlockingType.Hard : BlockingType.None, // 必填,使用枚举
actionParameters: a.actionParameters || [], // 使用原始的actionParameters或空数组
actionDescription: "action parameters", // 可选
}))
};
// console.log("=====>",headerless);
await masterController!.initiateInstantActions({ manufacturer: manufacturerValue, serialNumber: devId.serialNumber }, headerless, {
onActionStateChanged: (actionState, withError, action, agvId, state) => console.log("Instant action state changed: %o %o %o", actionState, withError, action),
onActionError: (error, action, agvId, state) => console.log("Instant action error: %o %o %o", error, action, state),
});
} catch (err) {
console.error("initiateInstantActions 异常", err);
}
} // end if(agvId)
} // end if(message.type === "instantActionsForwarded")
// 处理发送订单请求
if (message.type === "sendOrder") {
// Check if client is started before processing orders
if (!clientStarted || !masterController) {
console.warn("❌ VDA client not started yet, ignoring send order request");
return;
}
try {
const order: Headerless = {
orderId: message.orderId || masterController!.createUuid(),
orderUpdateId: 0,
nodes: message.nodes.map((node: any, index: number) => ({
nodeId: node.nodeId,
sequenceId: index * 2,
released: true,
nodePosition: node.nodePosition,
actions: node.actions || [],
})),
edges: [],
};
if (message.nodes.length > 1) {
for (let i = 0; i < message.nodes.length - 1; i++) {
order.edges.push({
edgeId: `edge${i}to${i + 1}`,
sequenceId: i * 2 + 1,
startNodeId: message.nodes[i].nodeId,
endNodeId: message.nodes[i + 1].nodeId,
released: true,
actions: [],
});
}
}
// 使用预注册列表中的第一个设备发送订单
let devId: any = undefined;
// currentDevices.forEach((device, key) => {
// if ( device.agvId.serialNumber === 'ZKG-0') {
// devId = device.agvId;
// }
// });
if (devId) {
await masterController!.assignOrder({ manufacturer: manufacturerValue, serialNumber: devId.serialNumber }, order, {
onOrderProcessed: (withError, byCancelation, active, ctx) => {
console.log("Order processed", { withError, byCancelation, active });
self.postMessage({
type: "orderCompleted",
orderId: order.orderId,
withError,
byCancelation,
active,
});
},
onNodeTraversed: (node, nextEdge, nextNode, ctx) => {
console.log("Order node traversed:", node);
self.postMessage({
type: "nodeTraversed",
node,
nextEdge,
nextNode,
});
},
onEdgeTraversing: (
edge,
startNode,
endNode,
stateChanges,
invocationCount,
ctx
) => {
console.log("Order edge traversing:", edge);
self.postMessage({
type: "edgeTraversing",
edge,
startNode,
endNode,
});
},
onEdgeTraversed: (edge, startNode, endNode, ctx) => {
console.log("Order edge traversed:", edge);
self.postMessage({ type: "edgeTraversed", edge, startNode, endNode });
},
});
}
console.log("Order assigned successfully");
self.postMessage({ type: "orderSent", orderId: order.orderId });
} catch (error) {
console.error("Failed to send order:", error);
self.postMessage({ type: "error", error: (error as Error).message });
}
}
// 处理取消订单请求
if (message.type === "cancelOrder") {
// 此处添加取消订单逻辑……
}
// 处理主动请求设备列表的消息
if (message.type === "discoverDevices") {
const devicesList = Array.from(currentDevices.values())
.filter(d => d.isOnline)
.map(d => ({
agvId: d.agvId,
isOnline: d.isOnline,
x: d.state?.agvPosition?.x,
y: d.state?.agvPosition?.y,
theta: d.state?.agvPosition?.theta,
actionStatus: d.state?.actionStates,
lastNodeId: d.state?.lastNodeId,
lastNodeSequenceId: d.state?.lastNodeSequenceId,
nodeStates: d.state?.nodeStates,
edgeStates: d.state?.edgeStates,
driving: d.state?.driving,
errors: d.state?.errors?.map(err => ({
errorType: err.errorType,
errorLevel: err.errorLevel,
errorDescription: err.errorDescription,
errorReferences: err.errorReferences?.map((ref: any) => ({
referenceKey: ref.referenceKey,
referenceValue: ref.referenceValue
}))
})),
information: d.state?.information || []
}));
console.log("currentDevices", JSON.stringify(Array.from(currentDevices.values()), null, 2));
self.postMessage({ type: "devicesList", data: devicesList });
}
// 处理factsheet请求
if (message.type === "factsheetRequest") {
// Check if client is started before processing factsheet requests
if (!clientStarted || !masterController) {
console.warn("❌ VDA client not started yet, ignoring factsheet request");
return;
}
const { agvId } = message.data || {};
if (!agvId) {
console.warn("❌ No agvId provided for factsheet request");
return;
}
console.log(`📋 Processing factsheet request for AGV: ${agvId}`);
// Find the device in currentDevices
let targetDevice: any = undefined;
currentDevices.forEach((device, key) => {
if (device.agvId.serialNumber === agvId) {
targetDevice = device.agvId;
}
});
if (targetDevice) {
try {
// Request factsheet using instant action
const factsheetAction: Headerless = {
actions: [{
actionType: "factsheetRequest",
actionId: `factsheet_${Date.now()}`,
blockingType: BlockingType.None,
actionParameters: [],
actionDescription: "Request device factsheet"
}]
};
await masterController!.initiateInstantActions(
{ manufacturer: manufacturerValue, serialNumber: targetDevice.serialNumber },
factsheetAction,
{
onActionStateChanged: (actionState, withError, action, agvId, state) => {
console.log("Factsheet action state changed:", actionState, withError ? "with error" : "success");
},
onActionError: (error, action, agvId, state) => {
console.error("Factsheet action error:", error);
},
}
);
console.log(`✅ Factsheet request sent for AGV: ${agvId}`);
} catch (err) {
console.error("❌ Failed to send factsheet request:", err);
}
} else {
console.warn(`⚠️ AGV ${agvId} not found in current devices`);
}
}
// 处理 shutdown 消息
if (message === "shutdown" || message.type === "shutdown") {
console.log("收到 shutdown 消息,退出 Worker");
// 此处可添加关闭逻辑
}
};
// 在 worker 退出时关闭 Master Controller
addEventListener("unload", () => {
console.log("Closing VDA 5050 Worker");
masterController?.stop().catch((err: Error) => console.log(err));
});