1111using System . Threading . Tasks ;
1212using SocketCANSharp ;
1313using SocketCANSharp . Network ;
14- using ThingSet . Common . Protocols . Binary ;
1514
1615namespace ThingSet . Common . Transports . Can ;
1716
1817/// <summary>
1918/// CAN transport for ThingSet clients. Supports request/response and
2019/// asynchronous reports.
2120/// </summary>
22- public class CanClientTransport : CanTransportBase , IClientTransport
21+ public class CanClientTransport : ClientTransportBase < byte > , IClientTransport
2322{
2423 private static readonly TimeSpan ExceptionBackoffInterval = TimeSpan . FromSeconds ( 5 ) ;
2524 private const int ExceptionThreshold = 12 ;
2625
2726 private delegate int CanFrameReader ( out uint canId , out byte length , out byte [ ] data ) ;
2827
28+ private readonly ThingSetCanInterface _canInterface ;
29+ private bool _disposeInterface ;
30+
2931 private readonly IsoTpCanSocket _requestResponseSocket ;
3032 private readonly RawCanSocket _subscriptionSocket ;
3133
@@ -34,23 +36,21 @@ public class CanClientTransport : CanTransportBase, IClientTransport
3436 private readonly byte _destinationBridge ;
3537 private readonly byte _destinationNodeAddress ;
3638
37- private readonly Dictionary < byte , ReceiveBuffer > _buffersByNodeAddress = new Dictionary < byte , ReceiveBuffer > ( ) ;
38-
39- private readonly Thread _subscriptionThread ;
40- private bool _runSubscriptionThread = true ;
41-
42- private Action < ReadOnlyMemory < byte > > ? _callback ;
39+ private readonly CanReportParser _reportParser = new CanReportParser ( ) ;
4340
4441 public CanClientTransport ( ThingSetCanInterface canInterface , byte destinationNodeAddress , bool leaveOpen ) : this ( canInterface , CanID . LocalBridge , destinationNodeAddress , leaveOpen )
4542 {
4643 }
4744
48- public CanClientTransport ( ThingSetCanInterface canInterface , byte destinationBridge , byte destinationNodeAddress , bool leaveOpen ) : base ( canInterface , leaveOpen )
45+ public CanClientTransport ( ThingSetCanInterface canInterface , byte destinationBridge , byte destinationNodeAddress , bool leaveOpen )
4946 {
47+ _canInterface = canInterface ;
48+ _disposeInterface = ! leaveOpen ;
49+
5050 _destinationBridge = destinationBridge ;
5151 _destinationNodeAddress = destinationNodeAddress ;
5252
53- _requestResponseSocket = CreateIsoTpCanSocket ( canInterface . IsFdMode ) ;
53+ _requestResponseSocket = IsoTpCanSocketFactory . CreateIsoTpCanSocket ( canInterface . IsFdMode ) ;
5454 _subscriptionSocket = new RawCanSocket
5555 {
5656 CanFilters = new [ ]
@@ -63,15 +63,11 @@ public CanClientTransport(ThingSetCanInterface canInterface, byte destinationBri
6363 } ;
6464
6565 _canFrameReader = canInterface . IsFdMode ? ReadCanFdFrame : ReadCanFrame ;
66-
67- _subscriptionThread = new Thread ( RunSubscriptionThread )
68- {
69- IsBackground = true ,
70- Name = $ "Subscription { _destinationNodeAddress : x} ",
71- } ;
7266 }
7367
74- public ValueTask ConnectAsync ( )
68+ protected override string Address => $ "{ _destinationNodeAddress : x} ";
69+
70+ public override ValueTask ConnectAsync ( )
7571 {
7672 _requestResponseSocket . Bind ( _canInterface . Interface ,
7773 txId : CanID . CreateCanID ( MessageType . RequestResponse , MessagePriority . Channel , _destinationBridge , _canInterface . NodeAddress , _destinationNodeAddress ) ,
@@ -82,29 +78,20 @@ public ValueTask ConnectAsync()
8278 return ValueTask . CompletedTask ;
8379 }
8480
85- public ValueTask SubscribeAsync ( Action < ReadOnlyMemory < byte > > callback )
81+ protected override ValueTask SubscribeAsync ( )
8682 {
87- if ( _callback is not null )
88- {
89- throw new InvalidOperationException ( "There is already a subscription established." ) ;
90- }
91-
92- _callback = callback ;
93-
9483 Console . WriteLine ( "Binding subscription socket" ) ;
9584 _subscriptionSocket . Bind ( _canInterface . Interface ) ;
96- _subscriptionThread . Start ( ) ;
97-
9885 return ValueTask . CompletedTask ;
9986 }
10087
101- public bool Write ( byte [ ] buffer , int length )
88+ public override bool Write ( byte [ ] buffer , int length )
10289 {
10390 int written = LibcNativeMethods . Write ( _requestResponseSocket . SafeHandle , buffer , length ) ;
10491 return written == length ;
10592 }
10693
107- public int Read ( byte [ ] buffer )
94+ public override int Read ( byte [ ] buffer )
10895 {
10996 try
11097 {
@@ -119,14 +106,12 @@ public int Read(byte[] buffer)
119106
120107 protected override void Dispose ( bool disposing )
121108 {
122- base . Dispose ( disposing ) ;
123- _runSubscriptionThread = false ;
124- if ( _subscriptionThread . IsAlive )
125- {
126- _subscriptionThread . Join ( 1000 ) ;
127- }
128109 _subscriptionSocket . Dispose ( ) ;
129110 _requestResponseSocket . Dispose ( ) ;
111+ if ( _disposeInterface )
112+ {
113+ _canInterface . Dispose ( ) ;
114+ }
130115 }
131116
132117 private int ReadCanFrame ( out uint canId , out byte length , out byte [ ] data )
@@ -147,116 +132,86 @@ private int ReadCanFdFrame(out uint canId, out byte length, out byte[] data)
147132 return read ;
148133 }
149134
150- private void RunSubscriptionThread ( )
135+ protected override ValueTask HandleIncomingPublicationsAsync ( )
151136 {
152- while ( _runSubscriptionThread )
137+ List < SocketCanException > exceptions = new List < SocketCanException > ( ) ;
138+ try
153139 {
154- List < SocketCanException > exceptions = new List < SocketCanException > ( ) ;
155- try
140+ int read = _canFrameReader ( out uint canId , out byte length , out byte [ ] data ) ;
141+ if ( read > 0 )
156142 {
157- int read = _canFrameReader ( out uint canId , out byte length , out byte [ ] data ) ;
158- if ( read > 0 )
143+ exceptions . Clear ( ) ;
144+ switch ( CanID . GetType ( canId ) )
159145 {
160- exceptions . Clear ( ) ;
161- switch ( CanID . GetType ( canId ) )
162- {
163- case MessageType . SingleFrameReport :
164- NotifyItem ( canId , data ) ;
165- break ;
166- case MessageType . MultiFrameReport :
167- byte source = CanID . GetSource ( canId ) ;
168- byte messageNumber = CanID . GetMessageNumber ( canId ) ;
169- byte sequence = CanID . GetSequenceNumber ( canId ) ;
170- if ( ! _buffersByNodeAddress . TryGetValue ( source , out ReceiveBuffer ? buffer ) )
171- {
172- _buffersByNodeAddress [ source ] = buffer = new ReceiveBuffer ( ) ;
173- }
174- MultiFrameMessageType type = CanID . GetMultiFrameMessageType ( canId ) ;
175- if ( type == MultiFrameMessageType . Single || type == MultiFrameMessageType . First )
176- {
177- buffer . Started = true ;
178- buffer . MessageNumber = messageNumber ;
179- }
180- else if ( buffer . MessageNumber != messageNumber )
181- {
182- buffer . Reset ( ) ;
183- continue ;
184- }
185- else if ( ! buffer . Started )
186- {
187- buffer . Reset ( ) ;
188- continue ;
189- }
190-
191- if ( sequence == ( buffer . Sequence ++ & 0xf ) )
192- {
193- ReadOnlySpan < byte > span = data ;
194- Span < byte > target = buffer . Buffer ;
195- span . CopyTo ( target . Slice ( buffer . Position ) ) ;
196- buffer . Position += length ;
197- if ( type == MultiFrameMessageType . Single || type == MultiFrameMessageType . Last )
198- {
199- ReadOnlyMemory < byte > memory = buffer . Buffer ;
200- if ( buffer . Buffer [ 0 ] == ( byte ) ThingSetRequest . Report )
201- {
202- NotifyReport ( memory . Slice ( 1 , buffer . Position - 1 ) ) ;
203- }
204- buffer . Reset ( ) ;
205- }
206- }
207- else
208- {
209- // invalid sequence; reset
210- }
211- break ;
212- }
146+ case MessageType . SingleFrameReport :
147+ NotifyItem ( canId , data ) ;
148+ break ;
149+ case MessageType . MultiFrameReport :
150+ MultiFrameMessageType type = CanID . GetMultiFrameMessageType ( canId ) ;
151+ byte sequenceNumber = CanID . GetSequenceNumber ( canId ) ;
152+ byte messageNumber = CanID . GetMessageNumber ( canId ) ;
153+ byte source = CanID . GetSource ( canId ) ;
154+ ReceiveBuffer buffer = _buffersBySender . GetOrAdd ( source , _ => new ReceiveBuffer ( ) ) ;
155+ if ( _reportParser . TryParse ( sequenceNumber , messageNumber , type , buffer , data , out ulong ? eui , out CborReader ? reader ) )
156+ {
157+ NotifyReport ( eui , reader ) ;
158+ }
159+ break ;
213160 }
214161 }
215- catch ( SocketCanException scex )
162+ }
163+ catch ( SocketCanException scex )
164+ {
165+ exceptions . Add ( scex ) ;
166+ if ( exceptions . Count > ExceptionThreshold )
216167 {
217- exceptions . Add ( scex ) ;
218- if ( exceptions . Count > ExceptionThreshold )
219- {
220- throw new AggregateException ( $ "Multiple errors occurred while reading from CAN interface { _canInterface . Interface . Name } .") ;
221- }
222- Thread . Sleep ( ExceptionBackoffInterval ) ;
168+ throw new AggregateException ( $ "Multiple errors occurred while reading from CAN interface { _canInterface . Interface . Name } .", exceptions ) ;
223169 }
170+ Thread . Sleep ( ExceptionBackoffInterval ) ;
224171 }
225- }
226-
227- private void NotifyReport ( ReadOnlyMemory < byte > body )
228- {
229- CborReader reader = new CborReader ( body , CborConformanceMode . Lax , allowMultipleRootLevelValues : true ) ;
230- reader . ReadUInt32 ( ) ;
231- _callback ? . Invoke ( body . Slice ( body . Length - reader . BytesRemaining ) ) ;
172+ return ValueTask . CompletedTask ;
232173 }
233174
234175 private void NotifyItem ( uint canId , ReadOnlyMemory < byte > body )
235176 {
236177 byte [ ] buffer = new byte [ body . Length + 4 ] ;
237178 buffer [ 0 ] = 0xA1 ; // map with 1 element
238- buffer [ 1 ] = 0x19 ; // ushort
239- Span < byte > span = buffer ;
240- BinaryPrimitives . WriteUInt16BigEndian ( span . Slice ( 2 ) , CanID . GetDataID ( canId ) ) ;
179+ ushort id = CanID . GetDataID ( canId ) ;
180+ int headerLength ;
181+ if ( id <= 23 )
182+ {
183+ buffer [ 1 ] = ( byte ) id ;
184+ headerLength = 2 ;
185+ }
186+ else if ( id < 256 )
187+ {
188+ buffer [ 1 ] = 0x18 ;
189+ buffer [ 2 ] = ( byte ) id ;
190+ headerLength = 3 ;
191+ }
192+ else
193+ {
194+ buffer [ 1 ] = 0x19 ; // ushort
195+ Span < byte > span = buffer ;
196+ BinaryPrimitives . WriteUInt16BigEndian ( span . Slice ( 2 ) , id ) ;
197+ headerLength = 4 ;
198+ }
241199 ReadOnlyMemory < byte > source = body ;
242200 Memory < byte > memory = buffer ;
243- source . CopyTo ( memory . Slice ( 4 ) ) ;
244- _callback ? . Invoke ( buffer ) ;
201+ source . CopyTo ( memory . Slice ( headerLength ) ) ;
202+ NotifyReport ( null , new CborReader ( memory ) ) ;
245203 }
246204
247- private class ReceiveBuffer
205+ private class CanReportParser : ReportParser < MultiFrameMessageType >
248206 {
249- public byte [ ] Buffer = new byte [ 32768 ] ;
250- public int Position ;
251- public byte Sequence ;
252- public byte MessageNumber ;
253- public bool Started ;
207+ protected override bool IsFirst ( MultiFrameMessageType type )
208+ {
209+ return type == MultiFrameMessageType . First || type == MultiFrameMessageType . Last ;
210+ }
254211
255- public void Reset ( )
212+ protected override bool IsLast ( MultiFrameMessageType type )
256213 {
257- Position = 0 ;
258- Sequence = 0 ;
259- Started = false ;
214+ return type == MultiFrameMessageType . First || type == MultiFrameMessageType . Last ;
260215 }
261216 }
262217}
0 commit comments