signalk-to-stalk
Version:
Signal K server plugin and read-only WebApp converting navigation data to SeaTalk1 with coherent settings, waypoint, units, lights, diagnostics, and calibration advice
104 lines (97 loc) • 3.23 kB
JavaScript
module.exports = function createTelemetry(options = {}) {
const capacity = Number.isInteger(options.capacity) && options.capacity > 0 ? options.capacity : 500
const events = []
const clients = new Set()
const startedAt = new Date().toISOString()
const counters = { emitted: 0, suppressed: 0, errors: 0 }
const perDatagram = {}
const lastDatagrams = {}
let running = false
let status = 'Stopped'
let navigation = {}
let units = {}
let lights = {}
let calibration = {}
let headingCalibration = {}
let configuration = {}
function record(event) {
const item = { time: new Date().toISOString(), ...event }
events.push(item)
if (events.length > capacity) events.splice(0, events.length - capacity)
if (item.type === 'emitted') {
counters.emitted += 1
perDatagram[item.datagram] = (perDatagram[item.datagram] || 0) + 1
lastDatagrams[item.datagram] = {
time: item.time,
sentence: item.sentence,
bytes: Array.isArray(item.bytes) ? [...item.bytes] : []
}
} else if (item.type === 'suppressed') counters.suppressed += 1
else if (item.type === 'error') counters.errors += 1
const payload = `data: ${JSON.stringify(item)}\n\n`
for (const client of clients) {
try { client.write(payload) } catch (_) { clients.delete(client) }
}
return item
}
function snapshot() {
return {
running,
status,
startedAt,
now: new Date().toISOString(),
outputEvent: 'stalkout',
totals: { ...counters },
perDatagram: { ...perDatagram },
lastDatagrams: structuredCloneSafe(lastDatagrams),
navigation: { ...navigation },
units: { ...units },
lights: { ...lights },
calibration: { ...calibration },
headingCalibration: { ...headingCalibration },
configuration: structuredCloneSafe(configuration),
recentCount: events.length,
capacity
}
}
function attachSse(req, res) {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive'
})
res.write(`data: ${JSON.stringify({ type: 'snapshot', ...snapshot() })}\n\n`)
clients.add(res)
const close = () => clients.delete(res)
req.on('close', close)
req.on('end', close)
}
return {
record,
snapshot,
recent(limit = 100) {
const count = Math.max(1, Math.min(capacity, Number(limit) || 100))
return events.slice(-count)
},
attachSse,
setRunning(value, text) { running = Boolean(value); if (text) status = text },
setNavigation(value) { navigation = value || {} },
setUnits(value) { units = value || {} },
setLights(value) { lights = value || {} },
setCalibration(value) { calibration = value || {} },
setHeadingCalibration(value) { headingCalibration = value || {} },
setConfiguration(value) { configuration = structuredCloneSafe(value || {}) },
stop() {
running = false
status = 'Stopped'
for (const client of clients) {
try { client.end() } catch (_) {}
}
clients.clear()
}
}
}
function structuredCloneSafe(value) {
return JSON.parse(JSON.stringify(value))
}