# White Paper: Printing from a Smart Print Controller to Meteor Boards Using OPC/UA ## 1. Abstract This white paper describes a demo implementation that submits a PDF job to Smart Print Controller (SPC), controls the print workflow over OPC/UA, and coordinates optional Meteor board operations through a separate Meteor OPC/UA endpoint. The sample application in this repository uses SPC OPC/UA for operator-level orchestration, job and media state management, print-run control, and print-configuration changes, while using the SPC waiting-queue hot folder for PDF ingress. When Meteor-specific features are enabled, the application additionally uses Meteor OPC/UA to apply device-side parameters such as cross-web offset, X resolution scaling, Y crop, and product-detect commands. The result is an end-to-end remote print-control path suitable for demonstration, integration prototyping, and engineering evaluation. ## 2. Scope and Purpose This document describes a demo implementation of printing from a Smart Print Controller to Meteor boards using OPC/UA. The purpose of the demo program is to show how to manage the entire printing workflow from a remote interface via OPC/UA, including job control, data transmission, and device interaction. The document covers: - End-to-end workflow using the sample project in this repository. - SPC-side OPC/UA control of media selection, settings application, job discovery, preprocessing monitoring, print-queue submission, and print-run start/stop state observation. - Meteor-side OPC/UA control used by the sample for cross-web offset, X resolution scaling, Y crop, readiness observation, and ForcePD. - The hybrid data path in which PDF payloads are delivered through the SPC waiting-queue hot folder and operational control is delivered through OPC/UA. - Demo-specific operational behaviors, including preload mode and print-configuration updates for side A or side B. The document is out of scope for: - Low-level board firmware behavior. - Meteor SDK internals not exposed through the used OPC/UA nodes and methods. - Press mechanics, motion control, transport tuning, and head electronics design. - Production hardening beyond the best-practice guidance provided here. - Any SPC or Meteor capability not used by the sample application. ## 3. Introduction Industrial digital printing systems commonly separate image-processing orchestration from print-engine execution. In this demo architecture, SPC acts as the supervisory print application that manages job intake, media configuration, preprocessing, queuing, and print-run lifecycle. Meteor hardware and its software interface provide access to print-engine and controller-level behaviors needed to drive the physical output path. OPC/UA is used because it provides a vendor-neutral, structured, and secure way to expose operational state and callable methods for external control. The official SPC OPC/UA interface guide positions this interface as an operator-level integration surface for external clients, with state grouped into top-level nodes such as `App`, `AllJobs`, `PrintRun`, `PrintRunConfig`, and `Servers`. The linked documentation also states that reads are broadly available but writes and method calls require external control to be granted by the SPC operator. In practice, this gives a remote application a controlled way to supervise printing without embedding custom logic inside SPC itself. The sample application in this repository is intentionally pragmatic: - It uses OPC/UA wherever SPC or Meteor expose control and status. - It uses the SPC waiting-queue hot folder for actual PDF submission. - It monitors workflow completion by polling documented SPC job state and print-run state. - It adds optional Meteor-specific controls for scenarios where engine-side tuning or product-detect interaction is required. ## 4. System Architecture ### 4.1 Functional Components - Smart Print Controller - Hosts the primary OPC/UA server used for print workflow orchestration. - Owns the waiting queue hot folder used for PDF import. - Manages media, preprocessing, queues, and print-run state. - OPC/UA communication layer - Connects the remote CLI to SPC and, when needed, to the Meteor endpoint. - Carries reads, writes, subscriptions, and method invocations. - Meteor board and Meteor monitor/plugin layer - Exposes board, controller, and head state through Meteor OPC/UA. - Accepts engine-related commands such as parameter updates and ForcePD. - Remote interface - Implemented here as the `SPC_RemoteControl` .NET CLI. - Acts as the remote operator workflow controller. - File ingress path - The PDF and its `.complete` metadata file are written directly into SPC's waiting-queue hot folder. ### 4.2 Logical Architecture Plain-text diagram description: The remote interface or CLI connects to the SPC OPC/UA endpoint and, when Meteor-specific features are needed, also connects to the Meteor OPC/UA endpoint. The same remote interface writes PDF jobs into the SPC waiting-queue hot folder. SPC then drives the workflow engine, which manages RIP, queueing, and print-run control. In parallel, the Meteor endpoint exposes board, controller, and head level state through the Meteor monitor or plugin layer. ### 4.3 Data Flow Summary 1. The remote application connects to SPC OPC/UA. 2. It verifies external control and resolves the active or requested media. 3. It optionally connects to Meteor OPC/UA if Meteor-specific features are requested. 4. It reads `App/WaitingQueueFolder` from SPC. 5. It copies the PDF into the hot folder and writes a matching `.complete` JSON file. 6. SPC imports the job and exposes it under `AllJobs/`. 7. The remote application monitors preprocessing through `PreprocessorStatus` and `Printability`. 8. It calls `AllJobs.SendToPrintQueue(jobGuid)`. 9. It calls `PrintRun.StartPrintRun()` in normal mode, or earlier in preload mode. 10. Once the print run is active, the remote application can issue print-config changes and plugin-based commands such as Y crop. 11. Meteor status is observed to determine readiness, buffer level, and PD behavior. ### 4.4 Sequence Diagram Plain-text sequence description: 1. The remote CLI connects to SPC OPC/UA and verifies `App.IsExternalControlActive`. 2. The CLI reads media, job, and waiting-folder nodes from SPC. 3. If Meteor features are enabled, the CLI connects to Meteor OPC/UA and prepares any required Meteor settings. 4. The CLI copies the PDF into the SPC hot folder and writes the `.complete` metadata file. 5. SPC imports the job and exposes a new job GUID in `AllJobs/WaitingQueue`. 6. The CLI polls `PreprocessorStatus` and `Printability`. 7. The CLI calls `AllJobs.SendToPrintQueue(jobGuid)`. 8. The CLI calls `PrintRun.StartPrintRun()`. 9. If post-start Meteor operations are needed, the CLI calls SPC plugin methods such as `Servers.RunPluginCommand(...)` and reads Meteor status including buffer, PD, controller, and head information. ## 5. Prerequisites and Requirements ### 5.1 Hardware - A system running Smart Print Controller with OPC/UA enabled. - One or more Meteor-enabled RIP servers or print-engine paths configured in SPC. - Meteor boards/controllers reachable through the Meteor monitor/plugin OPC/UA endpoint when Meteor-specific options are needed. - Shared or local filesystem access to the SPC waiting-queue hot folder. ### 5.2 Software - .NET 8 runtime or SDK for building and running the sample CLI. - SPC with the OPC/UA server enabled. - Meteor monitor/plugin environment exposing the Meteor OPC/UA endpoint if Meteor functions are required. - Trusted OPC/UA certificates for both client and server sides. ### 5.3 Network and Connectivity - TCP reachability to the SPC endpoint. - TCP reachability to the Meteor endpoint if used. - Filesystem access rights to the waiting-queue hot folder path reported by SPC. - Stable latency and timeout settings appropriate for preprocessing and apply operations. ### 5.4 Configuration Assumptions Used by the Sample - Default SPC endpoint: `opc.tcp://DIWITA:26542/spc` - Default Meteor endpoint: `opc.tcp://DIWITA:26642/spc-meteor` - Default Meteor template path for preload mode: `C:\ProgramData\Global Graphics Software Ltd\Smart Print Controller\ConfigFiles\RipDirect\Meteor_Output_Plugin.json` These defaults are demo values, not protocol requirements. ## 6. OPC/UA Communication Setup ### 6.1 Server and Client Roles - SPC acts as an OPC/UA server exposing operator-level workflow state and methods. - The sample CLI is an OPC/UA client to SPC. - When Meteor functions are requested, the sample CLI also acts as an OPC/UA client to the Meteor endpoint. ### 6.2 SPC Address Space Used by the Sample The sample aligns to the SPC guide's top-level structure and uses these areas directly: - `App` - `App/IsExternalControlActive` - `App/WaitingQueueFolder` - `App/ApplySettings()` - `ActiveMedia` - `ActiveMedia/Name` - `AllMedia` - `AllMedia/MediaNames` - `AllJobs` - `AllJobs/WaitingQueue` - `AllJobs//Name` - `AllJobs//PreprocessorStatus` - `AllJobs//Printability` - `AllJobs//CopyCount` - `AllJobs/ChangeActiveMedia(String mediaName)` - `AllJobs/SendToPrintQueue(String jobGuid)` - `PrintRun` - `PrintRun/IsActive` - `PrintRun/Status` - `PrintRun/StartPrintRun()` - optional stop and abort methods for diagnostics - `PrintRunConfig/ConfigSideA` and `PrintRunConfig/ConfigSideB` - `ActiveNozzles` - `CalibrationJustification` - `CalibrationOffset` - `PrintPosition` - `Servers` - `Servers/RunPluginCommand(Int32 serverIndex, String pluginName, String command)` ### 6.3 Meteor Address Space Used by the Sample The sample uses a second endpoint for Meteor operations. The official Meteor OPC/UA guide documents the relevant `Servers`, `Server`, `Controllers`, `Heads`, and `Status` trees, including: - `Servers/IsApplyInProgress` - `Servers/PDHint` - `Servers/ServerCount` - `Servers/Server/Controllers/ControllerCount` - `Servers/Server/Status/BufferLevel` - `Servers/Server/Status/PdCount` - `Servers/Server/Status/PrintCount` - `Servers/Server/SetParam(...)` The sample also invokes methods such as `ForcePD` and `SetCrossWebOffset` against the Meteor endpoint, following the object and method path conventions implemented in the service library. ### 6.4 Security Considerations The SPC guide states that OPC/UA communication requires both client and server application certificates and mutual trust. It also notes that the SPC certificate is stored under: `%programdata%\Global Graphics Software Ltd\Smart Print Controller\App Certificates\certs` For this demo, the key security implications are: - The client certificate store must trust SPC and, if applicable, Meteor certificates. - External control must be granted before write operations or method calls can succeed. - Running SPC in `/ServerMode` can automatically enable OPC and grant external control, but this should be governed operationally because it changes the remote-control posture of the system. - Encrypting and authenticating OPC/UA sessions is recommended for any non-lab deployment. ### 6.5 Timeout and Transport Sizing The official SPC and Meteor guides both recommend increasing communication timeouts under heavy system load. That recommendation is directly relevant here because preprocessing, apply operations, and print startup can exceed typical default client timeouts. The sample does not heavily customize transport limits, but a production implementation should consider: - Larger message sizes if large arrays or image data are read. - Longer session and operation timeouts. - Explicit reconnect handling. - Subscription-based monitoring for high-frequency state changes. ## 7. Printing Workflow ### 7.1 Normal Workflow The sample's default workflow is: 1. Parse and validate command-line parameters. 2. Connect to SPC OPC/UA. 3. Verify `App.IsExternalControlActive`. 4. Resolve the requested media or use the currently active media. 5. If needed, call `AllJobs.ChangeActiveMedia(mediaName)` and then `App.ApplySettings()`. 6. Open a Meteor connection only if Meteor-dependent options are active. 7. Apply optional Meteor pre-start operations such as cross-web offset and X resolution scaling. 8. Read `App/WaitingQueueFolder`. 9. Copy the PDF into that folder. 10. Write `.complete` JSON containing `CopyCount` and `TargetMediaName`. 11. Poll `AllJobs/WaitingQueue` until a new job GUID appears. 12. Poll `AllJobs//PreprocessorStatus` and `Printability` until preprocessing completes successfully. 13. Call `AllJobs.SendToPrintQueue(jobGuid)`. 14. Call `PrintRun.StartPrintRun()`. 15. Wait until `PrintRun/IsActive` becomes `true`. 16. Apply any post-start operations such as Y crop or print-config updates. 17. Observe Meteor buffer and readiness state. 18. Optionally trigger ForcePD. ### 7.2 Preload Workflow The sample's preload mode is materially different: 1. Connect to SPC. 2. Connect to Meteor early. 3. Rewrite `Meteor_Output_Plugin.json` so `"OutputMode"` becomes `PRELOAD`. 4. Call `App.ApplySettings()`. 5. Wait for Meteor `Servers/IsApplyInProgress` to return `false`. 6. Start the print run before importing the PDF. 7. Import the PDF into the SPC hot folder. 8. Wait for preprocessing to complete. 9. Overwrite the job copy count with `METEOR_PRELOAD:`. 10. Send the job to the print queue without starting the print run a second time. This mode demonstrates that the orchestration layer may need to coordinate configuration-file mutation, SPC apply state, and board-side readiness before job submission completes. ### 7.3 Print-Configuration-Only Workflow When no PDF is supplied but a print-config option is requested, the sample runs a lightweight SPC-only flow: 1. Connect to SPC. 2. Verify external control. 3. Read `ActiveNozzles` and `PrintPosition` for side A or B. 4. Write `CalibrationJustification` and/or `CalibrationOffset`. 5. Call `App.ApplySettings()`. 6. Read back values for confirmation. This directly aligns with the SPC guide, which states that changes to these nodes require external control and may need apply semantics when the print run is not already active. ## 8. Implementation Details ### 8.1 Project Structure - `SPC_RemoteControl/Program.cs` - Implements CLI parsing and the end-to-end workflow. - `MeteorDFE_Core/Services/SpcOpcUaService.cs` - Encapsulates SPC OPC/UA connectivity, reads, writes, and method calls. - `MeteorDFE_Core/Services/MeteorOpcUaService.cs` - Encapsulates Meteor OPC/UA connectivity, reads, writes, and method calls. - `SPC_RemoteControl/README.md` - Documents expected behavior and option semantics. This file accurately matches the current sample implementation and was used as primary explanatory guidance. ### 8.2 Design Pattern Used by the Sample The sample is not a generic OPC/UA abstraction layer. It is a workflow-specific orchestration client with three characteristics: - Thin service wrappers around OPC/UA nodes and methods. - Explicit polling loops for state transitions. - A direct mapping from CLI options to operational actions. That design is useful for a demo because it keeps state transitions visible and debuggable. ### 8.2.1 Library Alignment for C# Implementations The sample project and all C# integration guidance in this repository should be understood as targeting the Traeger OPC/UA client library for .NET, exposed through the `Opc.UaFx` / `Opc.UaFx.Client` API surface. This aligns with the SPC OPC/UA guide, which references the Traeger client library for software developers integrating with Smart Print Controller. Where auxiliary libraries are needed in related tooling, the project convention is: - use `Newtonsoft.Json` for JSON serialization and parsing - use BitMiracle `LibTiff.Net` for TIFF handling These conventions should be preferred over OPC Foundation sample stacks, `System.Text.Json`, or alternative TIFF libraries unless there is a deliberate project-wide decision to change them. ### 8.3 SPC Session and Meteor Session Wrappers `SpcSession` and `MeteorSession` are disposable wrappers that own the corresponding service instance and ensure clean disconnect on exit. `MeteorSession` also tracks whether the connection is already established, which matters because the sample defers Meteor connection until an option actually requires it. ### 8.4 Main Control Flow in `Program.cs` The top-level program has three modes: - Discovery mode via `--list-opc-servers` - Print-config-only mode - Full job submission and print mode The `Run` method is the main execution path for job printing. Its responsibilities are: - Validate the PDF path. - Connect to SPC and ensure external control. - Resolve and, if necessary, activate media. - Lazily connect to Meteor when needed. - Execute preload setup if requested. - Submit the PDF through the hot-folder mechanism. - Wait for job import and preprocessing. - Queue and start the print run. - Execute post-start print-config and Meteor actions. - Collect readiness and PD diagnostics. ### 8.5 Job Submission Mechanism The sample does not transmit PDF payload over OPC/UA. Instead, it uses a hybrid mechanism: - OPC/UA provides the folder path and all workflow control. - The filesystem delivers the PDF and its completion metadata. `SubmitPdfToHotFolder` performs the following: - Reads the hot-folder path from `App/WaitingQueueFolder`. - Fails early if the destination file already exists. - Copies the PDF. - Writes a `.complete` JSON file containing: - `CopyCount` - `TargetMediaName` - Polls SPC waiting-queue state until a new job GUID appears. This matches the SPC model in which jobs are materialized into `AllJobs` only after the file ingress path has been consumed by the application. ### 8.6 Preprocessing Monitoring `WaitForJobPreprocessing` polls two SPC job properties: - `PreprocessorStatus` - `Printability` The implementation considers preprocessing complete when `PreprocessorStatus == Complete`, and it treats either `PreprocessorStatus == Failed` or `Printability == Error` as fatal. That logic is consistent with the official guide, which documents: - `PreprocessorStatus` values: `Pending`, `Active`, `Complete`, `Failed` - `Printability` values including `Printable`, warnings, and `Error` ### 8.7 Queue Transfer and Print-Run Start Once preprocessing completes, the sample: - Calls `AllJobs.SendToPrintQueue(jobGuid)` - Calls `PrintRun.StartPrintRun()` - Polls `PrintRun/IsActive` until the run is active The official SPC guide states that `StartPrintRun()` returns a string result and that `IsActive` should be used to determine when the operation has completed. The sample follows this guidance exactly. ### 8.8 Media Activation Media selection is handled by: - Reading `AllMedia/MediaNames` - Reading `ActiveMedia/Name` - Optionally calling `AllJobs.ChangeActiveMedia(mediaName)` - Calling `App.ApplySettings()` - Polling until `ActiveMedia/Name` reports the requested media This is an important sequencing detail: media changes are not treated as complete until SPC reports the new active media and settings have been applied. ### 8.9 Print Configuration for Side A and Side B The sample exposes: - `CalibrationJustification` - `CalibrationOffset` - `ActiveNozzles` - `PrintPosition` under: - `PrintRunConfig/ConfigSideA/*` - `PrintRunConfig/ConfigSideB/*` This aligns with the SPC guide, which documents: - `CalibrationJustification` as `0 = Left`, `1 = Center`, `2 = Right` - `CalibrationOffset` as nozzle-space offset - `PrintPosition` values such as `GearLeft`, `Center`, and `OperatorRight` The sample supports both: - SPC-only print-config updates when no PDF is supplied - Post-start print-config updates after the print run becomes active ### 8.10 Meteor-Specific Operations The Meteor service layer supports and the sample uses: - `SetCrossWebOffsetForAllPlanes` - `SetParam` for X resolution scaling - controller-count discovery - status and buffer reads - `ForcePD` - `Servers/IsApplyInProgress` observation #### Cross-web offset The sample iterates across the server's controller count and calls `SetCrossWebOffset` for each plane. This is appropriate when the correction must be applied consistently across all active planes. #### X resolution scaling `BuildXResScaleValue` converts divider values `1`, `2`, or `4` into the packed integer expected by the Meteor `SetParam` call. The sample sends: - `paramId = 55` - `value = (1 << 16) | divider` This is a sample-specific encoding decision and should be validated against the target Meteor configuration in production. #### Y crop The sample applies Y crop through SPC's `Servers/RunPluginCommand(...)`, not directly through a Meteor-only method. The command string uses: `PCMD,2052404692,3,,,` The implementation also compensates for a server-index mismatch by converting the user-facing 1-based server index into the zero-based index expected by the plugin command path. This matches both: - the code comments in the sample - the official SPC guide note that `RunPluginCommand(...)` requires the print run to be started for the command to succeed #### ForcePD The sample first attempts `meteor.ForcePD(serverIndex)` over OPC/UA. If that fails, it falls back to a local `forcepd` shell command. This is valuable in a demo because it preserves operational continuity even when the exact OPC method binding differs between systems. ### 8.11 Meteor Readiness and Diagnostics After print-run start, the sample reads: - buffer levels - PD hint - controller count - head temperature - head state - printer and print counters It then computes readiness heuristically by checking whether the target server has non-zero buffer state, active head/controller information, or other observable signs that the engine path is prepared. This is not a formal SPC contract; it is demo-level operational logic intended to help an engineer decide whether additional action such as ForcePD is appropriate. ### 8.12 OPC/UA Discovery Support `ListOpcServers` uses OPC discovery to enumerate: - available application servers - endpoint URLs - security mode - security policy - transport profile This is useful during commissioning and integration testing when the exact SPC or Meteor endpoint URL is not yet known. ### 8.13 Function-by-Function Commentary The following tables summarize the operative purpose of each function in the sample project. This section is intended to satisfy engineering review needs by connecting the implementation to the documented SPC and Meteor behavior. #### A. `Program.cs` | Function | Purpose | Notes | | --- | --- | --- | | `RunPrintConfigOnly` | Executes SPC-only calibration operations without PDF submission. | Uses `PrintRunConfig/ConfigSideX/*` nodes and `App.ApplySettings()`. | | `ApplyPrintConfigAfterPrintRun` | Applies side-specific calibration after the print run becomes active. | Useful because the SPC guide notes some config changes apply immediately when the run is active. | | `Run` | Primary end-to-end print workflow. | Owns validation, connection, submission, queueing, start, and diagnostics. | | `ListOpcServers` | Discovers OPC/UA servers and endpoints. | Used for commissioning and endpoint verification. | | `EnsureMeteorConnected` | Lazily connects to Meteor only when required. | Avoids unnecessary second-endpoint dependency. | | `SelectMedia` | Resolves requested or active media. | Reads `AllMedia/MediaNames` and `ActiveMedia/Name`. | | `PrintAvailableMedia` | Emits media choices to the console. | Diagnostic and operator convenience. | | `EnsureActiveMedia` | Switches active media and applies settings when necessary. | Calls `ChangeActiveMedia` then `ApplySettings`. | | `SubmitPdfToHotFolder` | Copies the PDF and writes completion metadata. | Hybrid data-ingress path; not an OPC payload upload. | | `WaitForNewJob` | Detects the newly imported SPC job GUID. | Polls `AllJobs/WaitingQueue` and job name. | | `WaitForPrintRun` | Waits for `PrintRun/IsActive` to match the target state. | Follows SPC method guidance for asynchronous completion. | | `WaitForJobPreprocessing` | Monitors preprocessing and printability. | Stops on `Failed` or `Error`. | | `WaitUntil` | Generic polling helper. | Centralizes timeout behavior. | | `EnsureSuccess` | Validates string-returning SPC methods. | Implements the official `Success` convention. | | `BuildXResScaleValue` | Encodes the Meteor XRes scale parameter. | Demo-specific parameter packing helper. | | `UpdateMeteorTemplateOutputMode` | Rewrites the preload template JSON. | Used only in preload mode. | | `ApplySpcYCropForAllPlanes` | Applies Y crop using `RunPluginCommand`. | Requires the print run to be active. | | `PrintMeteorBufferStatus` | Emits current Meteor buffer information. | Operator diagnostics. | | `ReportMeteorReadiness` | Observes readiness during a fixed window. | Demo heuristic for engine state assessment. | | `ReadMeteorSnapshot` | Reads a full Meteor snapshot. | Aggregates server/controller/head state. | | `IsMeteorReady` | Evaluates readiness from the snapshot. | Heuristic, not a protocol guarantee. | | `HasAnyHeadActivity` | Checks whether any head reports meaningful activity. | Support routine for readiness. | | `PrintMeteorSnapshot` | Writes the snapshot to the console. | Debug and diagnostics. | | `ReadMeteorIntOrDefault` | Safe integer node read with fallback. | Defensive read helper. | | `TryReadMeteorUInt32` | Safe `UInt32` read from Meteor. | Defensive read helper. | | `TryReadMeteorIntArray` | Safe integer-array read from Meteor. | Used for temperatures. | | `TryReadMeteorElectronics` | Reads controller electronics state when present. | Optional diagnostics. | | `PromptForForcePd` | Interactive PD control loop. | Supports status, stop, and abort actions. | | `ObservePdHint` | Watches PD hint before prompting. | Samples both SPC and Meteor diagnostic state. | | `TryReadBooleanMeteorNode` | Reads a Meteor boolean node defensively. | Used to resolve PD hint variants. | | `PrintPdHintDiagnostics` | Prints a detailed diagnostic block. | Includes SPC and Meteor nodes. | | `PrintForcePdDiagnostics` | Prints PD-related post-action diagnostics. | Helps validate whether PD took effect. | | `TryStopPrintRun` | Attempts `StopPrintRun`. | Non-fatal operator aid. | | `TryAbortPrintRun` | Attempts `AbortPrintRun`. | Non-fatal operator aid. | | `SafeReadSpcString` | Best-effort SPC string read. | Diagnostic helper. | | `TryTriggerForcePd` | Attempts OPC ForcePD then local fallback. | Robust demo behavior across configurations. | | `TryRunLocalForcePdCommand` | Executes local `forcepd` shell command. | Operational fallback only. | | `PrintSpcNode` | Prints an SPC node value safely. | Diagnostics. | | `PrintMeteorNode` | Prints a Meteor node value safely. | Diagnostics. | | `TryReadInt` | Best-effort SPC integer read. | Helper for optional nodes. | | `TryGetActiveNozzles` | Reads `ActiveNozzles` safely. | Print-config helper. | | `PrintPrintPosition` | Reads and prints `PrintPosition`. | Print-config verification. | | `GetPrintConfigNodeId` | Builds side-specific SPC config node IDs. | Keeps side-A/side-B addressing consistent. | | `TryReadNodeString` | Safe SPC string read helper. | Defensive utility. | | `FormatNodeValue` | Formats array and scalar values for console output. | Diagnostics. | | `SpcSession` | Disposable SPC session wrapper. | Connection ownership helper. | | `MeteorSession` | Disposable Meteor session wrapper. | Connection ownership and lazy-connect state. | | `CliOptions` | Command-line parser and validation model. | Defines workflow modes and option dependencies. | | `YCrop` | Value object for `start:end` crop syntax. | Validates crop bounds. | | `MeteorHeadSnapshot` | Immutable head snapshot model. | Readiness diagnostics. | | `MeteorControllerSnapshot` | Immutable controller snapshot model. | Readiness diagnostics. | | `MeteorServerSnapshot` | Immutable server snapshot model. | Readiness diagnostics. | | `MeteorSnapshot` | Immutable full-snapshot model. | Readiness diagnostics. | #### B. `SpcOpcUaService` | Function | Purpose | Notes | | --- | --- | --- | | `ConnectWithRetry` | Repeatedly attempts SPC connection. | Useful in plugin or startup races. | | `StopRetry` | Stops the connection retry loop. | Lifecycle management. | | `Connect` | Creates and connects the OPC client. | Subscribes to print-run and external-control state. | | `Disconnect` | Stops monitoring and disconnects. | Clean shutdown path. | | `CanExecuteStop` | Checks whether stop is currently allowed. | Gates by external control and SPC state. | | `CanExecuteAbort` | Checks whether abort is currently allowed. | Gates by external control and SPC state. | | `Stop` | Calls `PrintRun/StopPrintRun`. | Uses execution gating. | | `Abort` | Calls `PrintRun/AbortPrintRun`. | Uses execution gating. | | `ReadPrintRunIsActive` | Reads `PrintRun/IsActive`. | Used by polling logic. | | `ReadString` | Generic string reader. | Thin OPC wrapper. | | `ReadBool` | Generic boolean reader. | Thin OPC wrapper. | | `ReadValue` | Typed node reader. | Generic utility. | | `ReadNodeValue` | Raw node value reader. | Underpins most accessors. | | `WriteNodeValue` | Generic node writer. | Requires external control when writing protected nodes. | | `CallMethod` | Generic OPC method invoker. | Normalizes array-based method returns. | | `WaitingQueueFolder` | Reads `App/WaitingQueueFolder`. | Hot-folder ingress anchor. | | `IsExternalControlActive` | Reads `App/IsExternalControlActive`. | Mandatory before control writes or methods. | | `ActiveMediaName` | Reads `ActiveMedia/Name`. | Media validation. | | `GetMediaNames` | Reads `AllMedia/MediaNames`. | Media enumeration. | | `ApplySettings` | Calls `App/ApplySettings`. | Required after media and config changes. | | `StartPrintRun` | Calls `PrintRun/StartPrintRun`. | Starts run asynchronously. | | `ChangeActiveMedia` | Calls `AllJobs/ChangeActiveMedia`. | Media switch operation. | | `SendToPrintQueue` | Calls `AllJobs/SendToPrintQueue`. | Waiting to print-queue transition. | | `GetWaitingQueueJobGuids` | Reads waiting-queue GUIDs. | Used to identify imported jobs. | | `GetJobName` | Reads `AllJobs//Name`. | Job correlation helper. | | `GetJobStatus` | Reads `AllJobs//Status`. | Available for diagnostics. | | `GetJobPreprocessorStatus` | Reads `PreprocessorStatus`. | Workflow gate. | | `GetJobPrintability` | Reads `Printability`. | Workflow gate. | | `SetJobCopyCount` | Writes integer copy count. | Valid for waiting-queue jobs. | | `SetJobCopyCountRaw` | Writes string-form copy count. | Used for `METEOR_PRELOAD:`. | | `GetActiveNozzles` | Reads side-specific active nozzles. | Print-config inspection. | | `GetCalibrationJustification` | Reads calibration justification. | Print-config inspection. | | `SetCalibrationJustification` | Writes calibration justification. | External-control protected. | | `GetCalibrationOffset` | Reads calibration offset. | Print-config inspection. | | `SetCalibrationOffset` | Writes calibration offset. | External-control protected. | | `GetPrintPosition` | Reads side-specific print position. | Verification helper. | | `EnsureExternalControl` | Throws if external control is absent. | Central guard clause. | #### C. `SpcOpcUaService` private helpers and background behavior | Function | Purpose | Notes | | --- | --- | --- | | `SafeDisconnect_NoEvents` | Disconnects the OPC client without raising caller-facing events. | Used during reconnect and disposal paths. | | `SubscribePrintRun` | Seeds and subscribes to `PrintRun` state. | Tracks `IsActive`, `CanStop`, `CanAbort`, and `Status`. | | `SubscribeExternalControl` | Seeds and subscribes to `App/IsExternalControlActive`. | Mirrors the SPC external-control gate locally. | | `GetPrintRunConfigNodeId` | Builds side-specific config node IDs. | Enforces valid side values. | | `StartConnectionMonitor` | Periodically probes SPC connectivity. | Emits connection-changed events between user actions. | | `StopConnectionMonitor` | Stops and disposes the connectivity timer. | Lifecycle cleanup helper. | #### D. `MeteorOpcUaService` | Function | Purpose | Notes | | --- | --- | --- | | `ConnectWithRetry` | Repeatedly attempts Meteor connection. | Useful during startup or apply windows. | | `StopRetry` | Stops Meteor retry logic. | Lifecycle management. | | `Connect` | Connects to the Meteor endpoint. | Can optionally enable subscriptions. | | `Disconnect` | Disconnects cleanly. | Lifecycle management. | | `ForcePD` | Invokes the ForcePD method. | Tries multiple object/method path candidates. | | `SetParam` | Invokes Meteor `SetParam`. | Used for X resolution scaling in this sample. | | `ReadNodeValue` | Reads a raw Meteor node. | Core accessor. | | `ReadBool` | Reads a boolean Meteor node. | Core accessor. | | `ReadInt32` | Reads an `Int32` Meteor node. | Core accessor. | | `ReadUInt32` | Reads a `UInt32` Meteor node. | Core accessor. | | `GetServerCount` | Reads `Servers/ServerCount`. | Topology discovery. | | `GetServerBufferLevel` | Reads a server buffer level. | Readiness and diagnostics. | | `GetPdHint` | Reads `Servers/PDHint`. | PD readiness hint. | | `WaitForApplyToFinish` | Polls `Servers/IsApplyInProgress`. | Required after SPC apply in preload mode. | | `SetCrossWebOffsetForAllPlanes` | Iterates planes and applies cross-web offset. | All-plane alignment action. | | `SetPlaneYCrop` | Sends one plane crop command. | Implemented through plugin command composition. | | `SetYCropForAllPlanes` | Applies crop to every plane. | Convenience wrapper. | #### E. `MeteorOpcUaService` private helpers and background behavior | Function | Purpose | Notes | | --- | --- | --- | | `SafeDisconnect_NoEvents` | Disconnects the OPC client without raising caller-facing events. | Used during reconnect and disposal paths. | | `SubscribeMeteor` | Discovers the configured Meteor topology and starts subscriptions. | Enumerates servers first, then descends into children. | | `SubscribeServer` | Subscribes to server buffer and PD state. | Also discovers controller and head counts. | | `SubscribeHead` | Subscribes to per-head temperature and state. | Provides the raw signals used by readiness diagnostics. | | `CallRunPluginCommand` | Invokes plugin commands using normalized server indexing. | Important for Y-crop and other plugin-driven features. | | `StartConnectionMonitor` | Periodically probes Meteor connectivity. | Emits connection-changed events between user actions. | | `StopConnectionMonitor` | Stops and disposes the connectivity timer. | Lifecycle cleanup helper. | ### 8.14 Alignment with Official Documentation The sample aligns strongly with the SPC guide in the following areas: - external-control gating - string-returning method success semantics - use of `App.ApplySettings()` after media and config changes - use of `PrintRun.IsActive` to confirm print-run state changes - use of `PrintRunConfig/ConfigSideA` and `ConfigSideB` - use of `Servers.RunPluginCommand(...)` only after print-run start Meteor-specific features such as `ForcePD`, `SetParam`, `IsApplyInProgress`, `PDHint`, and status monitoring are aligned with the official Meteor Inkjet OPC/UA guide and the local sample service implementation. ## 9. Error Handling and Diagnostics ### 9.1 Error Sources The sample explicitly handles errors from: - missing or invalid CLI arguments - missing PDF files - SPC external control not granted - media not found - failure to connect to SPC or Meteor - timeouts while waiting for media activation, job import, preprocessing, or print-run start - non-`Success` results from SPC methods - invalid Y crop syntax or ranges - missing Meteor template file in preload mode ### 9.2 OPC Method Result Validation The SPC guide states that many methods return a string and that success is indicated by `Success`. The sample centralizes this rule in `EnsureSuccess`, which throws on any other return string. This is a good pattern because it keeps state-change calls fail-fast and easy to audit. ### 9.3 Polling Timeouts The sample uses bounded waits for: - active media changes - job import into the waiting queue - job preprocessing - Meteor apply completion - print-run activation This prevents silent hangs and gives engineers actionable timeout boundaries when debugging an integration. ### 9.4 Diagnostics Collected by the Sample The sample can print: - SPC print-run status and activity - active job identity and current page - job preprocessing and printability state - Meteor buffer level - PD hint - controller count - head temperatures - head state - print and PD counters This makes the sample useful not only as a demo controller but also as a commissioning aid. ## 10. Performance Considerations ### 10.1 Latency Sources The main latency contributors are: - filesystem copy time for the PDF - SPC job import time - preprocessing time - media-change apply time - Meteor apply time in preload mode - print-run startup and engine readiness time ### 10.2 Polling Versus Subscriptions The underlying service classes support OPC data-change subscriptions for print-run and external-control state, but the CLI's workflow logic is primarily polling-based. That is acceptable for a demo because: - it is straightforward to reason about - transition logging is easy to read - timing remains deterministic enough for engineering validation For a production remote interface, subscriptions should be preferred for: - print-run state transitions - Meteor buffer and PD state - job state transitions where supported ### 10.3 Throughput and Buffering The sample monitors Meteor buffer level but does not implement adaptive throttling. In a higher-throughput system, the control layer should correlate: - SPC queue depth - preprocessing completion rate - Meteor buffer level - print count progression to prevent underflow or poorly timed PD actions. ### 10.4 Real-Time Constraints This sample is not a hard real-time controller. It is a supervisory orchestration client. Time-critical behavior remains within SPC, Meteor, and the print-engine environment. The remote application should therefore be treated as: - responsible for correct sequencing - not responsible for deterministic motion or fire timing ## 11. Best Practices - Verify `App.IsExternalControlActive` before any write or method call. - Treat SPC string-returning methods as successful only when they return `Success`. - Separate payload transfer from control logic, as this sample does. - Use the waiting-queue hot folder reported by SPC rather than hard-coding a path. - Apply settings explicitly after media or print-config changes when required. - Wait for `PrintRun/IsActive` rather than assuming immediate print-run state transition. - In preload mode, wait for Meteor `IsApplyInProgress` to clear before sending Meteor commands. - Use clear timeout boundaries and surface them as explicit operator errors. - Prefer subscriptions over polling for production event handling. - Preserve server-index conventions carefully, especially where one API uses one-based indexing and another path expects zero-based indexing. - Keep certificate trust stores under configuration control and document renewal procedures. - Log both SPC-side and Meteor-side state when diagnosing synchronization problems. - Treat local command fallbacks such as `forcepd` as operational contingencies, not as the primary integration contract. ## 12. Conclusion The sample project demonstrates a practical and technically coherent way to control printing from a remote interface by combining SPC OPC/UA, Meteor OPC/UA, and SPC hot-folder job ingress. The orchestration model is straightforward: - SPC owns workflow state. - Meteor owns device-side status and selected engine controls. - The remote client sequences the two and supervises completion. For engineering teams building an operator-facing or machine-to-machine integration, this architecture is a strong reference design because it shows the most important control boundaries clearly: - where job payload enters the system - where configuration becomes active - where print-run state is authoritative - where device readiness and PD behavior should be observed The implementation should be viewed as a demo and integration template rather than a finished production control layer, but it already expresses the essential end-to-end workflow needed to drive Meteor-backed printing from SPC using OPC/UA. ## References - Smart Print Controller OPC/UA Interface Guide: - Meteor Inkjet OPC/UA Interface Guide: - Sample project README: `SPC_RemoteControl/README.md` - Sample implementation: - `SPC_RemoteControl/Program.cs` - `MeteorDFE_Core/Services/SpcOpcUaService.cs` - `MeteorDFE_Core/Services/MeteorOpcUaService.cs`