Skip to content

Commit 165df0c

Browse files
committed
refactor(CanBus): Add FaultOccurred event and improve error handling in CAN bus adapters
1 parent a16f6ce commit 165df0c

File tree

12 files changed

+330
-245
lines changed

12 files changed

+330
-245
lines changed

samples/CanKit.Sample.IsoTpDemo/CanKit.Sample.IsoTpDemo.csproj

Lines changed: 0 additions & 12 deletions
This file was deleted.

samples/CanKit.Sample.IsoTpDemo/Program.cs

Lines changed: 0 additions & 128 deletions
This file was deleted.

src/adapters/CanKit.Adapter.ControlCAN/ControlCanBus.cs

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ public sealed class ControlCanBus : ICanBus<ControlCanBusRtConfigurator>, IOwner
3535
private Func<CanFrame, bool>? _softwareFilterPredicate;
3636

3737
private readonly HashSet<int> _autoSendIndexes = new();
38+
private readonly object _autoSendGate = new();
39+
3840
private readonly ControlCanDeviceKind _devType;
3941
private readonly uint _rawDevType;
4042
private readonly uint _devIndex;
@@ -326,17 +328,18 @@ public event EventHandler<ICanErrorInfo>? ErrorFrameReceived
326328
}
327329

328330
public event EventHandler<Exception>? BackgroundExceptionOccurred;
331+
public event EventHandler<Exception>? FaultOccurred;
329332

330333
public void Dispose()
331334
{
332335
if (_isDisposed) return;
333336
try
334337
{
338+
_isDisposed = true;
335339
StopReceiveLoop();
336340
}
337341
finally
338342
{
339-
_isDisposed = true;
340343
_owner?.Dispose();
341344
}
342345
}
@@ -368,8 +371,8 @@ private void StopReceiveLoop()
368371
catch { /* ignore on shutdown */ }
369372
finally
370373
{
371-
Interlocked.CompareExchange(ref _pollTask, task, null);
372-
Interlocked.CompareExchange(ref _pollCts, cts, null);
374+
Interlocked.CompareExchange(ref _pollTask, null, task);
375+
Interlocked.CompareExchange(ref _pollCts, null, cts);
373376
cts?.Dispose();
374377
CanKitLogger.LogDebug("ControlCAN: Poll loop stopped.");
375378
}
@@ -386,14 +389,21 @@ private void PollLoop(CancellationToken token)
386389
var errSnap = Volatile.Read(ref _errorOccurred);
387390
if (errSnap != null && Options.AllowErrorInfo && ReadErrorInfo(out var info) && info is not null)
388391
{
389-
try { errSnap.Invoke(this, info); } catch { }
392+
try
393+
{
394+
errSnap.Invoke(this, info);
395+
}
396+
catch (Exception e)
397+
{
398+
HandleBackgroundException(e, false);
399+
}
390400
}
391401
PreciseDelay.Delay(TimeSpan.FromMilliseconds(Math.Max(1, Options.PollingInterval)), ct: token);
392402
}
393403
}
394404
catch (Exception ex)
395405
{
396-
HandleBackgroundException(ex);
406+
HandleBackgroundException(ex, true);
397407
}
398408
}
399409

@@ -406,18 +416,39 @@ private void DrainReceive()
406416
{
407417
if (_softwareFilterPredicate != null && !_softwareFilterPredicate(frame.CanFrame))
408418
continue;
409-
_frameReceived?.Invoke(this, frame);
419+
try
420+
{
421+
var evSnap = Volatile.Read(ref _frameReceived);
422+
evSnap?.Invoke(this, frame);
423+
}
424+
catch (Exception e)
425+
{
426+
HandleBackgroundException(e, false);
427+
}
410428
_asyncRx.Publish(frame);
411429
any = true;
412430
}
413431
if (!any) break;
414432
}
415433
}
416434

417-
private void HandleBackgroundException(Exception ex)
435+
private void HandleBackgroundException(Exception ex, bool fault)
418436
{
419437
try { CanKitLogger.LogError("ControlCAN bus occurred background exception.", ex); } catch { }
420-
try { _asyncRx.ExceptionOccured(ex); } catch { }
438+
439+
if (fault)
440+
{
441+
try { _asyncRx.ExceptionOccured(ex); } catch { }
442+
443+
try
444+
{
445+
var faultSpan = Volatile.Read(ref FaultOccurred);
446+
faultSpan?.Invoke(this, ex);
447+
}
448+
catch { }
449+
StopReceiveLoop();
450+
}
451+
421452
try { var snap = Volatile.Read(ref BackgroundExceptionOccurred); snap?.Invoke(this, ex); } catch { }
422453
}
423454

@@ -452,21 +483,28 @@ internal int GetAutoSendIndex(bool add = true)
452483
{
453484
ThrowIfDisposed();
454485
int x = 0;
455-
while (true)
486+
lock (_autoSendGate)
456487
{
457-
if (!_autoSendIndexes.Contains(x))
458-
break;
459-
x++;
488+
while (true)
489+
{
490+
if (!_autoSendIndexes.Contains(x))
491+
break;
492+
x++;
493+
}
494+
if (add)
495+
_autoSendIndexes.Add(x);
460496
}
461-
if (add)
462-
_autoSendIndexes.Add(x);
497+
463498
return x;
464499
}
465500

466501
internal bool FreeAutoSendIndex(int index)
467502
{
468503
ThrowIfDisposed();
469-
return _autoSendIndexes.Remove(index);
504+
lock (_autoSendGate)
505+
{
506+
return _autoSendIndexes.Remove(index);
507+
}
470508
}
471509

472510
private bool ReadErrorInfo(out ICanErrorInfo? info)

src/adapters/CanKit.Adapter.Kvaser/KvaserBus.cs

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,7 @@ private void KvNotifyCallback(int handle, IntPtr context, int notifyEvent)
486486
}
487487
catch (Exception ex)
488488
{
489-
HandleBackgroundException(ex); //will not throw in poll loop
489+
HandleBackgroundException(ex, true); //will not throw in poll loop
490490
}
491491
finally
492492
{
@@ -514,21 +514,29 @@ private void DrainReceive()
514514
{
515515
var enableErrorCounter = (Options.Features & CanFeature.ErrorCounters) != 0;
516516
CanErrorCounters? errorCounters = enableErrorCounter ? ErrorCounters() : null;
517-
_errorOccured?.Invoke(this, new DefaultCanErrorInfo(
518-
FrameErrorType.Unknown,
519-
enableErrorCounter ?
520-
CanKitExtension.ToControllerStatus(errorCounters!.Value.ReceiveErrorCounter, errorCounters.Value.TransmitErrorCounter) : CanControllerStatus.Unknown,
521-
CanProtocolViolationType.Unknown,
522-
FrameErrorLocation.Invalid,
523-
CanTransceiverStatus.Unknown,
524-
DateTime.Now,
525-
0,
526-
null,
527-
FrameDirection.Unknown,
528-
null,
529-
errorCounters,
530-
null
531-
));
517+
try
518+
{
519+
var errSnap = Volatile.Read(ref _errorOccured);
520+
errSnap?.Invoke(this, new DefaultCanErrorInfo(
521+
FrameErrorType.Unknown,
522+
enableErrorCounter ?
523+
CanKitExtension.ToControllerStatus(errorCounters!.Value.ReceiveErrorCounter, errorCounters.Value.TransmitErrorCounter) : CanControllerStatus.Unknown,
524+
CanProtocolViolationType.Unknown,
525+
FrameErrorLocation.Invalid,
526+
CanTransceiverStatus.Unknown,
527+
DateTime.Now,
528+
0,
529+
null,
530+
FrameDirection.Unknown,
531+
null,
532+
errorCounters,
533+
null
534+
));
535+
}
536+
catch (Exception e)
537+
{
538+
HandleBackgroundException(e, false);
539+
}
532540
continue;
533541
}
534542

@@ -540,7 +548,15 @@ private void DrainReceive()
540548
continue;
541549
}
542550
}
543-
_frameReceived?.Invoke(this, rec);
551+
try
552+
{
553+
var evSnap = Volatile.Read(ref _frameReceived);
554+
evSnap?.Invoke(this, rec);
555+
}
556+
catch (Exception e)
557+
{
558+
HandleBackgroundException(e, false);
559+
}
544560
_asyncRx.Publish(rec);
545561
}
546562
if (!any) break;
@@ -561,11 +577,24 @@ public async Task<IReadOnlyList<CanReceiveData>> ReceiveAsync(int count = 1, int
561577
}
562578

563579
public event EventHandler<Exception>? BackgroundExceptionOccurred;
580+
public event EventHandler<Exception>? FaultOccurred;
564581

565-
private void HandleBackgroundException(Exception ex)
582+
private void HandleBackgroundException(Exception ex, bool fault)
566583
{
567584
try { CanKitLogger.LogError("Kvaser bus occured background exception.", ex); } catch { /*ignored*/ }
568-
try { _asyncRx.ExceptionOccured(ex); } catch { /*ignored*/ }
585+
if (fault)
586+
{
587+
try { _asyncRx.ExceptionOccured(ex); } catch { /*ignored*/ }
588+
try
589+
{
590+
var faultSpan = Volatile.Read(ref FaultOccurred);
591+
faultSpan?.Invoke(this, ex);
592+
}
593+
catch { }
594+
StopReceiveLoop();
595+
596+
}
597+
569598
try { var snap = Volatile.Read(ref BackgroundExceptionOccurred); snap?.Invoke(this, ex); } catch { /*ignored*/ }
570599
}
571600

src/adapters/CanKit.Adapter.PCAN/Native/PcanNative.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66

77
namespace CanKit.Adapter.PCAN.Native;
8+
89
using TPCANHandle = UInt16;
910
using TPCANTimestampFD = UInt64;
1011
internal static class PcanBasicNative

0 commit comments

Comments
 (0)