Compare commits
2 Commits
master
...
Uint8Array
Author | SHA1 | Date | |
---|---|---|---|
83143ca8c2 | |||
098633af13 |
@ -1,75 +1,31 @@
|
||||
import {MeterbusLibUtils} from './utils'
|
||||
|
||||
|
||||
export namespace MeterbusLibCodeTables {
|
||||
export namespace MeterbusLibCodeTables {
|
||||
export const MEDIUM_CODE : string[] =
|
||||
[
|
||||
'Other',
|
||||
'Oil',
|
||||
'Electrity',
|
||||
'Gas',
|
||||
'Heat (Volume measured at return temperature: outlet',
|
||||
'Steam',
|
||||
'Hot Water',
|
||||
'Water',
|
||||
'Heat Cost Allocator',
|
||||
'Compressed Air',
|
||||
'Cooling Load Meter (Volume measured at return temperature: outlet',
|
||||
'Cooling Load Meter (Volume measured at flow temperature: inlet',
|
||||
'Heat (Volume measured at flow temperature: inlet)',
|
||||
'Heat / Cooling Load Meter',
|
||||
'Bus / System',
|
||||
'Unknown Medium',
|
||||
'Reserved (10)',
|
||||
'Reserved (11)',
|
||||
'Reserved (12)',
|
||||
'Reserved (13)',
|
||||
'Reserved (14)',
|
||||
'Reserved (15)',
|
||||
'Cold Water',
|
||||
'Dual Water',
|
||||
'Pressure',
|
||||
'A/D Converter',
|
||||
['Other',
|
||||
'Oil',
|
||||
'Electrity',
|
||||
'Gas',
|
||||
'Heat (Volume measured at return temperature: outlet',
|
||||
'Steam',
|
||||
'Hot Water',
|
||||
'Water',
|
||||
'Heat Cost Allocator',
|
||||
'Compressed Air',
|
||||
'Cooling Load Meter (Volume measured at return temperature: outlet',
|
||||
'Cooling Load Meter (Volume measured at flow temperature: inlet',
|
||||
'Heat (Volume measured at flow temperature: inlet)',
|
||||
'Heat / Cooling Load Meter',
|
||||
'Bus / System',
|
||||
'Unknown Medium',
|
||||
'Reserved (10)',
|
||||
'Reserved (11)',
|
||||
'Reserved (12)',
|
||||
'Reserved (13)',
|
||||
'Reserved (14)',
|
||||
'Reserved (15)',
|
||||
'Cold Water',
|
||||
'Dual Water',
|
||||
'Pressure',
|
||||
'A/D Converter',
|
||||
]
|
||||
|
||||
export const DIF_FUNCTION_FIELD : string[] =
|
||||
[
|
||||
'Instantaneous value',
|
||||
'Maximum value',
|
||||
'Minimum value',
|
||||
'Value during error state'
|
||||
]
|
||||
|
||||
export class DCFt {
|
||||
name : string
|
||||
length : number
|
||||
encoder : (v: number[]) => any
|
||||
constructor(n : string, l : number, e : (v: number[]) => any) {
|
||||
this.name = n
|
||||
this.length = l
|
||||
this.encoder = e
|
||||
}
|
||||
}
|
||||
|
||||
export const DIF_CODING_FIELD : DCFt[] =
|
||||
[
|
||||
new DCFt('No Data', 0, (x)=>{ return x}),
|
||||
new DCFt('8 Bit Integer', 1, (x)=>{ return x[0]}),
|
||||
new DCFt('16 Bit Integer', 2, (x)=>{ return (x[1] << 8) + x[0]}),
|
||||
new DCFt('24 Bit Integer', 3, (x)=>{ return (x[2] << 16) + (x[1] << 8) + x[0]}),
|
||||
new DCFt('32 Bit Integer', 4, (x)=>{ return (x[3] << 24) + (x[2] << 16) + (x[1] << 8) + x[0]}),
|
||||
new DCFt('32 Bit Real', 4, (x)=>{ return x}), // FIXME
|
||||
new DCFt('48 Bit Integer', 6, (x)=>{ return (x[5] << 40) + (x[4] << 32) + (x[3] << 24) + (x[2] << 16) + (x[1] << 8) + x[0]}),
|
||||
new DCFt('64 Bit Integer', 8, (x)=>{ return (x[7] << 56) + (x[6] << 48) + (x[5] << 40) + (x[4] << 32) + (x[3] << 24) + (x[2] << 16) + (x[1] << 8) + x[0]}),
|
||||
new DCFt('Selection for Readout', 0, (x)=>{ return x}),
|
||||
new DCFt('2 Digit BCD', 1, (x)=>{ return MeterbusLibUtils.bcd(x)}),
|
||||
new DCFt('4 Digit BCD', 2, (x)=>{ return MeterbusLibUtils.bcd(x)}),
|
||||
new DCFt('6 Digit BCD', 3, (x)=>{ return MeterbusLibUtils.bcd(x)}),
|
||||
new DCFt('8 Digit BCD', 4, (x)=>{ return MeterbusLibUtils.bcd(x)}),
|
||||
new DCFt('variable length', -1, (x)=>{ return x}),
|
||||
new DCFt('12 Digit BCD', 6, (x)=>{ return MeterbusLibUtils.bcd(x)}),
|
||||
new DCFt('Special Function', 0, (x)=>{ return x})
|
||||
]
|
||||
|
||||
|
||||
}
|
@ -19,4 +19,7 @@ export namespace MeterbusLibExceptions {
|
||||
|
||||
export class PayloadTooShortError extends InvalidTelegramError {
|
||||
}
|
||||
|
||||
export class IllegalNumberError extends InvalidTelegramError {
|
||||
}
|
||||
}
|
214
src/longframe.ts
214
src/longframe.ts
@ -6,48 +6,33 @@ import {MeterbusLibCodeTables} from './codetables'
|
||||
export namespace MeterbusLibLongFrame {
|
||||
export class LongFrame extends MeterbusLibFrames.ControlFrame {
|
||||
private _fixedDataHeader : FixedDataHeader
|
||||
private _variableDataBlocks : DataBlock[] = []
|
||||
|
||||
get fixedDataHeader() : FixedDataHeader { return this._fixedDataHeader }
|
||||
|
||||
constructor(telegram : number[]) {
|
||||
constructor(telegram : Uint8Array) {
|
||||
super(telegram)
|
||||
}
|
||||
|
||||
parse2() {
|
||||
super.parse2() // control frame parse2 method
|
||||
|
||||
let consumed : number = 7 // 68 L L 68 C A CI
|
||||
this._fixedDataHeader = new FixedDataHeader(this._telegram.slice(consumed))
|
||||
this._fixedDataHeader = new FixedDataHeader(this._telegram.slice(7, 19))
|
||||
this._fixedDataHeader.parse()
|
||||
|
||||
consumed += this._fixedDataHeader.consumed
|
||||
let count : number = 0
|
||||
while (true) {
|
||||
let die : DataBlock =
|
||||
new DataBlock(this._telegram.slice(consumed))
|
||||
die.parse()
|
||||
this._variableDataBlocks.push(die)
|
||||
consumed += die.consumed
|
||||
count++
|
||||
if ((consumed) >= (this._telegram.length - 2)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getJSON() : string {
|
||||
return JSON.stringify(this)
|
||||
}
|
||||
}
|
||||
|
||||
export class FixedDataHeader {
|
||||
private _data : number[]
|
||||
private __consumed : number = 0
|
||||
private _data : Uint8Array
|
||||
|
||||
private _identNo : number
|
||||
private _manufacturer : string
|
||||
private _version : number
|
||||
private _medium : string
|
||||
private _accessNo : number
|
||||
private _status : number
|
||||
private _signature : number[]
|
||||
|
||||
private _signature : Uint8Array
|
||||
|
||||
get identNo() : number { return this._identNo }
|
||||
get manufacturer() : string { return this._manufacturer }
|
||||
@ -55,197 +40,24 @@ export namespace MeterbusLibLongFrame {
|
||||
get medium() : string { return this._medium }
|
||||
get accessNo() : number { return this._accessNo }
|
||||
get status() : number { return this._status }
|
||||
get signature() : number[] { return this._signature }
|
||||
get signature() : Uint8Array { return this._signature }
|
||||
|
||||
constructor(data : number[]) {
|
||||
constructor(data : Uint8Array) {
|
||||
this._data = data
|
||||
}
|
||||
|
||||
get consumed() : number {
|
||||
return this.__consumed
|
||||
}
|
||||
|
||||
parse() : void {
|
||||
this._identNo = MeterbusLibUtils.bcd(this._data.slice(0, 4))
|
||||
this.__consumed += 4
|
||||
this._manufacturer = MeterbusLibUtils.manufCode(this._data.slice(4, 6))
|
||||
this.__consumed += 2
|
||||
this._version = this._data[6]
|
||||
this.__consumed += 1
|
||||
this._medium = MeterbusLibCodeTables.MEDIUM_CODE[this._data[7]]
|
||||
this.__consumed += 1
|
||||
this._accessNo = this._data[8]
|
||||
this.__consumed += 1
|
||||
this._status = this._data[9]
|
||||
this.__consumed += 1
|
||||
this._signature = this._data.slice(10, 12)
|
||||
this.__consumed += 2
|
||||
}
|
||||
|
||||
toJSON() : any {
|
||||
return MeterbusLibUtils.jsonPrepaper(this, ["_data"])
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class ADataBlock {
|
||||
protected __consumed : number = 0
|
||||
|
||||
get consumed() : number {
|
||||
return this.__consumed
|
||||
}
|
||||
|
||||
abstract parse() : void
|
||||
|
||||
toJSON() : any {
|
||||
return MeterbusLibUtils.jsonPrepaper(this, [])
|
||||
}
|
||||
}
|
||||
|
||||
export class DataBlock extends ADataBlock {
|
||||
private _content : VariableDataBlock | ManufacturerSpecificData
|
||||
private _hex : string = ""
|
||||
private __data : number[]
|
||||
|
||||
constructor(data : number[]) {
|
||||
super()
|
||||
this.__data = data
|
||||
if ((data[0] & 0x0f) == 0x0f) {
|
||||
this._content = new ManufacturerSpecificData(data)
|
||||
} else {
|
||||
this._content = new VariableDataBlock(data)
|
||||
}
|
||||
}
|
||||
|
||||
get consumed() : number {
|
||||
return this._content.consumed
|
||||
}
|
||||
|
||||
parse() : void {
|
||||
this._content.parse()
|
||||
|
||||
for (let d of this.__data.slice(0, this.consumed)) {
|
||||
this._hex += "" + d.toString(16) + " "
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
toJSON() : any {
|
||||
return MeterbusLibUtils.jsonPrepaper(this, [])
|
||||
}
|
||||
}
|
||||
|
||||
export class DIF {
|
||||
raw : number
|
||||
function : number
|
||||
coding : number
|
||||
lsb : number
|
||||
extension : number
|
||||
|
||||
toJSON() : any {
|
||||
let dup = MeterbusLibUtils.clone(this)
|
||||
dup.function = MeterbusLibCodeTables.DIF_FUNCTION_FIELD[dup.function]
|
||||
dup.coding = MeterbusLibCodeTables.DIF_CODING_FIELD[dup.coding].name
|
||||
return dup
|
||||
}
|
||||
}
|
||||
|
||||
export class DIFE {
|
||||
raw : number
|
||||
extension : number
|
||||
}
|
||||
|
||||
export class VIF {
|
||||
raw : number
|
||||
unitAndMultiplier : number
|
||||
extension : number
|
||||
}
|
||||
|
||||
export class VIFE {
|
||||
raw : number
|
||||
extension : number
|
||||
}
|
||||
|
||||
export class VariableDataBlock extends ADataBlock {
|
||||
private _type : string = "VDB"
|
||||
private __indata : number[]
|
||||
private _dif : DIF
|
||||
private _dife : DIFE[] = []
|
||||
private _vif : VIF
|
||||
private _vife : VIFE[] = []
|
||||
private _data : number[]
|
||||
private _value : any
|
||||
|
||||
constructor(data : number[]) {
|
||||
super()
|
||||
this.__indata = data
|
||||
}
|
||||
|
||||
parse() : void {
|
||||
this._dif = new DIF()
|
||||
this._dif.raw = this.__indata[0]
|
||||
this.__consumed++
|
||||
this._dif.function = (this._dif.raw & 0x30) >> 4
|
||||
this._dif.coding = this._dif.raw & 0x0f
|
||||
this._dif.lsb = (this._dif.raw & 0x40) >> 6
|
||||
this._dif.extension = (this._dif.raw & 0x80) >> 7
|
||||
|
||||
if (this._dif.extension == 1) {
|
||||
while (true) {
|
||||
let dife = new DIFE()
|
||||
dife.raw = this.__indata[this.__consumed]
|
||||
this.__consumed++
|
||||
dife.extension = (dife.raw & 0x80) >> 7
|
||||
this._dife.push(dife)
|
||||
if (dife.extension == 0) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._vif = new VIF()
|
||||
this._vif.raw = this.__indata[this.__consumed]
|
||||
this.__consumed++
|
||||
this._vif.unitAndMultiplier = this._vif.raw & 0x7f
|
||||
this._vif.extension = (this._vif.raw & 0x80) >> 7
|
||||
|
||||
if (this._vif.extension == 1) {
|
||||
while (true) {
|
||||
let vife = new VIFE()
|
||||
vife.raw = this.__indata[this.__consumed]
|
||||
this.__consumed++
|
||||
vife.extension = (vife.raw & 0x80) >> 7
|
||||
this._vife.push(vife)
|
||||
if (vife.extension == 0) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let dcf : MeterbusLibCodeTables.DCFt = MeterbusLibCodeTables.DIF_CODING_FIELD[this._dif.coding]
|
||||
let actualLength = dcf.length
|
||||
if (dcf.length == -1) {
|
||||
actualLength = this.__indata[this.__consumed]
|
||||
this.__consumed++
|
||||
}
|
||||
this._data = this.__indata.slice(this.__consumed,
|
||||
this.__consumed + actualLength)
|
||||
this.__consumed += actualLength
|
||||
|
||||
this._value = dcf.encoder(this._data)
|
||||
}
|
||||
}
|
||||
|
||||
export class ManufacturerSpecificData extends ADataBlock {
|
||||
private _type : string = "MSD"
|
||||
private __indata : number[]
|
||||
|
||||
constructor(data : number[]) {
|
||||
super()
|
||||
this.__indata = data
|
||||
}
|
||||
|
||||
parse() : void {
|
||||
|
||||
getJSON() : string {
|
||||
return JSON.stringify(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@ import {MeterbusLibExceptions} from './exceptions'
|
||||
import {MeterbusLibFrames} from './simpleframes'
|
||||
import {MeterbusLibCodeTables} from './codetables'
|
||||
|
||||
|
||||
|
||||
import * as mocha from 'mocha'
|
||||
import * as chai from 'chai'
|
||||
|
||||
@ -15,9 +15,6 @@ let inputOkLongFrame_1phase_electric = "68 38 38 68 08 53 72 17 00 13 00 2E 19 2
|
||||
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"
|
||||
|
||||
let json_1phase_electric = '{"telegram":[104,56,56,104,8,83,114,23,0,19,0,46,25,36,2,214,0,0,0,140,16,4,1,2,0,0,140,17,4,1,2,0,0,2,253,201,255,1,228,0,2,253,219,255,1,3,0,2,172,255,1,1,0,130,64,172,255,1,250,255,32,22],"cField":8,"address":83,"ciField":114,"fixedDataHeader":{"identNo":17001300,"manufacturer":"FIN","version":36,"medium":"Electrity","accessNo":214,"status":0,"signature":[0,0]}}'
|
||||
|
||||
|
||||
// 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"
|
||||
|
||||
@ -43,9 +40,11 @@ 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"
|
||||
|
||||
let inputNOk_illegal_number = "10 01 260 03 16"
|
||||
|
||||
|
||||
|
||||
describe('The Meterbus Library', () => {
|
||||
@ -57,7 +56,7 @@ describe('The Meterbus Library', () => {
|
||||
it('should parse the hexString into the telegram array', () => {
|
||||
const telegram = new MeterbusLib.Telegram()
|
||||
telegram.fromHexString("01 02 03")
|
||||
expect(telegram.telegram).to.deep.equal([1, 2, 3])
|
||||
expect(telegram.telegram).to.deep.equal(new Uint8Array([1, 2, 3]))
|
||||
})
|
||||
it('should detect a control frame', () => {
|
||||
const telegram = new MeterbusLib.Telegram()
|
||||
@ -108,6 +107,10 @@ describe('The Meterbus Library', () => {
|
||||
telegram.fromHexString(inputNOKLongframe_too_short)
|
||||
expect(() => telegram.parse()).to.throw(MeterbusLibExceptions.PayloadTooShortError)
|
||||
})
|
||||
it('should detect an invalid number', () => {
|
||||
const telegram = new MeterbusLib.Telegram()
|
||||
expect(() => telegram.fromHexString(inputNOk_illegal_number)).to.throw(MeterbusLibExceptions.IllegalNumberError)
|
||||
})
|
||||
it('should parse cField from a short frame', () => {
|
||||
const telegram = new MeterbusLib.Telegram()
|
||||
telegram.fromHexString(inputOkShortframe)
|
||||
@ -138,49 +141,47 @@ describe('The Meterbus Library', () => {
|
||||
telegram.parse()
|
||||
expect((telegram.frame as MeterbusLibFrames.ControlFrame).ciField).to.equal(3)
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
|
||||
describe('The Meterbus Longframe Library', () => {
|
||||
let telegram : MeterbusLib.Telegram
|
||||
|
||||
beforeEach(() => {
|
||||
telegram = new MeterbusLib.Telegram()
|
||||
telegram.fromHexString(inputOkLongFrame_1phase_electric)
|
||||
telegram.parse()
|
||||
describe('The Meterbus Longframe Library', () => {
|
||||
let telegram : MeterbusLib.Telegram
|
||||
|
||||
beforeEach(() => {
|
||||
telegram = new MeterbusLib.Telegram()
|
||||
telegram.fromHexString(inputOkLongFrame_1phase_electric)
|
||||
telegram.parse()
|
||||
})
|
||||
|
||||
it('should find the identNo', () => {
|
||||
expect((telegram.frame as MeterbusLibLongFrame.LongFrame).fixedDataHeader.identNo)
|
||||
.to.equal(17001300)
|
||||
})
|
||||
it('should find the manufacturer', () => {
|
||||
expect((telegram.frame as MeterbusLibLongFrame.LongFrame).fixedDataHeader.manufacturer)
|
||||
.to.equal("FIN")
|
||||
})
|
||||
it('should find the version', () => {
|
||||
expect((telegram.frame as MeterbusLibLongFrame.LongFrame).fixedDataHeader.version)
|
||||
.to.equal(0x24)
|
||||
})
|
||||
it('should find the medium', () => {
|
||||
expect((telegram.frame as MeterbusLibLongFrame.LongFrame).fixedDataHeader.medium)
|
||||
.to.equal("Electrity")
|
||||
})
|
||||
it('should find the accessNo', () => {
|
||||
expect((telegram.frame as MeterbusLibLongFrame.LongFrame).fixedDataHeader.accessNo)
|
||||
.to.equal(0xd6)
|
||||
})
|
||||
it('should find the status', () => {
|
||||
expect((telegram.frame as MeterbusLibLongFrame.LongFrame).fixedDataHeader.status)
|
||||
.to.equal(0)
|
||||
})
|
||||
it('should find the signature', () => {
|
||||
expect((telegram.frame as MeterbusLibLongFrame.LongFrame).fixedDataHeader.signature)
|
||||
.to.deep.equal(new Uint8Array([0,0]))
|
||||
})
|
||||
it('should print itself as json', () => {
|
||||
console.log((telegram.frame as MeterbusLibLongFrame.LongFrame).getJSON())
|
||||
})
|
||||
})
|
||||
|
||||
it('should find the identNo', () => {
|
||||
expect((telegram.frame as MeterbusLibLongFrame.LongFrame).fixedDataHeader.identNo)
|
||||
.to.equal(17001300)
|
||||
})
|
||||
it('should find the manufacturer', () => {
|
||||
expect((telegram.frame as MeterbusLibLongFrame.LongFrame).fixedDataHeader.manufacturer)
|
||||
.to.equal("FIN")
|
||||
})
|
||||
it('should find the version', () => {
|
||||
expect((telegram.frame as MeterbusLibLongFrame.LongFrame).fixedDataHeader.version)
|
||||
.to.equal(0x24)
|
||||
})
|
||||
it('should find the medium', () => {
|
||||
expect((telegram.frame as MeterbusLibLongFrame.LongFrame).fixedDataHeader.medium)
|
||||
.to.equal("Electrity")
|
||||
})
|
||||
it('should find the accessNo', () => {
|
||||
expect((telegram.frame as MeterbusLibLongFrame.LongFrame).fixedDataHeader.accessNo)
|
||||
.to.equal(0xd6)
|
||||
})
|
||||
it('should find the status', () => {
|
||||
expect((telegram.frame as MeterbusLibLongFrame.LongFrame).fixedDataHeader.status)
|
||||
.to.equal(0)
|
||||
})
|
||||
it('should find the signature', () => {
|
||||
expect((telegram.frame as MeterbusLibLongFrame.LongFrame).fixedDataHeader.signature)
|
||||
.to.deep.equal([0,0])
|
||||
})
|
||||
it('should prepare itself as JSON', () => {
|
||||
console.log(JSON.stringify(telegram.frame as MeterbusLibLongFrame.LongFrame, null, 2))
|
||||
expect(JSON.stringify(telegram.frame as MeterbusLibLongFrame.LongFrame)).to.equal(json_1phase_electric)
|
||||
})
|
||||
})
|
||||
|
||||
})
|
@ -1,15 +1,13 @@
|
||||
import {MeterbusLibExceptions} from './exceptions'
|
||||
import {MeterbusLibFrames} from './simpleframes'
|
||||
import {MeterbusLibLongFrame} from './longframe'
|
||||
import {MeterbusLibUtils} from './utils'
|
||||
|
||||
|
||||
export namespace MeterbusLib {
|
||||
|
||||
|
||||
export class Telegram {
|
||||
private _hexString : string
|
||||
private _telegram : number[]
|
||||
private _telegram : Uint8Array
|
||||
private _frame : MeterbusLibFrames.Frame
|
||||
|
||||
constructor() {
|
||||
@ -19,7 +17,7 @@ export namespace MeterbusLib {
|
||||
return this._hexString
|
||||
}
|
||||
|
||||
get telegram() : number[] {
|
||||
get telegram() : Uint8Array {
|
||||
return this._telegram
|
||||
}
|
||||
|
||||
@ -29,9 +27,13 @@ export namespace MeterbusLib {
|
||||
|
||||
fromHexString(hexString : string) {
|
||||
this._hexString = hexString
|
||||
this._telegram = this._hexString.split(' ').map<number>((val) => {
|
||||
return parseInt(val, 16)
|
||||
})
|
||||
this._telegram = new Uint8Array(this._hexString.split(' ').map<number>((val) => {
|
||||
let n = parseInt(val, 16)
|
||||
if ((n < 0) || (n > 255)) {
|
||||
throw new MeterbusLibExceptions.IllegalNumberError()
|
||||
}
|
||||
return n
|
||||
}))
|
||||
}
|
||||
|
||||
parse() {
|
||||
|
@ -1,36 +1,31 @@
|
||||
import {MeterbusLibExceptions} from './exceptions'
|
||||
import {MeterbusLibUtils} from './utils'
|
||||
|
||||
|
||||
export namespace MeterbusLibFrames {
|
||||
export abstract class Frame {
|
||||
protected _telegram : number[]
|
||||
protected __startCharacter : number
|
||||
protected __frameLength : number
|
||||
protected __firstPayload : number
|
||||
protected __payloadLength : number
|
||||
protected _telegram : Uint8Array
|
||||
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
|
||||
firstPayload : number, telegram : Uint8Array) {
|
||||
this._startCharacter = startCharacter
|
||||
this._frameLength = frameLength
|
||||
this._firstPayload = firstPayload
|
||||
this._telegram = telegram
|
||||
this.__payloadLength = this.__frameLength - 6
|
||||
this._payloadLength = this._frameLength - 6
|
||||
|
||||
}
|
||||
|
||||
|
||||
toJSON() : any {
|
||||
return MeterbusLibUtils.jsonPrepaper(this, [])
|
||||
}
|
||||
|
||||
verifyChecksumAndEnd() {
|
||||
if (this.__firstPayload != 0) {
|
||||
if (this._firstPayload != 0) {
|
||||
if (this._telegram[this._telegram.length - 1] != 0x16) {
|
||||
throw new MeterbusLibExceptions.InvalidStopCharError()
|
||||
}
|
||||
let checksum : number = 0
|
||||
this._telegram.slice(this.__firstPayload, this._telegram.length -2).forEach((val) => {
|
||||
this._telegram.slice(this._firstPayload, this._telegram.length -2).forEach((val) => {
|
||||
checksum += val
|
||||
})
|
||||
// console.log(`calc. checksum ${checksum}, ${checksum & 0xff}`)
|
||||
@ -44,10 +39,10 @@ export namespace MeterbusLibFrames {
|
||||
abstract parse2()
|
||||
|
||||
parse() {
|
||||
if (this._telegram[0] != this.__startCharacter) {
|
||||
if (this._telegram[0] != this._startCharacter) {
|
||||
throw new MeterbusLibExceptions.InvalidStartCharError()
|
||||
}
|
||||
if (this._telegram.length != this.__frameLength) {
|
||||
if (this._telegram.length != this._frameLength) {
|
||||
throw new MeterbusLibExceptions.InvalidLengthError()
|
||||
}
|
||||
this.verifyChecksumAndEnd()
|
||||
@ -73,15 +68,15 @@ export namespace MeterbusLibFrames {
|
||||
return this._ciField
|
||||
}
|
||||
|
||||
constructor(telegram : number[]) {
|
||||
constructor(telegram : Uint8Array) {
|
||||
super(0x68, (telegram[1] + 6), 4, telegram)
|
||||
}
|
||||
|
||||
parse2() {
|
||||
if (this._telegram[2] != this.__payloadLength) {
|
||||
if (this._telegram[2] != this._payloadLength) {
|
||||
throw new MeterbusLibExceptions.InvalidSecondLengthError()
|
||||
}
|
||||
if (this.__payloadLength < 3) {
|
||||
if (this._payloadLength < 3) {
|
||||
throw new MeterbusLibExceptions.PayloadTooShortError()
|
||||
}
|
||||
|
||||
@ -103,7 +98,7 @@ export namespace MeterbusLibFrames {
|
||||
return this._address
|
||||
}
|
||||
|
||||
constructor(telegram : number[]) {
|
||||
constructor(telegram : Uint8Array) {
|
||||
super(0x10, 5, 1, telegram)
|
||||
}
|
||||
|
||||
@ -114,7 +109,7 @@ export namespace MeterbusLibFrames {
|
||||
}
|
||||
|
||||
export class SingleCharFrame extends Frame {
|
||||
constructor(telegram : number[]) {
|
||||
constructor(telegram : Uint8Array) {
|
||||
super(0xe5, 1, 0, telegram)
|
||||
}
|
||||
|
||||
|
@ -8,57 +8,21 @@ const expect = chai.expect
|
||||
|
||||
describe('The Meterbus Library Utils', () => {
|
||||
it('should convert a bcd to a number (10203)', () => {
|
||||
expect(MeterbusLibUtils.bcd([1, 2, 3])).to.equal(10203)
|
||||
expect(MeterbusLibUtils.bcd(new Uint8Array([1, 2, 3]))).to.equal(10203)
|
||||
})
|
||||
it('should convert a bcd to a number (0010203)', () => {
|
||||
expect(MeterbusLibUtils.bcd([0, 1, 2, 3])).to.equal(10203)
|
||||
expect(MeterbusLibUtils.bcd(new Uint8Array([0, 1, 2, 3]))).to.equal(10203)
|
||||
})
|
||||
it('should convert a bcd to a number (0)', () => {
|
||||
expect(MeterbusLibUtils.bcd([0, 0, 0])).to.equal(0)
|
||||
expect(MeterbusLibUtils.bcd(new Uint8Array([0, 0, 0]))).to.equal(0)
|
||||
})
|
||||
it('should convert a bcd to a number (0)', () => {
|
||||
expect(MeterbusLibUtils.bcd([0, 0, 0, 0])).to.equal(0)
|
||||
expect(MeterbusLibUtils.bcd(new Uint8Array([0, 0, 0, 0]))).to.equal(0)
|
||||
})
|
||||
it('should convert a bcd to a number (99999999)', () => {
|
||||
expect(MeterbusLibUtils.bcd([0x99, 0x99, 0x99, 0x99])).to.equal(99999999)
|
||||
expect(MeterbusLibUtils.bcd(new Uint8Array([0x99, 0x99, 0x99, 0x99]))).to.equal(99999999)
|
||||
})
|
||||
it('should convert two number to manufacturer code', () => {
|
||||
expect(MeterbusLibUtils.manufCode([0x2e, 0x19])).to.equal("FIN")
|
||||
expect(MeterbusLibUtils.manufCode(new Uint8Array([0x2e, 0x19]))).to.equal("FIN")
|
||||
})
|
||||
})
|
||||
|
||||
describe('The jsonPrepare function in the Meterbus Library Utils', () => {
|
||||
it('should forward regular properties of an object', () => {
|
||||
let objIn = {'a': 1, 'b':2}
|
||||
let objOut = objIn
|
||||
expect(MeterbusLibUtils.jsonPrepaper(objIn, [])).to.deep.equal(objOut)
|
||||
})
|
||||
it('should hide properties in the hide list', () => {
|
||||
let objIn = {'a': 1, 'b':2}
|
||||
let objOut = {'a': 1}
|
||||
expect(MeterbusLibUtils.jsonPrepaper(objIn, ['b'])).to.deep.equal(objOut)
|
||||
})
|
||||
it('should hide properties beginning with two underscores', () => {
|
||||
let objIn = {'a': 1, '__b':2}
|
||||
let objOut = {'a': 1}
|
||||
expect(MeterbusLibUtils.jsonPrepaper(objIn, [])).to.deep.equal(objOut)
|
||||
})
|
||||
it('should remove one leading underscore from property names', () => {
|
||||
let objIn = {'_a': 1, 'b':2}
|
||||
let objOut = {'a': 1, 'b':2}
|
||||
expect(MeterbusLibUtils.jsonPrepaper(objIn, [])).to.deep.equal(objOut)
|
||||
})
|
||||
})
|
||||
|
||||
describe('The clone function in the Meterbus Library Utils', () => {
|
||||
it('should clone the attributes of an object', () => {
|
||||
let objIn = {'a': 1, 'b': 2}
|
||||
let objOut = {'a': 1, 'b': 2}
|
||||
expect(MeterbusLibUtils.clone(objIn)).to.deep.equal(objOut)
|
||||
})
|
||||
it('should not just return the same object', () => {
|
||||
let objIn = {'a': 1, 'b':2}
|
||||
let objOut = objIn
|
||||
expect(MeterbusLibUtils.clone(objIn)).to.not.equal(objOut)
|
||||
})
|
||||
})
|
27
src/utils.ts
27
src/utils.ts
@ -1,5 +1,5 @@
|
||||
export namespace MeterbusLibUtils {
|
||||
export function bcd(data : number[]) : number {
|
||||
export function bcd(data : Uint8Array) : number {
|
||||
let v : string = ""
|
||||
data.forEach((c : number) => {
|
||||
v += (c & 0xf0) >> 4
|
||||
@ -9,29 +9,8 @@ export namespace MeterbusLibUtils {
|
||||
return r
|
||||
}
|
||||
|
||||
export function manufCode(data : number[]) : string {
|
||||
export function manufCode(data : Uint8Array) : string {
|
||||
let v : number = (data[1] << 8) + data[0]
|
||||
return String.fromCharCode((((v >> 10) & 0x1f) + 64), (((v >> 5) & 0x1f) + 64), ((v & 0x1f) + 64))
|
||||
}
|
||||
|
||||
export function jsonPrepaper(obj:any, hideKeys:string[]) : any {
|
||||
let dup = {}
|
||||
for (let key in obj) {
|
||||
if ((hideKeys.indexOf(key) == -1) && ! ((key[0] == "_") && (key[1] == "_"))) {
|
||||
let dkey = (key[0] == "_") ? key.slice(1) : key
|
||||
dup[dkey] = obj[key]
|
||||
}
|
||||
}
|
||||
return dup
|
||||
}
|
||||
|
||||
export function clone(obj:any) : any {
|
||||
let dup = {}
|
||||
for (let key in obj) {
|
||||
dup[key] = obj[key]
|
||||
}
|
||||
return dup
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user