parsing of short and control frame works
This commit is contained in:
parent
d1c969a793
commit
f9a4231e65
@ -5,6 +5,41 @@ import * as chai from 'chai'
|
|||||||
|
|
||||||
const expect = chai.expect
|
const expect = chai.expect
|
||||||
|
|
||||||
|
// dishwasher, electric
|
||||||
|
let inputOkLongFrame_1phase_electric = "68 38 38 68 08 53 72 17 00 13 00 2E 19 24 02 D6 00 00 00 8C 10 04 01 02 00 00 8C 11 04 01 02 00 00 02 FD C9 FF 01 E4 00 02 FD DB FF 01 03 00 02 AC FF 01 01 00 82 40 AC FF 01 FA FF 20 16"
|
||||||
|
let inputNOkLongFrame_1phase_electric_wrong_medium = "68 38 38 68 08 53 72 17 00 13 00 2E 19 24 FE D6 00 00 00 8C 10 04 01 02 00 00 8C 11 04 01 02 00 00 02 FD C9 FF 01 E4 00 02 FD DB FF 01 03 00 02 AC FF 01 01 00 82 40 AC FF 01 FA FF 1c 16"
|
||||||
|
let inputNOkLongFrame_1phase_electric_wrong_signature = "68 38 38 68 08 53 72 17 00 13 00 2E 19 24 02 D6 00 00 01 8C 10 04 01 02 00 00 8C 11 04 01 02 00 00 02 FD C9 FF 01 E4 00 02 FD DB FF 01 03 00 02 AC FF 01 01 00 82 40 AC FF 01 FA FF 21 16"
|
||||||
|
|
||||||
|
// electricity
|
||||||
|
let inputOkLongFrame_3phase_electric = "68 92 92 68 08 50 72 81 14 01 11 2E 19 16 02 88 00 00 00 8C 10 04 58 43 86 00 8C 11 04 58 43 86 00 8C 20 04 00 00 00 00 8C 21 04 00 00 00 00 02 FD C9 FF 01 E4 00 02 FD DB FF 01 5A 00 02 AC FF 01 D2 00 82 40 AC FF 01 00 00 02 FD C9 FF 02 DF 00 02 FD DB FF 02 0F 00 02 AC FF 02 21 00 82 40 AC FF 02 FD FF 02 FD C9 FF 03 E3 00 02 FD DB FF 03 04 00 02 AC FF 03 02 00 82 40 AC FF 03 F4 FF 02 FF 68 00 00 02 AC FF 00 F5 00 82 40 AC FF 00 F1 FF 01 FF 13 00 F4 16"
|
||||||
|
|
||||||
|
// water
|
||||||
|
let inputOkLongFrame_water = "68 46 46 68 08 30 72 45 71 43 00 24 23 25 07 F6 00 00 00 0C 13 97 95 43 00 8C 10 13 00 00 00 00 0B 3B 00 00 00 0B 26 14 60 01 02 5A AC 00 04 6D 14 0E EB 16 4C 13 96 41 33 00 CC 10 13 00 00 00 00 42 6C DF 1C 42 EC 7E FF 1C 99 16"
|
||||||
|
|
||||||
|
// gas
|
||||||
|
let inputOkLongFrame_gas = "68 56 56 68 08 40 72 43 60 52 00 77 04 14 03 FF 10 00 00 0C 78 76 03 01 10 0D 7C 08 44 49 20 2E 74 73 75 63 0A 30 30 30 30 30 30 30 30 30 30 04 6D 06 0E EB 16 02 7C 09 65 6D 69 74 20 2E 74 61 62 A3 09 04 13 98 AE 04 00 04 93 7F 4E 01 00 00 44 13 FC A5 04 00 0F 01 00 1F 51 16"
|
||||||
|
|
||||||
|
// Thermometer
|
||||||
|
let inputOkLongFrame_thermometer = "68 61 61 68 08 21 72 00 00 00 00 00 00 01 00 7F 00 00 00 01 24 02 01 25 30 01 26 10 02 27 07 00 05 67 43 B6 DA 3D 05 67 DC 90 50 BD 05 67 AA E8 AA 41 05 67 AF 57 BA 41 0F 77 C0 00 00 7F 27 00 00 0A 00 00 00 00 00 00 00 01 00 00 00 A2 C3 7F 3F A5 BA 7F 3F 85 A7 7F 3F E7 F9 7F 3F CD CC CC 3D E8 03 00 00 02 16"
|
||||||
|
let inputNOkLongFrame_thermometer_checksum_failure = "68 61 61 68 08 21 72 00 00 00 00 00 00 01 00 7F 00 00 00 01 24 02 01 25 30 01 26 10 02 27 07 00 05 67 43 B6 DA 3D 05 67 DC 90 50 BD 05 67 AA E8 AA 41 05 67 AF 57 BA 41 0F 77 C0 00 00 7F 27 00 00 0A 00 00 00 00 00 00 00 01 00 00 00 A2 C3 7F 3F A5 BA 7F 3F 85 A7 7F 3F E7 F9 7F 3F CD CC CC 3D E8 03 00 00 01 16"
|
||||||
|
|
||||||
|
// test1
|
||||||
|
let inputOkSingleCharacter = "e5"
|
||||||
|
|
||||||
|
// test2
|
||||||
|
let inputOkShortframe = "10 01 02 03 16"
|
||||||
|
let inputNOkShortframe_checksum_failure = "10 01 02 04 16"
|
||||||
|
|
||||||
|
// test3
|
||||||
|
let inputOKControlframe = "68 03 03 68 01 02 03 06 16"
|
||||||
|
let inputNOKControlframe_wrong_stopcode = "68 03 03 68 01 02 03 06 15"
|
||||||
|
let inputNOKControlframe_wrong_secondlength = "68 03 04 68 01 02 03 06 16"
|
||||||
|
let inputNOKLongframe_too_short = "68 02 02 68 01 02 03 16"
|
||||||
|
|
||||||
|
let inputNOk_wrong_startcode = "15 01 02 03 16"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
describe('The Meterbus Library', () => {
|
describe('The Meterbus Library', () => {
|
||||||
it('should store hexString', () => {
|
it('should store hexString', () => {
|
||||||
const telegram = new MeterbusLib.Telegram()
|
const telegram = new MeterbusLib.Telegram()
|
||||||
@ -18,27 +53,82 @@ describe('The Meterbus Library', () => {
|
|||||||
})
|
})
|
||||||
it('should detect a control frame', () => {
|
it('should detect a control frame', () => {
|
||||||
const telegram = new MeterbusLib.Telegram()
|
const telegram = new MeterbusLib.Telegram()
|
||||||
telegram.fromHexString("68 03")
|
telegram.fromHexString(inputOKControlframe)
|
||||||
telegram.parse()
|
telegram.parse()
|
||||||
expect(telegram.frame).instanceof(MeterbusLib.ControlFrame)
|
expect(telegram.frame).instanceof(MeterbusLib.ControlFrame)
|
||||||
})
|
})
|
||||||
it('should detect a short frame', () => {
|
it('should detect a short frame', () => {
|
||||||
const telegram = new MeterbusLib.Telegram()
|
const telegram = new MeterbusLib.Telegram()
|
||||||
telegram.fromHexString("10")
|
telegram.fromHexString(inputOkShortframe)
|
||||||
telegram.parse()
|
telegram.parse()
|
||||||
expect(telegram.frame).instanceof(MeterbusLib.ShortFrame)
|
expect(telegram.frame).instanceof(MeterbusLib.ShortFrame)
|
||||||
})
|
})
|
||||||
it('should detect a long frame', () => {
|
it('should detect a long frame', () => {
|
||||||
const telegram = new MeterbusLib.Telegram()
|
const telegram = new MeterbusLib.Telegram()
|
||||||
telegram.fromHexString("68 10")
|
telegram.fromHexString(inputOkLongFrame_1phase_electric)
|
||||||
telegram.parse()
|
telegram.parse()
|
||||||
expect(telegram.frame).instanceof(MeterbusLib.LongFrame)
|
expect(telegram.frame).instanceof(MeterbusLib.LongFrame)
|
||||||
})
|
})
|
||||||
it('should detect a single char frame', () => {
|
it('should detect a single char frame', () => {
|
||||||
const telegram = new MeterbusLib.Telegram()
|
const telegram = new MeterbusLib.Telegram()
|
||||||
telegram.fromHexString("e5")
|
telegram.fromHexString(inputOkSingleCharacter)
|
||||||
telegram.parse()
|
telegram.parse()
|
||||||
expect(telegram.frame).instanceof(MeterbusLib.SingleCharFrame)
|
expect(telegram.frame).instanceof(MeterbusLib.SingleCharFrame)
|
||||||
})
|
})
|
||||||
|
it('should detect an invalid telegram (invalid start char)', () => {
|
||||||
|
const telegram = new MeterbusLib.Telegram()
|
||||||
|
telegram.fromHexString(inputNOk_wrong_startcode)
|
||||||
|
expect(() => telegram.parse()).to.throw(MeterbusLib.InvalidStartCharError)
|
||||||
|
})
|
||||||
|
it('should detect an invalid telegram (invalid stop char)', () => {
|
||||||
|
const telegram = new MeterbusLib.Telegram()
|
||||||
|
telegram.fromHexString(inputNOKControlframe_wrong_stopcode)
|
||||||
|
expect(() => telegram.parse()).to.throw(MeterbusLib.InvalidStopCharError)
|
||||||
|
})
|
||||||
|
it('should detect an invalid telegram (invalid checksum)', () => {
|
||||||
|
const telegram = new MeterbusLib.Telegram()
|
||||||
|
telegram.fromHexString(inputNOkLongFrame_thermometer_checksum_failure)
|
||||||
|
expect(() => telegram.parse()).to.throw(MeterbusLib.InvalidChecksumError)
|
||||||
|
})
|
||||||
|
it('should detect an invalid telegram (invalid second length)', () => {
|
||||||
|
const telegram = new MeterbusLib.Telegram()
|
||||||
|
telegram.fromHexString(inputNOKControlframe_wrong_secondlength)
|
||||||
|
expect(() => telegram.parse()).to.throw(MeterbusLib.InvalidSecondLengthError)
|
||||||
|
})
|
||||||
|
it('should detect an invalid telegram (too short)', () => {
|
||||||
|
const telegram = new MeterbusLib.Telegram()
|
||||||
|
telegram.fromHexString(inputNOKLongframe_too_short)
|
||||||
|
expect(() => telegram.parse()).to.throw(MeterbusLib.PayloadTooShortError)
|
||||||
|
})
|
||||||
|
it('should parse cField from a short frame', () => {
|
||||||
|
const telegram = new MeterbusLib.Telegram()
|
||||||
|
telegram.fromHexString(inputOkShortframe)
|
||||||
|
telegram.parse()
|
||||||
|
expect((telegram.frame as MeterbusLib.ShortFrame).cField).to.equal(1)
|
||||||
|
})
|
||||||
|
it('should parse address from a short frame', () => {
|
||||||
|
const telegram = new MeterbusLib.Telegram()
|
||||||
|
telegram.fromHexString(inputOkShortframe)
|
||||||
|
telegram.parse()
|
||||||
|
expect((telegram.frame as MeterbusLib.ShortFrame).address).to.equal(2)
|
||||||
|
})
|
||||||
|
it('should parse cField from a control frame', () => {
|
||||||
|
const telegram = new MeterbusLib.Telegram()
|
||||||
|
telegram.fromHexString(inputOKControlframe)
|
||||||
|
telegram.parse()
|
||||||
|
expect((telegram.frame as MeterbusLib.ControlFrame).cField).to.equal(1)
|
||||||
|
})
|
||||||
|
it('should parse address from a control frame', () => {
|
||||||
|
const telegram = new MeterbusLib.Telegram()
|
||||||
|
telegram.fromHexString(inputOKControlframe)
|
||||||
|
telegram.parse()
|
||||||
|
expect((telegram.frame as MeterbusLib.ControlFrame).address).to.equal(2)
|
||||||
|
})
|
||||||
|
it('should parse ciField from a control frame', () => {
|
||||||
|
const telegram = new MeterbusLib.Telegram()
|
||||||
|
telegram.fromHexString(inputOKControlframe)
|
||||||
|
telegram.parse()
|
||||||
|
expect((telegram.frame as MeterbusLib.ControlFrame).ciField).to.equal(3)
|
||||||
|
})
|
||||||
|
|
||||||
})
|
})
|
133
src/meterbus.ts
133
src/meterbus.ts
@ -1,33 +1,148 @@
|
|||||||
export namespace MeterbusLib {
|
export namespace MeterbusLib {
|
||||||
export class Frame {
|
export class InvalidTelegramError extends Error {
|
||||||
private _telegram : number[]
|
}
|
||||||
|
|
||||||
constructor(telegram : number[]) {
|
export class InvalidStartCharError extends InvalidTelegramError {
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InvalidLengthError extends InvalidTelegramError {
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InvalidChecksumError extends InvalidTelegramError {
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InvalidStopCharError extends InvalidTelegramError {
|
||||||
|
}
|
||||||
|
|
||||||
|
export class InvalidSecondLengthError extends InvalidTelegramError {
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PayloadTooShortError extends InvalidTelegramError {
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class Frame {
|
||||||
|
protected _telegram : number[]
|
||||||
|
protected _startCharacter : number
|
||||||
|
protected _frameLength : number
|
||||||
|
protected _firstPayload : number
|
||||||
|
protected _payloadLength : number
|
||||||
|
|
||||||
|
constructor(startCharacter : number, frameLength : number,
|
||||||
|
firstPayload : number, telegram : number[]) {
|
||||||
|
this._startCharacter = startCharacter
|
||||||
|
this._frameLength = frameLength
|
||||||
|
this._firstPayload = firstPayload
|
||||||
this._telegram = telegram
|
this._telegram = telegram
|
||||||
|
this._payloadLength = this._frameLength - 6
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
verifyChecksumAndEnd() {
|
||||||
|
if (this._firstPayload != 0) {
|
||||||
|
if (this._telegram[this._telegram.length - 1] != 0x16) {
|
||||||
|
throw new InvalidStopCharError()
|
||||||
|
}
|
||||||
|
let checksum : number = 0
|
||||||
|
this._telegram.slice(this._firstPayload, this._telegram.length -2).forEach((val) => {
|
||||||
|
checksum += val
|
||||||
|
})
|
||||||
|
// console.log(`calc. checksum ${checksum}, ${checksum & 0xff}`)
|
||||||
|
// console.log(`found checksum ${this._telegram[this._telegram.length - 2]}`)
|
||||||
|
if ((checksum & 0xff) != this._telegram[this._telegram.length - 2]) {
|
||||||
|
throw new InvalidChecksumError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract parse2()
|
||||||
|
|
||||||
|
parse() {
|
||||||
|
if (this._telegram[0] != this._startCharacter) {
|
||||||
|
throw new InvalidStartCharError()
|
||||||
|
}
|
||||||
|
if (this._telegram.length != this._frameLength) {
|
||||||
|
throw new InvalidLengthError()
|
||||||
|
}
|
||||||
|
this.verifyChecksumAndEnd()
|
||||||
|
this.parse2()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ControlFrame extends Frame {
|
export class ControlFrame extends Frame {
|
||||||
|
private _cField : number
|
||||||
|
private _address : number
|
||||||
|
private _ciField : number
|
||||||
|
|
||||||
|
get cField() : number {
|
||||||
|
return this._cField
|
||||||
|
}
|
||||||
|
|
||||||
|
get address() : number {
|
||||||
|
return this._address
|
||||||
|
}
|
||||||
|
|
||||||
|
get ciField() : number {
|
||||||
|
return this._ciField
|
||||||
|
}
|
||||||
|
|
||||||
constructor(telegram : number[]) {
|
constructor(telegram : number[]) {
|
||||||
super(telegram)
|
super(0x68, (telegram[1] + 6), 4, telegram)
|
||||||
|
}
|
||||||
|
|
||||||
|
parse2() {
|
||||||
|
if (this._telegram[2] != this._payloadLength) {
|
||||||
|
throw new InvalidSecondLengthError()
|
||||||
|
}
|
||||||
|
if (this._payloadLength < 3) {
|
||||||
|
throw new PayloadTooShortError()
|
||||||
|
}
|
||||||
|
|
||||||
|
this._cField = this._telegram[4]
|
||||||
|
this._address = this._telegram[5]
|
||||||
|
this._ciField = this._telegram[6]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ShortFrame extends Frame {
|
export class ShortFrame extends Frame {
|
||||||
|
private _cField : number
|
||||||
|
private _address : number
|
||||||
|
|
||||||
|
get cField() : number {
|
||||||
|
return this._cField
|
||||||
|
}
|
||||||
|
|
||||||
|
get address() : number {
|
||||||
|
return this._address
|
||||||
|
}
|
||||||
|
|
||||||
constructor(telegram : number[]) {
|
constructor(telegram : number[]) {
|
||||||
super(telegram)
|
super(0x10, 5, 1, telegram)
|
||||||
|
}
|
||||||
|
|
||||||
|
parse2() {
|
||||||
|
this._cField = this._telegram[1]
|
||||||
|
this._address = this._telegram[2]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LongFrame extends Frame {
|
export class LongFrame extends ControlFrame {
|
||||||
constructor(telegram : number[]) {
|
constructor(telegram : number[]) {
|
||||||
super(telegram)
|
super(telegram)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parse2() {
|
||||||
|
super.parse2()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SingleCharFrame extends Frame {
|
export class SingleCharFrame extends Frame {
|
||||||
constructor(telegram : number[]) {
|
constructor(telegram : number[]) {
|
||||||
super(telegram)
|
super(0xe5, 1, 0, telegram)
|
||||||
|
}
|
||||||
|
|
||||||
|
parse2() {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,8 +183,10 @@ export namespace MeterbusLib {
|
|||||||
} else if (this._telegram[0] === 0xe5) {
|
} else if (this._telegram[0] === 0xe5) {
|
||||||
this._frame = new SingleCharFrame(this._telegram)
|
this._frame = new SingleCharFrame(this._telegram)
|
||||||
} else {
|
} else {
|
||||||
throw Error('invalid start char')
|
throw new InvalidStartCharError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._frame.parse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"target": "es5",
|
"target": "es6",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"outDir": "./dist"
|
"outDir": "./dist"
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user