diff --git a/DbcParserLib.Tests/ExtendedMultiplexingParserTests.cs b/DbcParserLib.Tests/ExtendedMultiplexingParserTests.cs new file mode 100644 index 0000000..8e90814 --- /dev/null +++ b/DbcParserLib.Tests/ExtendedMultiplexingParserTests.cs @@ -0,0 +1,89 @@ +using DbcParserLib.Observers; +using NUnit.Framework; +using System; +using System.Linq; + + +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 failureObserver = new SimpleFailureObserver(); + Parser.SetParsingFailuresObserver(failureObserver); + var dbc = Parser.Parse(dbcString); + var errorList = failureObserver.GetErrorList(); + + Assert.That(dbc.Messages.Count(), Is.EqualTo(1)); + + var message = dbc.Messages.First(); + + Assert.That(message.Signals, Has.Count.EqualTo(4)); + + Assert.That(errorList, Has.Count.EqualTo(0)); + + // ToDo: When multiplexing is in the model data it needs to be validated here + } + + [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 failureObserver = new SimpleFailureObserver(); + Parser.SetParsingFailuresObserver(failureObserver); + var dbc = Parser.Parse(dbcString); + var errorList = failureObserver.GetErrorList(); + + Assert.That(dbc.Messages.Count(), Is.EqualTo(1)); + + var message = dbc.Messages.First(); + + Assert.That(message.Signals, Has.Count.EqualTo(4)); + + Assert.That(errorList, Has.Count.EqualTo(0)); + + // ToDo: When multiplexing is in the model data it needs to be validated here + } + + [TestCase("SG_MUL_VAL_ abc Mux_2 Mux_1 3-3 5-10;")] + [TestCase("SG_MUL_VAL_ abc Mux_2 Mux_1 3-3, 5-10")] + [TestCase("SG_MUL_VAL_ abc Mux_2 Mux_1 3-3, 5-10;")] + [TestCase("SG_MUL_VAL_ 100 Mux_2Mux_1 3-3, 5-10;")] + [TestCase("SG_MUL_VAL_ 100 Mux_2 Mux_1 3-a;")] + public void ParseExtendedMultiplexingErrorIsObserved(string dbcString) + { + var failureObserver = new SimpleFailureObserver(); + Parser.SetParsingFailuresObserver(failureObserver); + var dbc = Parser.Parse(dbcString); + var errorList = failureObserver.GetErrorList(); + + Assert.Multiple(() => + { + Assert.That(errorList, Has.Count.EqualTo(1)); + }); + } + } +} \ No newline at end of file diff --git a/DbcParserLib.Tests/ExtraMessageTransmitterLineParserTests.cs b/DbcParserLib.Tests/ExtraMessageTransmitterLineParserTests.cs index bd7112c..2813fd8 100644 --- a/DbcParserLib.Tests/ExtraMessageTransmitterLineParserTests.cs +++ b/DbcParserLib.Tests/ExtraMessageTransmitterLineParserTests.cs @@ -209,6 +209,7 @@ public void ParseExtraTransmittersDuplicateErrorIsObserved() }); } + [Test] public void ParseExtraTransmittersMessageNotFoundErrorIsObserved() { var dbcString = @" diff --git a/DbcParserLib/DbcBuilder.cs b/DbcParserLib/DbcBuilder.cs index 67d5cb8..fb20f3c 100644 --- a/DbcParserLib/DbcBuilder.cs +++ b/DbcParserLib/DbcBuilder.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using DbcParserLib.Model; @@ -336,6 +337,11 @@ public void LinkNamedTableToSignal(uint messageId, string signalName, string tab m_observer.TableMapNameNotFound(tableName); } + public void AddSignalExtendedMultiplexingInfo(uint messageId, string signalName, string multiplexorSignal, List> multiplexorRanges) + { + // ToDo: Define what to do with the data + } + private bool TryGetValueMessageSignal(uint messageId, string signalName, out Signal signal) { if (m_signals.TryGetValue(messageId, out var signals) && signals.TryGetValue(signalName, out signal)) diff --git a/DbcParserLib/IDbcBuilder.cs b/DbcParserLib/IDbcBuilder.cs index 47b4d7a..e1148d1 100644 --- a/DbcParserLib/IDbcBuilder.cs +++ b/DbcParserLib/IDbcBuilder.cs @@ -1,4 +1,5 @@ using DbcParserLib.Model; +using System; using System.Collections.Generic; namespace DbcParserLib @@ -28,5 +29,6 @@ internal interface IDbcBuilder void AddEnvironmentVariableComment(string variableName, string comment); void AddEnvironmentDataVariable(string variableName, uint dataSize); void AddNodeEnvironmentVariable(string nodeName, string variableName); + void AddSignalExtendedMultiplexingInfo(uint messageId, string signalName, string multiplexorSignal, List> multiplexorRanges); } } \ No newline at end of file diff --git a/DbcParserLib/Observers/IParseFailureObserver.cs b/DbcParserLib/Observers/IParseFailureObserver.cs index 875be68..f6424cd 100644 --- a/DbcParserLib/Observers/IParseFailureObserver.cs +++ b/DbcParserLib/Observers/IParseFailureObserver.cs @@ -38,6 +38,7 @@ public interface IParseFailureObserver void PropertyValueOutOfIndex(string propertyName, string index); void ExtraMessageTransmittersSyntaxError(); void ExtraMessageTransmittersDuplicate(uint messageId, string duplicateTransmitter); + void SignalExtendedMultiplexingSyntaxError(); void UnknownLine(); void NoMessageFound(); void Clear(); diff --git a/DbcParserLib/Observers/SilentFailureObserver.cs b/DbcParserLib/Observers/SilentFailureObserver.cs index eb96a9c..564e65a 100644 --- a/DbcParserLib/Observers/SilentFailureObserver.cs +++ b/DbcParserLib/Observers/SilentFailureObserver.cs @@ -140,6 +140,10 @@ public void ExtraMessageTransmittersDuplicate(uint messageId, string duplicateTr { } + public void SignalExtendedMultiplexingSyntaxError() + { + } + public void UnknownLine() { } diff --git a/DbcParserLib/Observers/SimpleFailureObserver.cs b/DbcParserLib/Observers/SimpleFailureObserver.cs index 45e0a12..c9ceb15 100644 --- a/DbcParserLib/Observers/SimpleFailureObserver.cs +++ b/DbcParserLib/Observers/SimpleFailureObserver.cs @@ -184,6 +184,11 @@ public void ExtraMessageTransmittersDuplicate(uint messageId, string duplicateTr AddError($"Duplicate additional transmitter '{duplicateTransmitter}' in message '{messageId}'"); } + public void SignalExtendedMultiplexingSyntaxError() + { + AddError("[SG_MUL_VAL_] Signal extended multiplexing syntax error"); + } + public void UnknownLine() { AddError("Unknown syntax"); diff --git a/DbcParserLib/Parser.cs b/DbcParserLib/Parser.cs index af8844b..482dca6 100644 --- a/DbcParserLib/Parser.cs +++ b/DbcParserLib/Parser.cs @@ -27,6 +27,7 @@ private static void CreateLineParsers() new EnvironmentVariableLineParser(m_parseObserver), new EnvironmentDataVariableLineParser(m_parseObserver), new ExtraMessageTransmitterLineParser(m_parseObserver), + new ExtendedMultiplexingLineParser(m_parseObserver), new UnknownLineParser(m_parseObserver) // Used as a catch all }; } diff --git a/DbcParserLib/Parsers/ExtendedMultiplexingLineParser.cs b/DbcParserLib/Parsers/ExtendedMultiplexingLineParser.cs new file mode 100644 index 0000000..8260f91 --- /dev/null +++ b/DbcParserLib/Parsers/ExtendedMultiplexingLineParser.cs @@ -0,0 +1,73 @@ +using DbcParserLib.Observers; +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; + +namespace DbcParserLib.Parsers +{ + internal class ExtendedMultiplexingLineParser : ILineParser + { + private const string MessageIdGroup = "MessageId"; + private const string SignalGroup = "Signal"; + private const string MultiplexorSignalGroup = "MultiplexorSignal"; + private const string MultiplexValueRangesGroup = "MultiplexValueRanges"; + + private const string SignalLineStarter = "SG_MUL_VAL_ "; + private readonly string ExtendedMultiplexingRegex = $@"SG_MUL_VAL_\s+(?<{MessageIdGroup}>\d+)\s+(?<{SignalGroup}>\w+)\s+(?<{MultiplexorSignalGroup}>\w+)\s+(?<{MultiplexValueRangesGroup}>(?:\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, ExtendedMultiplexingRegex); + + if (match.Success) + { + var messageId = uint.Parse(match.Groups[MessageIdGroup].Value); + var signal = match.Groups[SignalGroup].Value; + var multiplexorSignal = match.Groups[MultiplexorSignalGroup].Value; + var multiplexorRanges = match.Groups[MultiplexValueRangesGroup].Value; + + var rangesArray = multiplexorRanges.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + var parsedRanges = new List>(); + + 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) + { + parsedRanges.Add(new Tuple(lower, upper)); + } + else + { + m_observer.SignalExtendedMultiplexingSyntaxError(); + return true; + } + } + + builder.AddSignalExtendedMultiplexingInfo(messageId, signal, multiplexorSignal, parsedRanges); + } + else + { + m_observer.SignalExtendedMultiplexingSyntaxError(); + } + + return true; + } + } +} diff --git a/DbcParserLib/Parsers/SignalLineParser.cs b/DbcParserLib/Parsers/SignalLineParser.cs index c0be5e4..16c592d 100644 --- a/DbcParserLib/Parsers/SignalLineParser.cs +++ b/DbcParserLib/Parsers/SignalLineParser.cs @@ -10,7 +10,7 @@ internal class SignalLineParser : ILineParser { private const string NameGroup = "Name"; private const string MultiplexerGroup = "Multiplexer"; - private const string StartBirGroup = "StartBit"; + private const string StartBitGroup = "StartBit"; private const string SizeGroup = "Size"; private const string ByteOrderGroup = "ByteOrder"; private const string ValueTypeGroup = "ValueType"; @@ -24,7 +24,7 @@ internal class SignalLineParser : ILineParser private const string SignedSymbol = "-"; private static readonly string[] CommaSpaceSeparator = { Helpers.Space, Helpers.Comma }; - private readonly string m_signalRegex = $@"\s*SG_\s+(?<{NameGroup}>[\w]+)\s*(?<{MultiplexerGroup}>[Mm\d]*)\s*:\s*(?<{StartBirGroup}>\d+)\|(?<{SizeGroup}>\d+)@(?<{ByteOrderGroup}>[01])" + + private readonly string m_signalRegex = $@"\s*SG_\s+(?<{NameGroup}>[\w]+)\s*(?<{MultiplexerGroup}>[Mm\d]*)\s*:\s*(?<{StartBitGroup}>\d+)\|(?<{SizeGroup}>\d+)@(?<{ByteOrderGroup}>[01])" + $@"(?<{ValueTypeGroup}>[+-])\s+\((?<{FactorGroup}>[\d\+\-eE.]+),(?<{OffsetGroup}>[\d\+\-eE.]+)\)\s+\[(?<{MinGroup}>[\d\+\-eE.]+)\|(?<{MaxGroup}>[\d\+\-eE.]+)\]" + $@"\s+""(?<{UnitGroup}>.*)""\s+(?<{ReceiverGroup}>[\w\s,]+)"; @@ -48,7 +48,7 @@ public bool TryParse(string line, IDbcBuilder builder, INextLineProvider nextLin { Multiplexing = match.Groups[MultiplexerGroup].Value, Name = match.Groups[NameGroup].Value, - StartBit = ushort.Parse(match.Groups[StartBirGroup].Value, CultureInfo.InvariantCulture), + StartBit = ushort.Parse(match.Groups[StartBitGroup].Value, CultureInfo.InvariantCulture), Length = ushort.Parse(match.Groups[SizeGroup].Value, CultureInfo.InvariantCulture), ByteOrder = byte.Parse(match.Groups[ByteOrderGroup].Value, CultureInfo.InvariantCulture), // 0 = MSB (Motorola), 1 = LSB (Intel) ValueType = (match.Groups[ValueTypeGroup].Value == SignedSymbol ? DbcValueType.Signed : DbcValueType.Unsigned),