304 lines
7.9 KiB
Markdown
304 lines
7.9 KiB
Markdown
# MQTT Client - Hướng dẫn sử dụng
|
|
|
|
## Tổng quan
|
|
|
|
MQTT Client (`mqttClient`) là một utility singleton được thiết kế để quản lý kết nối MQTT trong ứng dụng. File này nằm tại `src/utils/mqttClient.ts`.
|
|
|
|
## Cài đặt
|
|
|
|
Package `mqtt` được cài đặt qua npm:
|
|
|
|
```bash
|
|
npm install mqtt
|
|
```
|
|
|
|
## Cấu trúc
|
|
|
|
```
|
|
src/utils/
|
|
├── mqttClient.ts # MQTT Client utility
|
|
└── wsClient.ts # WebSocket Client utility (legacy)
|
|
```
|
|
|
|
## Cách sử dụng
|
|
|
|
### 1. Import
|
|
|
|
```typescript
|
|
import { mqttClient } from '@/utils/mqttClient';
|
|
```
|
|
|
|
### 2. Kết nối
|
|
|
|
```typescript
|
|
// Lấy credentials từ user metadata
|
|
const { frontend_thing_id, frontend_thing_key } =
|
|
initialState?.currentUserProfile?.metadata || {};
|
|
|
|
// Kết nối
|
|
mqttClient.connect({
|
|
username: frontend_thing_id,
|
|
password: frontend_thing_key,
|
|
});
|
|
```
|
|
|
|
### 3. Đăng ký Event Handlers
|
|
|
|
```typescript
|
|
// Khi kết nối thành công
|
|
const unConnect = mqttClient.onConnect(() => {
|
|
console.log('MQTT Connected!');
|
|
});
|
|
|
|
// Khi có lỗi
|
|
const unError = mqttClient.onError((error) => {
|
|
console.error('MQTT Error:', error);
|
|
});
|
|
|
|
// Khi kết nối đóng
|
|
const unClose = mqttClient.onClose(() => {
|
|
console.log('MQTT Closed');
|
|
});
|
|
|
|
// Cleanup khi component unmount
|
|
return () => {
|
|
unConnect();
|
|
unError();
|
|
unClose();
|
|
mqttClient.disconnect();
|
|
};
|
|
```
|
|
|
|
### 4. Publish Message
|
|
|
|
```typescript
|
|
const topic = `channels/${cfg_channel_id}/messages/cameraconfig/gmsv6`;
|
|
const payload = JSON.stringify(senmlData);
|
|
|
|
if (mqttClient.isConnected()) {
|
|
mqttClient.publish(topic, payload);
|
|
}
|
|
```
|
|
|
|
### 5. Subscribe Topic
|
|
|
|
```typescript
|
|
// Subscribe
|
|
mqttClient.subscribe('channels/123/messages/#');
|
|
|
|
// Nhận message
|
|
const unMessage = mqttClient.onMessage((topic, message, packet) => {
|
|
console.log('Received:', topic, message.toString());
|
|
});
|
|
|
|
// Unsubscribe
|
|
mqttClient.unsubscribe('channels/123/messages/#');
|
|
```
|
|
|
|
## API Reference
|
|
|
|
| Method | Mô tả |
|
|
| ---------------------------- | ---------------------------------- |
|
|
| `connect(credentials, url?)` | Kết nối MQTT với username/password |
|
|
| `disconnect()` | Ngắt kết nối |
|
|
| `subscribe(topic)` | Subscribe vào topic |
|
|
| `unsubscribe(topic)` | Unsubscribe khỏi topic |
|
|
| `publish(topic, payload)` | Publish message |
|
|
| `onConnect(callback)` | Đăng ký callback khi kết nối |
|
|
| `onClose(callback)` | Đăng ký callback khi đóng |
|
|
| `onError(callback)` | Đăng ký callback khi lỗi |
|
|
| `onMessage(callback)` | Đăng ký callback khi nhận message |
|
|
| `isConnected()` | Kiểm tra trạng thái kết nối |
|
|
| `getClient()` | Lấy MQTT client gốc |
|
|
|
|
## Cấu hình Proxy (Development)
|
|
|
|
Trong môi trường development, MQTT được proxy qua config trong `config/proxy.ts`:
|
|
|
|
```typescript
|
|
"/mqtt": {
|
|
target: "https://gms.smatec.com.vn",
|
|
changeOrigin: true,
|
|
ws: true,
|
|
},
|
|
```
|
|
|
|
## Format SenML
|
|
|
|
Dữ liệu MQTT được format theo chuẩn SenML:
|
|
|
|
```typescript
|
|
const senml = [
|
|
{
|
|
bn: `urn:dev:mac:${mac}:`, // Base name
|
|
n: 'ack', // Name
|
|
t: Date.now() / 1000, // Timestamp (seconds)
|
|
vs: uuidv4(), // Value string (ACK ID)
|
|
},
|
|
{
|
|
n: 'user@email.com', // Email (@ → :)
|
|
t: Date.now() / 1000,
|
|
vs: JSON.stringify(config), // Config dạng JSON string
|
|
},
|
|
];
|
|
```
|
|
|
|
## Topic Structure
|
|
|
|
```
|
|
channels/${channel_id}/messages/${type}/${gw_type}
|
|
```
|
|
|
|
| Phần | Mô tả |
|
|
| ------------ | ------------------------------------------------------- |
|
|
| `channel_id` | ID của kênh (từ `thing.metadata.cfg_channel_id`) |
|
|
| `type` | Loại message: `cameraconfig`, `config`, `log`, `notify` |
|
|
| `gw_type` | Loại gateway: `gmsv6`, `gmsv5` |
|
|
|
|
## Ví dụ hoàn chỉnh
|
|
|
|
```typescript
|
|
import { mqttClient } from '@/utils/mqttClient';
|
|
import { useModel } from '@umijs/max';
|
|
import { useEffect, useState } from 'react';
|
|
import { v4 as uuidv4 } from 'uuid';
|
|
|
|
const MyComponent = ({ thing }) => {
|
|
const { initialState } = useModel('@@initialState');
|
|
const [connected, setConnected] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const { frontend_thing_id, frontend_thing_key } =
|
|
initialState?.currentUserProfile?.metadata || {};
|
|
|
|
if (!frontend_thing_id || !frontend_thing_key) return;
|
|
|
|
mqttClient.connect({
|
|
username: frontend_thing_id,
|
|
password: frontend_thing_key,
|
|
});
|
|
|
|
const unConnect = mqttClient.onConnect(() => setConnected(true));
|
|
const unClose = mqttClient.onClose(() => setConnected(false));
|
|
|
|
return () => {
|
|
unConnect();
|
|
unClose();
|
|
mqttClient.disconnect();
|
|
};
|
|
}, [initialState]);
|
|
|
|
const handlePublish = () => {
|
|
const { cfg_channel_id, external_id } = thing.metadata;
|
|
const topic = `channels/${cfg_channel_id}/messages/cameraconfig/gmsv6`;
|
|
|
|
const payload = [
|
|
{
|
|
bn: `urn:dev:mac:${external_id.replaceAll('-', '')}:`,
|
|
n: 'ack',
|
|
t: Date.now() / 1000,
|
|
vs: uuidv4(),
|
|
},
|
|
{
|
|
n: initialState?.currentUserProfile?.email?.replaceAll('@', ':'),
|
|
t: Date.now() / 1000,
|
|
vs: JSON.stringify({ record_type: 'all' }),
|
|
},
|
|
];
|
|
|
|
if (mqttClient.isConnected()) {
|
|
mqttClient.publish(topic, JSON.stringify(payload));
|
|
}
|
|
};
|
|
|
|
return (
|
|
<button onClick={handlePublish} disabled={!connected}>
|
|
{connected ? 'Publish' : 'Connecting...'}
|
|
</button>
|
|
);
|
|
};
|
|
```
|
|
|
|
## So sánh mqttClient vs wsClient
|
|
|
|
Dự án có 2 utilities để giao tiếp real-time:
|
|
|
|
### Bảng so sánh
|
|
|
|
| Tiêu chí | mqttClient | wsClient |
|
|
| --- | --- | --- |
|
|
| **Thư viện** | `mqtt` (MQTT.js) | `reconnecting-websocket` |
|
|
| **Protocol** | MQTT over WebSocket | WebSocket thuần |
|
|
| **Xác thực** | Username/Password (MQTT credentials) | Token (access_token) hoặc không |
|
|
| **Topics** | Hỗ trợ MQTT topics, subscribe/unsubscribe | Không có khái niệm topic |
|
|
| **Publish** | `publish(topic, payload)` | `send(data)` |
|
|
| **Subscribe** | `subscribe(topic)` + `onMessage()` | `subscribe(callback)` |
|
|
| **Reconnect** | Tự động (built-in) | Tự động (reconnecting-websocket) |
|
|
| **Use case** | Giao tiếp với IoT Gateway/Devices | Giao tiếp WebSocket server |
|
|
|
|
### Khi nào dùng mqttClient?
|
|
|
|
✅ **Dùng mqttClient khi:**
|
|
|
|
- Gửi/nhận dữ liệu với IoT Gateways (GMSv5, GMSv6)
|
|
- Cấu hình thiết bị (camera, nodes, schedules)
|
|
- Cần subscribe nhiều topics khác nhau
|
|
- Làm việc với Mainflux platform
|
|
|
|
### Khi nào dùng wsClient?
|
|
|
|
✅ **Dùng wsClient khi:**
|
|
|
|
- Cần WebSocket connection đơn giản
|
|
- Giao tiếp với WebSocket server không phải MQTT broker
|
|
- Xác thực bằng access_token
|
|
|
|
### Code comparison
|
|
|
|
**mqttClient:**
|
|
|
|
```typescript
|
|
import { mqttClient } from '@/utils/mqttClient';
|
|
|
|
// Connect với username/password
|
|
mqttClient.connect({
|
|
username: 'thing_id',
|
|
password: 'thing_key',
|
|
});
|
|
|
|
// Subscribe topic
|
|
mqttClient.subscribe('channels/123/messages/#');
|
|
|
|
// Publish với topic
|
|
mqttClient.publish('channels/123/messages/config', payload);
|
|
|
|
// Nhận message
|
|
mqttClient.onMessage((topic, message) => {
|
|
console.log(topic, message.toString());
|
|
});
|
|
```
|
|
|
|
**wsClient:**
|
|
|
|
```typescript
|
|
import { wsClient } from '@/utils/wsClient';
|
|
|
|
// Connect với hoặc không có token
|
|
wsClient.connect('/mqtt', false);
|
|
|
|
// Không có subscribe topic
|
|
// Chỉ nhận tất cả messages
|
|
wsClient.subscribe((data) => {
|
|
console.log(data);
|
|
});
|
|
|
|
// Gửi data (không có topic)
|
|
wsClient.send({ action: 'update', data: payload });
|
|
```
|
|
|
|
## Xem thêm
|
|
|
|
- [Mainflux.md](../Mainflux.md) - Tài liệu giao tiếp MQTT chi tiết
|
|
- [mqttClient.ts](../src/utils/mqttClient.ts) - Source code MQTT Client
|
|
- [wsClient.ts](../src/utils/wsClient.ts) - Source code WebSocket Client
|