Psychopoulet / split-frames

Split Buffer frames from streams
ISC License
5 stars 3 forks source link

split-frames

Split Buffer frames from streams

Build Status Coverage Status Dependency Status Dev dependency Status Issues Pull requests

Installation

$ npm install split-frames

Doc

Works very well with, for example, serialport for industrial protocols like Concert

/!\ this version only split frames properly, and does not remove start & end tags or escape bits anymore.

Methods

Options

All these options are optionnal

type ControlBits = "none" | "end+1" | "end+2";
type Tag: number | Buffer | Array< number | Buffer >

"startTimeout" is a timeout (in milliseconds) which end frame in "start only" mode, if no second "start" bit is encountered after the first one

"specifics" is a [ key: string => value: Tag ] object which fire a "key" event when a "value" tag is found out of the message and not escaped ex : { "specifics": { "nak": 0x25 } } will fire an "nak" event when 0x25 bit is encountered

Examples

In the following exemples, "Splitter" and "Readable" classes are defined like that :

// typescript
import Splitter = require("split-frames");
import { Readable } from "stream";
// javascript
const Splitter = require("split-frames");
const { Readable } = require("stream");

The "createReadStream" function is defined like that :

function createReadStream () {
    return new Readable({
        read () { }
    });
}

And the "STX", "DLE" and "ETX" constants are defined like that :

const STX = 0x02, ETX = 0x03, DLE = 0x10, ACK = 0x06, NAK = 0x15, WAK = 0x13;

want to split frames content on a start bit ?

const stream = createReadStream();

stream.pipe(new Splitter({
    "startWith": STX
})).on("data", (chunk) => {
    // Buffer([ STX, 0x24, 0x25, 0x26 ])
    // Buffer([ STX, 0x24, 0x25 ])
});

stream.push(Buffer.from([ 0x20, STX, 0x24, 0x25, 0x26, STX, 0x24 ]));
stream.push(Buffer.from([ 0x25, STX ]));

prefere an end bit ?

const stream = createReadStream();

stream.pipe(new Splitter({
    "endWith": ETX
})).on("data", (chunk) => {
    // Buffer([ 0x24, 0x25, 0x26, ETX ])
    // Buffer([ 0x24, 0x25, ETX ])
});

stream.push(Buffer.from([ 0x24, 0x25, 0x26, ETX, 0x24, 0x25 ]));
stream.push(Buffer.from([ ETX ]));

want both ?

const stream = createReadStream();

stream.pipe(new Splitter({
    "startWith": STX, "endWith": ETX
})).on("data", (chunk) => {
    // Buffer([ STX, 0x24, 0x25, 0x26, ETX ])
    // Buffer([ STX, 0x24, 0x25, ETX ])
});

stream.push(Buffer.from([ 0x20, STX, 0x24, 0x25, 0x26, ETX, 0x24, 0x25, STX ]));
stream.push(Buffer.from([ 0x24, 0x25, ETX ]));

what about two end bits (works with start one as well) ?

const stream = createReadStream();

stream.pipe(new Splitter({
    "startWith": STX, "endWith": Buffer.from([ DLE, ETX ])
})).on("data", (chunk) => {
    // Buffer([ STX, 0x24, 0x25, 0x26, DLE, ETX ])
});

stream.push(Buffer.from([ 0x20, STX, 0x24, 0x25, 0x26, DLE, ETX, STX, 0x24, 0x25 ]));

Do you need to parse escaped bits ?

const stream = createReadStream();

stream.pipe(new Splitter({
    "startWith": STX, "endWith": ETX,
    "escapeWith": DLE, "escaped": [ DLE, STX, ETX ]
})).on("data", (chunk) => {
    // Buffer([ STX, 0x24, DLE, STX, 0x25, 0x26, DLE, DLE, 0x27, DLE, ETX, 0x28, ETX ])
});

stream.push(Buffer.from([ 0x20, STX, 0x24, DLE, STX, 0x25, 0x26 ]));
stream.push(Buffer.from([ DLE, DLE, 0x27, DLE, ETX, 0x28, ETX, STX, 0x24, 0x25 ]));

And what do you think about multiple start (or end !) bits possibilities ?

For even parity in seriaport, for example

const STX2 = 0x82;
const stream = createReadStream();

stream.pipe(new Splitter({
    "startWith": [ STX, STX2 ], "endWith": ETX,
    "escapeWith": DLE, "escaped": [ DLE, STX, ETX ]
})).on("data", (chunk) => {
    // Buffer([ STX, 0x24, DLE, STX, 0x25, 0x26, DLE, DLE, 0x27, DLE, ETX, 0x28, ETX ])
    // Buffer([ STX2, 0x24, DLE, STX, 0x25, 0x26, DLE, DLE, 0x27, DLE, ETX, 0x28, ETX ])
});

stream.push(Buffer.from([ 0x24, STX, 0x24, DLE, STX, 0x25, ACK ]));
stream.push(Buffer.from([ DLE, DLE, 0x27, DLE, ETX, 0x28, ETX, ACK, 0x24, 0x25 ]));
stream.push(Buffer.from([ STX2, 0x24, DLE, STX, 0x25, ACK ]));
stream.push(Buffer.from([ DLE, DLE, 0x27, DLE, ETX, 0x28, ETX, ACK, 0x24, 0x25 ]));

Want to extract specific tags ?

positive acknowledgement, negative acknowledgement, waiting for acknowledgement, whatever... only with no tags || start AND end tags || end tags (for firsts bits)

const stream = createReadStream();

stream.pipe(new Splitter({
    "startWith": STX, "endWith": ETX,
    "specifics": {
        "ack": ACK, "nak": NAK, "wak": WAK, "whatever": 0x51
    },
    "escapeWith": DLE, "escaped": [ DLE, ACK, NAK, WAK ]
})).on("ack", () => {
    console.log("ack received"); // (only 1x) -> good, escaped, in data
}).on("nak", () => {
    console.log("nak received"); // (only 1x) -> in data, good, escaped
}).on("wak", () => {
    console.log("wak received"); // (only 1x) -> in data, good, escaped
}).on("whatever", () => {
    console.log("whatever received"); // (only 1x)
}).on("data", (chunk) => {
    // Buffer([ STX, 0x20, 0x21, 0x22, ACK, NAK, WAK, 0x23, ETX ]) (x1)
});

stream.push(Buffer.from([ 0x51, 0x24, ACK, DLE, ACK, STX, 0x20, 0x21, 0x22, ACK, NAK, WAK ]));
stream.push(Buffer.from([ 0x23, ETX, NAK, DLE, NAK, WAK, DLE, WAK, 0x20, 0x21 ]));

Want to use control bits ?

used to compute LRC, MSB, LSB, etc... this example is for a structure like STX ETX LRC, where LRC compute all bits


function _computeLRC (frame) {

    let lrc = 0x00;

        for (let i = 0; i < frame.length; ++i) {
            lrc ^= frame[i];
        }

    return lrc;

}

const stream = createReadStream();

stream.pipe(new Splitter({
    "startWith": STX, "endWith": ETX,
    "controlBits": "end+1"
})).on("data", (chunk) => {

    // Buffer([ STX, 0x20, 0x21, 0x22, 0x24, ETX, 0x07 ]) (x1)

    const data = chunk.slice(1, chunk.length - 2); // Buffer([ 0x20, 0x21, 0x22, 0x24 ])
    const LRC = chunk[chunk.length - 1];

    if (_computeLRC(data) === LRC) {
        console.log("OK");
    }
    else {
        console.error(new Error("not well-computed data :" + data.toString("hex") + "|" + Buffer.from([ LRC ]).toString("hex")));
    }

});

stream.push(Buffer.from([ 0x51, 0x24, STX, 0x20, 0x21, 0x22, 0x24, ETX, 0x07, 0x24 ]));

Tests

$ npm run-script tests

License

ISC