Skip to content

Commit 7911da9

Browse files
committed
implemented multi comm
1 parent 1926e4d commit 7911da9

File tree

4 files changed

+387
-7
lines changed

4 files changed

+387
-7
lines changed

apps/summit-integration/src/SentryTowerAgent.ts

Lines changed: 130 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import { EventEmitter } from 'events';
77
import { SummitClient, FireDetection } from './SummitClient';
88
import * as sharp from 'sharp';
9+
import { MultiLinkManager, LinkType, MessagePriority } from './communication';
910

1011
export interface TowerConfig {
1112
towerId: string;
@@ -17,6 +18,22 @@ export interface TowerConfig {
1718
};
1819
thermalCamera: ThermalCameraConfig;
1920
acousticArray: AcousticArrayConfig;
21+
communication: {
22+
multiLink: {
23+
enabled: boolean;
24+
primaryLink: 'radio_mesh' | 'cellular' | 'satellite' | 'wifi';
25+
failoverOrder: Array<'radio_mesh' | 'cellular' | 'satellite' | 'wifi'>;
26+
autonomousMode: { enabled: boolean; syncInterval: number; bufferSizeMB: number };
27+
};
28+
radioMesh: {
29+
frequency: '900MHz' | '2.4GHz' | '5GHz';
30+
meshId: string;
31+
powerLevel: 'low' | 'medium' | 'high';
32+
encryption: 'wpa3' | 'none';
33+
};
34+
cellular: { carriers: ['primary', 'secondary'] | string[]; dataLimitMB: number };
35+
satellite: { provider: 'starlink' | string; backupOnly: boolean };
36+
};
2037
summitIntegration: {
2138
apiUrl: string;
2239
apiKey: string;
@@ -79,6 +96,7 @@ export interface MultiSensorFusion {
7996
export class SentryTowerAgent extends EventEmitter {
8097
private config: TowerConfig;
8198
private summitClient: SummitClient;
99+
private multiLink?: MultiLinkManager;
82100
private isRunning: boolean = false;
83101
private detectionHistory: FireDetectionResult[] = [];
84102
private fusionEngine: MultiSensorFusion;
@@ -105,6 +123,9 @@ export class SentryTowerAgent extends EventEmitter {
105123
try {
106124
console.log(`Starting Sentry Tower Agent for tower ${this.config.towerId}`);
107125

126+
// Initialize multi-link communications
127+
this.initializeMultiLink();
128+
108129
// Connect to Summit.OS
109130
await this.summitClient.connect();
110131

@@ -267,25 +288,41 @@ export class SentryTowerAgent extends EventEmitter {
267288
this.detectionHistory = this.detectionHistory.slice(-1000);
268289
}
269290

270-
// Create Summit.OS detection
271-
const summitDetection: Omit<FireDetection, 'id'> = {
291+
// Prepare alert payload
292+
const alert = {
272293
deviceId: this.config.towerId,
273294
timestamp: detection.timestamp,
274295
type: detection.type,
275296
confidence: detection.confidence,
276297
position: detection.position,
277298
bearing: detection.bearing,
278-
mediaRef: `tower_${this.config.towerId}_${Date.now()}`,
279-
source: 'edge'
299+
source: 'edge' as const,
280300
};
281-
301+
302+
// 1) Immediate mesh broadcast (<50ms)
303+
if (this.multiLink) {
304+
await this.multiLink.sendMessage(
305+
'sentinel/alerts/fire',
306+
alert,
307+
MessagePriority.CRITICAL,
308+
{ preferredLink: LinkType.RADIO_MESH }
309+
);
310+
}
311+
282312
if (this.offlineMode) {
283313
// Store for later sync
284314
this.offlineData.push(detection);
285315
this.emit('offlineDetection', detection);
286316
} else {
287-
// Report to Summit.OS immediately
288-
await this.summitClient.reportFireDetection(summitDetection);
317+
// 2) Send to command via best available link (cellular/satellite/wifi)
318+
if (this.multiLink) {
319+
await this.multiLink.sendMessage('sentinel/alerts/fire/cloud', alert, MessagePriority.CRITICAL);
320+
}
321+
// Maintain existing Summit.OS reporting path
322+
await this.summitClient.reportFireDetection({
323+
...alert,
324+
mediaRef: `tower_${this.config.towerId}_${Date.now()}`,
325+
});
289326
this.emit('fireDetection', detection);
290327
}
291328

@@ -325,6 +362,92 @@ export class SentryTowerAgent extends EventEmitter {
325362
this.summitClient.on('error', (error) => {
326363
this.emit('summitError', error);
327364
});
365+
366+
// Multi-link link status to drive autonomous mode
367+
this.on('linkUpdated', () => {
368+
if (!this.multiLink) return;
369+
const wide = this.multiLink.hasWideAreaConnectivity();
370+
if (!wide && !this.offlineMode) {
371+
// Enter autonomous mode
372+
this.enableOfflineMode();
373+
}
374+
if (wide && this.offlineMode) {
375+
// Exit autonomous mode and trigger flush
376+
this.disableOfflineMode();
377+
this.multiLink.flushBuffer();
378+
}
379+
});
380+
}
381+
382+
private initializeMultiLink(): void {
383+
if (!this.config.communication?.multiLink?.enabled) return;
384+
const ml = new MultiLinkManager({
385+
deviceId: this.config.towerId,
386+
failoverOrder: this.config.communication.multiLink.failoverOrder.map((t) => t as unknown as LinkType),
387+
autonomousMode: {
388+
enabled: this.config.communication.multiLink.autonomousMode.enabled,
389+
syncIntervalSec: this.config.communication.multiLink.autonomousMode.syncInterval,
390+
bufferSizeMB: this.config.communication.multiLink.autonomousMode.bufferSizeMB,
391+
},
392+
});
393+
394+
// Seed link states (these would be updated by real adapters/health checks)
395+
ml.addOrUpdateLink({
396+
type: LinkType.RADIO_MESH,
397+
id: this.config.communication.radioMesh.meshId,
398+
enabled: true,
399+
up: true,
400+
preferred: this.config.communication.multiLink.primaryLink === 'radio_mesh',
401+
costScore: 1,
402+
quality: { latencyMs: 20, throughputKbps: 1000, lossPct: 0.5 },
403+
});
404+
405+
ml.addOrUpdateLink({
406+
type: LinkType.CELLULAR,
407+
id: 'dual-sim-modem',
408+
enabled: true,
409+
up: true,
410+
preferred: this.config.communication.multiLink.primaryLink === 'cellular',
411+
costScore: 5,
412+
quality: { latencyMs: 80, throughputKbps: 20000, lossPct: 1.0 },
413+
});
414+
415+
ml.addOrUpdateLink({
416+
type: LinkType.SATELLITE,
417+
id: this.config.communication.satellite.provider,
418+
enabled: true,
419+
up: !this.config.communication.satellite.backupOnly, // still available but used as backup
420+
preferred: this.config.communication.multiLink.primaryLink === 'satellite',
421+
costScore: 9,
422+
quality: { latencyMs: 600, throughputKbps: 10000, lossPct: 1.5 },
423+
});
424+
425+
ml.addOrUpdateLink({
426+
type: LinkType.WIFI,
427+
id: 'wifi-ap',
428+
enabled: true,
429+
up: true,
430+
preferred: this.config.communication.multiLink.primaryLink === 'wifi',
431+
costScore: 2,
432+
quality: { latencyMs: 30, throughputKbps: 54000, lossPct: 0.5 },
433+
});
434+
435+
ml.setFailoverOrder([
436+
LinkType.RADIO_MESH,
437+
LinkType.CELLULAR,
438+
LinkType.SATELLITE,
439+
LinkType.WIFI,
440+
]);
441+
442+
ml.start();
443+
444+
// Bubble link events
445+
ml.on('linkUpdated', (s) => this.emit('linkUpdated', s));
446+
ml.on('sent', (s) => this.emit('linkSent', s));
447+
ml.on('buffered', (s) => this.emit('linkBuffered', s));
448+
ml.on('flushed', (s) => this.emit('linkFlushed', s));
449+
450+
this.multiLink = ml;
328451
}
329452
}
330453

0 commit comments

Comments
 (0)