diff --git a/src/codetables.ts b/src/codetables.ts index 39786c1..331b7b1 100644 --- a/src/codetables.ts +++ b/src/codetables.ts @@ -1,31 +1,95 @@ -export namespace MeterbusLibCodeTables { +import {MeterbusLibUtils} from './utils' + + +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}) + ] + +/* + DATA_FIELD_CODES = ( + ('No Data', 0, None), + ('8 Bit Integer', 1, lambda x: x[0]), + ('16 Bit Integer', 2, lambda x: (x[1] << 8) + x[0]), + ('24 Bit Integer', 3, lambda x: (x[2] << 16) + (x[1] << 8) + x[0]), + ('32 Bit Integer', 4, lambda x: (x[3] << 24) + (x[2] << 16) + (x[1] << 8) + x[0]), + ('32 Bit Real', 4, lambda x: struct.unpack('f', str(bytearray(x)))[0]), + ('48 Bit Integer', 6, lambda x: (x[5] << 40) + (x[4] << 32) + (x[3] << 24) + (x[2] << 16) + (x[1] << 8) + x[0]), + ('64 Bit Integer', 8, lambda x: (x[7] << 56) + (x[6] << 48) + (x[5] << 40) + (x[4] << 32) + (x[3] << 24) + (x[2] << 16) + (x[1] << 8) + x[0]), + ('Selection for Readout', 0, None), + ('2 Digit BCD', 1, lambda x: MeterbusTypeConversion.bcd(x)), + ('4 Digit BCD', 2, lambda x: MeterbusTypeConversion.bcd(x)), + ('6 Digit BCD', 3, lambda x: MeterbusTypeConversion.bcd(x)), + ('8 Digit BCD', 4, lambda x: MeterbusTypeConversion.bcd(x)), + ('variable length', -1, None), + ('12 Digit BCD', 6, lambda x: MeterbusTypeConversion.bcd(x)), + ('Special Function', 0, None), + ) +*/ + } \ No newline at end of file diff --git a/src/longframe.ts b/src/longframe.ts index bb33a7e..89acf8e 100644 --- a/src/longframe.ts +++ b/src/longframe.ts @@ -6,6 +6,8 @@ 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[]) { @@ -16,6 +18,16 @@ export namespace MeterbusLibLongFrame { super.parse2() // control frame parse2 method this._fixedDataHeader = new FixedDataHeader(this._telegram.slice(7, 19)) this._fixedDataHeader.parse() + + let consumed : number = 0 + while (true) { + let die : DataBlock = + new DataBlock(this._telegram.slice(19 + consumed)) + die.parse() + this._variableDataBlocks.push(die) + consumed += die.consumed + break //FIXME + } } } @@ -61,4 +73,157 @@ export namespace MeterbusLibLongFrame { } } + export abstract class ADataBlock { + protected __consumed : number = 0 + + get consumed() : number { + return this.__consumed + } + + abstract parse() : void + + toJSON() : any { + console.log("x") + return MeterbusLibUtils.jsonPrepaper(this, []) + } + + getJSON() : string { + return JSON.stringify(this) + } + } + + export class DataBlock extends ADataBlock { + private _content : VariableDataBlock | ManufacturerSpecificData + + constructor(data : number[]) { + super() + if ((data[0] & 0x0f) == 0x0f) { + this._content = new ManufacturerSpecificData(data) + } else { + this._content = new VariableDataBlock(data) + } + } + + + parse() : void { + this._content.parse() + } + + toJSON() : any { + return MeterbusLibUtils.jsonPrepaper(this._content, []) + } + } + + 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] + this._data = this.__indata.slice(this.__consumed, + this.__consumed + dcf.length) + this.__consumed += dcf.length + + console.log(dcf.encoder) + this._value = dcf.encoder(this._data) + console.log(this._value) + } + } + + export class ManufacturerSpecificData extends ADataBlock { + private _type : string = "MSD" + private _data : number[] + + constructor(data : number[]) { + super() + this._data = data + } + + parse() : void { + + } + } + } \ No newline at end of file diff --git a/src/meterbus.test.ts b/src/meterbus.test.ts index 1153c5d..b578df2 100644 --- a/src/meterbus.test.ts +++ b/src/meterbus.test.ts @@ -180,6 +180,7 @@ describe('The Meterbus Longframe Library', () => { .to.deep.equal([0,0]) }) it('should prepare itself as JSON', () => { + console.log((telegram.frame as MeterbusLibLongFrame.LongFrame).getJSON()) expect((telegram.frame as MeterbusLibLongFrame.LongFrame).getJSON()).to.equal(json_1phase_electric) }) }) diff --git a/src/utils.test.ts b/src/utils.test.ts index ec90883..b58c99d 100644 --- a/src/utils.test.ts +++ b/src/utils.test.ts @@ -48,4 +48,17 @@ describe('The jsonPrepare function in the Meterbus Library Utils', () => { 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) + }) }) \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index 3f42a9a..04afde2 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -24,5 +24,14 @@ export namespace MeterbusLibUtils { } return dup } + + export function clone(obj:any) : any { + let dup = {} + for (let key in obj) { + dup[key] = obj[key] + } + return dup + } + }