From cab929901d05fe5e960e84d85ba1bf178e21c2eb Mon Sep 17 00:00:00 2001 From: Leonid Meleshin Date: Tue, 27 Feb 2024 23:25:36 +0400 Subject: [PATCH 1/2] feat(Parser): allow left-padded values Closes #20 --- src/msgAggregatorWorker.test.ts | 20 +++++++++++++++++ src/msgAggregatorWorker.ts | 40 ++++++++++++++++++++------------- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/msgAggregatorWorker.test.ts b/src/msgAggregatorWorker.test.ts index 6d14944..b023969 100644 --- a/src/msgAggregatorWorker.test.ts +++ b/src/msgAggregatorWorker.test.ts @@ -101,6 +101,26 @@ describe("Parsing data", () => { ); }); + test("labeled padded", () => { + const messages = [ + `0${trailingFieldDelimiter}${recordDelimiter}`, + `label_1: 1${fieldDelimiter}label_2: 20${trailingFieldDelimiter}${recordDelimiter}`, + `label_1: 300${fieldDelimiter}label_2:4000${trailingFieldDelimiter}${recordDelimiter}`, + ]; + + const assertion = { + datasetNames: ["label_1", "label_2"], + parsedLines: [ + { label_1: 1, label_2: 20 }, + { label_1: 300, label_2: 4000 }, + ], + }; + + expect(messageAggregator.parseSerialMessages(messages)).toEqual( + assertion + ); + }); + test("buffering", () => { // Incomplete record let messages = [ diff --git a/src/msgAggregatorWorker.ts b/src/msgAggregatorWorker.ts index 9b96757..0b98a60 100644 --- a/src/msgAggregatorWorker.ts +++ b/src/msgAggregatorWorker.ts @@ -18,9 +18,9 @@ ctx.addEventListener("message", (event) => { let buffer = ""; let discardFirstLine = true; -const separator = "\r?\n"; +const lineSeparator = "\r?\n"; const delimiter = "[, \t]+"; // Serial Plotter protocol supports Comma, Space & Tab characters as delimiters -var separatorRegex = new RegExp(`(${separator})`, "g"); +var lineSeparatorRegex = new RegExp(`(${lineSeparator})`, "g"); var delimiterRegex = new RegExp(delimiter, "g"); export const parseSerialMessages = ( @@ -33,8 +33,8 @@ export const parseSerialMessages = ( // so we need to discard it and start aggregating from the first encountered separator let joinMessages = messages.join(""); if (discardFirstLine) { - separatorRegex.lastIndex = 0; // Reset lastIndex to ensure match happens from beginning of string - const separatorMatch = separatorRegex.exec(joinMessages); + lineSeparatorRegex.lastIndex = 0; // Reset lastIndex to ensure match happens from beginning of string + const separatorMatch = lineSeparatorRegex.exec(joinMessages); if (separatorMatch && separatorMatch.index > -1) { joinMessages = joinMessages.substring( separatorMatch.index + separatorMatch[0].length @@ -50,14 +50,16 @@ export const parseSerialMessages = ( //add any leftover from the buffer to the first line const messagesAndBuffer = ((buffer || "") + joinMessages) - .split(separatorRegex) + .split(lineSeparatorRegex) .filter((message) => message.length > 0); // remove the previous buffer buffer = ""; - separatorRegex.lastIndex = 0; + lineSeparatorRegex.lastIndex = 0; // check if the last message contains the delimiter, if not, it's an incomplete string that needs to be added to the buffer - if (!separatorRegex.test(messagesAndBuffer[messagesAndBuffer.length - 1])) { + if ( + !lineSeparatorRegex.test(messagesAndBuffer[messagesAndBuffer.length - 1]) + ) { buffer = messagesAndBuffer[messagesAndBuffer.length - 1]; messagesAndBuffer.splice(-1); } @@ -66,10 +68,15 @@ export const parseSerialMessages = ( const parsedLines: { [key: string]: number }[] = []; // for each line, explode variables - separatorRegex.lastIndex = 0; + lineSeparatorRegex.lastIndex = 0; messagesAndBuffer - .filter((message) => !separatorRegex.test(message)) + .filter((message) => !lineSeparatorRegex.test(message)) .forEach((message) => { + // replace all delimiters with a single space for uniform parsing + message = message.replace(delimiterRegex, " "); + // replace multiple spaces with a single space + message = message.replace(/\s+/g, " "); + const parsedLine: { [key: string]: number } = {}; // Part Separator symbols i.e. Space, Tab & Comma are fully supported @@ -80,12 +87,15 @@ export const parseSerialMessages = ( // if we find a colon, we assume the latter is being used let tokens: string[] = []; if (message.indexOf(":") > 0) { - message.split(delimiterRegex).forEach((keyValue: string) => { - let [key, value] = keyValue.split(":"); - key = key && key.trim(); - value = value && value.trim(); - if (key && key.length > 0 && value && value.length > 0) { - tokens.push(...[key, value]); + // Splitting by the separator and handling possible spaces + const keyValuePairs = message.split(":").map((kv) => kv.trim()); + let reformedLine = keyValuePairs.join(":").split(delimiterRegex); + + reformedLine.forEach((kv) => { + const [key, value] = kv.split(":"); + if (key && value) { + tokens.push(key.trim()); + tokens.push(value.trim()); } }); } else { From aff3e748a609ea2eeeb33e9feef9697475eec3f2 Mon Sep 17 00:00:00 2001 From: Leonid Meleshin Date: Tue, 27 Feb 2024 23:36:27 +0400 Subject: [PATCH 2/2] feat(Parser): allow equals (`=`) sign for label-value separator Closes #22 --- src/msgAggregatorWorker.test.ts | 81 +++++++++++++++++---------------- src/msgAggregatorWorker.ts | 2 + 2 files changed, 45 insertions(+), 38 deletions(-) diff --git a/src/msgAggregatorWorker.test.ts b/src/msgAggregatorWorker.test.ts index b023969..b9ddc51 100644 --- a/src/msgAggregatorWorker.test.ts +++ b/src/msgAggregatorWorker.test.ts @@ -81,44 +81,49 @@ describe("Parsing data", () => { ); }); - test("labeled", () => { - const messages = [ - `0${trailingFieldDelimiter}${recordDelimiter}`, - `label_1:1${fieldDelimiter}label_2:2${trailingFieldDelimiter}${recordDelimiter}`, - `label_1:3${fieldDelimiter}label_2:4${trailingFieldDelimiter}${recordDelimiter}`, - ]; - - const assertion = { - datasetNames: ["label_1", "label_2"], - parsedLines: [ - { label_1: 1, label_2: 2 }, - { label_1: 3, label_2: 4 }, - ], - }; - - expect(messageAggregator.parseSerialMessages(messages)).toEqual( - assertion - ); - }); - - test("labeled padded", () => { - const messages = [ - `0${trailingFieldDelimiter}${recordDelimiter}`, - `label_1: 1${fieldDelimiter}label_2: 20${trailingFieldDelimiter}${recordDelimiter}`, - `label_1: 300${fieldDelimiter}label_2:4000${trailingFieldDelimiter}${recordDelimiter}`, - ]; - - const assertion = { - datasetNames: ["label_1", "label_2"], - parsedLines: [ - { label_1: 1, label_2: 20 }, - { label_1: 300, label_2: 4000 }, - ], - }; - - expect(messageAggregator.parseSerialMessages(messages)).toEqual( - assertion - ); + describe.each([ + ["colon", ":"], + ["equals", "="], + ])("%s label delimiter", (_, labelDelimiter) => { + test("labeled", () => { + const messages = [ + `0${trailingFieldDelimiter}${recordDelimiter}`, + `label_1${labelDelimiter}1${fieldDelimiter}label_2${labelDelimiter}2${trailingFieldDelimiter}${recordDelimiter}`, + `label_1${labelDelimiter}3${fieldDelimiter}label_2${labelDelimiter}4${trailingFieldDelimiter}${recordDelimiter}`, + ]; + + const assertion = { + datasetNames: ["label_1", "label_2"], + parsedLines: [ + { label_1: 1, label_2: 2 }, + { label_1: 3, label_2: 4 }, + ], + }; + + expect(messageAggregator.parseSerialMessages(messages)).toEqual( + assertion + ); + }); + + test("labeled padded", () => { + const messages = [ + `0${trailingFieldDelimiter}${recordDelimiter}`, + `label_1${labelDelimiter} 1${fieldDelimiter}label_2${labelDelimiter} 20${trailingFieldDelimiter}${recordDelimiter}`, + `label_1${labelDelimiter} 300${fieldDelimiter}label_2${labelDelimiter}4000${trailingFieldDelimiter}${recordDelimiter}`, + ]; + + const assertion = { + datasetNames: ["label_1", "label_2"], + parsedLines: [ + { label_1: 1, label_2: 20 }, + { label_1: 300, label_2: 4000 }, + ], + }; + + expect(messageAggregator.parseSerialMessages(messages)).toEqual( + assertion + ); + }); }); test("buffering", () => { diff --git a/src/msgAggregatorWorker.ts b/src/msgAggregatorWorker.ts index 0b98a60..2001837 100644 --- a/src/msgAggregatorWorker.ts +++ b/src/msgAggregatorWorker.ts @@ -76,6 +76,8 @@ export const parseSerialMessages = ( message = message.replace(delimiterRegex, " "); // replace multiple spaces with a single space message = message.replace(/\s+/g, " "); + // replace all equal signs with a colon + message = message.replace(/=/g, ":"); const parsedLine: { [key: string]: number } = {};