Repository navigation
feat(canopen): add opt-in controller passive EMCYs #487
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
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
7ef6bca
feat(canopen): add opt-in controller passive EMCYs
dborgards 14c86a3
fix(canopen): refresh controller baseline after reset
dborgards ac9cf2c
fix(canopen): retry only expected controller read failures
dborgards 86480a5
fix(canopen): isolate controller reads from reset completion
dborgards a4cc274
fix(canopen): invalidate obsolete controller baseline work
dborgards File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,110 @@ | ||
| using System; | ||
| using CanKit.Abstractions.API.Common.Definitions; | ||
| using CanKit.Core.Exceptions; | ||
| using CanKit.Pro.Reliability; | ||
|
|
||
| namespace CanKit.Pro.CANopen; | ||
|
|
||
| // #423 / FR-CO-011: optional 8120h is an observed controller-level cause. Bus-off and unknown | ||
| // readings do not prove that a previously observed passive condition has been repaired. | ||
| internal sealed partial class CanOpenNode | ||
| { | ||
| private BusStateMonitor? _controllerEmcyMonitor; | ||
| private bool _controllerErrorPassive; | ||
| private bool _controllerEmcyStarted; | ||
| private IDisposable? _controllerBaselineRetry; | ||
| private long _controllerBaselineGeneration; | ||
|
|
||
| private void InitializeControllerErrorMonitor() | ||
| { | ||
| if (!_options.EmitControllerErrorEmcy) return; | ||
| _controllerEmcyMonitor = new BusStateMonitor(_service.Bus, _actor); | ||
| _controllerEmcyMonitor.StateChanged += OnControllerEmcyStateChanged; | ||
| } | ||
|
|
||
| private void InvalidateControllerErrorBaseline() | ||
| { | ||
| _controllerEmcyStarted = false; | ||
| _controllerBaselineGeneration++; | ||
| _controllerBaselineRetry?.Dispose(); | ||
| _controllerBaselineRetry = null; | ||
| } | ||
|
|
||
| private void StartControllerErrorMonitoring() | ||
| { | ||
| if (_controllerEmcyMonitor is null) return; | ||
| InvalidateControllerErrorBaseline(); | ||
| long generation = _controllerBaselineGeneration; | ||
| // Diagnostic reads are a separate actor job. Even an unexpected adapter exception must | ||
| // not interrupt the reset caller's echo bookkeeping or an LSS configure-node-id reply. | ||
| _actor.Post(() => RefreshControllerErrorBaseline(generation)); | ||
| } | ||
|
|
||
| private void RefreshControllerErrorBaseline(long generation) | ||
| { | ||
| if (_controllerEmcyMonitor is null) return; | ||
| if (generation != _controllerBaselineGeneration) return; | ||
| _controllerBaselineRetry?.Dispose(); | ||
| _controllerBaselineRetry = null; | ||
| _controllerEmcyStarted = false; | ||
|
|
||
| // A reset starts a new error history. Refresh both the physical sample and the monitor's | ||
| // comparison baseline: reusing CurrentState could replay a repaired pre-reset lapse, and | ||
| // sampling only the bus could hide the next passive edge behind the monitor's old cache. | ||
| BusStateMonitor fresh; | ||
| try | ||
| { | ||
| fresh = new BusStateMonitor(_service.Bus, _actor); | ||
| } | ||
| catch (Exception ex) when (ex is CanKitException or ObjectDisposedException) | ||
| { | ||
| // A transient status-read failure must neither invent a fresh error nor remove the | ||
| // existing monitor. Retry on the same actor clock; normal teardown cancels this work. | ||
| RaiseBackgroundException(ex); | ||
| _controllerBaselineRetry = _actor.Schedule(System.TimeSpan.FromMilliseconds(50), () => RefreshControllerErrorBaseline(generation)); | ||
| return; | ||
| } | ||
| fresh.StateChanged += OnControllerEmcyStateChanged; | ||
| DisposeControllerErrorMonitor(); | ||
| _controllerEmcyMonitor = fresh; | ||
| _controllerErrorPassive = false; | ||
| _controllerEmcyStarted = true; | ||
| ObserveControllerEmcyState(fresh.CurrentState); | ||
| } | ||
|
|
||
| private void OnControllerEmcyStateChanged(object? sender, BusStateChangedEventArgs e) | ||
| => ObserveControllerEmcyState(e.Current); | ||
|
|
||
| private void ObserveControllerEmcyState(BusState state) | ||
| { | ||
| switch (state) | ||
| { | ||
| case BusState.ErrPassive: | ||
| _controllerErrorPassive = true; | ||
| break; | ||
| case BusState.None: | ||
| case BusState.ErrActive: | ||
| case BusState.ErrWarning: | ||
| _controllerErrorPassive = false; | ||
| EndCommunicationError(CommunicationError.ControllerPassive); | ||
| return; | ||
| } | ||
| ReportActiveControllerError(); | ||
| } | ||
|
dborgards marked this conversation as resolved.
Resolved
|
||
|
|
||
| private void ReportActiveControllerError() | ||
| { | ||
| if (_controllerEmcyStarted && _controllerErrorPassive) | ||
| RaiseCommunicationError(CommunicationError.ControllerPassive, 0, 0x8120); | ||
| } | ||
|
|
||
| private void DisposeControllerErrorMonitor() | ||
| { | ||
| _controllerBaselineRetry?.Dispose(); | ||
| _controllerBaselineRetry = null; | ||
| if (_controllerEmcyMonitor is not { } monitor) return; | ||
| monitor.StateChanged -= OnControllerEmcyStateChanged; | ||
| monitor.Dispose(); | ||
| _controllerEmcyMonitor = null; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.