@mos-connection/connector
Version:
MOS compliant TCP/IP Socket connection.
150 lines • 6.37 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MosMessageParser = void 0;
const eventemitter3_1 = require("eventemitter3");
const helper_1 = require("@mos-connection/helper");
class MosMessageParser extends eventemitter3_1.EventEmitter {
constructor(description) {
super();
this.description = description;
this.dataChunks = '';
this.debug = false;
}
parseMessage(messageString) {
this.debugTrace(`Socket got data (${this.description}): "${messageString}"`);
this.dataChunks += messageString;
// parse as many messages as possible from the data
while (this.dataChunks.length > 0) {
// whitespace before a mos message is junk
this.dataChunks = this.dataChunks.trimStart();
const lengthBefore = this.dataChunks.length;
this._tryParseData();
const lengthAfter = this.dataChunks.length;
if (lengthAfter === lengthBefore) {
// Nothing was plucked, so abort
break;
}
}
}
_tryParseData() {
const startMatch = '<mos>'; // <mos>
const endMatch = '</mos>'; // </mos>
let messageString;
const startIndexes = this.indexesOf(this.dataChunks, startMatch);
if (startIndexes.length === 0) {
// No start tag, so looks like we have jibberish
this.dataChunks = '';
}
else {
const firstStartIndex = startIndexes[0];
if (firstStartIndex > 0) {
const junkStr = this.dataChunks.slice(0, firstStartIndex);
this.debugTrace(`${this.description} Discarding message fragment: "${junkStr}"`);
// trim off anything before <mos>, as we'll never be able to parse that anyway.
this.dataChunks = this.dataChunks.slice(firstStartIndex);
}
const endIndexes = this.indexesOf(this.dataChunks, endMatch);
if (endIndexes.length > 0) {
// We have an end tag too
/** null = message is not complete */
let useEndIndex = null;
if (startIndexes.length === 1 && endIndexes.length === 1) {
// fast-path:
useEndIndex = endIndexes[0];
}
else {
const tags = [
...startIndexes.map((index) => ({
start: true,
index,
})),
...endIndexes.map((index) => ({
start: false,
index,
})),
].sort((a, b) => a.index - b.index);
// Figure out where in the message the end tag closes the start tag:
let tagBalance = 0;
for (const tag of tags) {
if (tag.start)
tagBalance++;
else
tagBalance--;
if (tagBalance < 0) {
// Hmm, something is wrong, there should never be more end tags than start tags
// trim off anything before this end tag, we'll never be able to parse that anyway.
this.dataChunks = this.dataChunks.slice(tag.index + endMatch.length);
break;
}
else if (tagBalance === 0) {
// We have a complete message, so pluck it out
useEndIndex = tag.index;
break;
}
}
}
if (useEndIndex !== null) {
const endIndex2 = useEndIndex + endMatch.length;
messageString = this.dataChunks.slice(0, endIndex2);
this.dataChunks = this.dataChunks.slice(endIndex2);
}
}
}
let parsed = null;
try {
if (messageString) {
const data = (0, helper_1.xml2js)(messageString); // , { compact: true, trim: true, nativeType: true })
if (typeof data.mos !== 'object')
throw Error(`Bad mos message, <mos> missing`);
if (typeof data.mos.mosID !== 'string')
throw Error(`Bad mos message, <mosID> missing`);
if (typeof data.mos.ncsID !== 'string')
throw Error(`Bad mos message, <ncsID> missing`);
if (data.mos.messageID && typeof data.mos.messageID !== 'string')
throw Error(`Bad mos message, <messageID> missing`);
parsed = {
data: data,
messageString,
};
}
}
catch (err) {
// eslint-disable-next-line no-console
console.log('dataChunks-------------\n', this.dataChunks);
// eslint-disable-next-line no-console
console.log('messageString---------\n', messageString);
// this.emit('error', e)
throw err;
}
if (parsed) {
this.emit('message', parsed.data, parsed.messageString);
}
}
debugTrace(str) {
if (this.debug) {
// Suppress console spam:
if (!/<heartbeat>/.exec(`${str}`)) {
// eslint-disable-next-line no-console
console.log(str);
}
}
}
/** Returns a list of indexes for the occurences of searchString in str */
indexesOf(str, searchString) {
if (!searchString.length)
throw new Error('searchString cannot be empty');
const indexes = [];
let prevIndex = 0;
for (let i = 0; i < str.length; i++) {
// ^ Just to avoid an infinite loop
const index = str.indexOf(searchString, prevIndex);
if (index === -1)
break;
indexes.push(index);
prevIndex = index + searchString.length;
}
return indexes;
}
}
exports.MosMessageParser = MosMessageParser;
//# sourceMappingURL=mosMessageParser.js.map