|
|
@@ -0,0 +1,141 @@ |
|
|
/* |
|
|
Copyright (C) 2022 Arduino SA |
|
|
|
|
|
This program is free software: you can redistribute it and/or modify |
|
|
it under the terms of the GNU Affero General Public License as published |
|
|
by the Free Software Foundation, either version 3 of the License, or |
|
|
(at your option) any later version. |
|
|
|
|
|
This program is distributed in the hope that it will be useful, |
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
GNU Affero General Public License for more details. |
|
|
|
|
|
You should have received a copy of the GNU Affero General Public License |
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>. |
|
|
*/ |
|
|
|
|
|
class WorkerStub { |
|
|
listener = (_: { data: { command: string } }) => {}; |
|
|
addEventListener(_: string, listenerCallback: (event: object) => void) { |
|
|
this.listener = listenerCallback; |
|
|
} |
|
|
} |
|
|
let worker = new WorkerStub(); |
|
|
(global as any).self = worker; |
|
|
|
|
|
const messageAggregator = require("./msgAggregatorWorker"); |
|
|
|
|
|
beforeEach(() => { |
|
|
worker.listener({ data: { command: "cleanup" } }); |
|
|
}); |
|
|
|
|
|
describe("Parsing data", () => { |
|
|
describe.each([ |
|
|
["space", " "], |
|
|
["tab", "\t"], |
|
|
["comma", ","], |
|
|
])("%s field delimiter", (_, fieldDelimiter) => { |
|
|
describe.each([ |
|
|
["trailing", fieldDelimiter], |
|
|
["no trailing", ""], |
|
|
])("%s", (_, trailingFieldDelimiter) => { |
|
|
describe.each([ |
|
|
["LF", "\n"], |
|
|
["CRLF", "\r\n"], |
|
|
])("%s record delimiter", (_, recordDelimiter) => { |
|
|
test("single field", () => { |
|
|
const messages = [ |
|
|
`0${trailingFieldDelimiter}${recordDelimiter}`, |
|
|
`1${trailingFieldDelimiter}${recordDelimiter}`, |
|
|
`2${trailingFieldDelimiter}${recordDelimiter}`, |
|
|
]; |
|
|
|
|
|
const assertion = { |
|
|
datasetNames: ["value 1"], |
|
|
parsedLines: [{ "value 1": 1 }, { "value 1": 2 }], |
|
|
}; |
|
|
|
|
|
expect(messageAggregator.parseSerialMessages(messages)).toEqual( |
|
|
assertion |
|
|
); |
|
|
}); |
|
|
|
|
|
test("multi-field", () => { |
|
|
const messages = [ |
|
|
`0${trailingFieldDelimiter}${recordDelimiter}`, |
|
|
`1${fieldDelimiter}2${trailingFieldDelimiter}${recordDelimiter}`, |
|
|
`3${fieldDelimiter}4${trailingFieldDelimiter}${recordDelimiter}`, |
|
|
]; |
|
|
|
|
|
const assertion = { |
|
|
datasetNames: ["value 1", "value 2"], |
|
|
parsedLines: [ |
|
|
{ "value 1": 1, "value 2": 2 }, |
|
|
{ "value 1": 3, "value 2": 4 }, |
|
|
], |
|
|
}; |
|
|
|
|
|
expect(messageAggregator.parseSerialMessages(messages)).toEqual( |
|
|
assertion |
|
|
); |
|
|
}); |
|
|
|
|
|
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("buffering", () => { |
|
|
// Incomplete record |
|
|
let messages = [ |
|
|
`0${trailingFieldDelimiter}${recordDelimiter}`, |
|
|
`1${fieldDelimiter}`, |
|
|
]; |
|
|
|
|
|
// Incomplete message is buffered |
|
|
let assertion: { |
|
|
datasetNames: string[]; |
|
|
parsedLines: { [key: string]: number }[]; |
|
|
} = { |
|
|
datasetNames: [], |
|
|
parsedLines: [], |
|
|
}; |
|
|
|
|
|
expect(messageAggregator.parseSerialMessages(messages)).toEqual( |
|
|
assertion |
|
|
); |
|
|
|
|
|
// Second part of the record |
|
|
messages = [`2${trailingFieldDelimiter}${recordDelimiter}`]; |
|
|
|
|
|
assertion = { |
|
|
datasetNames: ["value 1", "value 2"], |
|
|
parsedLines: [{ "value 1": 1, "value 2": 2 }], |
|
|
}; |
|
|
|
|
|
expect(messageAggregator.parseSerialMessages(messages)).toEqual( |
|
|
assertion |
|
|
); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|
|
|
export {}; |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a regression here for a use case that is not explicitly supported by the specification, but is supported by the Arduino IDE 1.x Serial Plotter and previous implementation of this project. That use case is the combination of a trailing delimiter and
\r\nseparator.For example:
You can see how code that generates data via a
forloop favors a trailing delimiter, and of courseSerial.println();is a convenient way to produce a separator.Previously, that sketch produced 3 variables as expected:
Arduino IDE 1.8.19:
arduino-serial-plotter-webapp@eac6d39 / arduino-serial-plotter-webapp@0.1.0 / Arduino IDE 2.0.0-rc9.3:
After the change made here, it produces 4 variables:
The problem is the
\rin the\r\nseparator produced bySerial.println();is being treated as a separate value. Previously, that\rwas consumed bymessage.split(/\s/);(because it matches/\s/).Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if the separator regex is modified to include the carriage return character
\r? Would that solve the problem or am I still missing something ?Is there a particular reason why
Serial.printlnoutputs a\r\nand not\n? We cannot have two different conventions for EOL character. Either we amend the SerialPlotter protocol or drop support for\r\nentirely.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That general approach should work. However,
separatoris used in the code directly as a string in addition to its use in the definition ofseparatorRegex, so the implementation is not quite so simple.It will need to be something like this (not tested):
Why not? Inconsistency in EOL is a common situation. Not so long ago, Windows, Linux, and macOS each used a different EOL.
No idea. It has been that way since 2006, and in the proud tradition of Arduino the person who made the change didn't bother to explain the reason:
arduino/Arduino@650c275
I think it is a good idea. The protocol already does specify that
\r\nis supported by usingSerial.println()in the all the example programs, but that is too vague. It should be stated explicitly that both are supported.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be too disruptive. I'm certain the code can be made to support either without any real difficulty.
The only real challenge here is that there is no test coverage in this project, despite the fact that this code is very easy to write unit tests for.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to contribute by writing the tests but I still haven't managed to setup my dev environment right.
Ideal case, I would like to debug the modifications before committing it to the repo. However, I have not been able to interface my Arduino with the standalone web-app or write a websocket script to feed values to the web-app.
Currently, I've been modifying the randomValueGenerator code manually to feed various types of messages and check its results. If you could guide me in setting this up, I would be able to contribute a lot more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have to support
\ras an EOL character? In that case, the regex would beThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. It has never been supported and I haven't seen any complaints about the lack of support (I think it is not a very common separator in Arduino sketches despite being supported in the line ending menu of Serial Plotter and Serial Monitor)
For example, this sketch:
Produces no output in the plotter:
arduino-serial-plotter-webapp@0.1.0
Arduino IDE 1.8.19