Code Monkey home page Code Monkey logo

node-open-protocol's Introduction

Node Open Protocol

A library to interface with Power Tools using the Atlas Copco Open Protocol

This node was created by Smart-Tech as part of the ST-One project.

Documentation: Open Protocol R 2.8.0

What is Open Protocol?

Open Protocol is an interface for building applications for remote control or data subscription of industrial tightening controllers. It's an open specification led by Atlas Copco Industrial Technique AB, though it is implemented by most of the other tightening controller manufacturers.

What is this node?

This node is a Node.js library that implements the Atlas Copco's Open Protocol, being possible establish communication with tightening controllers. This library has several and the most common MIDs implemented.

Features

  • Full protocol support: This library features not only simple MID parsing and serializing, but also establishing and managing the whole lifecycle of an Open Protocol connection. The library user needs only to configure the IP Address and port number and to call the needed functions. All the logic necessary to securely perform the communication, including message queuing, acknowledgement and validation, is performed by the library internally.

  • Structured implementation: The library is implemented as different layers, making protocol logic separation clear and easy. Implementing support for a new MID is as easy as dropping a new file with the respective parser and serializer functions.

  • Support for out-of-spec MIDs: Controllers may have an implementation that can be sligtly out-of-spec, and they would be therefore incompatible with this library. We aim to support these edge cases by disabling the parsing of the payload of selected MIDs, and they're delivered then as the original string as sent by the controller, so that the user can parse it. When sending a message, there's a specific function so the user can directly input the payload as it will be sent to the controller.

  • Support for multi-part messages: In some cases, when a telegram is bigger than the maximum allowed telegram length, there's a mechanism so the tightening controller can split the payload into multiple messages. This library supports such messages, and all the parts are reassebled into a single payload so it can be correctly parsed.

  • LinkLayer support with auto-negotiation: This library supports Link Layer Acknowledgement (as per chapter 3.2.2) by implementing and exchanging MIDs 9997 and 9998. This is auto-negotiated on connection startup, but can be either enforced or fully disabled.

  • Generic MID request/subscription: The use of Generic Application data request (MID 0006) and Generic Application data (un)subscription (MID 0008 and 0009) are supported by this library, as per chapter 3.9.3 and 3.9.4. Please consider the support of Generic MIDs experimental

How use this node?

Install

npm install node-open-protocol

Instance and connection

The variable options is optional, in the example below it will be created with default values.

const openProtocol = require('node-open-protocol');

let options = {

    // Weather to use LinkLayer Acknowledgement
    //true: enforce activation
    //false: enforce deactivation
    //undefined: auto negotiate
    linkLayerActivate: undefined,

    // Weather to user Generic MID request/subscription
    //true: use generics
    //false or undefined: don't use generics
    genericMode: undefined,

    // How oft we send keep-alive messages (MID 9999), in milliseconds
    keepAlive: 10000,

    // Weather the library will append a parameter "_raw" with the Buffer as received from the controller
    rawData: false,

    // How long we'll wait for an acknowledge of the controller to a message that we send, in milliseconds
    timeOut: 3000,

    // How many times we'll retry to send a message if we don't receive an answer
    retryTimes: 3,

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

let controllerIp = "127.0.0.1";
let controllerPort = 4545;

let op = openProtocol.createClient(controllerPort, controllerIp , options, (data) => {
    console.log("Connected");
    console.log("MID 0002", data);
});

or then simply:

const openProtocol = require('node-open-protocol');

let controllerIp = "127.0.0.1";
let controllerPort = "4545";

let op = openProtocol.createClient(controllerPort, controllerIp, (data) => {
    // data is the content of MID 0002
    console.log("Connected");
    console.log("MID 0002", data);
});

Methods

All calls follow a standard structure:

op.__OPERATION__(midGroup, opts, callback);

where:

  • __OPERATION__ is either subscribe, unsubscribe, request, or command, depending on the category of the intended MID.

  • midGroup is the "name" of the MID being called, as per this list docs/MIDsGroups.md.

  • opts may be optional. If it's sent, it will be assigned to the body of the MID. Otherwhise, it will use the standard MID structure.

  • callback is also optionaland if not given, the method will return a Promise instead. They may contain any reply data and eventual errors.

//To MIDs not implemented
let opts = {
    revision: 1,
    payload: "mid body" //String or buffer
}

//To MIDs implemented with additional parameters
//Example MID 0018 - selectPset
let opts = {
    payload: {
        parameterSetID: 2
    }
}

Subscribe

The subscribe method is used to subscribe to controller events. The incoming data from these events will be then emitted as events, that can be listened to by using the on method.

List of subscribe midGroups

op.subscribe(midGroup, opts, callback);
op.on("lastTightening", (midData) => {  // will get the events
    console.log("Received data on subscribe", midData);
});

op.subscribe("lastTightening", (err, data) => {
    if (err) {
        console.log("Error on subscribing to 'lastTightening'", err);
        return;
    }

    console.log("Subscribed to 'lastTightening'");
});

Unsubscribe

The unsubscribe method is used to unsubscribe to controller events. The same events from subscribe are supported

List of unsubscribe midGroups

op.unsubscribe(midGroup, opts, callback);
op.unsubscribe("lastTightening", (err, data) => {
    if (err) {
        console.log("Error on unsubscribing to 'lastTightening'", err);
        return;
    }

    console.log("Unsubscribed to 'lastTightening'");
});

Request

The request method is used to request informations from the controller. request MIDs normally have a conterpart MID for the reply containing the requested data, that can here be received in the callback or the Promise.

op.request(midGroup, opts, callback);
op.request("readTimeUpload", (err, data) => {
    if (err) {
        console.log("Error getting current time of the controller", err);
        return;
    }

    console.log("Controller clock is", data.payload);
});

Command

The command method is used to perform various actions on the controller.

op.command(midGroup, opts, callback);
op.command("disableTool", (err, data) => {
    if (err) {
        console.log("Error disabling the tool", err);
        return;
    }

    console.log("Tool disabled successfully");
});

SendMid

Method responsible for making a generic call, here it is possible to send a not implemented MID. If only midNumber is passed, the sent message will only contain default values and revision = 1, if you need to pass revision or others parameters use the opts parameter. The callback function is the function called in cases of error, passing the error as a parameter. The incoming data from these calls will be then emitted as events, that can be listened to by using the on method.

op.sendMid(midNumber, opts, callback);
op.on("data", (data) => {
    console.log("Data received", data);        
});

op.sendMid(1, (err) => {

    if (err) {
        console.log("Error", err);
        return;
    }

});

Example

This example shows how to perform a subscribe in lastTightening and make a tightening call.

const openProtocol = require('node-open-protocol');

let op = openProtocol.createClient(4545, "127.0.0.1", () => {
    console.log("Connected!");

    op.subscribe("lastTightening", (err, data) => {
        if (err) {
            return console.log("Error on Subscribe", err);
        }

        startTightening(1, "ASDEDCUHBG34563EDFRCVGFR6");
    });
});

op.on("error", (error) => {
    console.log("Error on OpenProtocol", error);
});

op.on("lastTightening", (midData) => {
    console.log("Tightening received!", JSON.stringify(midData));
});

function startTightening(parameterSetID, numberVIN) {
    // --> Abort Job --> Select Pset --> Set VehicleId --> Disable Tool --> Enable Tool

    op.command("abortJob", (err) => {
        if (err) {
            return console.log("Fail on abortJob", err);
        }

        op.command("selectPset", { payload: { parameterSetID } }, (err) => {

            if (err) {
                return console.log("Fail on selectPset", err);
            }

            op.command("vinDownload", { payload: { numberVIN } }, (err) => {

                if (err) {
                    return console.log("Fail on vinDownload", err);
                }

                op.command("disableTool", (err, data) => {

                    if (err) {
                        return console.log("Fail on disableTool", err);
                    }

                    op.command("enableTool", (err, data) => {

                        if (err) {
                            return console.log("Fail on enableTool", err);
                        }

                        console.log("waiting for the operator to tighten");
                    });
                });
            });
        });
    });
}

Or then with async/await functions:

const openProtocol = require('node-open-protocol');

async function onClientConnected() {
    console.log("Connected!");

    // subscribe to tightening results
    await op.subscribe("lastTightening");
    op.on("lastTightening", result => {
        console.log("Hooray! Result received!", result);
    });

    // start tigtening process
    // --> Abort Job --> Select Pset --> Set VehicleId --> Disable Tool --> Enable Tool
    let pset = 1;
    let vin = "ASDEDCUHBG34563EDFRCVGFR6";
    await op.command("abortJob");
    await op.command("selectPset", { payload: { parameterSetID: pset } });
    await op.command("vinDownload", { payload: { numberVIN: vin } });
    await op.command("disableTool");
    await op.command("enableTool");

    console.log(`Tightening started on VIN [${vin}] with pset [${pset}]`);
}

let op = openProtocol.createClient(4545, "127.0.0.1", data => {
    onClientConnected().catch(err => {
        console.log("¡Ay caramba!", err.toString());
    });
});

Controllers supported

The marked controllers have been tested to some degree, so we can assure basic communication support.

  • Atlas Copco PowerFocus4000
  • Atlas Copco PowerFocus6000
  • Atlas Copco PowerMacs
  • Stanley Alpha 4

Please contact us by sending an e-mail to [email protected] if you'd like to have your controller tested and validated by us and to appear on this list.

Disclaimer

  • This software is not affiliated with Atlas Copco in any way. PowerFocus4000, PowerFocus6000, and PowerMacs are trademarks of Atlas Copco AB.

  • This software is not affiliated with Stanley in any way. Alpha 4 are trademarks of Stanley Black & Decker, Inc.

Contributing

For contributing see instructions in CONTRIBUTING.md

License

Copyright: (c) 2018-2020, Smart-Tech

GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)

node-open-protocol's People

Contributors

diegorampim avatar ferm10n avatar gfcittolin avatar hugueds avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

node-open-protocol's Issues

LinkLayer causes timeout waiting for an ack to an ack

My team encountered this problem a few months ago while working with a PowerMACS 4000, which didn't support acknowledgement via sequence numbers. I've been meaning to get around to opening issues for the things we found and push up my changes!

Why the timeout happens:

  • SessionControlClient.useLinkLayer is false
  • A data MID is received, such as 0106
  • Because useLinkLayer is false, the appropriate ack MID is returned (0108)
  • Whenever LinkLayer sends a MID in _onDataMidSerializer(), it normally sets a timeout UNLESS the mid being sent is a POSITIVE_ACK or NEGATIVE_ACK
  • The timeout is set so the MID can be resent, if it is not acknowledged
  • However, an ack MID like 0108 is passed to _onDataMidSerializer, the timer for _resentMid is set
  • Heartbeat messages keeping the connection alive are not able to be sent, because the LinkLayer is stuck waiting for an ack to an ack, and the connection dies

node version: v10.15.1
node-open-protocol version: 1.0.3

Connection Fails for Rexroth Nexo Nutrunner

Running the example code from here, the tool briefly shows connection then disconnects without triggering the connection callback.

Output of NODE_DEBUG='open-protocol' node test.js:

OPEN-PROTOCOL 761: new SessionControlClient
OPEN-PROTOCOL 761: new LinkLayer {
  stream: <ref *1> Socket {
    connecting: true,
    _hadError: false,
    _parent: null,
    _host: null,
    _readableState: ReadableState {
      objectMode: false,
      highWaterMark: 16384,
      buffer: BufferList { head: null, tail: null, length: 0 },
      length: 0,
      pipes: [],
      flowing: null,
      ended: false,
      endEmitted: false,
      reading: false,
      sync: true,
      needReadable: false,
      emittedReadable: false,
      readableListening: false,
      resumeScheduled: false,
      errorEmitted: false,
      emitClose: false,
      autoDestroy: false,
      destroyed: false,
      errored: null,
      closed: false,
      closeEmitted: false,
      defaultEncoding: 'utf8',
      awaitDrainWriters: null,
      multiAwaitDrain: false,
      readingMore: false,
      decoder: null,
      encoding: null,
      [Symbol(kPaused)]: null
    },
    _events: [Object: null prototype] {
      end: [Function: onReadableStreamEnd],
      connect: [Function],
      timeout: [Function]
    },
    _eventsCount: 3,
    _maxListeners: undefined,
    _writableState: WritableState {
      objectMode: false,
      highWaterMark: 16384,
      finalCalled: false,
      needDrain: false,
      ending: false,
      ended: false,
      finished: false,
      destroyed: false,
      decodeStrings: false,
      defaultEncoding: 'utf8',
      length: 0,
      writing: false,
      corked: 0,
      sync: true,
      bufferProcessing: false,
      onwrite: [Function: bound onwrite],
      writecb: null,
      writelen: 0,
      afterWriteTickInfo: null,
      buffered: [],
      bufferedIndex: 0,
      allBuffers: true,
      allNoop: true,
      pendingcb: 0,
      prefinished: false,
      errorEmitted: false,
      emitClose: false,
      autoDestroy: false,
      errored: null,
      closed: false,
      closeEmitted: false,
      writable: true
    },
    allowHalfOpen: false,
    _sockname: null,
    _pendingData: null,
    _pendingEncoding: '',
    server: null,
    _server: null,
    timeout: 20000,
    [Symbol(async_id_symbol)]: 2,
    [Symbol(kHandle)]: TCP {
      reading: false,
      onconnection: null,
      [Symbol(owner_symbol)]: [Circular *1]
    },
    [Symbol(kSetNoDelay)]: false,
    [Symbol(lastWriteQueueSize)]: 0,
    [Symbol(timeout)]: Timeout {
      _idleTimeout: 20000,
      _idlePrev: [TimersList],
      _idleNext: [TimersList],
      _idleStart: 487,
      _onTimeout: [Function: bound ],
      _timerArgs: undefined,
      _repeat: null,
      _destroyed: false,
      [Symbol(refed)]: false,
      [Symbol(kHasPrimitive)]: false,
      [Symbol(asyncId)]: 4,
      [Symbol(triggerId)]: 1
    },
    [Symbol(kBuffer)]: null,
    [Symbol(kBufferCb)]: null,
    [Symbol(kBufferGen)]: null,
    [Symbol(kCapture)]: false,
    [Symbol(kBytesRead)]: 0,
    [Symbol(kBytesWritten)]: 0
  },
  timeOut: 3000,
  retryTimes: 3,
  rawData: false,
  disableMidParsing: {}
}
OPEN-PROTOCOL 761: new OpenProtocolParser
OPEN-PROTOCOL 761: new openProtocolSerializer
OPEN-PROTOCOL 761: new MIDParser
OPEN-PROTOCOL 761: new MIDSerializer
OPEN-PROTOCOL 761: SessionControlClient connect
OPEN-PROTOCOL 761: SessionControlClient sendMidOne
OPEN-PROTOCOL 761: LinkLayer _write { mid: 1, revision: 6 }
OPEN-PROTOCOL 761: MIDSerializer _transform { mid: 1, revision: 6 }
OPEN-PROTOCOL 761: LinkLayer _onDataMidSerializer { mid: 1, revision: 6, payload: <Buffer > }
OPEN-PROTOCOL 761: openProtocolSerializer _transform { mid: 1, revision: 6, payload: <Buffer > }
OPEN-PROTOCOL 761: openProtocolSerializer _transform publish <Buffer 30 30 32 30 30 30 30 31 30 30 36 30 30 31 30 31 30 30 30 30 00>
OPEN-PROTOCOL 761: LinkLayer _onDataOpSerializer <Buffer 30 30 32 30 30 30 30 31 30 30 36 30 30 31 30 31 30 30 30 30 00>
OPEN-PROTOCOL 761: LinkLayer _read 16
OPEN-PROTOCOL 761: LinkLayer _onDataStream <Buffer 30 30 32 36 30 30 30 34 30 30 31 30 30 30 30 30 30 30 30 30 30 30 30 31 39 37 00>
OPEN-PROTOCOL 761: OpenProtocolParser _transform <Buffer 30 30 32 36 30 30 30 34 30 30 31 30 30 30 30 30 30 30 30 30 30 30 30 31 39 37 00>
OPEN-PROTOCOL 761: LinkLayer _onDataOpParser {
  mid: 4,
  revision: 1,
  noAck: false,
  stationID: 1,
  spindleID: 1,
  sequenceNumber: 0,
  messageParts: 0,
  messageNumber: 0,
  payload: <Buffer 30 30 30 31 39 37>
}
OPEN-PROTOCOL 761: new MIDParser _transform {
  mid: 4,
  revision: 1,
  noAck: false,
  stationID: 1,
  spindleID: 1,
  sequenceNumber: 0,
  messageParts: 0,
  messageNumber: 0,
  payload: <Buffer 30 30 30 31 39 37>
}
OPEN-PROTOCOL 761: LinkLayer _onDataMidParser {
  mid: 4,
  revision: 1,
  noAck: false,
  stationID: 1,
  spindleID: 1,
  sequenceNumber: 0,
  messageParts: 0,
  messageNumber: 0,
  payload: { midNumber: 1, errorCode: 97 }
}
OPEN-PROTOCOL 761: SessionControlClient receivedReply {
  mid: 4,
  revision: 1,
  noAck: false,
  stationID: 1,
  spindleID: 1,
  sequenceNumber: 0,
  messageParts: 0,
  messageNumber: 0,
  payload: { midNumber: 1, errorCode: 97 }
}
OPEN-PROTOCOL 761: LinkLayer finishCycle undefined
OPEN-PROTOCOL 761: SessionControlClient sendMidOne
OPEN-PROTOCOL 761: LinkLayer _write { mid: 1, revision: 5 }
OPEN-PROTOCOL 761: MIDSerializer _transform { mid: 1, revision: 5 }
OPEN-PROTOCOL 761: LinkLayer _onDataMidSerializer { mid: 1, revision: 5, payload: <Buffer > }
OPEN-PROTOCOL 761: openProtocolSerializer _transform { mid: 1, revision: 5, payload: <Buffer > }
OPEN-PROTOCOL 761: openProtocolSerializer _transform publish <Buffer 30 30 32 30 30 30 30 31 30 30 35 30 30 31 30 31 30 30 30 30 00>
OPEN-PROTOCOL 761: LinkLayer _onDataOpSerializer <Buffer 30 30 32 30 30 30 30 31 30 30 35 30 30 31 30 31 30 30 30 30 00>
OPEN-PROTOCOL 761: LinkLayer _read 16
OPEN-PROTOCOL 761: SessionControlClient stream_close
OPEN-PROTOCOL 761: SessionControlClient close Error: Stream Close
    at Socket.<anonymous> (/mnt/c/Users/me1rjco/node_modules/node-open-protocol/src/sessionControlClient.js:217:24)
    at Socket.emit (events.js:315:20)
    at TCP.<anonymous> (net.js:673:12)
OPEN-PROTOCOL 761: LinkLayer _destroy

LinkLayer _destroy implementation prevents error propagation and proper teardown

In order for error propagation to work, the _destroy function must call the given callback with the error. This is why these tests fail:

These tests will finish when the error event is emitted, but it never comes.

fs library issue

Hi...
Thanks for the great library . i had tried this in node js.
It is working fine.
But my main intention is i want to use this library in react native. since i am developing mobile application in react native.
I could able to add the library to react native project using ''npm'', and i am able to see in 'package.json' file under dependencies.

"dependencies": {
"node-open-protocol": "^1.1.1",
"react": "16.11.0",
"react-native": "0.62.2"}

After spending so much of time in this final i could able to run this library. But there is small issue now. from "fs" library it's throwing, few methods it's not able to find .
here i am attaching the error. Kindly help me on this.
Screenshot_20200709-161215

Thanks in advance!!

Fix test on Open Protocol Parser

this unit test is failing:

it('should emit an error when input is not a buffer', (done) => {
let parser = new OpenProtocolParser();
parser.on('error', (err) => {
expect(err).to.be.an('error');
done();
});
parser.write({});
});

This is because nodejs will throw the error synchronously, instead of emitting an error event (as this test expects).

Side thought: I'm not even sure this test is needed, since it's basically testing nodejs internals.

Desoutter connection

I'm trying to make a connection with a Desoutter tool, but I get this error:

Error: [Session Control Client] [Connect] negative acknowledge, MID[1], Error [Command failed]
    at receivedReply (/open-protocol-tests/node_modules/node-open-protocol/src/sessionControlClient.js:321:29)
    at LinkLayer.ll.on (/open-protocol-tests/node_modules/node-open-protocol/src/sessionControlClient.js:400:38)
    at emitOne (events.js:115:13)
    at LinkLayer.emit (events.js:210:7)
    at addChunk (_stream_readable.js:263:12)
    at readableAddChunk (_stream_readable.js:250:11)
    at LinkLayer.Readable.push (_stream_readable.js:208:10)
    at LinkLayer._onDataMidParser (/open-protocol-tests/node_modules/node-open-protocol/src/linkLayer.js:324:19)
    at MIDParser.LinkLayer.midParser.on (/open-protocol-tests/node_modules/node-open-protocol/src/linkLayer.js:106:50)
    at emitOne (events.js:115:13)

I have this simple code running:

let op = openProtocol.createClient(port, ip, (data) => {
    console.log(data);
});

On the controller itself there is a message:
'Open Protocol lost connection TU1'

Any ideas? How can we help debug this?

This library will support to react native or not ?

Hi...
Thanks for the great library . i had tried this in node js.
It is working fine.
But my main intention is i want to use this library in react native. since i am developing mobile application in react native.
I could able to add the library to react native project using ''npm'', and i am able to see in 'package.json' file under dependencies.

"dependencies": {
"node-open-protocol": "^1.1.1",
"react": "16.11.0",
"react-native": "0.62.2"}

late, when i run my code i am getting error like library not found. Could you please help on this.

Thanks in advance!!

Check error ENETUNREACH

  • After this error the node not retry connection.
  • This error is not treated.
30 Aug 17:25:47 - [info] Started flows
30 Aug 17:25:47 - [error] [op node:aa869f5d.ad70f] error on requestError: service unavailable
30 Aug 17:25:47 - [error] [op node:d52517aa.1a1018] error on requestError: service unavailable
30 Aug 17:25:47 - [error] [op node:d52517aa.1a1018] error on requestError: service unavailable
30 Aug 17:25:47 - [error] [op config:OP234] Error: connect ENETUNREACH 192.168.0.33:4545 - Local (0.0.0.0:0)
(node:3543) UnhandledPromiseRejectionWarning: Error: service unavailable
    at SessionControlClient.close (/home/guilherme/.node-red/node_modules/node-open-protocol/src/sessionControlClient.js:411:21)
    at OpenProtocolConfig.node.on (/home/guilherme/.node-red/node_modules/node-red-contrib-open-protocol/red/openProtocol.js:201:21)
    at OpenProtocolConfig.Node.close (/home/guilherme/test/red/node_modules/node-red/red/runtime/nodes/Node.js:102:22)
    at /home/guilherme/test/red/node_modules/node-red/red/runtime/nodes/flows/Flow.js:166:47
    at init (/home/guilherme/test/red/node_modules/when/lib/makePromise.js:39:5)
    at new Promise (/home/guilherme/test/red/node_modules/when/lib/makePromise.js:27:53)
    at Function.promise (/home/guilherme/test/red/node_modules/when/when.js:97:10)
    at /home/guilherme/test/red/node_modules/node-red/red/runtime/nodes/flows/Flow.js:163:38
    at init (/home/guilherme/test/red/node_modules/when/lib/makePromise.js:39:5)
    at new Promise (/home/guilherme/test/red/node_modules/when/lib/makePromise.js:27:53)
    at Function.promise (/home/guilherme/test/red/node_modules/when/when.js:97:10)
    at /home/guilherme/test/red/node_modules/node-red/red/runtime/nodes/flows/Flow.js:158:34
    at init (/home/guilherme/test/red/node_modules/when/lib/makePromise.js:39:5)
    at new Promise (/home/guilherme/test/red/node_modules/when/lib/makePromise.js:27:53)
    at Function.promise (/home/guilherme/test/red/node_modules/when/when.js:97:10)
    at Flow.stop (/home/guilherme/test/red/node_modules/node-red/red/runtime/nodes/flows/Flow.js:127:21)
(node:3543) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
(node:3543) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:3543) UnhandledPromiseRejectionWarning: Error: service unavailable
    at SessionControlClient.close (/home/guilherme/.node-red/node_modules/node-open-protocol/src/sessionControlClient.js:411:21)
    at OpenProtocolConfig.node.on (/home/guilherme/.node-red/node_modules/node-red-contrib-open-protocol/red/openProtocol.js:201:21)
    at OpenProtocolConfig.Node.close (/home/guilherme/test/red/node_modules/node-red/red/runtime/nodes/Node.js:102:22)
    at /home/guilherme/test/red/node_modules/node-red/red/runtime/nodes/flows/Flow.js:166:47
    at init (/home/guilherme/test/red/node_modules/when/lib/makePromise.js:39:5)
    at new Promise (/home/guilherme/test/red/node_modules/when/lib/makePromise.js:27:53)
    at Function.promise (/home/guilherme/test/red/node_modules/when/when.js:97:10)
    at /home/guilherme/test/red/node_modules/node-red/red/runtime/nodes/flows/Flow.js:163:38
    at init (/home/guilherme/test/red/node_modules/when/lib/makePromise.js:39:5)
    at new Promise (/home/guilherme/test/red/node_modules/when/lib/makePromise.js:27:53)
    at Function.promise (/home/guilherme/test/red/node_modules/when/when.js:97:10)
    at /home/guilherme/test/red/node_modules/node-red/red/runtime/nodes/flows/Flow.js:158:34
    at init (/home/guilherme/test/red/node_modules/when/lib/makePromise.js:39:5)
    at new Promise (/home/guilherme/test/red/node_modules/when/lib/makePromise.js:27:53)
    at Function.promise (/home/guilherme/test/red/node_modules/when/when.js:97:10)
    at Flow.stop (/home/guilherme/test/red/node_modules/node-red/red/runtime/nodes/flows/Flow.js:127:21)
(node:3543) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
(node:3543) UnhandledPromiseRejectionWarning: Error: service unavailable
    at SessionControlClient.close (/home/guilherme/.node-red/node_modules/node-open-protocol/src/sessionControlClient.js:411:21)
    at OpenProtocolConfig.node.on (/home/guilherme/.node-red/node_modules/node-red-contrib-open-protocol/red/openProtocol.js:201:21)
    at OpenProtocolConfig.Node.close (/home/guilherme/test/red/node_modules/node-red/red/runtime/nodes/Node.js:102:22)
    at /home/guilherme/test/red/node_modules/node-red/red/runtime/nodes/flows/Flow.js:166:47
    at init (/home/guilherme/test/red/node_modules/when/lib/makePromise.js:39:5)
    at new Promise (/home/guilherme/test/red/node_modules/when/lib/makePromise.js:27:53)
    at Function.promise (/home/guilherme/test/red/node_modules/when/when.js:97:10)
    at /home/guilherme/test/red/node_modules/node-red/red/runtime/nodes/flows/Flow.js:163:38
    at init (/home/guilherme/test/red/node_modules/when/lib/makePromise.js:39:5)
    at new Promise (/home/guilherme/test/red/node_modules/when/lib/makePromise.js:27:53)
    at Function.promise (/home/guilherme/test/red/node_modules/when/when.js:97:10)
    at /home/guilherme/test/red/node_modules/node-red/red/runtime/nodes/flows/Flow.js:158:34
    at init (/home/guilherme/test/red/node_modules/when/lib/makePromise.js:39:5)
    at new Promise (/home/guilherme/test/red/node_modules/when/lib/makePromise.js:27:53)
    at Function.promise (/home/guilherme/test/red/node_modules/when/when.js:97:10)
    at Flow.stop (/home/guilherme/test/red/node_modules/node-red/red/runtime/nodes/flows/Flow.js:127:21)

Controller Timeout

Having a super strange bug.. My connection will send a MID 1 -> Return a MID 2 with no problem. But if I send a Mid 12 with a payload of '002' to get the parameterSetID it fails with a controller internal request timeout. From what I see the code sends identical messages as the tools software.. Anyone experience this before?

I was thinking we were not sending the NULL character at the end of the message but it appears that it is wired correctly.

Error:  Error: [Session Control Client] negative acknowledge, MID[12], Error[Controller internal request timeout]
    at SessionControlClient._receiverData (/Users/e346901/Projects/GitLab/node-open-protocol/src/sessionControlClient.js:1056:19)
    at SessionControlClient._onDataLinkLayer (/Users/e346901/Projects/GitLab/node-open-protocol/src/sessionControlClient.js:999:10)
    at LinkLayer.<anonymous> (/Users/e346901/Projects/GitLab/node-open-protocol/src/sessionControlClient.js:363:41)
    at LinkLayer.emit (events.js:200:13)
    at addChunk (_stream_readable.js:294:12)
    at readableAddChunk (_stream_readable.js:275:11)
    at LinkLayer.Readable.push (_stream_readable.js:210:10)
    at LinkLayer._onDataMidParser (/Users/e346901/Projects/GitLab/node-open-protocol/src/linkLayer.js:333:15)
    at MIDParser.<anonymous> (/Users/e346901/Projects/GitLab/node-open-protocol/src/linkLayer.js:110:44)
    at MIDParser.emit (events.js:200:13)

Support for MID900

Hello,

I have a machine in my lab. If someone helps we can work on it.
I have tried several combinations of MID0008 with MID0900 but does not work.

Regards,

Controller internal request timeout

Hello, I can't disconnect from the tool, MID0044 'disconnectTool'. I want to execute this command because when I launch 5 screwdriving commands everything works fine and when I send the 6th I have a busy protocol! So I don't know if this is the right method. It's my app that sends the data to my API, which makes the connection with the controller.

Error: [Session Control Client] negative acknowledge, MID[44], Error[Controller internal request timeout] at SessionControlClient._receiverData (D:\wamp64\www\Projet_Paperless\api_modop_v2\node_modules\node-open-protocol-desoutter\src\sessionControlClient.js:1057:23) at SessionControlClient._onDataLinkLayer (D:\wamp64\www\Projet_Paperless\api_modop_v2\node_modules\node-open-protocol-desoutter\src\sessionControlClient.js:1000:14) at LinkLayer.<anonymous> (D:\wamp64\www\Projet_Paperless\api_modop_v2\node_modules\node-open-protocol-desoutter\src\sessionControlClient.js:363:51) at LinkLayer.emit (node:events:514:28) at addChunk (node:internal/streams/readable:324:12) at readableAddChunk (node:internal/streams/readable:297:9) at Readable.push (node:internal/streams/readable:234:10) at LinkLayer._onDataMidParser (D:\wamp64\www\Projet_Paperless\api_modop_v2\node_modules\node-open-protocol-desoutter\src\linkLayer.js:326:19) at MIDParser.<anonymous> (D:\wamp64\www\Projet_Paperless\api_modop_v2\node_modules\node-open-protocol-desoutter\src\linkLayer.js:111:50) at MIDParser.emit (node:events:514:28)

Some functions of my code:
let options = {};

const connectToServer = (ip, port, pset, number_screw) => {

 op = openProtocol.createClient(port, ip, options, (data) => {
     console.log('Connected');
     setupCommands(op, pset, number_screw);
 });

 return op;
};

const setupCommands = (op, pset, number_screw) => {
 op.command("abortJob");
 op.command("selectPset", { payload: { parameterSetID:pset } }, (err) => {

     if (err) {
         return console.log("Fail on selectPset", err);
     }
     
     op.command('setPsetBatchSize', { payload: {parameterSetID: pset, batchSize: number_screw} }, (err, data) => {
         if (err) {
             return console.error('Error in setPsetBatchSize:', err);
         }
         enableTool(op);
     });
 })


};

const enableTool = (op) => {
 op.command("disableTool", (err, data) => {
     if (err) {
         return console.log("Fail on disableTool", err);
     }
     op.command("enableTool", (err, data) => {
         if (err) {
             return console.error("Error in enableTool:", err);
         }

         subscribeLastTightening(op); // Subscribe to the lastTightening event after enabling the tool
     });
 })
};

const subscribeLastTightening = (op) => {
 op.on("lastTightening", (midData) => {
     console.log("Received data on subscribe", midData);

     if(midData.payload.batchCounter === 1 && midData.payload._batchStatus === 1) {
         lastVissageData = null; // Reset the last tightening data after the first screw has been tightened
     }
     
     lastVissageData = midData;
     updateProductionData(op, idProductionData, midData.payload);
 });

 op.subscribe("lastTightening", (err, data) => {
     if (err) {
         console.error("Error subscribing to 'lastTightening':", err);
         return;
     }

     console.log("Subscribed to 'lastTightening'");
     console.log("Data received on subscribe", data);

 })
};

exports.changePset = (req, res) => {
 const { ip, port, pset, number_screw, id } = req.query;
 idProductionData = id;
 connectToServer(ip, port, pset, number_screw); // Connect to the controller
};

exports.latestScrewing = (req, res) => {
 try {
     if (lastVissageData) {
         res.json(lastVissageData);
     } else {
         res.status(204).send("Waiting"); // 
     }
 } catch (error) {
     console.error("Error :", error);
     res.status(500).send("An error occurred while processing the request.");
 }
};

exports.resetScrewingData = (req, res) => {

 if (op) { // Vérifier si op est défini
     console.log('Closing connection...');
     //op.close();
     /*op.command("disableTool", (err, data) => {
         if (err) {
             return console.error('Error in disableTool:', err);
         }
         console.log('Tool disabled');

         op.command("abortJob", (err, data) => {
             if (err) {
                 return console.error('Error in abortJob:', err);
             }
             console.log('Job aborted');

             op.command("communicationStop", (err, data) => {
                 if (err) {
                     return console.error('Error in communicationStop:', err);
                 }
                 console.log('Communication stopped');*/

                 op.command("disconnectTool", (err, data) => {
                     if (err) {
                         return console.error('Error in disconnect:', err);
                     }
                     console.log('Connection closed');
                 });
             /*});
         });
     });*/
 } else {
     console.log('Connection already closed or not established');
     res.status(400).send("Connection already closed or not established");
 }

 lastVissageData = null;
 idProductionData = null;
 res.send(true);
};```

Do you have any solutions ?

MID0064

Hi,

Thank you very much for this NodeJS library. I am trying to get the response for MID0064 oldTighteningResultUpload. But not getting reponse from controller. Below is the code: Is it correct?.

`const openProtocol = require('node-open-protocol');

let op = openProtocol.createClient(4545, "127.0.0.1", () => {
console.log("Connected!");

tighteningID=""
let opts = {
    tighteningID
};

op.request("oldTighteningResultUpload", opts, (err, data) => {
    //onCallback("Request", "oldTighteningResultUpload", err, data);
    if (err) {
        console.log("Error getting old tightening request from the controller", err);
        return;
    }
    console.log("Old Tightening Response", data.payload);
});

});

op.on("error", (error) => {
console.log("Error on OpenProtocol", error);
});
`

Support for MID900

Hello,

I have a machine in my lab. If someone helps we can work on it.
I have tried several combinations of MID0008 with MID0900 but does not work.

Regards,

Add debug mode

Currently only some classes have debug, add debug to all classes.

Reconnection when return an error

I have a problem whenever any of connected tools is shutdown.
I get an error message and can't deal with the bug.

Error: [Session Control Client] [Connect] negative acknowledge, MID[1], Error [Command failed]

This error closes the service even if I put it in a "try catch" to deal with it. Can anything be done to deal with the error?

Thank you!

Sequence Break after Alarm Subscription

After subscribing to the alarm event (MID 70) if another call is made without waiting there will be an out of sequence error that will happen.

This is done using 1.0.3 of this library, with the PF4000 Torque tool, the tool is running open protocol version 2.5, controller software version 10.17.12.SR1.

This code will generate an error after calling the line "await op.subscribe('alarm');", during the "await op.subscribe('psetSelected');" function, the code will end with the error "close Result received! Error: [LinkLayer] sequence number invalid, MID[4]". If "await op.subscribe('alarm');" is not called, the code works properly.

'use strict';

// Create a socket
const openProtocol = require('node-open-protocol');

let options = {
    linkLayerActivate: undefined,
    genericMode: false,
    keepAlive: 1000,
    rawData: false,
    timeOut: 3000,
    retryTimes: 3,
    disableMidParsing: {}
};

let createClient = async function (port, ip, opt) {
  let run = false; //Run Once Var
  return new Promise ((resolve, reject) => {
      let op = openProtocol.createClient(port, ip, opt, () => {
          if (run) return;
          run = true;
          resolve(op);
      });
      op.on('error', () => {  });
      op.on('close', err => {
          if (!run) {
              run = true;
              return reject(err);
          }
      });
  })
};

async function onClientConnected(port, ip, opt) {
    let op = await createClient(port, ip, opt);
    op.on('close', (stat) => {
        console.log(`close Result received!`, stat);
    });
    await op.subscribe('lastTightening');
    await op.subscribe('alarm');
    await op.subscribe('psetSelected');
    await op.command('disableTool');
    return op.close();
}

onClientConnected(4545, '10.1.203.133', options).catch( err => {
  console.log('Error happened', err);
}).then(() =>{
  console.log('Running');
},err =>{
    console.log('Failed!', err);
});

This is the same code, but with a slight delay using 'setTimeoutAsync' between the alarm subscription and a call to subscribe to pset events, this code works properly with our tool and will receive alarm events.

'use strict';

// Create a socket
const openProtocol = require('node-open-protocol');

let options = {
    linkLayerActivate: undefined,
    genericMode: false,
    keepAlive: 1000,
    rawData: false,
    timeOut: 3000,
    retryTimes: 3,
    disableMidParsing: {}
};

let createClient = async function (port, ip, opt) {
  let run = false; //Run Once Var
  return new Promise ((resolve, reject) => {
      let op = openProtocol.createClient(port, ip, opt, () => {
          if (run) return;
          run = true;
          resolve(op);
      });
      op.on('error', () => {  });
      op.on('close', err => {
          if (!run) {
              run = true;
              return reject(err);
          }
      });
  })
};

let setTimeoutAsync = t => new Promise(res=>setTimeout(res, t));

async function onClientConnected(port, ip, opt) {
    let op = await createClient(port, ip, opt);
    op.on('close', (stat) => {
        console.log(`close Result received!`, stat);
    });
    await op.subscribe('lastTightening');
    await op.subscribe('alarm');
    await setTimeoutAsync(500); //Delay 500 milliseconds
    await op.subscribe('psetSelected');
    await op.command('disableTool');
    return op.close();
}

onClientConnected(4545, '10.1.203.133', options).catch( err => {
  console.log('Error happened', err);
}).then(() =>{
  console.log('Running');
},err =>{
    console.log('Failed!', err);
});

I've capture all the messages sent between the two examples and provide them below

These are the messages sent using the code that generates errors. It should be noted that it is on sequence number 4 that causes the error

Data Out:	00200001006001010000 
Data In:	01790002006     0500010000020003                         04ACT052.5                0610.17.12.SR1       077.16.45            08Silver (Ag)             09D857104   1000111001121130 
Data Out:	00200060007001010100 
Data In:	00249997        02000060 
Data In:	00260004        0100006097 
Data Out:	002499970010010102000004 
Data Out:	00200060006001010200 
Data In:	00249997        03000060 
Data In:	00240005        02000060 
Data Out:	002499970010010103000005 
Data Out:	00200070002001010300 
Data In:	00249997        04000070 
Data In:	00240005        03000070 
Data Out:	002499970010010104000005 
Data Out:	00200014002001010400 
Data In:	00560076        040001002E544031041052018-10-01:15:16:12 
Data Out:	002499970010010105000076 
Data In:	00249997        05000014 
Data In:	00260004        0400001497 
Data Out:	0028999800100101050000040003 
close Result received! Error: [LinkLayer] sequence number invalid, MID[4]
    at LinkLayer._onDataOpParser (/home/timothy/Git/driver-node-torque-tool/node_modules/node-open-protocol/src/linkLayer.js:279:48)
    at OpenProtocolParser.LinkLayer.opParser.on (/home/timothy/Git/driver-node-torque-tool/node_modules/node-open-protocol/src/linkLayer.js:105:49)
    at emitOne (events.js:116:13)
    at OpenProtocolParser.emit (events.js:211:7)
    at addChunk (_stream_readable.js:263:12)
    at readableAddChunk (_stream_readable.js:250:11)
    at OpenProtocolParser.Readable.push (_stream_readable.js:208:10)
    at OpenProtocolParser.Transform.push (_stream_transform.js:147:32)
    at OpenProtocolParser._transform (/home/timothy/Git/driver-node-torque-tool/node_modules/node-open-protocol/src/openProtocolParser.js:245:18)

These are all messages sent in the code that works using the Delay.

Data Out:	00200001006001010000 
Data In:	01790002006     0800010000020003                         04ACT052.5                0610.17.12.SR1       077.16.45            08Silver (Ag)             09D857104   1000111001121130 
Data Out:	00200060007001010100 
Data In:	00249997        02000060 
Data In:	00260004        0100006097 
Data Out:	002499970010010102000004 
Data Out:	00200060006001010200 
Data In:	00249997        03000060 
Data In:	00240005        02000060 
Data Out:	002499970010010103000005 
Data Out:	00200070002001010300 
Data In:	00249997        04000070 
Data In:	00240005        03000070 
Data Out:	002499970010010104000005 
Data In:	00560076        040001002E544031041052018-10-01:15:16:12 
Data Out:	002499970010010105000076 
Data Out:	00200014002001010400 
Data In:	00249997        05000014 
Data In:	00260004        0500001497 
Data Out:	002499970010010106000004 
Data Out:	00200014001001010500 
Data In:	00249997        06000014 
Data In:	00240005        06000014 
Data Out:	002499970010010107000005 
Data Out:	00200042001001010600 
Data In:	00420015        07000012018-09-25:17:32:15 
Data Out:	002499970010010108000015 
Data In:	00249997        07000042 
Data In:	00240005        08000042 
Data Out:	002499970010010109000005 
close Result received! undefined
Running

I spent some time debugging this issue but had to move on. This also seems to have issue with RC Focus tools, but I only had very temporary access to that device and can no longer test against it.
I have gotten around the issue on both devices by just not subscribing to alarms.

If you want more information/clarification or for me to test any fixes, just let me know

Micro Torque Focus 6000 Integration

We are integrating an Atlas Copco MT Focus 6000. I came upon your code and wanted to know if you can validate our controller. I was able to instantiate a connection to our controller but was unable to call the "lastTightening" command. I believe the MID set for "lastTightening" may be different for our controller. Can I just clone the directory, change the json file then run the subscribe function? Please see the attached files for MTF6000 open protocol information.

Files.zip

MIDParser silently errors because _destroy() is a no-op

I'm discovering that if there's an error during parsing, MidParser does not emit it, and the stream is silently destroyed. This might be caused by something I did while tinkering with the project, but it'd really help if I had some context about this section:

_destroy() {
//no-op, needed to handle older node versions
}

It seems to be responsible for why the error gets eaten. If I remove it, the default handler emits the error event correctly.

How did earlier versions of node behave that prompted _destroy() to become a noop? I'd like to fix this without breaking backwards compat, but can't do that without more history on why it was added.

(I'm using node v14 btw)

Help needed

Dear all,

Does this library support creating a simulation of the controller, so in other words can I create a server that sends data to the client we create using this library, is it possible to do so? If yes some help from you would be great on how can I achieve that and if not is is there any available library that allows me to also create a server that acts as the controller ?

Thank you in advance!

Uncaught exception

When using "custom MID" and selecting MID 80 with Power Focus 6000, NR gives an uncaught exception:

capture_2

Apparently it's crashing during the parsing process of the received message.

Mid 0061 toolSerialNumber Miss Type

Solution,
In the file "src/mid/0061.js"
Line 406
I changed toolSerialNumber from type "number" to type "string"

Ran into this while testing it out on an Atlas 4000 series, the tool serial number started with an F and failed to parse into a number triggering a parseError. But after the solution, the serial number came out correctly

Node version "v8.11.2", Node Open Protocol version "1.0.2"

Cannot reopen connection after it is closed

  • The initial value of SessionControlClient.autoRevision is {}
  • When the connection is closed, it is being set to null
  • If the connection is reopened, then errors occur when trying to access the properties of autoRevision

MID 0002 revision 6 is incorrectly implemented

Open Protocol spec defines rev 6 with these new fields:

  • sequenceNumberSupport
  • linkingHandlingSupport
  • stationID
  • stationName
  • clientID

Missing Keys / Fields

The parser is missing the last three keys:

  • processKey(msg, buffer, "linkingHandlingSupport", 13, 2, position, cb) &&
  • payload: {
    cellID: 1,
    channelID: 1,
    controllerName: "Teste Airbag",
    supplierCode: "ABC",
    openProtocolVersion: "AAAAAAAAAAAAAAAAAAA",
    controllerSoftwareVersion: "BBBBBBBBBBBBBBBBBBB",
    toolSoftwareVersion: "CCCCCCCCCCCCCCCCCCC",
    rbuType: "AS254DFCVFDCVGTREDFGHJKL",
    controllerSerialNumber: "123987ASD6",
    systemType: 3,
    systemSubtype: 2,
    sequenceNumberSupport: 0,
    linkingHandlingSupport: 1
    }

Interestingly, the serializer has all the keys:

  • serializerField(msg, buf, "clientID", "number", 1, position, cb) &&
    serializerKey(msg, buf, 16, 2, position, cb) &&
    serializerField(msg, buf, "stationName", "string", 25, position, cb) &&
    serializerKey(msg, buf, 15, 2, position, cb) &&
    serializerField(msg, buf, "stationID", "string", 10, position, cb) &&
    serializerKey(msg, buf, 14, 2, position, cb) &&
    serializerField(msg, buf, "linkingHandlingSupport", "number", 1, position, cb) &&
    serializerKey(msg, buf, 13, 2, position, cb) &&
    serializerField(msg, buf, "sequenceNumberSupport", "number", 1, position, cb) &&
    serializerKey(msg, buf, 12, 2, position, cb);
  • payload: {
    cellID: 1,
    channelID: 1,
    controllerName: "Teste Airbag",
    supplierCode: "ABC",
    openProtocolVersion: "AAAAAAAAAAAAAAAAAAA",
    controllerSoftwareVersion: "BBBBBBBBBBBBBBBBBBB",
    toolSoftwareVersion: "CCCCCCCCCCCCCCCCCCC",
    rbuType: "AS254DFCVFDCVGTREDFGHJKL",
    controllerSerialNumber: "123987ASD6",
    systemType: 3,
    systemSubtype: 2,
    sequenceNumberSupport: 0,
    linkingHandlingSupport: 1,
    stationID: "AESDR56RDT",
    stationName: "QASWWEDXCVFR562 DERF34EDF",
    clientID: 9
    }

I would expect the parser and serializer to behave as inverses to each other.

Wrong type for stationID

Additionally, the type for stationID is also wrong. The official protocol says it's supposed to be a number, but node-open-protocol tries to interpret it as a string!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.