Skip to content
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

Closed
wants to merge 9 commits into from
124 changes: 124 additions & 0 deletions DbcParserLib.Tests/ExtendedMultiplexingParserTests.cs
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));
});
}
}
}
19 changes: 19 additions & 0 deletions DbcParserLib/DbcBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,25 @@ public void AddSignalValueType(uint messageId, string signalName, DbcValueType v
m_observer.SignalNameNotFound(messageId, signalName);
}

public void AddSignalExtendedMultiplexingInfo(uint messageId, string signalName, string multiplexorSignal, string ranges)
{
if (TryGetValueMessageSignal(messageId, signalName, out var signal))
{
if (TryGetValueMessageSignal(messageId, multiplexorSignal, out var _))
{
signal.ParsingMultiplexing.AddExtendedMultiplexingInformation(multiplexorSignal, ranges);
}
else
{
m_observer.SignalExtendedMultiplexingSyntaxError();
}
}
else
{
m_observer.SignalNameNotFound(messageId, signalName);
}
}

public void AddNodeComment(string nodeName, string comment)
{
var node = m_nodes.FirstOrDefault(n => n.Name.Equals(nodeName));
Expand Down
24 changes: 1 addition & 23 deletions DbcParserLib/ExtensionsAndHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,31 +32,9 @@ internal static ulong BitMask(this Signal signal)
return (ulong.MaxValue >> (64 - signal.Length));
}

private const string MultiplexorLabel = "M";
private const string MultiplexedLabel = "m";

public static MultiplexingInfo MultiplexingInfo(this Signal signal)
{
if(string.IsNullOrWhiteSpace(signal.Multiplexing))
return new MultiplexingInfo(MultiplexingRole.None);

if(signal.Multiplexing.Equals(MultiplexorLabel))
return new MultiplexingInfo(MultiplexingRole.Multiplexor);

if(signal.Multiplexing.StartsWith(MultiplexedLabel))
{
var substringLength = signal.Multiplexing.Length - (signal.Multiplexing.EndsWith(MultiplexorLabel) ? 2 : 1);

if(int.TryParse(signal.Multiplexing.Substring(1, substringLength), out var group))
return new MultiplexingInfo(MultiplexingRole.Multiplexed, group);
}

return new MultiplexingInfo(MultiplexingRole.Unknown);
}

public static bool IsMultiplexed(this Message message)
{
return message.Signals.Any(s => s.MultiplexingInfo().Role == MultiplexingRole.Multiplexor);
return message.Signals.Any(s => s.ParsingMultiplexing.Role == MultiplexingRole.Multiplexor);
}

internal static void AdjustExtendedId(this Message message)
Expand Down
1 change: 1 addition & 0 deletions DbcParserLib/IDbcBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ internal interface IDbcBuilder
void AddSignal(Signal signal);
void AddSignalComment(uint messageId, string signalName, string comment);
void AddSignalValueType(uint messageId, string signalName, DbcValueType valueType);
void AddSignalExtendedMultiplexingInfo(uint messageId, string signalName, string multiplexorSignal, string ranges);
void LinkNamedTableToSignal(uint messageId, string signalName, string tableName);
void LinkTableValuesToSignal(uint messageId, string signalName, IReadOnlyDictionary<int, string> dictValues);
void LinkTableValuesToEnvironmentVariable(string variableName, IReadOnlyDictionary<int, string> dictValues);
Expand Down
26 changes: 9 additions & 17 deletions DbcParserLib/Model/MultiplexingInfo.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
using System.Linq;

namespace DbcParserLib.Model
{
public struct MultiplexingInfo
{
public MultiplexingInfo(MultiplexingRole role)
: this(role, 0)
{
}
public class MultiplexingInfo
{
public MultiplexingRole Role { get; }
public uint Group { get; }

public MultiplexingInfo(MultiplexingRole role, int group)
public MultiplexingInfo(ParsingMultiplexing parsingMultiplexing)
{
Role = role;
Group = group;
Role = parsingMultiplexing.Role;
Group = parsingMultiplexing.MultiplexerValues.FirstOrDefault();
}

public MultiplexingRole Role {get;}
public int Group {get;}
}

public enum MultiplexingRole
{
None, Unknown, Multiplexed, Multiplexor
}
}
12 changes: 12 additions & 0 deletions DbcParserLib/Model/MultiplexingRole.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace DbcParserLib.Model
{
[Flags]
public enum MultiplexingRole
{
None,
Multiplexed,
Multiplexor
}
}
103 changes: 103 additions & 0 deletions DbcParserLib/Model/ParsingMultiplexing.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using DbcParserLib.Observers;
using System;
using System.Collections.Generic;

namespace DbcParserLib.Model
{
public class ParsingMultiplexing
{
public MultiplexingRole Role { get; }
public string MultiplexorSignal { get; private set; }

//Initialize so that FirstOrDefault wouldn't crash when converting to current MultiplexingInfo
public ISet<uint> MultiplexerValues { get; } = new HashSet<uint>();

private const string MultiplexorLabel = "M";
private const string MultiplexedLabel = "m";

private bool valid = true;
private bool hasExtendedInfo = false;

Check warning on line 19 in DbcParserLib/Model/ParsingMultiplexing.cs

View workflow job for this annotation

GitHub Actions / test

The field 'ParsingMultiplexing.hasExtendedInfo' is assigned but its value is never used

Check warning on line 19 in DbcParserLib/Model/ParsingMultiplexing.cs

View workflow job for this annotation

GitHub Actions / test

The field 'ParsingMultiplexing.hasExtendedInfo' is assigned but its value is never used
private readonly IParseFailureObserver m_parseFailureObserver;

public ParsingMultiplexing(string signalMultiplexingString, IParseFailureObserver parseFailureObserver)
{
m_parseFailureObserver = parseFailureObserver;

if (string.IsNullOrWhiteSpace(signalMultiplexingString))
{
Role = MultiplexingRole.None;
return;
}

if (signalMultiplexingString.Equals(MultiplexorLabel))
{
Role = MultiplexingRole.Multiplexor;
return;
}

if (signalMultiplexingString.StartsWith(MultiplexedLabel))
{
var isMultiplexedMultiplexor = signalMultiplexingString.EndsWith(MultiplexorLabel);
var substringLength = signalMultiplexingString.Length - (isMultiplexedMultiplexor ? 2 : 1);

if (uint.TryParse(signalMultiplexingString.Substring(1, substringLength), out var multiplexerValue))
{
MultiplexerValues.Add(multiplexerValue);
}
else
{
valid = false;
m_parseFailureObserver.SignalMultiplexingSyntaxError();
return;
}

if (isMultiplexedMultiplexor)
{
Role = MultiplexingRole.Multiplexed | MultiplexingRole.Multiplexor;
return;
}
Role = MultiplexingRole.Multiplexed;
return;
}

valid = false;
m_parseFailureObserver.SignalMultiplexingSyntaxError();
}

public void AddExtendedMultiplexingInformation(string multiplexorSignal, string ranges)
{
if (valid == false)
{
m_parseFailureObserver.SignalExtendedMultiplexingSyntaxError();
return;
}

var rangesArray = ranges.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

foreach (var range in rangesArray)
{
var rangeClean = range.Trim();
var numbers = rangeClean.Split('-');

var lowerParsed = uint.TryParse(numbers[0], out var lower);
var upperParsed = uint.TryParse(numbers[1], out var upper);

if (lowerParsed == true && upperParsed == true && lower <= upper)
{
for (var i = lower; i <= upper; i++)
{
MultiplexerValues.Add(i);
}
}
else
{
m_parseFailureObserver.SignalExtendedMultiplexingSyntaxError();
return;
}
}

MultiplexorSignal = multiplexorSignal;
hasExtendedInfo = true;
}
}
}
6 changes: 3 additions & 3 deletions DbcParserLib/Model/Signal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ internal class ImmutableSignal
public string[] Receiver { get; }
public IReadOnlyDictionary<int, string> ValueTableMap { get; }
public string Comment { get; }
public string Multiplexing { get; }
public MultiplexingInfo Multiplexing { get; }
public IReadOnlyDictionary<string, CustomProperty> CustomProperties { get; }

internal ImmutableSignal(Signal signal)
Expand All @@ -42,7 +42,7 @@ internal ImmutableSignal(Signal signal)
Receiver = signal.Receiver;
ValueTableMap = signal.ValueTableMap;
Comment = signal.Comment;
Multiplexing = signal.Multiplexing;
Multiplexing = new MultiplexingInfo(signal.ParsingMultiplexing);
CustomProperties = signal.CustomProperties;
}
}
Expand All @@ -65,7 +65,7 @@ public class Signal
public string[] Receiver;
public IReadOnlyDictionary<int, string> ValueTableMap = new Dictionary<int, string>();
public string Comment;
public string Multiplexing;
internal ParsingMultiplexing ParsingMultiplexing;
public Message Parent;
public readonly Dictionary<string, CustomProperty> CustomProperties = new Dictionary<string, CustomProperty>();
public double InitialValue
Expand Down
2 changes: 2 additions & 0 deletions DbcParserLib/Observers/IParseFailureObserver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public interface IParseFailureObserver
void PropertyDefaultSyntaxError();
void SignalSyntaxError();
void SignalValueTypeSyntaxError();
void SignalMultiplexingSyntaxError();
void SignalExtendedMultiplexingSyntaxError();
void ValueTableDefinitionSyntaxError();
void ValueTableSyntaxError();
void SignalNameNotFound(uint messageId, string signalName);
Expand Down
8 changes: 8 additions & 0 deletions DbcParserLib/Observers/SilentFailureObserver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ public void SignalValueTypeSyntaxError()
{
}

public void SignalMultiplexingSyntaxError()
{
}

public void SignalExtendedMultiplexingSyntaxError()
{
}

public void ValueTableDefinitionSyntaxError()
{
}
Expand Down
Loading
Loading