Skip to content

Commit

Permalink
feature: add support for Digital Yacht’s iKonvert (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
sbender9 authored Dec 12, 2018
1 parent 4372d9a commit 7907371
Show file tree
Hide file tree
Showing 13 changed files with 413 additions and 26,444 deletions.
2 changes: 1 addition & 1 deletion bin/candumpanalyzerjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env node

const fromPgnStream = new (require('../lib/fromPgnStream'))()
const canbus = new (require('../index').canbus)({})
const canbus = new (require('../index').canbus)({fromStdIn:true})
const { Transform } = require('stream');

const toStringTr = new Transform({
Expand Down
49 changes: 49 additions & 0 deletions bin/ikonvert-serial
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env node

const { Transform } = require('stream');
const SerialPort = require('serialport')

if ( process.argv.length < 3 ) {
console.error('Please specify a device')
console.error('usage: ikonvert-serial [device] [baud,default:230400]')
process.exit(1)
}

let device = process.argv[2]

let baud = process.argv.length > 3 ? Number(process.argv[3]) : 230400

let serial = new SerialPort(device, {
baudRate: baud
})

const toStringTr = new Transform({
objectMode: true,

transform(line, encoding, callback) {
//this.push(JSON.stringify(chunk) + "\n");
console.log(line)

if ( line.startsWith('$PDGY,000000,,,,,') ) {
serial.write('$PDGY,N2NET_INIT,ALL\r\n')
}

callback();
}
});

serial.on(
'open',
function () {
const parser = new SerialPort.parsers.Readline()
serial.pipe(parser).pipe(toStringTr)
}
)

serial.on(
'error',
function (x) {
console.log(x)
}
)

6 changes: 3 additions & 3 deletions bin/to-pgn
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env node

const { toPgn, toActisenseSerialFormat} = require('../index')
const { toPgn, pgnToActisenseSerialFormat, pgnToiKonvertSerialFormat} = require('../index')

var readline = require('readline')
var rl = readline.createInterface({
Expand All @@ -12,6 +12,6 @@ var rl = readline.createInterface({
var input = []
rl.on('line', function (line) {
var pgn = JSON.parse(line)
var data = toPgn(pgn)
console.log(toActisenseSerialFormat(pgn.pgn, data))
console.log(pgnToActisenseSerialFormat(pgn))
//console.log(pgnToiKonvertSerialFormat(pgn))
})
4 changes: 3 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,7 @@ module.exports = {
toPgn: require('./lib/toPgn').toPgn,
toActisenseSerialFormat: require('./lib/toPgn').toActisenseSerialFormat,
pgnToActisenseSerialFormat: require('./lib/toPgn').pgnToActisenseSerialFormat,
canbus: require('./lib/canbus')
pgnToiKonvertSerialFormat: require('./lib/toPgn').pgnToiKonvertSerialFormat,
canbus: require('./lib/canbus'),
iKonvert: require('./lib/ikonvert')
}
5 changes: 5 additions & 0 deletions lib/canbus.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ function CanbusStream (options) {
options.app.setProviderError(options.providerId, msg)
}
: () => {}

if ( options.fromStdIn ) {
return
}

var socketcan;

Expand Down Expand Up @@ -136,6 +140,7 @@ function CanbusStream (options) {
} catch (e) {
setProviderError(e.message)
console.error(`unable to open canbus ${canDevice}: ${e}`)
console.error(e.stack)
}
}
}
Expand Down
23 changes: 4 additions & 19 deletions lib/candevice.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const debug = require('debug')('canboatjs:candevice')
const EventEmitter = require('events')
const _ = require('lodash')
const Uint64LE = require('int64-buffer').Uint64LE
const { getManufacturerCode, getIndustryCode, getDeviceClassCode, getCanIdFromPGN } = require('./utilities')
const { getManufacturerCode, getIndustryCode, getDeviceClassCode, getCanIdFromPGN, defaultTransmitPGNs } = require('./utilities')
const { toPgn } = require('./toPgn')

const addressClaim = {
Expand All @@ -36,23 +36,6 @@ const addressClaim = {
"Reserved2": 2
}

const defaultTransmitPGNs = [
60928,
59904,
126996,
126464,
128267,
129794,
129038,
129041,
127506,
127508,
129026,
129025,
129029,
127250,
130306
]

class CanDevice extends EventEmitter {
constructor (canbus, options) {
Expand All @@ -74,7 +57,9 @@ class CanDevice extends EventEmitter {
this.options.transmitPGNs)
}

options.app.on('N2KAnalyzerOut', this.n2kMessage.bind(this))
if ( options.app ) {
options.app.on('N2KAnalyzerOut', this.n2kMessage.bind(this))
}
}

start() {
Expand Down
32 changes: 26 additions & 6 deletions lib/fromPgn.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ const FASTPACKET_MAX_SIZE = (FASTPACKET_BUCKET_0_SIZE + FASTPACKET_BUCKET_N_SIZE
const ignoredPgns = []//130820, 126720 ]

function organizedPGNs() {
const pgns = require('./pgns')
const pgns = require('@canboat/pgns')
const res = {}
pgns.PGNs.forEach(pgn => {
if ( !res[pgn.PGN] ) {
Expand Down Expand Up @@ -266,7 +266,8 @@ class Parser extends EventEmitter {
var split = pgn_data.split(',')

var pgn = {}
var array
var buffer
var len

if ( split[0] === '$PCDIN' ) {
// $PCDIN,01F119,00000000,0F,2AAF00D1067414FF*59
Expand All @@ -281,24 +282,43 @@ class Parser extends EventEmitter {
if ( data.indexOf('*') != -1 ) {
data = data.split('*')[0]
}
array = new Int16Array(data.length/2)
let array = new Int16Array(data.length/2)
for ( var i = 0, j = 0; i < data.length; i += 2, j++ ) {
array[j] = parseInt(data.slice(i, i+2), 16)
}
buffer = new Buffer(array)
len = data.length/2
} else if ( split[0] === '!PDGY' ) {
//iKonvert
pgn.pgn = Number(split[1])
pgn.prio = Number(split[2])
pgn.src = Number(split[3])
pgn.dst = Number(split[4])
pgn.timer = Number(split[5])
pgn.timestamp = new Date().toISOString()


if ( split.length < 7 ) {
this.emit('warning', pgn, `ignoring pgn ${pgn_data}`)
return
}

buffer = new Buffer(split[6], 'base64')
len = buffer.length
} else {
mainFields.forEach((key, index) => {
var val = split[index]
pgn[key] = key === 'timestamp' ? val : Number(val)
})

var len = Number(split[5])
array = new Int16Array(len)
len = Number(split[5])
let array = new Int16Array(len)
for ( var i = 6; i < (len+6); i++ ) {
array[i-6] = parseInt(split[i], 16)
}
buffer = new Buffer(array)
}

var buffer = new Buffer(array)
var bv = new BitView(buffer);
var bs = new BitStream(bv)

Expand Down
175 changes: 175 additions & 0 deletions lib/ikonvert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/**
* 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 spawn = require('child_process').spawn
const { getPGNFromCanId, getCanIdFromPGN, actisenseSerialToBuffer, defaultTransmitPGNs } = require('./utilities')

const pgnsSent = {}
const rateLimit = 200

function iKonvertStream (options) {
if (!(this instanceof iKonvertStream)) {
return new iKonvertStream(options)
}

Transform.call(this, {
objectMode: true
})

this.plainText = false
this.reconnect = options.reconnect || true
this.options = options
this.cansend = false
this.buffer = new Buffer(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)
}
: () => {}

var that = this

if ( this.options.app ) {
options.app.on('nmea2000out', (msg) => {
that.sendActisensePGN(msg)
})
options.app.on('nmea2000JsonOut', (msg) => {
that.sendPGN(msg)
})

this.sendString('$PDGY,N2NET_OFFLINE')

debug('started')
//this.options.app.emit('nmea2000OutAvailable')
}
}

require('util').inherits(iKonvertStream, Transform)

iKonvertStream.prototype.start = function () {
}

iKonvertStream.prototype.sendString = function (msg) {
debug('sending %s', msg)
this.options.app.emit('ikonvertOut', msg)
}

iKonvertStream.prototype.sendPGN = function (pgn) {
if ( this.cansend ) {
let now = Date.now()
let lastSent = pgnsSent[pgn.pgn]
if ( !lastSent || now - lastSent > rateLimit ) {
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()

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]
if ( !lastSent || now - lastSent > rateLimit ) {
let msg = pgnToiKonvertSerialFormat(pgn)
that.sendString(msg)
pgnsSent[pgn.pgn] = now
}
})
}
this.parser.parseString(msg)
}
}

iKonvertStream.prototype.setup = function () {
let txPgns = '$PDGY,TX_LIST'
defaultTransmitPGNs.forEach(pgn => {
txPgns = txPgns + `,${pgn}`
})
debug('sending pgn tx list')
this.sendString(txPgns)
}

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') ) {
if ( line === '$PDGY,000000,,,,,,' ) {
//the iKonvert is not initialized
if ( !this.didSetup ) {
this.setup()
this.setProviderStatus('Initializing...')
this.didSetup = true
}
} else if ( line === '$PDGY,ACK,TX_LIST' ) {
debug('sending net init')
this.sendString('$PDGY,N2NET_INIT,ALL')
this.setProviderStatus('Initialized...')
} else if ( line === '$PDGY,ACK,N2NET_INIT,ALL' && !this.cansend ) {
this.cansend = true;
this.setProviderStatus('Connected')
//this.sendString('$PDGY,SHOW_LISTS')
} else if ( line.startsWith('$PDGY,TEXT') ) {
debug(line)
} else if ( line.startsWith('$PDGY,000000') ) {
let parts = line.split(',')
debug('ikonvert can address: %s', parts[6])
debug(line)
} else if ( line.startsWith('$PDGY,NAK') ) {
let parts = line.split(',')
let msg = `iKonvert error ${parts[2]}: ${parts[3]}`
console.error(msg)
this.setProviderError(msg)
}
} else {
this.push(line)
}

done()
}

iKonvertStream.prototype.end = function () {
}

module.exports = iKonvertStream
Loading

0 comments on commit 7907371

Please sign in to comment.