@canboat/canboatjs
Version:
Native javascript version of canboat
246 lines (212 loc) • 7.04 kB
JavaScript
/**
* Copyright 2018 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 debug = require('debug')('canboatjs:ikonvert')
const Transform = require('stream').Transform
const isArray = require('lodash').isArray
const BitStream = require('bit-buffer').BitStream
const BitView = require('bit-buffer').BitView
const {toPgn, pgnToiKonvertSerialFormat} = require('./toPgn')
const Parser = require('./fromPgn').Parser
const _ = require('lodash')
const CanDevice = require('./candevice')
const { defaultTransmitPGNs } = require('./codes')
const { parseCanId } = require('./canId')
const pgnsSent = {}
function iKonvertStream (options) {
if (!(this instanceof iKonvertStream)) {
return new iKonvertStream(options)
}
Transform.call(this, {
objectMode: true
})
this.isTcp = options.tcp === true
this.outEvent = this.isTcp? 'navlink2-out' : 'ikonvertOut'
this.plainText = false
this.reconnect = options.reconnect || true
this.options = options
this.cansend = false
this.buffer = Buffer.alloc(500)
this.bufferOffset = 0
this.start()
this.setProviderStatus = options.app && options.app.setProviderStatus
? (msg) => {
options.app.setProviderStatus(options.providerId, msg)
}
: () => {}
this.setProviderError = options.app && options.app.setProviderError
? (msg) => {
options.app.setProviderError(options.providerId, msg)
}
: () => {}
this.transmitPGNs = defaultTransmitPGNs
if ( this.options.transmitPGNs ) {
this.transmitPGNs = _.union(this.transmitPGNs,
this.options.transmitPGNs)
}
var that = this
if ( this.options.app ) {
options.app.on(this.options.outEevent || 'nmea2000out', (msg) => {
if ( typeof msg === 'string' ) {
that.sendActisensePGN(msg)
} else {
that.sendPGN(msg)
}
options.app.emit('connectionwrite', { providerId: options.providerId })
})
options.app.on(options.jsonOutEvent || 'nmea2000JsonOut', (msg) => {
that.sendPGN(msg)
options.app.emit('connectionwrite', { providerId: options.providerId })
})
this.isSetup = false
//this.cansend = true
this.state = 0
this.setupCommands = this.getSetupCommands()
this.expecting = false
debug('started')
}
}
require('util').inherits(iKonvertStream, Transform)
iKonvertStream.prototype.start = function () {
}
iKonvertStream.prototype.sendString = function (msg) {
debug('sending %s', msg)
if ( this.isTcp ) {
msg = msg + "\n\r"
}
this.options.app.emit(this.outEvent, msg)
}
iKonvertStream.prototype.sendPGN = function (pgn) {
if ( this.cansend ) {
let now = Date.now()
let lastSent = pgnsSent[pgn.pgn]
let msg = pgnToiKonvertSerialFormat(pgn)
this.sendString(msg)
pgnsSent[pgn.pgn] = now
}
}
iKonvertStream.prototype.sendActisensePGN = function (msg) {
if ( this.cansend ) {
if ( !this.parser ) {
this.parser = new Parser(this.options)
let that = this
this.parser.on('error', (pgn, error) => {
console.error(`Error parsing ${pgn.pgn} ${error}`)
console.error(error.stack)
})
this.parser.on('pgn', (pgn) => {
let now = Date.now()
let lastSent = pgnsSent[pgn.pgn]
let msg = pgnToiKonvertSerialFormat(pgn)
that.sendString(msg)
pgnsSent[pgn.pgn] = now
})
}
this.parser.parseString(msg)
}
}
iKonvertStream.prototype.setup = function () {
let txPgns = '$PDGY,TX_LIST'
this.transmitPGNs.forEach(pgn => {
txPgns = txPgns + `,${pgn}`
})
debug('sending pgn tx list')
this.sendString(txPgns)
}
iKonvertStream.prototype.getSetupCommands = function () {
let txPgns = '$PDGY,TX_LIST'
this.transmitPGNs.forEach(pgn => {
txPgns = txPgns + `,${pgn}`
})
const setupCommands = [];
setupCommands.push('$PDGY,N2NET_OFFLINE:$PDGY,TEXT,Digital_Yacht_')
if ( this.isTcp ) {
setupCommands.push('$PDGY,N2NET_MODE,15:$PDGY,ACK,N2NET_MODE')
}
setupCommands.push('$PDGY,TX_LIMIT,OFF:$PDGY,') // NACK is ok with old firmware
setupCommands.push(`${txPgns}:$PDGY,ACK,TX_LIST`)
setupCommands.push('$PDGY,N2NET_INIT,ALL:$PDGY,ACK,N2NET_INIT,ALL')
return setupCommands
}
iKonvertStream.prototype._transform = function (chunk, encoding, done) {
let line = chunk.toString().trim()
line = line.substring(0, line.length) // take off the \r
if ( line.startsWith('$PDGY,TEXT') ) {
debug(line)
} else if ( line.startsWith('$PDGY,000000,') ) {
let parts = line.split(',')
if ( this.options.sendNetworkStats && parts[2] && parts[2].length > 0 ) {
const pgn = {
pgn: 0x40100,
prio: 7,
dst: 255,
src: 0,
'CAN network load': Number(parts[2]),
Errors: Number(parts[3]),
'Device count': Number(parts[4]),
'Uptime': Number(parts[5]),
'Gateway address': Number(parts[6]),
'Rejected TX requests': Number(parts[7])
}
const buf = toPgn(pgn)
if ( buf ) {
this.push(`!PDGY,${pgn.pgn},${pgn.prio},${pgn.src},${pgn.dst},0,${buf.toString('base64')}`)
}
done()
return
}
} else if ( line.startsWith('$PDGY,NAK') ) {
let parts = line.split(',')
let msg = `NavLink2 error ${parts[2]}: ${parts[3]}`
console.error(msg)
//this.setProviderError(msg)
}
if ( !this.isSetup ) {
debug(line)
let command = this.setupCommands[this.state].split(':')
if ( !this.expecting ) {
this.sendString(command[0])
this.expecting = true
this.sentTime = Date.now()
debug(`Waiting for ${command[1]}`)
} else {
if ( line.startsWith(command[1]) ) {
this.state = this.state + 1
if ( this.state == this.setupCommands.length ) {
this.isSetup = true
this.cansend = true
this.options.app.emit('nmea2000OutAvailable')
debug('Setup completed')
} else {
command = this.setupCommands[this.state].split(':')
this.sendString(command[0])
this.expecting = true
this.sentTime = Date.now()
debug(`Waiting for ${command[1]}`)
}
} else if ( Date.now() - this.sentTime > 5000 ) {
debug(`Did not receive expected: ${command[1]}, retrying...`)
this.sendString(command[0])
this.sentTime = Date.now()
}
}
} else {
this.push(line)
}
done()
}
iKonvertStream.prototype.end = function () {
}
module.exports = iKonvertStream