secutorpro commited on
Commit
d07a6aa
·
verified ·
1 Parent(s): 1dd35a9

simulated fleet for testing GPS, SOS and PTT flow

Browse files
Files changed (1) hide show
  1. server/simulator.js +94 -0
server/simulator.js ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Simulated fleet: drives GPS updates, an SOS and PTT floor requests against a
2
+ // running server, so you can watch the console move without real devices.
3
+ //
4
+ // cd server && npm run sim
5
+ // SERVER_URL=http://localhost:8080 SIM_CENTER=48.8566,2.3522 node simulator.js
6
+ import WebSocket from 'ws';
7
+
8
+ const BASE = process.env.SERVER_URL || 'http://localhost:8080';
9
+ const N = Number(process.env.SIM_UNITS) || 5;
10
+ const [clat, clng] = (process.env.SIM_CENTER || '48.8566,2.3522').split(',').map(Number);
11
+
12
+ async function main() {
13
+ const login = await fetch(`${BASE}/api/login`, {
14
+ method: 'POST',
15
+ headers: { 'content-type': 'application/json' },
16
+ body: JSON.stringify({ username: process.env.ADMIN_USER || 'admin', password: process.env.ADMIN_PASS || 'admin' }),
17
+ });
18
+ if (!login.ok) throw new Error(`login failed: ${login.status}`);
19
+ const { token } = await login.json();
20
+ const headers = { authorization: `Bearer ${token}`, 'content-type': 'application/json' };
21
+
22
+ const groups = await (await fetch(`${BASE}/api/groups`, { headers })).json();
23
+ if (!groups.length) throw new Error('no talkgroups exist');
24
+ const groupId = groups[0].id;
25
+
26
+ const existing = await (await fetch(`${BASE}/api/members`, { headers })).json();
27
+ const units = [];
28
+ for (let i = 1; i <= N; i++) {
29
+ const deviceId = `sim-${i}`;
30
+ let m = existing.find((x) => x.device_id === deviceId);
31
+ if (!m) {
32
+ const res = await fetch(`${BASE}/api/members`, {
33
+ method: 'POST', headers,
34
+ body: JSON.stringify({ name: `SIM Unit ${i}`, callsign: `SIM-${i}`, device_id: deviceId, group_id: groupId }),
35
+ });
36
+ if (!res.ok) throw new Error(`could not create ${deviceId}: ${await res.text()}`);
37
+ m = await res.json();
38
+ }
39
+ units.push(m);
40
+ }
41
+
42
+ const states = units.map((m) => ({
43
+ m,
44
+ lat: clat + (Math.random() - 0.5) * 0.08,
45
+ lng: clng + (Math.random() - 0.5) * 0.12,
46
+ battery: 60 + Math.floor(Math.random() * 40),
47
+ ws: null,
48
+ }));
49
+
50
+ for (const s of states) {
51
+ const ws = new WebSocket(BASE.replace(/^http/, 'ws') + '/ws');
52
+ ws.on('open', () => ws.send(JSON.stringify({ type: 'auth', token, memberId: s.m.id })));
53
+ ws.on('message', (d) => {
54
+ const msg = JSON.parse(d.toString());
55
+ if (msg.type === 'ptt_denied') console.log(`[${s.m.name}] PTT denied — floor held by ${msg.holder}`);
56
+ });
57
+ s.ws = ws;
58
+ }
59
+
60
+ setInterval(() => {
61
+ for (const s of states) {
62
+ s.lat += (Math.random() - 0.5) * 0.002;
63
+ s.lng += (Math.random() - 0.5) * 0.003;
64
+ if (Math.random() < 0.1) s.battery = Math.max(5, s.battery - 1);
65
+ if (s.ws.readyState === WebSocket.OPEN) {
66
+ s.ws.send(JSON.stringify({ type: 'gps', lat: s.lat, lng: s.lng, speed: 5 + Math.random() * 40, battery: s.battery }));
67
+ }
68
+ }
69
+ }, 5000);
70
+
71
+ // one unit raises an SOS shortly after start
72
+ setTimeout(() => {
73
+ const s = states[0];
74
+ if (s.ws.readyState === WebSocket.OPEN) {
75
+ s.ws.send(JSON.stringify({ type: 'sos' }));
76
+ console.log(`[${s.m.name}] SOS sent`);
77
+ }
78
+ }, 25000);
79
+
80
+ // units take turns requesting the PTT floor
81
+ let turn = 0;
82
+ setInterval(() => {
83
+ const s = states[turn++ % states.length];
84
+ if (s.ws.readyState !== WebSocket.OPEN) return;
85
+ s.ws.send(JSON.stringify({ type: 'ptt_start' }));
86
+ setTimeout(() => {
87
+ if (s.ws.readyState === WebSocket.OPEN) s.ws.send(JSON.stringify({ type: 'ptt_stop' }));
88
+ }, 4000);
89
+ }, 30000);
90
+
91
+ console.log(`simulating ${N} units against ${BASE} — Ctrl+C to stop`);
92
+ }
93
+
94
+ main().catch((e) => { console.error(e); process.exit(1); });