api-amr/vda_worker.ts
2025-06-04 19:15:02 +08:00

837 lines
30 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/// <reference lib="deno.worker" />
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<string> = 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<Order>
const order: Headerless<Order> = {
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<InstantActions> = {
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<Order> = {
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<InstantActions> = {
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));
});