UNPKG

signalk-server

Version:

An implementation of a [Signal K](http://signalk.org) server for boats.

192 lines (190 loc) 7.38 kB
"use strict"; /* * Copyright 2020 Scott Bender <scott@scottbender.net> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //const canboatjs = require('@signalk/streams/canboatjs') //const N2kToSignalK = require('@signalk/streams/n2k-signalk') //const nmea0183Signalk = require('@signalk/streams/nmea0183-signalk') const Parser0183 = require('@signalk/nmea0183-signalk'); const N2kMapper = require('@signalk/n2k-signalk').N2kMapper; const { putPath, deletePath } = require('../put'); const { isN2KString, FromPgn, pgnToActisenseSerialFormat } = require('@canboat/canboatjs'); const serverRoutesPrefix = '/skServer'; let n2kOutAvailable = false; module.exports = function (app) { const n2kMapper = new N2kMapper({ app }, app.propertyValues); const pgnParser = new FromPgn({ useCamelCompat: true }, app.propertyValues); app.on('nmea2000OutAvailable', () => { n2kOutAvailable = true; }); const processors = { n2k: (msgs, sendToServer) => { const n2kJson = []; const deltas = msgs.map((msg) => { const n2k = pgnParser.parseString(msg); if (n2k) { if (sendToServer) { app.emit('N2KAnalyzerOut', n2k); } n2kJson.push(n2k); return n2kMapper.toDelta(n2k); } }); return { deltas, n2kJson: n2kJson, n2kOutAvailable }; }, '0183': (msgs) => { const parser = new Parser0183({ app }); return { deltas: msgs.map(parser.parse.bind(parser)) }; }, 'n2k-json': (msgs) => { return processors.n2k(msgs.map(pgnToActisenseSerialFormat)); } }; function detectType(message) { let type; let msg = message.trim(); if (msg.charAt(0) === '{' || msg.charAt(0) === '[') { try { const parsed = JSON.parse(msg); const first = Array.isArray(parsed) ? parsed[0] : parsed; if (first.pgn) { type = 'n2k-json'; } else if (first.updates || first.put || first.delete) { type = 'signalk'; } else { return { error: 'unknown JSON format' }; } const msgs = Array.isArray(parsed) ? parsed : [parsed]; return { type, msgs }; } catch (ex) { console.error(ex); return { error: ex.message }; } } else if (isN2KString) { // temporary until new canboatjs is released if (isN2KString(msg)) { type = 'n2k'; } else if (msg.charAt(0) === '$' || msg.charAt(0) === '!') { type = '0183'; } else { return { error: 'unable to determine message type' }; } } else if (msg.charAt(0) === '$' || msg.charAt(0) === '!') { type = '0183'; } else { type = 'n2k'; } return { type, msgs: msg.split('\n').filter((s) => s.length > 0) }; } app.post(`${serverRoutesPrefix}/inputTest`, (req, res) => { const sendToServer = req.body.sendToServer; const sendToN2K = req.body.sendToN2K; if ((sendToServer || sendToN2K) && !app.securityStrategy.isDummy() && !app.securityStrategy.allowConfigure(req)) { res.status(400).json({ error: 'permission denied' }); return; } const { type, msgs, error } = detectType(req.body.value); if (error) { res.status(400).json({ error: error }); return; } if (sendToN2K && type != 'n2k-json' && type != 'n2k') { res.status(400).json({ error: 'Please enter NMEA 2000 json format or Actisense format' }); return; } if (type === 'signalk') { let puts = []; if (sendToServer) { msgs.forEach((msg) => { if (msg.put) { puts.push(new Promise((resolve) => { setTimeout(() => { resolve('Timed out waiting for put result'); }, 5000); putPath(app, msg.context, msg.put.path, msg.put, req, msg.requestId, (reply) => { if (reply.state !== 'PENDING') { resolve(reply); } }); })); } else if (msg.delete) { puts.push(new Promise((resolve) => { setTimeout(() => { resolve('Timed out waiting for put result'); }, 5000); deletePath(app, msg.context, msg.delete.path, req, msg.requestId, (reply) => { if (reply.state !== 'PENDING') { resolve(reply); } }); })); } else { app.handleMessage('input-test', msg); } }); } if (puts.length > 0) { Promise.all(puts).then((results) => { res.json({ deltas: msgs, putResults: results }); }); } else { res.json({ deltas: msgs }); } } else if (sendToN2K) { const event = type == 'n2k' ? 'nmea2000out' : 'nmea2000JsonOut'; msgs.forEach((msg) => { app.emit(event, msg); }); res.json({ deltas: [] }); } else { try { const data = processors[type](msgs, sendToServer); if (data.deltas) { data.deltas = data.deltas.filter((m) => typeof m !== 'undefined' && m != null && m.updates.length > 0 && m.updates[0].values && m.updates[0].values.length > 0); } res.json(data); if (sendToServer) { data.deltas.forEach((msg) => { app.handleMessage('input-test', msg); }); } } catch (ex) { console.error(ex); res.status(400).json({ error: ex.message }); } } }); };