diff --git a/packages/binding-mbus/src/mbus-client.ts b/packages/binding-mbus/src/mbus-client.ts index 86452cf3c..91cf58bd1 100644 --- a/packages/binding-mbus/src/mbus-client.ts +++ b/packages/binding-mbus/src/mbus-client.ts @@ -105,6 +105,10 @@ export default class MBusClient implements ProtocolClient { new MBusConnection(host, port, { connectionTimeout: form["mbus:timeout"] || DEFAULT_TIMEOUT }) ); connection = this._connections.get(hostAndPort); + if (!connection) { + debug(`MbusConnection undefined`); + throw new Error("MbusConnection undefined"); + } } else { debug(`Reusing MbusConnection for ${hostAndPort}`); } @@ -124,8 +128,14 @@ export default class MBusClient implements ProtocolClient { const query = parsed.searchParams; input["mbus:unitID"] = parseInt(pathComp[1], 10) || input["mbus:unitID"]; - input["mbus:offset"] = parseInt(query.get("offset"), 10) || input["mbus:offset"]; - input["mbus:timeout"] = parseInt(query.get("timeout"), 10) || input["mbus:timeout"]; + const stringOffset = query.get("offset"); + if (stringOffset !== null) { + input["mbus:offset"] = parseInt(stringOffset, 10); + } + const stringTimeout = query.get("timeout"); + if (stringTimeout !== null) { + input["mbus:timeout"] = parseInt(stringTimeout, 10); + } } private validateAndFillDefaultForm(form: MBusForm): MBusForm { diff --git a/packages/binding-mbus/src/mbus-connection.ts b/packages/binding-mbus/src/mbus-connection.ts index 551f78ac3..dc0eada82 100644 --- a/packages/binding-mbus/src/mbus-connection.ts +++ b/packages/binding-mbus/src/mbus-connection.ts @@ -61,14 +61,14 @@ export class MBusConnection { client: any; // MBusClient.IMBusRTU connecting: boolean; connected: boolean; - timer: NodeJS.Timer; // connection idle timer - currentTransaction: MBusTransaction; // transaction currently in progress or null + timer?: NodeJS.Timer; // connection idle timer + currentTransaction?: MBusTransaction; // transaction currently in progress or undefined queue: Array; // queue of further transactions config: { connectionTimeout?: number; operationTimeout?: number; connectionRetryTime?: number; - maxRetries?: number; + maxRetries: number; }; constructor( @@ -92,8 +92,8 @@ export class MBusConnection { }); this.connecting = false; this.connected = false; - this.timer = null; - this.currentTransaction = null; + this.timer = undefined; + this.currentTransaction = undefined; this.queue = new Array(); this.config = Object.assign(configDefaults, config); @@ -156,7 +156,7 @@ export class MBusConnection { } } - async execute(op: PropertyOperation): Promise> { + async execute(op: PropertyOperation): Promise | undefined> { this.trigger(); return op.execute(); } @@ -182,20 +182,25 @@ export class MBusConnection { // inform all the operations that the connection cannot be recovered this.queue.forEach((transaction) => { transaction.operations.forEach((op) => { - op.failed(error); + op.failed(error instanceof Error ? error : new Error(JSON.stringify(error))); }); }); } } else if (this.connected && this.currentTransaction == null && this.queue.length > 0) { // take next transaction from queue and execute this.currentTransaction = this.queue.shift(); + if (!this.currentTransaction) { + warn(`Current transaction is undefined -> transaction not executed`); + return; + } + try { await this.executeTransaction(this.currentTransaction); - this.currentTransaction = null; + this.currentTransaction = undefined; this.trigger(); } catch (err) { warn(`Transaction failed: ${err}`); - this.currentTransaction = null; + this.currentTransaction = undefined; this.trigger(); } } @@ -211,7 +216,9 @@ export class MBusConnection { } catch (err) { warn(`Read operation failed on unit ${transaction.unitId}. ${err}.`); // inform all operations and the invoker - transaction.operations.forEach((op) => op.failed(err)); + transaction.operations.forEach((op) => + op.failed(error instanceof Error ? error : new Error(JSON.stringify(error))) + ); throw err; } } @@ -249,7 +256,7 @@ export class MBusConnection { } }); clearInterval(this.timer); - this.timer = null; + this.timer = undefined; } } @@ -259,11 +266,14 @@ export class MBusConnection { export class PropertyOperation { unitId: number; base: number; - resolve: (value?: Content | PromiseLike) => void; - reject: (reason?: Error) => void; + resolve?: (value?: Content | PromiseLike) => void; + reject?: (reason?: Error) => void; constructor(form: MBusForm) { this.unitId = form["mbus:unitID"]; + if (form["mbus:offset"] === undefined) { + throw new Error("form['mbus:offset'] is undefined"); + } this.base = form["mbus:offset"]; } @@ -271,7 +281,7 @@ export class PropertyOperation { * Trigger execution of this operation. * */ - async execute(): Promise> { + async execute(): Promise<(Content | PromiseLike) | undefined> { return new Promise( (resolve: (value?: Content | PromiseLike) => void, reject: (reason?: Error) => void) => { this.resolve = resolve; @@ -310,6 +320,9 @@ export class PropertyOperation { const resp = new Content("application/json", Readable.from(JSON.stringify(payload))); + if (!this.resolve) { + throw new Error("Function 'done' was invoked before executing the Mbus operation"); + } // resolve the Promise given to the invoking script this.resolve(resp); } @@ -319,9 +332,12 @@ export class PropertyOperation { * * @param reason Reason of failure */ - failed(reason: string): void { - warn("Operation failed:", reason); + failed(reason: Error): void { + warn(`Operation failed: ${reason}`); + if (!this.reject) { + throw new Error("Function 'failed' was invoked before executing the Mbus operation"); + } // reject the Promise given to the invoking script - this.reject(new Error(reason)); + this.reject(reason); } } diff --git a/packages/binding-mbus/test/mbus-client-test.ts b/packages/binding-mbus/test/mbus-client-test.ts index 3c066efdb..00d4316f0 100644 --- a/packages/binding-mbus/test/mbus-client-test.ts +++ b/packages/binding-mbus/test/mbus-client-test.ts @@ -12,7 +12,7 @@ * * SPDX-License-Identifier: EPL-2.0 OR W3C-20150513 ********************************************************************************/ -import { should } from "chai"; +import { expect, should } from "chai"; import * as chai from "chai"; import MBusClient from "../src/mbus-client"; import { MBusForm } from "../src/mbus"; @@ -56,8 +56,16 @@ describe("mbus client test", () => { // eslint-disable-next-line dot-notation client["overrideFormFromURLPath"](form); form["mbus:unitID"].should.be.equal(2, "Form value not overridden"); - form["mbus:offset"].should.be.equal(2, "Form value not overridden"); - form["mbus:timeout"].should.be.equal(5, "Form value not overridden"); + if (form["mbus:offset"]) { + form["mbus:offset"].should.be.equal(2, "Form value not overridden"); + } else { + expect.fail("mbus:offset undefined"); + } + if (form["mbus:timeout"]) { + form["mbus:timeout"].should.be.equal(5, "Form value not overridden"); + } else { + expect.fail("mbus:timeout undefined"); + } }); describe("read resource", () => { diff --git a/packages/binding-mbus/tsconfig.json b/packages/binding-mbus/tsconfig.json index e9fa7c062..df9cd1d90 100644 --- a/packages/binding-mbus/tsconfig.json +++ b/packages/binding-mbus/tsconfig.json @@ -2,7 +2,8 @@ "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "dist", - "rootDir": "src" + "rootDir": "src", + "strict": true }, "include": ["src/**/*"], "references": [{ "path": "../td-tools" }, { "path": "../core" }]