Separate the OPC UA logic of the Workshop clients from their windows - #866
Merged
Merged
Conversation
… the foundation Adds the UI-free half of the sample clients as a library, Opc.Ua.Samples.Client: the SampleClientModel base class (attach/detach lifecycle, reconnect entry points, events posted to the thread the model was created on), the session helpers moved out of ClientUtils (which keeps forwarders), the subscription pumps, and a managed session factory. Adds the headless model tier, Tests/SampleClientModels.Tests, with its fixture base, event sink and the contract test which keeps every model free of Windows Forms. Splits the Empty client (the template) and the Boiler client (the reference implementation) into a Model/<X>ClientModel and a thin window. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ents into a model and a thin window Each client keeps its OPC UA logic in Model/<X>ClientModel, built on SampleClientModel; the window hands it the session of the connect control and renders what it reports. The model tier gains a fixture per client; Aggregation is declared as its one gap, as in tier 2. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s-samples-architecture-1375ef
…sfer clients into a model and a thin window Each client gets a UI-free Model/<Sample>ClientModel.cs on SampleClientModel which owns the session-dependent state and answers OperationResult/Outcome records for the status bar; the forms keep ConnectServerCTRL, hand the session over in Server_ConnectCompleteAsync and render what the model found. Every handler and control name tier 2 reflects on survives verbatim. One headless fixture per sample drives the model against the in-process sample server. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s-samples-architecture-1375ef
…s clients into a model and a thin window Each client keeps its OPC UA logic in a Model/<Sample>ClientModel on the shared SampleClientModel base: StateMachines streams both machines through a SubscriptionPump and serialises the UserExecutable reads so PermittedCauses is final when the attach returns; NodeManagement answers every service with an OperationResult and pumps the model change events; PerfTest wraps the Tester, which moves to Model/; HistoricalAccess keeps the selection and the two questions the history control answers for itself. The windows only map controls to model calls and render what the model reports. A headless fixture per sample drives the models against the in-process sample servers. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s-samples-architecture-1375ef
…thin window DataAccessClientModel owns the lazy subscription, the monitored item entries, browse/read/write and the paged history reads; HistoricalEventsClientModel owns the area, the filter, the paged event history and an EventStream which runs the streaming enumeration on a SubscriptionPump and computes the display texts in order. The forms, EventListView and the dialogs only render and call one model method per handler. Two headless fixtures drive the models against the in-process sample servers. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s-samples-architecture-1375ef
…ist the model fixtures in the testing guide Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
AlarmConditionClientModel owns the event subscription, the condition table and the Part 9 calls; AuditTrailModel owns the streaming audit subscription; MainForm and AuditEventForm only render snapshots and forward clicks. Every event notification now goes through one SerialNotificationPump consumer, which is what fixes the documented reentrancy of the old async void handler. The filter asks for the RefreshStart and RefreshEnd events explicitly (the stack runs them through the where clause), the refresh is addressed to the monitored item (a subscription refresh replays nothing on preview.4), and a filter swap removes the old item before it creates the new one, because the other order leaves the new item without live events. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s-samples-architecture-1375ef
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Contributor
Author
|
Merged |
Master extended four of the clients this branch had already split, so their new features are ported into the models rather than back into the windows: - RoleManagement (#864): the identity criteria (UserName/Thumbprint/X509Subject) and the criteria strings this client can be matched by, the CustomConfiguration flag, the AccessRestrictions and Endpoints columns, and the audit trail stream all live in RoleManagementClientModel; the window renders them. - StateMachines (#860): the materialized Part 16 model (available states and transitions), the sub state machine below Running with its StartBatch cause, and the effective state stream live in StateMachinesClientModel. - NodeManagement: the hand written model change pump is replaced by the stack's ModelChangeTracker inside the model. - UserAuthentication: the Kerberos and SAML stubs are gone from the model and the window, as on master. The model fixtures gained cases for the new surface; the testing guide keeps the model fixture column with master's updated StateMachines row. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Every Workshop sample client kept its whole OPC UA logic inside
MainForm.cs, inasync voidhandlers next to theListViewupdates. That is why the samples could only be tested through a never-shown form on an STA thread driven by reflection, and why defects in the sample logic (the RoleManagement Reset button, the HistoricalEventsDateTimeUtcunbox, the AlarmCondition reentrancy) survived until a user pressed a button. The servers already went through this split (#856); this PR does the clients.What changes
Samples/Client.Common(Opc.Ua.Samples.Client), the UI-free half of a sample client: theSampleClientModelbase class (attach/detach lifecycle, reconnect entry points, events posted to the thread the model was created on, a detach that never throws because the connect control disposes its session before it reports the disconnect),SampleSession(the helpers moved out ofClientUtils, which keeps forwarders so nothing else changes),SubscriptionPump,SerialNotificationPump<T>,OperationResultandSampleSessionFactory. See its README.Model/<X>ClientModel.cswhich never references Windows Forms, and a thinMainForm.cswhich hands the connect control's session to the model and renders its events. The Boiler client is the reference implementation, the Empty client the template. Every name tier 2 reflects on survived verbatim; the only tier 2 source edit is theConditionSnapshotcast inSampleClientActionTests.Tests/SampleClientModels.Tests: a fixture per model against the in-process sample server over a managed session, no STA thread, no message loop, no reflection.ModelContractTestsenforces the rule by reflection: any field, property, event, parameter or return type fromSystem.Windows.Forms,System.Drawingor the control library in aModelnamespace fails, and so does a client without a model.SerialNotificationPump, one consumer does the awaits in arrival order; the fixture asserts that handlers are never entered concurrently and that the list is cleared before a refresh replays.docs/TESTING.md(the tier, the fourth "why this works" property, a fixture column in the per-client table), the library README, links from the Hosting README and the root README.Verification
All five tiers on the merged branch, both solutions building with zero errors:
Worth knowing
Workshop/Common) are collected in Client models: the follow-ups left open by the Workshop client split #865.🤖 Generated with Claude Code