Skip to content

Commit dd44fe5

Browse files
KyllianAubryGitHub Enterprise
authored andcommitted
SKA-993: fmi-ls-bus ethernet (#213)
* SKA-993: SIL Kit Ethernet API wrapped * SKA-993: add Ethernet support in SIL Kit / FMU bridge * SKA-993: add Ethernet handling in FmuImporter * SKA-993: add Ethernet terminal validation
1 parent 66d091a commit dd44fe5

14 files changed

Lines changed: 1064 additions & 71 deletions

File tree

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// SPDX-License-Identifier: MIT
2+
// Copyright (c) Vector Informatik GmbH. All rights reserved.
3+
4+
using System.Buffers.Binary;
5+
using System.Runtime.InteropServices;
6+
7+
namespace Fmi.FmiModel.Internal;
8+
9+
public enum EthernetOperations : uint
10+
{
11+
Format_Error = 0x01,
12+
Transmit = 0x10,
13+
Confirm = 0x20,
14+
Bus_Error = 0x30,
15+
Configuration = 0x40,
16+
Wakeup = 0x41
17+
}
18+
19+
[StructLayout(LayoutKind.Sequential, Pack = 1)]
20+
public class EthernetTransmitOperation
21+
{
22+
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
23+
public byte[] OPCode;
24+
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
25+
public byte[] Length;
26+
public byte StartDelimiter;
27+
public byte FragmentCounter;
28+
public byte LastFragment;
29+
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
30+
public byte[] DestinationAddress;
31+
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
32+
public byte[] SourceAddress;
33+
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
34+
public byte[] TypeLength;
35+
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
36+
public byte[] DataLength;
37+
// Data has an unknown size
38+
public IntPtr Data;
39+
40+
public EthernetTransmitOperation()
41+
{
42+
OPCode = new byte[4];
43+
Length = new byte[4];
44+
StartDelimiter = 0xD5; // default value for ordinary Ethernet frames
45+
FragmentCounter = 0; // not a continuation mPacket
46+
LastFragment = 1; // true if full Ethernet frame
47+
DestinationAddress = new byte[6];
48+
SourceAddress = new byte[6];
49+
TypeLength = new byte[2];
50+
DataLength = new byte[4];
51+
}
52+
53+
public EthernetTransmitOperation(int dataSize) : this()
54+
{
55+
Data = Marshal.AllocHGlobal(dataSize);
56+
SetDataLength(dataSize);
57+
}
58+
59+
public void SetOPCode(Int32 opCode)
60+
{
61+
BinaryPrimitives.WriteInt32LittleEndian(OPCode, opCode);
62+
}
63+
64+
public void SetLength(UInt32 length)
65+
{
66+
BinaryPrimitives.WriteUInt32LittleEndian(Length, length);
67+
}
68+
69+
public void SetDestinationAddress(byte[] address)
70+
{
71+
if (address.Length != 6)
72+
{
73+
throw new ArgumentException("Ethernet address must be 6 bytes long");
74+
}
75+
Array.Copy(address, DestinationAddress, 6);
76+
}
77+
78+
public byte[] GetDestinationAddress()
79+
{
80+
return DestinationAddress;
81+
}
82+
public void SetSourceAddress(byte[] address)
83+
{
84+
if (address.Length != 6)
85+
{
86+
throw new ArgumentException("Ethernet address must be 6 bytes long");
87+
}
88+
Array.Copy(address, SourceAddress, 6);
89+
}
90+
public byte[] GetSourceAddress()
91+
{
92+
return SourceAddress;
93+
}
94+
public void SetTypeLength(byte[] typeLength)
95+
{
96+
if (typeLength.Length != 2)
97+
{
98+
throw new ArgumentException("Ethernet TypeLength must be 2 bytes long");
99+
}
100+
Array.Copy(typeLength, TypeLength, 2);
101+
}
102+
public byte[] GetTypeLength()
103+
{
104+
return TypeLength;
105+
}
106+
107+
public void SetDataLength(Int32 dataLength)
108+
{
109+
BinaryPrimitives.WriteInt32LittleEndian(DataLength, dataLength);
110+
}
111+
112+
public Int32 GetDataLength()
113+
{
114+
return BinaryPrimitives.ReadInt32LittleEndian(DataLength);
115+
}
116+
117+
public void SetData(byte[] bytes)
118+
{
119+
if (Data != IntPtr.Zero)
120+
{
121+
Marshal.FreeHGlobal(Data);
122+
Data = IntPtr.Zero;
123+
}
124+
125+
Data = Marshal.AllocHGlobal(GetDataLength());
126+
Marshal.Copy(bytes, 0, Data, GetDataLength());
127+
}
128+
129+
public byte[] GetData()
130+
{
131+
var size = GetDataLength();
132+
byte[] byteArray = new byte[size];
133+
Marshal.Copy(Data, byteArray, 0, size);
134+
return byteArray;
135+
}
136+
137+
public byte[] GetBytes()
138+
{
139+
var bytes = OPCode.Concat(Length).Concat(new byte[] { StartDelimiter }).Concat(new byte[] { FragmentCounter })
140+
.Concat(new byte[] { LastFragment }).Concat(DestinationAddress).Concat(SourceAddress).Concat(TypeLength)
141+
.Concat(DataLength).Concat(GetData());
142+
143+
return bytes.ToArray();
144+
}
145+
}

FmuImporter/FmiBridge/FmiModel/Internal/Terminal.cs

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,34 @@ public enum InternalTerminalKind
99
{
1010
UNKNOWN = 0,
1111
CAN = 1,
12-
RPC_CLIENT = 2,
13-
RPC_SERVER = 3
12+
ETHERNET = 2,
13+
RPC_CLIENT = 3,
14+
RPC_SERVER = 4
1415
}
1516

1617
public class Terminal
1718
{
1819
public static class Constants
1920
{
21+
// General FMI-LS-BUS Constants
22+
public const string LS_BUS_TerminalKind = "org.fmi-ls-bus.network-terminal";
23+
public const string LS_BUS_MatchingRule = "org.fmi-ls-bus.transceiver";
24+
public const string LS_BUS_VariableKind = "signal";
25+
public const string LS_BUS_ConfigurationTerminalKind = "org.fmi-ls-bus.network-terminal.configuration";
26+
public const string LS_BUS_ConfigurationMatchingRule = "bus";
27+
public const string LS_BUS_ConfigurationTerminalName = "Configuration";
28+
2029
// CAN Terminal Constants
2130
public const string CanMimeType = "application/org.fmi-standard.fmi-ls-bus.can";
22-
public const string CanTerminalKind = "org.fmi-ls-bus.network-terminal";
23-
public const string CanMatchingRule = "org.fmi-ls-bus.transceiver";
24-
public const string CanNestedTerminalKind = "org.fmi-ls-bus.network-terminal.configuration";
25-
public const string CanNestedMatchingRule = "bus";
26-
public const string CanNestedTerminalName = "Configuration";
31+
32+
// Ethernet Terminal Constants
33+
public const string EthernetMimeType = "application/org.fmi-standard.fmi-ls-bus.ethernet";
34+
public const string EthernetSegmentTerminalKind = "org.fmi-ls-bus.ethernet-segment-terminal";
35+
public const string EthernetSwitchTerminalKind = "org.fmi-ls-bus.ethernet-switch-terminal";
2736

2837
// RPC Terminal Constants
2938
public const string RpcTerminalKind = "vnd.vector.operation-terminal.v1";
3039
public const string RpcMatchingRule = "plug";
31-
32-
// Member Names
33-
public const string SignalVariableKind = "signal";
3440
}
3541

3642
public InternalTerminalKind InternalTerminalKind { get; set; }
@@ -120,9 +126,15 @@ private void CheckTerminalKind(ModelDescription modelDescription)
120126
if (variable.MimeType?.Contains(Constants.CanMimeType) == true)
121127
{
122128
// if a mimeType for fmi-ls-bus can has been found, validate the whole corresponding Terminal
123-
ValidateCANTerminal();
129+
ValidateCanTerminal();
130+
InternalTerminalKind = InternalTerminalKind.CAN;
124131
break;
125132
}
133+
else if (variable.MimeType?.Contains(Constants.EthernetMimeType) == true)
134+
{
135+
ValidateEthernetTerminal();
136+
InternalTerminalKind = InternalTerminalKind.ETHERNET;
137+
}
126138
else if (TerminalKind.Equals(Constants.RpcTerminalKind))
127139
{
128140
if (variable.Name.Contains("Tx_CallId") || variable.Name.Contains("Rx_ReturnId"))
@@ -141,28 +153,37 @@ private void CheckTerminalKind(ModelDescription modelDescription)
141153
}
142154
}
143155

144-
private void ValidateCANTerminal()
156+
private void ValidateLsBusTerminal()
145157
{
146-
if ((MatchingRule != Constants.CanMatchingRule) || (TerminalKind != Constants.CanTerminalKind))
158+
if ((MatchingRule != Constants.LS_BUS_MatchingRule) || (TerminalKind != Constants.LS_BUS_TerminalKind))
147159
{
148-
throw new TerminalsAndIconsException($"Terminal {Name} does not match fmi-ls-bus can " +
149-
$"matchingRule=\"{Constants.CanMatchingRule}\" or terminalKind=\"{Constants.CanTerminalKind}\".");
160+
throw new TerminalsAndIconsException($"Terminal {Name} does not match fmi-ls-bus " +
161+
$"matchingRule=\"{Constants.LS_BUS_MatchingRule}\" or terminalKind=\"{Constants.LS_BUS_TerminalKind}\".");
150162
}
151163

152-
InternalTerminalKind = InternalTerminalKind.CAN;
153164
foreach (var pairNameMember in TerminalMemberVariables)
154165
{
155166
if (pairNameMember.Value.MemberName is not ("Rx_Clock" or "Tx_Clock" or "Rx_Data" or "Tx_Data"))
156167
{
157168
throw new TerminalsAndIconsException($"TerminalMemberVariable {pairNameMember.Key} must match one of the " +
158169
$"following memberName: Rx_Clock, Tx_Clock, Rx_Data, Tx_Data.");
159170
}
160-
if (pairNameMember.Value.VariableKind != Constants.SignalVariableKind)
171+
if (pairNameMember.Value.VariableKind != Constants.LS_BUS_VariableKind)
161172
{
162-
throw new TerminalsAndIconsException($"TerminalMemberVariable {pairNameMember.Key} must have '{Constants.SignalVariableKind}'" +
163-
$"as variableKind.");
173+
throw new TerminalsAndIconsException($"TerminalMemberVariable {pairNameMember.Key} must have '{Constants.LS_BUS_VariableKind}'" +
174+
$"as variableKind.");
164175
}
165176
}
177+
}
178+
179+
private void ValidateEthernetTerminal()
180+
{
181+
ValidateLsBusTerminal();
182+
}
183+
184+
private void ValidateCanTerminal()
185+
{
186+
ValidateLsBusTerminal();
166187

167188
if ((NestedTerminals == null) || (NestedTerminals.Count == 0))
168189
{
@@ -175,21 +196,21 @@ private void ValidateCANTerminal()
175196
}
176197

177198
var nestedTerminal = NestedTerminals.First().Value;
178-
if (nestedTerminal.Name != Constants.CanNestedTerminalName)
199+
if (nestedTerminal.Name != Constants.LS_BUS_ConfigurationTerminalName)
179200
{
180201
throw new TerminalsAndIconsException($"Terminal {Name} contains the nested terminal " +
181-
$"{NestedTerminals.First().Key} with the name {nestedTerminal.Name}. It must be {Constants.CanNestedTerminalName}.");
202+
$"{NestedTerminals.First().Key} with the name {nestedTerminal.Name}. It must be {Constants.LS_BUS_ConfigurationTerminalName}.");
182203
}
183-
if (nestedTerminal.TerminalKind != Constants.CanNestedTerminalKind)
204+
if (nestedTerminal.TerminalKind != Constants.LS_BUS_ConfigurationTerminalKind)
184205
{
185206
throw new TerminalsAndIconsException($"Terminal {Name} contains the nested terminal " +
186207
$"{NestedTerminals.First().Key} with the terminalKind {nestedTerminal.TerminalKind}. It must be " +
187-
$"{Constants.CanNestedTerminalKind}.");
208+
$"{Constants.LS_BUS_ConfigurationTerminalKind}.");
188209
}
189-
if (nestedTerminal.MatchingRule != Constants.CanNestedMatchingRule)
210+
if (nestedTerminal.MatchingRule != Constants.LS_BUS_ConfigurationMatchingRule)
190211
{
191212
throw new TerminalsAndIconsException($"Terminal {Name} contains the nested terminal " +
192-
$"{NestedTerminals.First().Key} with the matchingRule {nestedTerminal.MatchingRule}. It must be {Constants.CanNestedMatchingRule}.");
213+
$"{NestedTerminals.First().Key} with the matchingRule {nestedTerminal.MatchingRule}. It must be {Constants.LS_BUS_ConfigurationMatchingRule}.");
193214
}
194215
}
195216

@@ -228,9 +249,9 @@ void validateRpcVariables(string[] required, string[] optional)
228249
var memberVariable = pairNameMember.Value;
229250

230251
// check variable kind
231-
if (memberVariable.VariableKind != Constants.SignalVariableKind)
252+
if (memberVariable.VariableKind != Constants.LS_BUS_VariableKind)
232253
{
233-
throw new TerminalsAndIconsException($"TerminalMemberVariable {variableName} must have '{Constants.SignalVariableKind}' " +
254+
throw new TerminalsAndIconsException($"TerminalMemberVariable {variableName} must have '{Constants.LS_BUS_VariableKind}' " +
234255
$"as variableKind.");
235256
}
236257

0 commit comments

Comments
 (0)