diff --git a/conversions/ais.js b/conversions/ais.js index 561a81b..2759a21 100644 --- a/conversions/ais.js +++ b/conversions/ais.js @@ -65,7 +65,7 @@ module.exports = (app, plugin) => { return null } - var vessel = _.get(app.signalk.root, delta.context) + var vessel = app.getPath(delta.context) var mmsi = _.get(vessel, 'mmsi') || findDeltaValue(delta, 'mmsi'); if ( !mmsi ) { @@ -82,7 +82,7 @@ module.exports = (app, plugin) => { } return res } else if ( delta.context.startsWith('atons.') ) { - var vessel = _.get(app.signalk.root, delta.context) + var vessel = app.getPath(delta.context) var mmsi = _.get(vessel, 'mmsi') || findDeltaValue(delta, 'mmsi'); if ( !mmsi ) { @@ -91,7 +91,126 @@ module.exports = (app, plugin) => { return [ generateAtoN(vessel, mmsi, delta) ] } - } + }, + tests: [{ + input: [{ + "context":"vessels.urn:mrn:imo:mmsi:367301250", + "updates":[{"values":[ + { + "path":"navigation.position", + "value": {"longitude":-76.3947165,"latitude":39.1296167} + }, + {"path":"navigation.courseOverGroundTrue","value":1.501}, + {"path":"navigation.speedOverGround","value":0.05}, + {"path":"navigation.headingTrue","value":5.6199}, + {"path":"navigation.rateOfTurn","value":0}, + {"path":"navigation.state","value":"motoring"}, + {"path":"navigation.destination.commonName","value":"BALTIMORE"}, + {"path":"sensors.ais.fromBow","value":9}, + {"path": "design.draft", "value": { "maximum": 4.2 }}, + {"path": "design.length","value": {"overall": 30}}, + {"path": "design.aisShipType", "value": {"id": 52, "name": "Tug"}}, + {"path": "design.beam","value": 7}, + {"path":"","value":{"mmsi":"367301250"}}, + {"path":"","value":{"name":"SOME BOAT"}} + ]} + ]}], + expected: [{ + "prio": 2, + "pgn": 129038, + "dst": 255, + "fields": { + "Message ID": "Scheduled Class A position report", + "User ID": 367301250, + "Longitude": -76.3947165, + "Latitude": 39.1296167, + "Position Accuracy": "Low", + "RAIM": "not in use", + "Time Stamp": "0", + "COG": 1.501, + "SOG": 0.05, + "AIS Transceiver information": "Channel A VDL reception", + "Heading": 5.6199, + "Rate of Turn": 0, + "Nav Status": "Under way using engine" + } + },{ + "prio": 2, + "pgn": 129794, + "dst": 255, + "fields": { + "Message ID": "Static and voyage related data", + "User ID": 367301250, + "Callsign": "", + "Name": "SOME BOAT", + "Type of ship": "Tug", + "Length": 30, + "Beam": 7, + "Position reference from Bow": 9, + "Draft": 4.2, + "Destination": "BALTIMORE", + "AIS version indicator": "ITU-R M.1371-1", + "DTE": "Available", + "Reserved1": 1, + "AIS Transceiver information": "Channel A VDL reception" + } + }] + },{ + input: [{ + "context": "atons.urn:mrn:imo:mmsi:993672085", + "updates": [ + { + "values":[ + {"path": "","value": {"name": "78A"}}, + { + "path": "navigation.position", + "value": { + "longitude": -76.4313882, + "latitude": 38.5783333 + } + }, + { + "path": "atonType", + "value": { + "id": 14, + "name": "Beacon, Starboard Hand" + } + }, + { + "path": "", + "value": { + "mmsi": "993672085" + } + }, + { + "path": "sensors.ais.class", + "value": "ATON" + } + ] + } + ]}], + expected: [{ + "prio": 2, + "pgn": 129041, + "dst": 255, + "fields": { + "Message ID": 0, + "Repeat Indicator": "Initial", + "User ID": 993672085, + "Longitude": -76.4313882, + "Latitude": 38.5783333, + "Position Accuracy": "Low", + "RAIM": "not in use", + "Time Stamp": "0", + "AtoN Type": "Fixed beacon: starboard hand", + "Off Position Indicator": "Yes", + "Virtual AtoN Flag": "Yes", + "Assigned Mode Flag": "Assigned mode", + "Spare": 1, + "AtoN Name": "78A" + } + }] + }] } } diff --git a/conversions/attitude.js b/conversions/attitude.js index 1371d64..66d397e 100644 --- a/conversions/attitude.js +++ b/conversions/attitude.js @@ -15,6 +15,26 @@ module.exports = (app, plugin) => { Yaw: attitude.yaw, Roll: attitude.roll }] - } + }, + tests: [ + { + input: [ { + "yaw": 1.8843, + "pitch": 0.042, + "roll": 0.042 + } ], + expected: [ { + "dst": 255, + "fields": { + "Pitch": 0.042, + "Roll": 0.042, + "SID": 87, + "Yaw": 1.8843, + }, + "pgn": 127257, + "prio": 2 + }] + } + ] } } diff --git a/conversions/battery.js b/conversions/battery.js index 32c76d8..2f1c52c 100644 --- a/conversions/battery.js +++ b/conversions/battery.js @@ -36,6 +36,17 @@ module.exports = (app, plugin) => { } }, + testOptions: { + BATTERYv2: { + batteries: [ + { + signalkId: 0, + instanceId: 1 + } + ] + } + }, + conversions: (options) => { if ( !_.get(options, 'BATTERYv2.batteries') ) { return null @@ -73,12 +84,38 @@ module.exports = (app, plugin) => { "Instance": battery.instanceId, 'State of Charge': stateOfCharge, 'State of Health': stateOfHealth, - 'Time Remaining': timeRemaining, + 'Time Remaining': timeRemaining, 'Ripple Voltage': ripple }) } return res - } + }, + tests: [{ + input: [12.5, 23.1, 290.15, 0.93, 12340, 0.6, 12.0], + expected: [{ + "prio": 2, + "pgn": 127508, + "dst": 255, + "fields": { + "Instance": 1, + "Voltage": 12.5, + "Current": 23.1, + "Temperature": 290.15 + } + },{ + "prio": 2, + "pgn": 127506, + "dst": 255, + "fields": { + "Instance": 1, + "DC Type": "Battery", + "State of Charge": 93, + "State of Health": 60, + "Time Remaining": "03:26:00", + "Ripple Voltage": 12 + } + }] + }] } }) } diff --git a/conversions/cogSOG.js b/conversions/cogSOG.js index 34d1261..50fed74 100644 --- a/conversions/cogSOG.js +++ b/conversions/cogSOG.js @@ -20,6 +20,19 @@ module.exports = (app, plugin) => { } catch ( err ) { console.error(err) } - } + }, + tests: [{ + input: [ 2.1, 9 ], + expected: [{ + "prio": 2, + "pgn": 129026, + "dst": 255, + "fields": { + "COG Reference": "True", + "COG": 2.1, + "SOG": 9 + } + }] + }] } } diff --git a/conversions/depth.js b/conversions/depth.js index be71464..f6f70c2 100644 --- a/conversions/depth.js +++ b/conversions/depth.js @@ -6,10 +6,8 @@ module.exports = (app, plugin) => { optionKey: 'DEPTHv2', keys: ["environment.depth.belowTransducer"], callback: (belowTransducer) => { - var surfaceToTransducer = _.get(app.signalk.self, - 'environment.depth.surfaceToTransducer.value') - var transducerToKeel = _.get(app.signalk.self, - 'environment.depth.transducerToKeel.value') + var surfaceToTransducer = app.getSelfPath('environment.depth.surfaceToTransducer.value') + var transducerToKeel = app.getSelfPath('environment.depth.transducerToKeel.value') var offset = _.isUndefined(surfaceToTransducer) ? (_.isUndefined(transducerToKeel) ? 0 : transducerToKeel) : surfaceToTransducer try { return [ @@ -23,7 +21,36 @@ module.exports = (app, plugin) => { } catch ( err ) { console.error(err) } - } + }, + tests: [{ + input: [ 4.5 ], + skSelfData: { + 'environment.depth.surfaceToTransducer.value': 1 + }, + expected: [{ + "prio": 2, + "pgn": 128267, + "dst": 255, + "fields": { + "Depth": 4.5, + "Offset": 1 + } + }] + },{ + input: [ 2.1 ], + skSelfData: { + 'environment.depth.transducerToKeel.value': 3 + }, + expected: [{ + "prio": 2, + "pgn": 128267, + "dst": 255, + "fields": { + "Depth": 2.1, + "Offset": 3 + } + }] + }] } } diff --git a/conversions/engineParameters.js b/conversions/engineParameters.js index 278a8ed..7b58bc5 100644 --- a/conversions/engineParameters.js +++ b/conversions/engineParameters.js @@ -48,6 +48,15 @@ module.exports = (app, plugin) => { } }, + testOptions: { + EXHAUST_TEMPERATURE: { + engines: [{ + signalkId: 10, + tempInstanceId: 1 + }] + } + }, + conversions: (options) => { if ( !_.get(options, 'EXHAUST_TEMPERATURE.engines') ) { return null @@ -63,10 +72,23 @@ module.exports = (app, plugin) => { SID: 0xff, "Temperature Instance": engine.tempInstanceId, "Instance": engine.tempInstanceId, - "Temperature Source": 14, + "Source": 14, "Actual Temperature": temperature, }] - } + }, + tests: [{ + input: [ 281.2 ], + expected: [{ + "prio": 2, + "pgn": 130312, + "dst": 255, + "fields": { + "Instance": 1, + "Actual Temperature": 281.2, + "Source": "Exhaust Gas Temperature", + } + }] + }] } }) } @@ -95,6 +117,15 @@ module.exports = (app, plugin) => { } }, + testOptions: { + ENGINE_PARAMETERS: { + engines: [{ + signalkId: 0, + instanceId: 1 + }] + } + }, + conversions: (options) => { if ( !_.get(options, 'ENGINE_PARAMETERS.engines') ) { return null @@ -119,9 +150,34 @@ module.exports = (app, plugin) => { "Discrete Status 1": [], "Discrete Status 2": [], "Percent Engine Load": engLoad === null ? undefined : engLoad * 100, - "Percent Engine Torque": engTorque === null ? undefined : engTorque * 100 + "Engine Load": engLoad === null ? undefined : engLoad * 100, + "Percent Engine Torque": engTorque === null ? undefined : engTorque * 100, + "Engine Torque": engTorque === null ? undefined : engTorque * 100 }] - } + }, + tests: [{ + input: [ 102733, 210, 220, 13.1, 100, 201123, 202133, 11111111, 0.5, 1.0 ], + expected: [{ + "prio": 2, + "pgn": 127489, + "dst": 255, + "fields": { + "Instance": "Dual Engine Starboard", + "Oil pressure": 1000, + "Oil temperature": 210, + "Temperature": 220, + "Alternator Potential": 13.1, + "Fuel Rate": -2355.2, + "Total Engine hours": "55:52:03", + "Coolant Pressure": 2000, + "Fuel Pressure": 111000, + "Discrete Status 1": [], + "Discrete Status 2": [], + "Engine Load": 50, + "Engine Torque": 100 + } + }] + }] } }) @@ -138,7 +194,21 @@ module.exports = (app, plugin) => { "Boost Pressure": boostPressure === null ? undefined : boostPressure / 100, "Tilt/Trim": trimState === null ? undefined : trimState * 100 }] - } + }, + tests: [{ + input: [ 1001, 20345, 0.5 ], + expected: [{ + "prio": 2, + "pgn": 127488, + "dst": 255, + "fields": { + "Instance": "Dual Engine Starboard", + "Speed": 10908, + "Boost Pressure": 200, + "Tilt/Trim": 50 + } + }] + }] } }) diff --git a/conversions/environmentParameters.js b/conversions/environmentParameters.js index fea10de..d26c73c 100644 --- a/conversions/environmentParameters.js +++ b/conversions/environmentParameters.js @@ -17,6 +17,17 @@ module.exports = (app, plugin) => { } catch ( err ) { console.error(err) } - } + }, + tests: [{ + input: [ 3507100 ], + expected: [{ + "prio": 2, + "pgn": 130311, + "dst": 255, + "fields": { + "Atmospheric Pressure": 3507100 + } + }] + }] } } diff --git a/conversions/gps.js b/conversions/gps.js index 05caaf6..48fc675 100644 --- a/conversions/gps.js +++ b/conversions/gps.js @@ -44,12 +44,53 @@ module.exports = (app, plugin) => { HDOP:0.64, 'Geoidal Separation': -0.01, 'Reference Stations': 1, - 'Reference Station Type': 'GPS+SBAS/WAAS', - 'Reference Station ID': 7 + list: [{ + 'Reference Station Type': 'GPS+SBAS/WAAS', + 'Reference Station ID': 7 + }] //'Age of DGNSS Corrections': }) } return res - } + }, + tests: [{ + input: [ { longitude: -75.487264, + latitude: 32.0631296 } ], + expected: [{ + "prio": 2, + "pgn": 129025, + "dst": 255, + "fields": { + "Latitude": 32.0631296, + "Longitude": -75.487264 + } + },{ + "__preprocess__": (testResult) => { + //these change every time + delete testResult.fields.Date + delete testResult.fields.Time + }, + "prio": 2, + "pgn": 129029, + "dst": 255, + "fields": { + "Latitude": 32.0631296, + "Longitude": -75.487264, + "GNSS type": "GPS+SBAS/WAAS", + "Method": "DGNSS fix", + "Integrity": "No integrity checking", + "Number of SVs": 16, + "HDOP": 0.64, + "Geoidal Separation": -0.01, + "Reference Stations": 1, + "list": [ + { + "Reference Station Type": "GPS+SBAS/WAAS", + "Reference Station ID": 7 + } + ] + } + }] + }] } } diff --git a/conversions/heading.js b/conversions/heading.js index 0a2faef..6b6290f 100644 --- a/conversions/heading.js +++ b/conversions/heading.js @@ -16,6 +16,20 @@ module.exports = (app, plugin) => { "Variation": variation, Reference: "Magnetic" }] - } + }, + tests: [{ + input: [ 1.2, 0.7 ], + expected: [{ + "prio": 2, + "pgn": 127250, + "dst": 255, + "fields": { + "SID": 87, + "Heading": 1.2, + "Variation": 0.7, + "Reference": "Magnetic" + } + }] + }] } } diff --git a/conversions/leeway.js b/conversions/leeway.js index 0f5a217..52aefb0 100644 --- a/conversions/leeway.js +++ b/conversions/leeway.js @@ -17,6 +17,17 @@ module.exports = (app, plugin) => { } catch ( err ) { console.error(err) } - } + }, + tests: [{ + input: [ 0.24 ], + expected: [{ + "prio": 2, + "pgn": 128000, + "dst": 255, + "fields": { + "Leeway Angle": 0.24 + } + }] + }] } } diff --git a/conversions/navigationdata.js b/conversions/navigationdata.js index 155e87a..a1bddfe 100644 --- a/conversions/navigationdata.js +++ b/conversions/navigationdata.js @@ -12,6 +12,19 @@ module.exports = (app, plugin) => { XTE, "XTE mode": "Autonomous", "Navigation Terminated": "No" + }], + tests: [{ + input: [ 0.12 ], + expected: [{ + "prio": 2, + "pgn": 129283, + "dst": 255, + "fields": { + "XTE mode": "Autonomous", + "Navigation Terminated": "No", + "XTE": 0.12 + } + }] }] }, { pgn: 129284, @@ -57,6 +70,33 @@ module.exports = (app, plugin) => { "Destination Longitude" : dest.longitude, "Waypoint Closing Velocity" : WCV, }] - } + }, + tests: [{ + input: [ 12, 1.23, 3.1, { longitude: -75.487264, latitude: 32.0631296 } , 4.0, null, 1, 5 ], + expected: [{ + "__preprocess__": (testResult) => { + //these change every time + delete testResult.fields["ETA Date"] + delete testResult.fields["ETA Time"] + }, + "prio": 2, + "pgn": 129284, + "dst": 255, + "fields": { + "SID": 136, + "Distance to Waypoint": 12, + "Course/Bearing reference": "True", + "Perpendicular Crossed": "Yes", + "Arrival Circle Entered": "No", + "Calculation Type": "Rhumbline", + "Bearing, Origin to Destination Waypoint": 3.1, + "Bearing, Position to Destination Waypoint": 1.23, + "Destination Waypoint Number": 5, + "Destination Latitude": 32.0631296, + "Destination Longitude": -75.487264, + "Waypoint Closing Velocity": 4 + } + }] + }] }] } diff --git a/conversions/notifications.js b/conversions/notifications.js index 0b5f87b..e6ac9e8 100644 --- a/conversions/notifications.js +++ b/conversions/notifications.js @@ -123,6 +123,64 @@ module.exports = (app, plugin) => { } catch (err) { console.error(err) } - } + }, + tests: [{ + input: [ { + "context":"vessels.urn:mrn:imo:mmsi:367301250", + "updates":[{"values":[ + { + "path":"notifications.environment.inside.refrigerator.temperature", + "value": { + "state": "alert", + "message": "The Fridge Temperature is high", + "alertId": 1 + } + } + ]}] + }], + expected: [{ + "prio": 2, + "pgn": 126985, + "dst": 255, + "fields": { + "Alert Type": "Caution", + "Alert Category": "Technical", + "Alert System": 5, + "Alert Sub-System": 0, + "Alert ID": 1, + "Data Source Network ID NAME": 1, + "Data Source Instance": 0, + "Data Source Index-Source": 0, + "Alert Occurrence Number": 0, + "Language ID": "English (US)", + "Alert Text Description": "The Fridge Temperature is high" + } + },{ + "prio": 2, + "pgn": 126983, + "dst": 255, + "fields": { + "Alert Type": "Caution", + "Alert Category": "Technical", + "Alert System": 5, + "Alert Sub-System": 0, + "Alert ID": 1, + "Data Source Network ID NAME": 1, + "Data Source Instance": 0, + "Data Source Index-Source": 0, + "Alert Occurrence Number": 0, + "Temporary Silence Status": "No", + "Acknowledge Status": "Yes", + "Escalation Status": "No", + "Temporary Silence Support": "Yes", + "Acknowledge Support": "Yes", + "Escalation Support": "No", + "Trigger Condition": "Auto", + "Threshold Status": "Threshold Exceeded", + "Alert Priority": 0, + "Alert State": "Acknowledged" + } + }] + }] } } diff --git a/conversions/raymarineAlarms.js b/conversions/raymarineAlarms.js index fa6d87f..6497248 100644 --- a/conversions/raymarineAlarms.js +++ b/conversions/raymarineAlarms.js @@ -70,6 +70,34 @@ module.exports = (app, plugin) => { console.error(err) } - } + }, + tests: [{ + input: [ { + "context":"vessels.urn:mrn:imo:mmsi:367301250", + "updates":[{"values":[ + { + "path":"notifications.navigation.anchor", + "value": { + "state": "alert", + "method": [ "sound" ] + } + } + ]}] + }], + expected: [{ + "prio": 2, + "pgn": 65288, + "dst": 255, + "fields": { + "Manufacturer Code": "Raymarine", + "Industry Code": "Marine Industry", + "SID": 1, + "Alarm Status": "Alarm condition met and not silenced", + "Alarm ID": "Deep Anchor", + "Alarm Group": "Instrument", + "Alarm Priority": 1 + } + }] + }] } } diff --git a/conversions/seaTemp.js b/conversions/seaTemp.js index b62a0c2..152a7ba 100644 --- a/conversions/seaTemp.js +++ b/conversions/seaTemp.js @@ -18,6 +18,19 @@ module.exports = (app, plugin) => { } catch ( err ) { console.error(err) } - } + }, + tests: [{ + input: [ 281.2, 291, 20100 ], + expected: [{ + "prio": 2, + "pgn": 130310, + "dst": 255, + "fields": { + "Water Temperature": 281.2, + "Outside Ambient Air Temperature": 291, + "Atmospheric Pressure": 20100 + } + }] + }] } } diff --git a/conversions/solar.js b/conversions/solar.js index 39d657b..b3e167e 100644 --- a/conversions/solar.js +++ b/conversions/solar.js @@ -40,6 +40,16 @@ module.exports = (app, plugin) => { } }, + testOptions: { + SOLAR: { + chargers:[{ + signalkId: 'bimini', + instanceId: 10, + panelInstanceId: 11 + }] + } + }, + conversions: (options) => { if ( !_.get(options, 'SOLAR.chargers') ) { return null @@ -73,7 +83,29 @@ module.exports = (app, plugin) => { } return res - } + }, + tests: [{ + input: [ 13, 5, 2, 45.0 ], + expected: [{ + "prio": 2, + "pgn": 127508, + "dst": 255, + "fields": { + "Instance": 10, + "Voltage": 13, + "Current": 5 + } + },{ + "prio": 2, + "pgn": 127508, + "dst": 255, + "fields": { + "Instance": 11, + "Voltage": 45, + "Current": 2 + } + }] + }] } }) } diff --git a/conversions/speed.js b/conversions/speed.js index d9e151a..4698492 100644 --- a/conversions/speed.js +++ b/conversions/speed.js @@ -17,6 +17,17 @@ module.exports = (app, plugin) => { } catch ( err ) { console.error(err) } - } + }, + tests: [{ + input: [ 3 ], + expected: [{ + "prio": 2, + "pgn": 128259, + "dst": 255, + "fields": { + "Speed Water Referenced": 3 + } + }] + }] } } diff --git a/conversions/systemTime.js b/conversions/systemTime.js index e9fdc35..3b053e7 100644 --- a/conversions/systemTime.js +++ b/conversions/systemTime.js @@ -5,8 +5,8 @@ module.exports = (app, plugin) => { sourceType: 'timer', interval: 1000, optionKey: 'SYSTEM_TIMEv2', - callback: (app) => { - var dateObj = new Date(); + callback: (app, date) => { + var dateObj = date ? date : new Date(); var date = Math.trunc(dateObj.getTime() / 86400 / 1000); var time = dateObj.getUTCHours() * (60 * 60) + @@ -20,6 +20,18 @@ module.exports = (app, plugin) => { Time: time } ] - } + }, + tests: [{ + input: [ undefined, new Date('2017-04-15T14:59:53.123Z') ], + expected: [{ + "prio": 2, + "pgn": 126992, + "dst": 255, + "fields": { + "Date": "2017.04.15", + "Time": "14:59:53" + } + }] + }] } } diff --git a/conversions/tanks.js b/conversions/tanks.js index c2a8bd3..fe630a9 100644 --- a/conversions/tanks.js +++ b/conversions/tanks.js @@ -51,6 +51,18 @@ module.exports = (app, plugin) => { } }, + testOptions: { + TANKS: + { + tanks: [ + { + signalkPath: 'tanks.fuel.0', + instanceId: 1 + } + ] + } + }, + conversions: (options) => { if ( !_.get(options, 'TANKS.tanks') ) { return null @@ -76,7 +88,21 @@ module.exports = (app, plugin) => { } return res - } + }, + tests: [{ + input: [ 0.35, 12 ], + expected: [{ + "prio": 2, + "pgn": 127505, + "dst": 255, + "fields": { + "Instance": 1, + "Type": "Fuel", + "Level": 35, + "Capacity": 12 + } + }] + }] } } else diff --git a/conversions/temperature.js b/conversions/temperature.js index 2dae192..9f4ea9e 100644 --- a/conversions/temperature.js +++ b/conversions/temperature.js @@ -20,6 +20,19 @@ module.exports = (app, plugin) => { callback: (temperature) => { return tempMessage(temperature, 101, 1) }, + tests: [{ + input: [ 281.2 ], + expected: [{ + "prio": 2, + "pgn": 130312, + "dst": 255, + "fields": { + "Instance": 101, + "Source": "Outside Temperature", + "Actual Temperature": 281.2 + } + }] + }] }, { pgn: 130312, @@ -30,7 +43,20 @@ module.exports = (app, plugin) => { ], callback: (temperature) => { return tempMessage(temperature, 102, 2) - } + }, + tests: [{ + input: [ 281.2 ], + expected: [{ + "prio": 2, + "pgn": 130312, + "dst": 255, + "fields": { + "Instance": 102, + "Source": "Inside Temperature", + "Actual Temperature": 281.2 + } + }] + }] }, { pgn: 130312, @@ -41,7 +67,20 @@ module.exports = (app, plugin) => { ], callback: (temperature) => { return tempMessage(temperature, 103, 3) - } + }, + tests: [{ + input: [ 281.2 ], + expected: [{ + "prio": 2, + "pgn": 130312, + "dst": 255, + "fields": { + "Instance": 103, + "Source": "Engine Room Temperature", + "Actual Temperature": 281.2 + } + }] + }] }, { pgn: 130312, @@ -52,7 +91,20 @@ module.exports = (app, plugin) => { ], callback: (temperature) => { return tempMessage(temperature, 107, 7) - } + }, + tests: [{ + input: [ 281.2 ], + expected: [{ + "prio": 2, + "pgn": 130312, + "dst": 255, + "fields": { + "Instance": 107, + "Source": "Refrigeration Temperature", + "Actual Temperature": 281.2 + } + }] + }] }, { pgn: 130312, @@ -63,7 +115,20 @@ module.exports = (app, plugin) => { ], callback: (temperature) => { return tempMessage(temperature, 107, 13) - } + }, + tests: [{ + input: [ 281.2 ], + expected: [{ + "prio": 2, + "pgn": 130312, + "dst": 255, + "fields": { + "Instance": 107, + "Source": "Freezer Temperature", + "Actual Temperature": 281.2 + } + }] + }] }, { pgn: 130312, @@ -74,7 +139,20 @@ module.exports = (app, plugin) => { ], callback: (temperature) => { return tempMessage(temperature, 107, 4) - } + }, + tests: [{ + input: [ 281.2 ], + expected: [{ + "prio": 2, + "pgn": 130312, + "dst": 255, + "fields": { + "Instance": 107, + "Source": "Main Cabin Temperature", + "Actual Temperature": 281.2 + } + }] + }] }, { pgn: 130312, @@ -85,7 +163,20 @@ module.exports = (app, plugin) => { ], callback: (temperature) => { return tempMessage(temperature, 107, 8) - } + }, + tests: [{ + input: [ 281.2 ], + expected: [{ + "prio": 2, + "pgn": 130312, + "dst": 255, + "fields": { + "Instance": 107, + "Source": "Heating System Temperature", + "Actual Temperature": 281.2 + } + }] + }] }, { pgn: 130312, @@ -96,7 +187,20 @@ module.exports = (app, plugin) => { ], callback: (temperature) => { return tempMessage(temperature, 107, 9) - } + }, + tests: [{ + input: [ 281.2 ], + expected: [{ + "prio": 2, + "pgn": 130312, + "dst": 255, + "fields": { + "Instance": 107, + "Source": "Dew Point Temperature", + "Actual Temperature": 281.2 + } + }] + }] }, { pgn: 130312, @@ -107,7 +211,20 @@ module.exports = (app, plugin) => { ], callback: (temperature) => { return tempMessage(temperature, 107, 10) - } + }, + tests: [{ + input: [ 281.2 ], + expected: [{ + "prio": 2, + "pgn": 130312, + "dst": 255, + "fields": { + "Instance": 107, + "Source": "Apparent Wind Chill Temperature", + "Actual Temperature": 281.2 + } + }] + }] }, { pgn: 130312, @@ -118,7 +235,20 @@ module.exports = (app, plugin) => { ], callback: (temperature) => { return tempMessage(temperature, 107, 11) - } + }, + tests: [{ + input: [ 281.2 ], + expected: [{ + "prio": 2, + "pgn": 130312, + "dst": 255, + "fields": { + "Instance": 107, + "Source": "Theoretical Wind Chill Temperature", + "Actual Temperature": 281.2 + } + }] + }] }, { pgn: 130312, @@ -129,7 +259,20 @@ module.exports = (app, plugin) => { ], callback: (temperature) => { return tempMessage(temperature, 107, 12) - } + }, + tests: [{ + input: [ 281.2 ], + expected: [{ + "prio": 2, + "pgn": 130312, + "dst": 255, + "fields": { + "Instance": 107, + "Source": "Heat Index Temperature", + "Actual Temperature": 281.2 + } + }] + }] } ] } diff --git a/conversions/wind.js b/conversions/wind.js index bf45954..248ce44 100644 --- a/conversions/wind.js +++ b/conversions/wind.js @@ -17,7 +17,37 @@ module.exports = (app, plugin) => { } catch ( err ) { console.error(err) } - } + }, + + tests: [{ + input: [ 2.0944, 1.2 ], + expected: [ + { + pgn: 130306, + dst: 255, + prio: 2, + fields: { + 'Wind Speed': 1.2, + 'Wind Angle': 2.0944, + 'Reference': "Apparent" + } + } + ] + },{ + input: [ -2.0944, 1.5 ], + expected: [ + { + pgn: 130306, + dst: 255, + prio: 2, + fields: { + 'Wind Speed': 1.5, + 'Wind Angle': 4.1888, + 'Reference': "Apparent" + } + } + ] + }] } } diff --git a/conversions/windTrueWater.js b/conversions/windTrueWater.js index d8bd3f6..bc5a9b1 100644 --- a/conversions/windTrueWater.js +++ b/conversions/windTrueWater.js @@ -17,7 +17,36 @@ module.exports = (app, plugin) => { } catch ( err ) { console.error(err) } - } + }, + tests: [{ + input: [ 2.0944, 1.2 ], + expected: [ + { + pgn: 130306, + dst: 255, + prio: 2, + fields: { + 'Wind Speed': 1.2, + 'Wind Angle': 2.0944, + 'Reference': "True (boat referenced)" + } + } + ] + },{ + input: [ -2.0944, 1.5 ], + expected: [ + { + pgn: 130306, + dst: 255, + prio: 2, + fields: { + 'Wind Speed': 1.5, + 'Wind Angle': 4.1888, + 'Reference': "True (boat referenced)" + } + } + ] + }] } } diff --git a/index.js b/index.js index 7f0b36f..9240f8b 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,5 @@ const Bacon = require("baconjs"); const util = require("util"); -const { toPgn, toActisenseSerialFormat } = require("@canboat/canboatjs"); const _ = require('lodash') const path = require('path') const fs = require('fs') @@ -27,8 +26,6 @@ module.exports = function(app) { 'to-n2k' - The output will be sent through the to-n2k package (https://github.com/tkurki/to-n2k) - 'buffer' - The output should be a buffer that is sent directly to nmea2000out - sourceType defaults to 'onValueChange' outputType defaults to 'to-n2k' */ @@ -41,8 +38,7 @@ module.exports = function(app) { } var outputTypes = { - 'to-n2k': processToN2K, - 'buffer': processBufferOutput + 'to-n2k': processToN2K } plugin.id = "sk-to-nmea2000"; @@ -156,26 +152,11 @@ module.exports = function(app) { fpath = path.join(__dirname, 'conversions') files = fs.readdirSync(fpath) return files.map(fname => { - pgn = path.basename(fname, '.js') + let pgn = path.basename(fname, '.js') return require(path.join(fpath, pgn))(app, plugin); }).filter(converter => { return typeof converter !== 'undefined'; }); } - function processBufferOutput(pgns) { - if ( pgns ) { - pgns.filter(pgn => pgn != null).forEach(pgn => { - try { - const msg = toActisenseSerialFormat(pgn.pgn, pgn.buffer); - app.debug(`emit nmea2000out ${JSON.stringify(pgn)}`) - app.emit("nmea2000out", msg); - } catch ( err ) { - console.error(`error writing pgn ${JSON.stringify(pgn)}`) - console.error(err.stack) - } - }) - } - } - function processToN2K(pgns) { if ( pgns ) { pgns.filter(pgn => pgn != null).forEach(pgn => { diff --git a/package.json b/package.json index f5104c3..fb6f5e2 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,6 @@ ], "license": "ISC", "dependencies": { - "@canboat/canboatjs": "1.x", "baconjs": "^0.7.88", "lodash": "^4.17.4" }, @@ -31,8 +30,10 @@ "url": "https://github.com/SignalK/signalk-to-nmea2000" }, "devDependencies": { + "@canboat/canboatjs": "2.x", "@signalk/github-create-release": "^1.2.1", "chai": "^4.1.2", + "chai-json-equal": "^0.0.1", "mocha": "^5.0.0", "signalk-server": "^1.4.0", "sinon": "^4.1.6" diff --git a/test/test.js b/test/test.js new file mode 100644 index 0000000..8c8a1a2 --- /dev/null +++ b/test/test.js @@ -0,0 +1,114 @@ +const { pgnToActisenseSerialFormat, FromPgn } = require("@canboat/canboatjs"); +const path = require('path') +const fs = require('fs') +const chai = require('chai') +const assert = chai.assert +chai.Should() +//chai.use(require('chai-things')) +chai.use(require('chai-json-equal')); + +const parser = new FromPgn() + +let skSelfData = {} +let skData = {} + +const app = { + getSelfPath: (path) => { + return skSelfData[path] + }, + getPath: (path) => { + return skData[path] + }, + debug: (msg) => { + } +} + +function load_conversions () { + fpath = path.join(__dirname, '../conversions') + files = fs.readdirSync(fpath) + return files.map(fname => { + pgn = path.basename(fname, '.js') + return require(path.join(fpath, pgn))(app, {}); + }).filter(converter => { return typeof converter !== 'undefined'; }); +} + +const conversions = load_conversions() + +describe('every conversion has a test', () => { + conversions.forEach(conversion => { + if ( !Array.isArray(conversion) ) { + conversion = [ conversion ] + } + + conversion.forEach(conversion => { + it(`${conversion.title} has a test`, function (done) { + var subConversions = conversion.conversions + if ( typeof subConversions === 'undefined' ) { + subConversions = [ conversion ] + } else if ( typeof subConversions === 'function' ) { + subConversions = subConversions(conversion.testOptions || {}) + } + assert(subConversions != undefined) + subConversions.forEach(subConv => { + subConv.should.have.property('tests') + }) + done() + }) + }) + }) +}) + +describe('conversions work', () => { + conversions.forEach(conversion => { + if ( !Array.isArray(conversion) ) { + conversion = [ conversion ] + } + + conversion.forEach(conversion => { + var subConversions = conversion.conversions + if ( typeof subConversions === 'undefined' ) { + subConversions = [ conversion ] + } else if ( typeof subConversions === 'function' ) { + subConversions = subConversions(conversion.testOptions || {}) + } + subConversions.forEach(subConv => { + //subConv.should.have.property('tests') + if ( subConv.tests ) { + subConv.tests.forEach((test, idx) => { + it(`${conversion.title} test # ${idx} works`, function (done) { + skData = test.skData || {} + skSelfData = test.skSelfData || {} + let results = subConv.callback.call(null, ...test.input) + assert.equal(results.length, test.expected.length, 'number of results returned does not match the number of expected results') + let error + results.forEach((res, idx) => { + try + { + let encoded = pgnToActisenseSerialFormat(res) + let pgn = parser.parseString(encoded) + delete pgn.description + delete pgn.src + delete pgn.timestamp + delete pgn.input + + let expected = test.expected[idx] + let preprocess = expected["__preprocess__"] + if ( preprocess ) { + preprocess(pgn) + delete expected["__preprocess__"] + } + //console.log('parsed: ' + JSON.stringify(pgn, null, 2)) + pgn.should.jsonEqual(expected) + } catch ( e ) { + error = e + } + }) + done(error) + }) + }) + } + }) + }) + }) +}) +