Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,14 @@ let options = {
retryTimes: 3,

// A list of MIDs for which we'll not parse the payload
disableMidParsing: {}
disableMidParsing: {},

// Weather to send the stationID and spindleID header fields as blanks,
// which is the only value Desoutter controllers accept
//true: enforce activation
//false: enforce deactivation
//undefined: auto detect from the supplier code of the MID 0002 reply
desoutterCompatibilityMode: undefined
}

let controllerIp = "127.0.0.1";
Expand Down
10 changes: 10 additions & 0 deletions src/linkLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class LinkLayer extends Duplex {
* @param {number} opts.retryTimes
* @param {boolean} opts.rawData
* @param {boolean} opts.disableMidParsing
* @param {boolean} opts.desoutterCompatibilityMode
*/
constructor(opts) {
debug("new LinkLayer", opts);
Expand Down Expand Up @@ -114,6 +115,15 @@ class LinkLayer extends Duplex {
//RECEIVER DATA
}

/**
* @description Enables or disables the Desoutter compatibility mode at runtime.
* @param {boolean} enabled
*/
setDesoutterCompatibilityMode(enabled) {
debug("LinkLayer setDesoutterCompatibilityMode", enabled);
this.opSerializer.setDesoutterCompatibilityMode(enabled);
}

_onErrorSerializer(err) {
debug("LinkLayer _onErrorSerializer", err);

Expand Down
11 changes: 11 additions & 0 deletions src/openProtocolSerializer.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@ class OpenProtocolSerializer extends Transform {
this.desoutterCompatibilityMode = opts.desoutterCompatibilityMode || false;
}

/**
* @description Enables or disables the Desoutter compatibility mode at runtime.
* In this mode the stationID and spindleID header fields are serialized as
* blanks, as Desoutter controllers reject any other value.
* @param {boolean} enabled
*/
setDesoutterCompatibilityMode(enabled) {
debug("openProtocolSerializer setDesoutterCompatibilityMode", enabled);
this.desoutterCompatibilityMode = !!enabled;
}

_transform(chunk, encoding, cb) {
debug("openProtocolSerializer _transform", chunk);

Expand Down
47 changes: 47 additions & 0 deletions src/sessionControlClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ const REQUEST = "request";
const MANUAL = "manual";
const GENERIC = "generic";

//Supplier code prefix reported by Desoutter controllers on MID 2
const DESOUTTER_SUPPLIER_PREFIX = "DE";

//Status Connect
const CONN_NOT_CONNECT = 0;
const CONN_CONNECTING = 1;
Expand Down Expand Up @@ -125,6 +128,7 @@ class SessionControlClient extends EventEmitter {
* @param {object} [opts.disableMidParsing = {}]
* @param {number} [opts.timeOut = 3000]
* @param {number} [opts.retryTimes = 3]
* @param {boolean} [opts.desoutterCompatibilityMode] true = always send blank stationID/spindleID / false = never / undefined = auto-detect from the supplier code of the MID 2 reply
*
* @example
* // Instantiate SessionControlClient with default values
Expand Down Expand Up @@ -202,6 +206,11 @@ class SessionControlClient extends EventEmitter {
desoutterCompatibilityMode: opts.desoutterCompatibilityMode,
});

//Desoutter compatibility mode
//If the caller didn't take an explicit position, we auto-detect it on
//the MID 2 reply of the handshake. An explicit value is always honored.
this.autoDetectDesoutter = opts.desoutterCompatibilityMode === undefined;

this.ll.on("error", (err) => this._onErrorLinkLayer(err));

this.changeRevisionGeneric = false;
Expand Down Expand Up @@ -248,6 +257,42 @@ class SessionControlClient extends EventEmitter {
return !!this.useLinkLayer;
}

/**
* @description Enables or disables the Desoutter compatibility mode at runtime.
* In this mode the stationID and spindleID header fields are sent as blanks,
* as Desoutter controllers reject any other value.
* @param {boolean} enabled
*/
setDesoutterCompatibilityMode(enabled) {
debug("SessionControlClient setDesoutterCompatibilityMode", enabled);
this.ll.setDesoutterCompatibilityMode(enabled);
}

/**
* @description Turns on the Desoutter compatibility mode if the controller
* identified itself as a Desoutter one on the MID 2 reply. Never turns it
* off, so that an explicitly requested mode can't be undone by a controller
* that reports an unexpected supplier code.
* @private
* @param {object} data the MID 2 message
*/
_detectDesoutter(data) {
if (!this.autoDetectDesoutter) {
return;
}

let supplierCode = data.payload && data.payload.supplierCode;

if (typeof supplierCode !== "string") {
return;
}

if (supplierCode.trim().toUpperCase().startsWith(DESOUTTER_SUPPLIER_PREFIX)) {
debug("SessionControlClient _detectDesoutter detected", supplierCode);
this.setDesoutterCompatibilityMode(true);
}
}

/**
* @description This method makes a connection with the controller.
* If add a callback function, it will add as listener of connect @event.
Expand Down Expand Up @@ -358,6 +403,8 @@ class SessionControlClient extends EventEmitter {

this.ll.removeAllListeners();

this._detectDesoutter(data);

this.statusConnection = CONN_CONNECTED;
this.controllerData = data;

Expand Down
47 changes: 47 additions & 0 deletions test/openProtocolSerialzer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -275,4 +275,51 @@ describe("Open Protocol Serializer", () => {
});
});

it('should toggle the desoutter compatibility mode at runtime', (done) => {
let serializer = new OpenProtocolSerializer();
let received = [];

//00230240001001010000250[Null]
const normal = Buffer.from('00230240001001010000250');
//002302400010 0000250[Null]
const desoutter = Buffer.from('002302400010 0000250');

let write = () => serializer.write({
mid: 240,
revision: 1,
noAck: false,
stationID: 1,
spindleID: 1,
sequenceNumber: 0,
messageParts: 0,
messageNumber: 0,
payload: "250"
});

serializer.on('data', (data) => {
received.push(data);

switch (received.length) {
case 1:
expect(data).to.be.deep.equal(normal);
serializer.setDesoutterCompatibilityMode(true);
write();
break;

case 2:
expect(data).to.be.deep.equal(desoutter);
serializer.setDesoutterCompatibilityMode(false);
write();
break;

case 3:
expect(data).to.be.deep.equal(normal);
done();
break;
}
});

write();
});

});
101 changes: 101 additions & 0 deletions test/sessionControlClient.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1059,4 +1059,105 @@ describe("Session Control Client", () => {
sessionControlClient.connect();
});

it("Should enable the desoutter compatibility mode when the supplier code starts with DE", (done) => {

let writes = [];
let sessionControlClient;

let stream = createStreamHelper((data) => {

writes.push(data);

if (writes.length === 1) {
//MID 2 revision 2, supplier code "DE3"
stream.push(Buffer.from("00620002002000000000010001020103Airbag1 04DE3\u0000"));
return;
}

if (writes.length === 2) {
expect(data.toString("ascii", 12, 16)).to.be.equal(" ");
sessionControlClient.close();
done();
}
});

sessionControlClient = new SessionControlClient({
stream: stream
});

sessionControlClient.on("connect", (data) => {
expect(data.payload.supplierCode).to.be.equal("DE3");
sessionControlClient.sendMid(9999, {}, () => {});
});

sessionControlClient.connect();
});

it("Should keep sending the stationID and spindleID for a non desoutter supplier code", (done) => {

let writes = [];
let sessionControlClient;

let stream = createStreamHelper((data) => {

writes.push(data);

if (writes.length === 1) {
//MID 2 revision 2, supplier code "AC "
stream.push(Buffer.from("00620002002000000000010001020103Airbag1 04AC \u0000"));
return;
}

if (writes.length === 2) {
expect(data.toString("ascii", 12, 16)).to.be.equal("0101");
sessionControlClient.close();
done();
}
});

sessionControlClient = new SessionControlClient({
stream: stream
});

sessionControlClient.on("connect", () => {
sessionControlClient.sendMid(9999, {}, () => {});
});

sessionControlClient.connect();
});

it("Should not auto detect the desoutter compatibility mode when it is explicitly disabled", (done) => {

let writes = [];
let sessionControlClient;

let stream = createStreamHelper((data) => {

writes.push(data);

if (writes.length === 1) {
//MID 2 revision 2, supplier code "DE3"
stream.push(Buffer.from("00620002002000000000010001020103Airbag1 04DE3\u0000"));
return;
}

if (writes.length === 2) {
expect(data.toString("ascii", 12, 16)).to.be.equal("0101");
sessionControlClient.close();
done();
}
});

sessionControlClient = new SessionControlClient({
stream: stream,
desoutterCompatibilityMode: false
});

sessionControlClient.on("connect", () => {
sessionControlClient.sendMid(9999, {}, () => {});
});

sessionControlClient.connect();
});

});
Loading