Files
cod-fe/src/components/Global/MqttConnection.jsx
2026-04-09 15:25:00 +07:00

172 lines
4.6 KiB
JavaScript

// mqttService.js
import mqtt from 'mqtt';
const mqttUrl = `${import.meta.env.VITE_MQTT_SERVER ?? 'ws://localhost:1884'}`;
const topics = [
'PIU_COD/AIR_DRYER/OVERVIEW',
'PIU_COD/AIR_DRYER/AIR_DRYER_A',
'PIU_COD/AIR_DRYER/AIR_DRYER_B',
'PIU_COD/AIR_DRYER/AIR_DRYER_C',
'PIU_COD/COMPRESSOR/OVERVIEW',
'PIU_COD/COMPRESSOR/COMPRESSOR_A',
'PIU_COD/COMPRESSOR/COMPRESSOR_B',
'PIU_COD/COMPRESSOR/COMPRESSOR_C',
'PIU_COD/ERROR_CODE/SIM',
];
const options = {
keepalive: 30,
clientId: 'react_mqtt_' + Math.random().toString(16).substr(2, 8),
protocolId: 'MQTT',
protocolVersion: 4,
clean: true,
reconnectPeriod: 1000,
connectTimeout: 30 * 1000,
username: `${import.meta.env.VITE_MQTT_USERNAME ?? ''}`, // jika ada
password: `${import.meta.env.VITE_MQTT_PASSWORD ?? ''}`, // jika ada
};
const client = mqtt.connect(mqttUrl, options);
// Track connection status
let isConnected = false;
client.on('connect', () => {
console.log('MQTT Connected');
isConnected = true;
// Subscribe default topic
client.subscribe(topics, (err) => {
if (err) console.error('Subscribe error:', err);
else console.log(`Subscribed to topics: ${topics.join(', ')}`);
});
});
client.on('error', (err) => {
console.error('Connection error: ', err);
client.end();
});
client.on('close', () => {
console.log('MQTT Disconnected');
isConnected = false;
});
/**
* Publish message to MQTT
* @param {string} topic
* @param {string} message
*/
const publishMessage = (topic, message) => {
if (client && isConnected && message.trim() !== '') {
client.publish(topic, message);
} else {
console.warn('MQTT not connected or message empty');
}
};
/**
* Listen to incoming messages
* @param {function} callback - Function(topic, message)
*/
const listenMessage = (callback) => {
client.on('message', (topic, message) => {
callback(topic, message.toString());
});
};
const colorIds = ['c_1023', 'c_2023', 'c_2023'];
const StatusColor = (el, num) => {
switch (num) {
case 1:
el.style.fill = 'orange';
break;
case 2:
el.style.fill = 'rgb(7,250,14)';
break;
default:
el.style.fill = 'rgb(216,216,216)';
}
};
const dryerIds = ['c_4018', 'c_4021', 'c_5018', 'c_5021', 'c_6018', 'c_6021'];
const handleBoolean = (svg, el, value) => {
if (!dryerIds.includes(el.id)) {
el.style.display = value ? '' : 'none';
return;
}
const i = dryerIds.indexOf(el.id);
const id1 = dryerIds[i - (i % 2)];
const id2 = dryerIds[i - (i % 2) + 1];
const el1 = svg.getElementById(id1);
const el2 = svg.getElementById(id2);
if (!el1 || !el2) return;
el1.style.fill = value ? 'rgb(216,216,216)' : 'yellow';
el2.style.fill = value ? 'yellow' : 'rgb(216,216,216)';
};
const handleNumber = (el, value) => {
const num = Number(value);
if (colorIds.includes(el.id)) {
StatusColor(el, num);
} else {
el.textContent = num.toFixed(2);
}
};
const handleOther = (el, value) => {
el.textContent = value;
};
const setValSvg = (listenTopic, svg) => {
client.on('message', (topic, message) => {
if (topic === listenTopic) {
const objChanel = JSON.parse(message);
Object.entries(objChanel).forEach(([key, value]) => {
const el = svg.getElementById(key);
if (!el) return;
// else if (el.id === 'c_2014' || el.id === 'c_2024') {
// el.setAttribute('text-anchor', 'middle');
// el.setAttribute('x', '10%');
// }
if (typeof value === 'boolean') {
handleBoolean(svg, el, value);
} else if (!isNaN(value)) {
handleNumber(el, value);
} else {
handleOther(el, value);
}
});
const target = svg.querySelector('text[x="225.25"][y="537.752"]');
if (target) target.remove();
}
});
};
// === NOTIFICATION LISTENER ===
const notifListeners = [];
const onNotifUpdate = (callback) => {
notifListeners.push(callback);
};
client.on('message', (topic, message) => {
if (topic === import.meta.env.VITE_MQTT_TOPIC_COD) {
try {
const payload = JSON.parse(message.toString());
notifListeners.forEach((cb) => cb(payload));
} catch (err) {
console.error('Invalid notif payload', err);
}
}
});
export { publishMessage, listenMessage, setValSvg, onNotifUpdate };