83 lines
2.3 KiB
JavaScript
83 lines
2.3 KiB
JavaScript
|
|
import { useState, useEffect } from 'react';
|
|||
|
|
import mqtt from 'mqtt';
|
|||
|
|
import { MQTT_URL, MQTT_TOPIC_COMMAND, MQTT_TOPIC_TICK, MQTT_USERNAME, MQTT_PASSWORD } from '../config';
|
|||
|
|
|
|||
|
|
/* =========================================================
|
|||
|
|
MQTT 客户端(mqtt.js)—— 后端控制通道
|
|||
|
|
订阅 opc/display/command(页面/媒体/卡片控制)与 opc/dashboard/tick(数据快照)
|
|||
|
|
断线自动重连;未连接时静默降级(页面仍可本地操作)
|
|||
|
|
========================================================= */
|
|||
|
|
|
|||
|
|
let client = null;
|
|||
|
|
let status = 'connecting'; // connecting | connected | offline
|
|||
|
|
const listeners = new Set();
|
|||
|
|
|
|||
|
|
function notifyStatus() {
|
|||
|
|
window.dispatchEvent(new CustomEvent('dpm:mqtt-status', { detail: { status } }));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function connectMqtt() {
|
|||
|
|
if (client) return;
|
|||
|
|
try {
|
|||
|
|
client = mqtt.connect(MQTT_URL, {
|
|||
|
|
reconnectPeriod: 3000,
|
|||
|
|
connectTimeout: 8000,
|
|||
|
|
clientId: `dpm-screen-${Math.random().toString(16).slice(2, 8)}`,
|
|||
|
|
clean: true,
|
|||
|
|
username: MQTT_USERNAME || undefined,
|
|||
|
|
password: MQTT_PASSWORD || undefined,
|
|||
|
|
});
|
|||
|
|
client.on('connect', () => {
|
|||
|
|
status = 'connected';
|
|||
|
|
notifyStatus();
|
|||
|
|
client.subscribe([MQTT_TOPIC_COMMAND, MQTT_TOPIC_TICK], { qos: 1 });
|
|||
|
|
});
|
|||
|
|
client.on('reconnect', () => {
|
|||
|
|
status = 'connecting';
|
|||
|
|
notifyStatus();
|
|||
|
|
});
|
|||
|
|
client.on('close', () => {
|
|||
|
|
status = 'offline';
|
|||
|
|
notifyStatus();
|
|||
|
|
});
|
|||
|
|
client.on('error', () => {
|
|||
|
|
status = 'offline';
|
|||
|
|
notifyStatus();
|
|||
|
|
});
|
|||
|
|
client.on('message', (topic, payload) => {
|
|||
|
|
let data = null;
|
|||
|
|
try {
|
|||
|
|
data = JSON.parse(payload.toString());
|
|||
|
|
} catch {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
listeners.forEach((fn) => fn(topic, data));
|
|||
|
|
});
|
|||
|
|
} catch (e) {
|
|||
|
|
status = 'offline';
|
|||
|
|
notifyStatus();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function getMqttStatus() {
|
|||
|
|
return status;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 订阅 MQTT 消息,返回取消订阅函数 */
|
|||
|
|
export function onMqttMessage(fn) {
|
|||
|
|
listeners.add(fn);
|
|||
|
|
return () => listeners.delete(fn);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** React Hook:MQTT 连接状态 */
|
|||
|
|
export function useMqttStatus() {
|
|||
|
|
const [s, setS] = useState(status);
|
|||
|
|
useEffect(() => {
|
|||
|
|
connectMqtt();
|
|||
|
|
const on = (e) => setS(e.detail.status);
|
|||
|
|
window.addEventListener('dpm:mqtt-status', on);
|
|||
|
|
return () => window.removeEventListener('dpm:mqtt-status', on);
|
|||
|
|
}, []);
|
|||
|
|
return s;
|
|||
|
|
}
|