UNPKG

@signalk/streams

Version:

Utilities for handling streams of Signal K data

196 lines (176 loc) 5.81 kB
/* * Copyright 2014-2015 Fabian Tollenaar <fabian@starting-point.nl> * * 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 Transform = require('stream').Transform const N2kMapper = require('@signalk/n2k-signalk').N2kMapper require('util').inherits(ToSignalK, Transform) function ToSignalK(options) { Transform.call(this, { objectMode: true }) const n2kOutEvent = 'nmea2000JsonOut' this.sourceMeta = {} this.notifications = {} this.options = options this.app = options.app if (options.filters && options.filtersEnabled) { this.filters = options.filters.filter((f) => { return (f.source && f.source.length) || (f.pgn && f.pgn.length) }) } this.n2kMapper = new N2kMapper({ ...options, sendMetaData: true }) this.n2kMapper.on('n2kOut', (pgn) => this.app.emit('nmea2000JsonOut', pgn)) this.n2kMapper.on('n2kSourceMetadata', (n2k, meta) => { const existing = this.sourceMeta[n2k.src] || {} this.sourceMeta[n2k.src] = { ...existing, ...meta } const delta = { context: this.app.selfContext, updates: [ { source: { ...this.sourceMeta[n2k.src], label: this.options.providerId, type: 'NMEA2000', pgn: Number(n2k.pgn), src: n2k.src.toString() }, timestamp: n2k.timestamp.substring(0, 10) + 'T' + n2k.timestamp.substring(11, n2k.timestamp.length), values: [] } ] } this.app.deltaCache.setSourceDelta( `${this.options.providerId}.${n2k.src}`, delta ) }) this.n2kMapper.on('n2kSourceMetadataTimeout', (pgn, src) => { if (pgn == 60928) { console.warn(`n2k-signalk: unable to detect can name for src ${src}`) this.sourceMeta[src].unknowCanName = true } }) this.n2kMapper.on('n2kSourceChanged', (src, from, to) => { console.warn(`n2k-signalk: address ${src} changed from ${from} ${to}`) if (this.sourceMeta[src]) { delete this.sourceMeta[src] } }) if (this.app.isNmea2000OutAvailable) { this.n2kMapper.n2kOutIsAvailable(this.app, n2kOutEvent) } else { this.app.on('nmea2000OutAvailable', () => this.n2kMapper.n2kOutIsAvailable(this.app, n2kOutEvent) ) } } ToSignalK.prototype.isFiltered = function (source) { return ( this.filters && this.filters.find((filter) => { const sFilter = this.options.useCanName ? source.canName : source.src return ( (!filter.source || filter.source.length === 0 || filter.source == sFilter) && (!filter.pgn || filter.pgn.length === 0 || filter.pgn == source.pgn) ) }) ) } ToSignalK.prototype._transform = function (chunk, encoding, done) { try { const delta = this.n2kMapper.toDelta(chunk) const src = Number(chunk.src) if (!this.sourceMeta[src]) { this.sourceMeta[src] = {} } if ( delta && delta.updates[0].values.length > 0 && !this.isFiltered(delta.updates[0].source) ) { if (!this.options.useCanName) { delete delta.updates[0].source.canName } const canName = delta.updates[0].source.canName if ( this.options.useCanName && !canName && !this.sourceMeta[src].unknowCanName ) { done() return } delta.updates.forEach((update) => { update.values.forEach((kv) => { if (kv.path && kv.path.startsWith('notifications.')) { if ( kv.value.state === 'normal' && this.notifications[kv.path] && this.notifications[kv.path][src] ) { clearInterval(this.notifications[kv.path][src].interval) delete this.notifications[kv.path][src] } else if (kv.value.state !== 'normal') { if (!this.notifications[kv.path]) { this.notifications[kv.path] = {} } if (!this.notifications[kv.path][src]) { const interval = setInterval(() => { if ( Date.now() - this.notifications[kv.path][src].lastTime > 10000 ) { const copy = JSON.parse(JSON.stringify(kv)) copy.value.state = 'normal' const normalDelta = { context: delta.context, updates: [ { source: update.source, values: [copy] } ] } delete this.notifications[kv.path][src] clearInterval(interval) this.app.handleMessage(this.options.providerId, normalDelta) } }, 5000) this.notifications[kv.path][src] = { lastTime: Date.now(), interval: interval } } else { this.notifications[kv.path][src].lastTime = Date.now() } } } }) }) this.push(delta) } } catch (ex) { console.error(ex) } done() } module.exports = ToSignalK