-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsimulator.ts
More file actions
136 lines (115 loc) · 4.04 KB
/
simulator.ts
File metadata and controls
136 lines (115 loc) · 4.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import bleno from '@abandonware/bleno';
import fs from 'fs';
import { parse } from 'csv-parse';
import path from 'path';
// --- CONFIGURATION ---
const SERVICE_UUID = 'A07498CA-AD5B-474E-940D-16F1FBE7E8CD';
const CHARACTERISTIC_UUID = '51FF12BB-3ED8-46E5-B4F9-D64E2FEC021B';
const CSV_FILE = 'sensor_data.csv';
const UPDATE_INTERVAL = 2000; // Update every 2 seconds
// --- DATA STRUCTURE ---
// Based on your specific CSV headers
interface LogisticsData {
Timestamp: string;
Asset_ID: string;
Temperature: string;
Humidity: string;
Inventory_Level: string;
Shipment_Status: string;
}
let dataRows: LogisticsData[] = [];
let currentIndex = 0;
// --- LOAD CSV ---
console.log(`[Init] Loading ${CSV_FILE}...`);
const csvPath = path.resolve(__dirname, CSV_FILE);
if (fs.existsSync(csvPath)) {
fs.createReadStream(csvPath)
.pipe(parse({
columns: true,
trim: true,
skip_empty_lines: true,
bom: true
}))
.on('data', (row) => {
// Push row directly; names match the CSV headers
dataRows.push(row);
})
.on('end', () => {
console.log(`[Init] Successfully loaded ${dataRows.length} rows.`);
if(dataRows.length > 0) {
console.log("Sample Row:", dataRows[0]); // Debug print
}
});
} else {
console.error(`[Error] File ${CSV_FILE} not found!`);
}
// --- DEFINE BLE CHARACTERISTIC ---
class LogisticsCharacteristic extends bleno.Characteristic {
updateValueCallback: ((data: Buffer) => void) | null;
timer: NodeJS.Timeout | null;
constructor() {
super({
uuid: CHARACTERISTIC_UUID,
properties: ['read', 'notify'],
value: null
});
this.updateValueCallback = null;
this.timer = null;
}
// Return the current value as a formatted string
getCurrentValue(): Buffer {
if (dataRows.length === 0) return Buffer.from("No Data");
const row = dataRows[currentIndex];
// Format: "AssetID,Temp,Hum,Status"
// Example: "Truck_7,27.0,67.8,Delayed"
const payload = `${row.Asset_ID},${row.Temperature},${row.Humidity},${row.Shipment_Status}`;
return Buffer.from(payload);
}
onReadRequest(offset: number, callback: (result: number, data?: Buffer) => void) {
const data = this.getCurrentValue();
console.log(`[Read] Sending: ${data.toString()}`);
callback(this.RESULT_SUCCESS, data);
}
onSubscribe(maxValueSize: number, updateValueCallback: (data: Buffer) => void) {
console.log('[BLE] Client Subscribed (Notifications ON)');
this.updateValueCallback = updateValueCallback;
this.timer = setInterval(() => {
if (dataRows.length === 0) return;
// Move to next row
currentIndex = (currentIndex + 1) % dataRows.length;
const data = this.getCurrentValue();
if (this.updateValueCallback) {
console.log(`[Notify] Broadcasting: ${data.toString()}`);
this.updateValueCallback(data);
}
}, UPDATE_INTERVAL);
}
onUnsubscribe() {
console.log('[BLE] Client Unsubscribed');
this.updateValueCallback = null;
if (this.timer) clearInterval(this.timer);
}
}
// --- START BLE SERVICE ---
const logisticsChar = new LogisticsCharacteristic();
bleno.on('stateChange', (state) => {
console.log(`[BLE] Adapter State: ${state}`);
if (state === 'poweredOn') {
bleno.startAdvertising('LogisticsSim', [SERVICE_UUID]);
} else {
bleno.stopAdvertising();
}
});
bleno.on('advertisingStart', (error) => {
if (!error) {
console.log('[BLE] Service Active. Waiting for connections...');
bleno.setServices([
new bleno.PrimaryService({
uuid: SERVICE_UUID,
characteristics: [logisticsChar]
})
]);
} else {
console.error(`[BLE] Advertising Error: ${error}`);
}
});