-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Basic parsing for extended multiplexing #76
Changes from 8 commits
d1d3cf9
4f7969d
565466b
42b056c
15fe8db
def6bc3
37036f0
02fdd1e
3a665dc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
using System.Linq; | ||
using NUnit.Framework; | ||
|
||
|
||
namespace DbcParserLib.Tests | ||
{ | ||
internal class ExtendedMultiplexingParserTests | ||
{ | ||
[Test] | ||
public void ParseSignalExtendedMultiplexingCase1() | ||
{ | ||
var dbcString = @" | ||
BO_ 2024 OBD2: 8 Vector__XXX | ||
SG_ S1_PID_0D_VehicleSpeed m13 : 31|8@0+ (1,0) [0|255] ""km/h"" Vector__XXX | ||
SG_ S1_PID_11_ThrottlePosition m17 : 31|8@0+ (0.39216,0) [0|100] ""%"" Vector__XXX | ||
SG_ S1 m1M : 23|8@0+ (1,0) [0|255] """" Vector__XXX | ||
SG_ Service M : 11|4@0+ (1,0) [0|15] """" Vector__XXX | ||
|
||
SG_MUL_VAL_ 2024 S1_PID_0D_VehicleSpeed S1 13-13; | ||
SG_MUL_VAL_ 2024 S1_PID_11_ThrottlePosition S1 17-17; | ||
SG_MUL_VAL_ 2024 S1 Service 1-1;"; | ||
|
||
|
||
var dbc = Parser.Parse(dbcString); | ||
|
||
Assert.That(dbc.Messages.Count(), Is.EqualTo(1)); | ||
|
||
var message = dbc.Messages.First(); | ||
|
||
Assert.That(message.Signals, Has.Count.EqualTo(4)); | ||
|
||
var signal1 = message.Signals.FirstOrDefault(x => x.Name.Equals("S1_PID_0D_VehicleSpeed")); | ||
var signal2 = message.Signals.FirstOrDefault(x => x.Name.Equals("S1_PID_11_ThrottlePosition")); | ||
var signal3 = message.Signals.FirstOrDefault(x => x.Name.Equals("S1")); | ||
var signal4 = message.Signals.FirstOrDefault(x => x.Name.Equals("Service")); | ||
|
||
Assert.Multiple(() => | ||
{ | ||
Assert.That(signal1?.ExtendedMultiplexing, Is.Not.Null); | ||
Assert.That(signal1?.ExtendedMultiplexing.MultiplexorSignal, Is.EqualTo("S1")); | ||
Assert.That(signal1?.ExtendedMultiplexing.MultiplexingRanges, Has.Count.EqualTo(1)); | ||
Assert.That(signal1?.ExtendedMultiplexing.MultiplexingRanges.FirstOrDefault()?.IsRange, Is.EqualTo(false)); | ||
Assert.That(signal1?.ExtendedMultiplexing.MultiplexingRanges.FirstOrDefault()?.Lower, Is.EqualTo(13)); | ||
Assert.That(signal1?.ExtendedMultiplexing.MultiplexingRanges.FirstOrDefault()?.Upper, Is.EqualTo(13)); | ||
|
||
Assert.That(signal2?.ExtendedMultiplexing, Is.Not.Null); | ||
Assert.That(signal2?.ExtendedMultiplexing.MultiplexorSignal, Is.EqualTo("S1")); | ||
Assert.That(signal2?.ExtendedMultiplexing.MultiplexingRanges, Has.Count.EqualTo(1)); | ||
Assert.That(signal2?.ExtendedMultiplexing.MultiplexingRanges.FirstOrDefault()?.IsRange, Is.EqualTo(false)); | ||
Assert.That(signal2?.ExtendedMultiplexing.MultiplexingRanges.FirstOrDefault()?.Lower, Is.EqualTo(17)); | ||
Assert.That(signal2?.ExtendedMultiplexing.MultiplexingRanges.FirstOrDefault()?.Upper, Is.EqualTo(17)); | ||
|
||
Assert.That(signal3?.ExtendedMultiplexing, Is.Not.Null); | ||
Assert.That(signal3?.ExtendedMultiplexing.MultiplexorSignal, Is.EqualTo("Service")); | ||
Assert.That(signal3?.ExtendedMultiplexing.MultiplexingRanges, Has.Count.EqualTo(1)); | ||
Assert.That(signal3?.ExtendedMultiplexing.MultiplexingRanges.FirstOrDefault()?.IsRange, Is.EqualTo(false)); | ||
Assert.That(signal3?.ExtendedMultiplexing.MultiplexingRanges.FirstOrDefault()?.Lower, Is.EqualTo(1)); | ||
Assert.That(signal3?.ExtendedMultiplexing.MultiplexingRanges.FirstOrDefault()?.Upper, Is.EqualTo(1)); | ||
|
||
Assert.That(signal4, Is.Not.Null); | ||
Assert.That(signal4?.ExtendedMultiplexing, Is.Null); | ||
}); | ||
} | ||
|
||
[Test] | ||
public void ParseSignalExtendedMultiplexingCase2() | ||
{ | ||
var dbcString = @" | ||
BO_ 100 MuxMsg: 1 Vector__XXX | ||
SG_ Mux_4 m2 : 6|2@1+ (1,0) [0|0] """" Vector__XXX | ||
SG_ Mux_3 m3M : 4|2@1+ (1,0) [0|0] """" Vector__XXX | ||
SG_ Mux_2 m3M : 2|2@1+ (1,0) [0|0] """" Vector__XXX | ||
SG_ Mux_1 M : 0|2@1+ (1,0) [0|0] """" Vector__XXX | ||
|
||
SG_MUL_VAL_ 100 Mux_2 Mux_1 3-3, 5-10; | ||
SG_MUL_VAL_ 100 Mux_3 Mux_2 3-3; | ||
SG_MUL_VAL_ 100 Mux_4 Mux_3 2-2;"; | ||
|
||
|
||
var dbc = Parser.Parse(dbcString); | ||
|
||
Assert.That(dbc.Messages.Count(), Is.EqualTo(1)); | ||
|
||
var message = dbc.Messages.First(); | ||
|
||
Assert.That(message.Signals, Has.Count.EqualTo(4)); | ||
|
||
var signal1 = message.Signals.FirstOrDefault(x => x.Name.Equals("Mux_1")); | ||
var signal2 = message.Signals.FirstOrDefault(x => x.Name.Equals("Mux_2")); | ||
var signal3 = message.Signals.FirstOrDefault(x => x.Name.Equals("Mux_3")); | ||
var signal4 = message.Signals.FirstOrDefault(x => x.Name.Equals("Mux_4")); | ||
|
||
Assert.Multiple(() => | ||
{ | ||
Assert.That(signal1, Is.Not.Null); | ||
Assert.That(signal1?.ExtendedMultiplexing, Is.Null); | ||
|
||
Assert.That(signal2?.ExtendedMultiplexing, Is.Not.Null); | ||
Assert.That(signal2?.ExtendedMultiplexing.MultiplexorSignal, Is.EqualTo("Mux_1")); | ||
Assert.That(signal2?.ExtendedMultiplexing.MultiplexingRanges, Has.Count.EqualTo(2)); | ||
Assert.That(signal2?.ExtendedMultiplexing.MultiplexingRanges.FirstOrDefault()?.IsRange, Is.EqualTo(false)); | ||
Assert.That(signal2?.ExtendedMultiplexing.MultiplexingRanges.FirstOrDefault()?.Lower, Is.EqualTo(3)); | ||
Assert.That(signal2?.ExtendedMultiplexing.MultiplexingRanges.FirstOrDefault()?.Upper, Is.EqualTo(3)); | ||
Assert.That(signal2?.ExtendedMultiplexing.MultiplexingRanges.LastOrDefault()?.IsRange, Is.EqualTo(true)); | ||
Assert.That(signal2?.ExtendedMultiplexing.MultiplexingRanges.LastOrDefault()?.Lower, Is.EqualTo(5)); | ||
Assert.That(signal2?.ExtendedMultiplexing.MultiplexingRanges.LastOrDefault()?.Upper, Is.EqualTo(10)); | ||
|
||
Assert.That(signal3?.ExtendedMultiplexing, Is.Not.Null); | ||
Assert.That(signal3?.ExtendedMultiplexing.MultiplexorSignal, Is.EqualTo("Mux_2")); | ||
Assert.That(signal3?.ExtendedMultiplexing.MultiplexingRanges, Has.Count.EqualTo(1)); | ||
Assert.That(signal3?.ExtendedMultiplexing.MultiplexingRanges.FirstOrDefault()?.IsRange, Is.EqualTo(false)); | ||
Assert.That(signal3?.ExtendedMultiplexing.MultiplexingRanges.FirstOrDefault()?.Lower, Is.EqualTo(3)); | ||
Assert.That(signal3?.ExtendedMultiplexing.MultiplexingRanges.FirstOrDefault()?.Upper, Is.EqualTo(3)); | ||
|
||
Assert.That(signal4?.ExtendedMultiplexing, Is.Not.Null); | ||
Assert.That(signal4?.ExtendedMultiplexing.MultiplexorSignal, Is.EqualTo("Mux_3")); | ||
Assert.That(signal4?.ExtendedMultiplexing.MultiplexingRanges, Has.Count.EqualTo(1)); | ||
Assert.That(signal4?.ExtendedMultiplexing.MultiplexingRanges.FirstOrDefault()?.IsRange, Is.EqualTo(false)); | ||
Assert.That(signal4?.ExtendedMultiplexing.MultiplexingRanges.FirstOrDefault()?.Lower, Is.EqualTo(2)); | ||
Assert.That(signal4?.ExtendedMultiplexing.MultiplexingRanges.FirstOrDefault()?.Upper, Is.EqualTo(2)); | ||
}); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
namespace DbcParserLib.Model | ||
{ | ||
public class MultiplexingRange | ||
{ | ||
public bool IsRange { get; } | ||
public uint Lower { get; } | ||
public uint Upper { get; } | ||
|
||
public MultiplexingRange(uint lower, uint upper) | ||
{ | ||
Lower = lower; | ||
Upper = upper; | ||
IsRange = lower != upper; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
namespace DbcParserLib.Model | ||
{ | ||
public enum MultiplexingRole | ||
{ | ||
None, | ||
Unknown, // Used if parsing fails | ||
Multiplexed, | ||
Multiplexor, | ||
MultiplexedMultiplexor | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or should mark |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
using System.Collections.Generic; | ||
|
||
namespace DbcParserLib.Model | ||
{ | ||
internal class ParsingExtendedMultiplexing | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's say we have instead: internal class ParsingGeneralMultiplexing
{
public MultiplexingRole Role{ get; set; }
/*
For muxers the below is always null
For standard mux info, the following is null at the end of the parsing but can be filled in the finish up
phase by picking the only muxer expected in the message (if more than one, parsing error). Moreover for standard
the multiplexer value is filled with a single element, what is called group in the multiplexing info
For advanced mux multiplexor signal is filled and multiplexer values are replaced (SG_ parsed values should be
overwritten)
*/
public string MultiplexorSignal { get; set;}
public ISet<uint> MultiplexerValue { get; }
// May be handy for checks in the wrap up phase. Should not be reported in the immutable public object
public bool Extended {get; set;}
} So, simple one
Phase 1: SG parsing
Wrap upIn the The extended
This becomes a 3-phase stuff. Phase 1: SG parsingAs the standard one, values will contain numbers extracted from standard muxing (i.e. Phase 2: SG_MUL_VAL_We set the muxor name and we clear and fill values with ranges exploded. Wrap upNames are filled, so no action needed. LayoutsFor unpacking this whole information should be converted into something more handy that I would call layouts and look like what CANdb++ is showing. The idea is that not the user, but the message itself can unpack a
I like this abstraction because the procedure above can be set up by the lib itself in the wrap up phase so having even more info that what we want to expose in the public API. These are more or less my ideas, I hope that they are clearer with this example. Cheers There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Best way forward for me would be if i wait for the proof of concept for immutability go thorugh as i would have to do pretty much the same stuff as in there to achieve that and rebase this after that. main resons being: Some remarks on your comment: To assign the single multiplexor to any signal (in simple multiplexing mode) i still need some strange logic i dont like. the message parses signals one by one and each signals builds it multiplexing info. Either pre parse some info that can then be using in calculating the multiplexing info or add the value later for the packer stuff i agree i would probably reverse the multiplexing tree and always start with the multiplexors and see which signals they apply to. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yep. If we are fine with the immutability poc I can merge that. That will have some API changes, but if we think it's the first step....
Generally speaking, if the dbc is flawed, we should fail parsing. I prefer that that say all good and then decode the wrong stuff. I would not throw, but use the parsing observer
no, would replace with
I do not like that logic either but that's a dbc format limitation, it's not indexed. Moreover, in parsing reading the file is most expensive thing, so pre parsing is weird the same way. The disconfort here is minimal and if we keep the pain in the parsing phase then fixing in the wrap up phase we have to safe places for the checks and the public item is fine, which is the most important thing. With the SG_ parsing it's intrinsic that the multiplexer is unknown until the message parsing is over (for simple mux) and until the file is over (for advanced). Anyway, the way we handle parsing and wrap up can be changed anytime without affecting the user, so it doesn't need to be perfect out of the box. As long as we provide a consistent user API, how we manage our internals can be changed any time so to me it is a bit lower priority to agree on a solution, especially for details. A |
||
{ | ||
public string MultiplexorSignal { get; } | ||
public List<MultiplexingRange> MultiplexingRanges { get; } | ||
|
||
public ParsingExtendedMultiplexing(string multiplexorSignal, List<MultiplexingRange> multiplexingRanges) | ||
{ | ||
MultiplexorSignal = multiplexorSignal; | ||
MultiplexingRanges = multiplexingRanges; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
using DbcParserLib.Model; | ||
using DbcParserLib.Observers; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Text.RegularExpressions; | ||
|
||
namespace DbcParserLib.Parsers | ||
{ | ||
internal class ExtendedMultiplexingLineParser : ILineParser | ||
{ | ||
private const string SignalLineStarter = "SG_MUL_VAL_ "; | ||
private const string SignalRegex = @"SG_MUL_VAL_\s+(\d+)\s+(\S+)\s+(\S+)\s+((?:\d+-\d+,?\s*)+);?"; | ||
|
||
private readonly IParseFailureObserver m_observer; | ||
|
||
public ExtendedMultiplexingLineParser(IParseFailureObserver observer) | ||
{ | ||
m_observer = observer; | ||
} | ||
|
||
public bool TryParse(string line, IDbcBuilder builder, INextLineProvider nextLineProvider) | ||
{ | ||
if (!line.TrimStart().StartsWith(SignalLineStarter)) | ||
{ | ||
return false; | ||
} | ||
|
||
var match = Regex.Match(line, SignalRegex); | ||
|
||
if (match.Success) | ||
{ | ||
var multiplexorSignal = match.Groups[3].Value; | ||
var multiplexorRanges = match.Groups[4].Value; | ||
|
||
var rangesArray = multiplexorRanges.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); | ||
|
||
var rangesParsed = new List<MultiplexingRange>(); | ||
|
||
foreach (var range in rangesArray) | ||
{ | ||
var rangeClean = range.Trim(); | ||
var numbers = rangeClean.Split('-'); | ||
|
||
var lower = uint.Parse(numbers[0]); | ||
var upper = uint.Parse(numbers[1]); | ||
|
||
rangesParsed.Add(new MultiplexingRange(lower, upper)); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, just wondering var triggerValues = new List<uint>();
foreach (var range in rangesArray)
{
var rangeClean = range.Trim();
var numbers = rangeClean.Split('-');
var lower = uint.Parse(numbers[0]);
var upper = uint.Parse(numbers[1]);
// Maybe check if upper >= lower?
rangesParsed.AddRange(Enumerable.Range(lower, upper - lower + 1));
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. theres so much that could be checked here:
I would go with this:
for the final message parsing having ranges sucks anyway it way slower than checking i hashset on contains. |
||
|
||
var extendendMultiplexing = new ParsingExtendedMultiplexing(multiplexorSignal, rangesParsed); | ||
|
||
builder.AddSignalExtendedMultiplexingInfo(uint.Parse(match.Groups[1].Value), match.Groups[2].Value, extendendMultiplexing); | ||
} | ||
else | ||
{ | ||
m_observer.SignalExtendedMultiplexingError(); | ||
} | ||
|
||
return true; | ||
} | ||
} | ||
} |
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 about expanding the valid values?
So that
1-1, 5-7
is exploded into[1, 5, 6, 7]
.I was wondering if that would simplify, after all range or not, from a decoding point of view, we only need to read a Muxer value and pick the right layout based on that value.
Just reasoning