168 lines
4.4 KiB
JavaScript
168 lines
4.4 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 = 'green';
|
|
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;
|
|
|
|
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 };
|