### Initialization sequence example Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/packet-codec.md Example showing the initialization sequence: opening the port, waiting, sending initialize and display-on commands, then encoding data. ```csharp port.Open("COM5"); Thread.Sleep(100); // the session waits >=100 ms after opening before init port.Write(PacketCodec.Control(255)); // initialize Thread.Sleep(50); port.Write(PacketCodec.Control(109)); // display on port.Write(PacketCodec.Encode(42, 65, 3, 6)); ``` -------------------------------- ### Example: create and iterate profiles Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/profile-library.md Example usage: creates the built-in library, prints each profile's BuiltInId, Name, and Group, and demonstrates UniqueName returning "Classic 2" when "Classic" is taken. ```csharp var library = ProfileLibrary.Create(); foreach (var profile in library) Console.WriteLine("{0}\t{1} [{2}]", profile.BuiltInId, profile.Name, ProfileLibrary.Group(profile)); var name = ProfileLibrary.UniqueName(settings.Profiles, "Classic"); // "Classic 2" when taken ``` -------------------------------- ### Encode example with output Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/packet-codec.md Example encoding CPU 42°C, GPU 65°C, levels 3 and 6, showing the resulting byte array and printing it with BitConverter. ```csharp var packet = PacketCodec.Encode(42.0, 65.0, cpuFrame: 3, gpuFrame: 6); // 00 04 10 00 00 A9 04 02 06 05 03 06 00 00 00 00 00 00 00 00 Console.WriteLine(BitConverter.ToString(packet)); ``` -------------------------------- ### StartupRegistration Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/windows-wiring.md Documentation for the StartupRegistration class, including interface, constructor, members, and usage examples. ```APIDOC ## StartupRegistration ### Description Registers this executable under the current user's `Software\Microsoft\Windows\CurrentVersion\Run` key (value name `ZX6DisplayControl`) so it starts with Windows (no administrator rights; runs as `"" --tray`). ### Interface ```csharp public interface IStartupStore {string Read();void Write(string value);void Delete();} ``` Storage seam; the production implementation is `RegistryStartupStore` (internal). ### Constructor ```csharp public StartupRegistration(IStartupStore store=null) ``` | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | `store` | `IStartupStore` | no | `null` → `RegistryStartupStore` | Injectable registry access. | ### Members | Member | Signature | Behavior / Throws | |--------|-----------|-------------------| | `GetEnabled(exePath)` | `public bool GetEnabled(string exePath)` | True when the stored command equals `Command(exePath)` (ordinal-ignore-case). | | `SetEnabled(enabled, exePath)` | `public void SetEnabled(bool enabled,string exePath)` | Writes `Command(exePath)` or deletes the value. | | `CommitEnabled(enabled, exePath, persistSettings)` | `public void CommitEnabled(bool enabled,string exePath,Action persistSettings)` | Writes the registry change, runs `persistSettings()`, and on a persistence failure **restores the previous registry value** when it changed. If the restore itself fails, throws `AggregateException("Settings could not be saved and the previous startup setting could not be restored. Check Start with Windows before exiting.", saveError, rollbackError)`. | | `Command(exePath)` (private static) | — | **Throws `ArgumentException("Invalid executable path.")`** for blank, non-rooted or quote-containing paths. Returns `"" --tray`. | ### Example — save preferences with rollback (the `MainForm` pattern) ```csharp var startup = new StartupRegistration(); startup.CommitEnabled(snapshot.StartWithWindows, exePath, () => store.Save(snapshot)); // registry rolls back if Save throws ``` ```csharp bool registered = startup.GetEnabled(exePath); Console.WriteLine(registered ? "This copy is registered to start with Windows." : "This copy is not registered to start with Windows."); ``` ``` -------------------------------- ### AnimationPresets usage example Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/ui-channel.md Creates a "Follow usage" preset for CPU and prints the sensor ID and matched preset name. ```csharp var settings = AnimationPresets.Create("Follow usage", AnimationMode.Cycle, SequenceKind.Forward, gpu: false, temperatureId: "TCPU"); Console.WriteLine(settings.SensorId); // "SCPUUTI" Console.WriteLine(AnimationPresets.Match(settings, false, "TCPU")); // "Follow usage" ``` -------------------------------- ### Start application from Program.cs Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/main-form.md Starts the application from Program.cs. Creates a SessionConfiguration from active profile settings, then constructs a SessionController with a HolderSession factory and a controller failure log callback. The MainForm is created with dependencies and wired to handle BringToFrontRequested and Shown events (the latter checks for the --tray argument). After Application.Run, the controller is stopped and its completion is waited on for up to 2000 ms. ```csharp var initial = new SessionConfiguration { Cpu = settings.ActiveProfile.Cpu.Copy(), Gpu = settings.ActiveProfile.Gpu.Copy(), DisplayEnabled = settings.DisplayEnabled }; using (var controller = new SessionController( () => new HolderSession(new SharedMemoryReader(), new DeviceDiscovery(), new SerialTransport(), new MonotonicClock(), s => log.Write("session", s)), initial, (message, error) => log.Write("controller failure", message, error))) using (var form = new MainForm(settings, store, controller, log, new StartupRegistration(), Application.ExecutablePath, new UpdateService(directory))) { form.BringToFrontRequested += () => { if (instance.ConsumeOpenRequest()) form.ShowWindow(); }; form.Shown += (s, e) => { if (args.Contains("--tray")) form.StartInTray(); }; Application.Run(form); controller.Stop(); bool ended = controller.Completion.Wait(2000); } ``` -------------------------------- ### Wiring an editor to the session (as MainForm does) Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/ui-channel.md Shows how to instantiate ChannelControl, subscribe to DraftChanged, load settings, set catalog, availability, preview, and get the draft. Includes error handling for FormatException. ```csharp var cpuChannel = new ChannelControl(); // CPU side cpuChannel.DraftChanged += (s, e) => RefreshStatus(); cpuChannel.Load(config.Cpu, snapshot); // snapshot may be null cpuChannel.SetCatalog(state.Snapshot); // new readings every 125 ms cpuChannel.SetAvailable(state.AidaStatus == "Ready"); cpuChannel.SetPreview(state.Cpu); ChannelSettings draft = cpuChannel.GetDraft(); // may throw FormatException if (cpuChannel.ValidationErrors().Count == 0) { Console.WriteLine("CPU: " + draft.TemperatureId + " level per " + draft.Animation.Mode); } ``` -------------------------------- ### DeviceDiscovery.Find usage example Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/windows-wiring.md Example usage of DeviceDiscovery.Find: connects to the single found holder device, or warns when multiple are present. ```csharp var discovery = new DeviceDiscovery(); var devices = discovery.Find(); if (devices.Count == 1) { Console.WriteLine("Connecting to " + devices[0].PortName); port.Open(devices[0].PortName); } else if (devices.Count > 1) { Console.WriteLine("Multiple matching displays found. Connect one holder at a time."); } ``` -------------------------------- ### settings.json schema example (AppSettings, version 1) Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/configuration.md Schema example for the AppSettings JSON file, version 1. Validation rules require SchemaVersion to be exactly 1, all profiles valid and uniquely named, ActiveProfileName to match exactly one profile, and built-in profiles must remain unchanged from the canonical library when PresetLibraryVersion is 2 or higher. ```json { "SchemaVersion": 1, "Profiles": [ { "Name": "Classic", "BuiltInId": "classic", "Cpu": { "TemperatureId": "TCPU", "Animation": { "Mode": "Cycle", "Sequence": "Forward", "RateMode": "Fixed", "SensorId": null, "InputMin": 0, "InputMax": 100, "FixedFps": 2, "MinFps": 1, "MaxFps": 4, "FilterSeconds": 0, "HysteresisPercent": 2, "Invert": false, "FixedFrame": 0 }, "Paused": false }, "Gpu": { "TemperatureId": "TGPU1", "Animation": { ... }, "Paused": false } } ], "ActiveProfileName": "Classic", "DisplayEnabled": true, "StartWithWindows": false, "CloseToTrayExplained": false, "CloseToTray": true, "MinimizeToTray": false, "CheckUpdatesOnStartup": true, "Theme": "System", "PresetLibraryVersion": 2 } ``` -------------------------------- ### Example: feeding live and draft states (MainForm pattern) Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/ui-preview.md Shows how to feed live output and pending draft states to the preview. Live output uses last written values from the session; draft mode labels the preview as not applied. ```csharp var preview = new HolderPreview { Dock = DockStyle.Fill }; // Live output: last written values from the session preview.ShowOutput( caption: "Output sent to display", cpu: state.CpuNumber, gpu: state.GpuNumber, cpuLevel: state.Cpu != null ? state.Cpu.Frame : (int?)null, gpuLevel: state.Gpu != null ? state.Gpu.Frame : (int?)null, draft: false); // Pending draft: separate preview engines, labelled not applied preview.ShowOutput("Preview · not applied", 42, 61, 3, 6, draft: true); ``` -------------------------------- ### Check startup registration with GetEnabled Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/windows-wiring.md Checks whether the current executable is registered to start with Windows and prints a corresponding message. ```csharp bool registered = startup.GetEnabled(exePath); Console.WriteLine(registered ? "This copy is registered to start with Windows." : "This copy is not registered to start with Windows."); ``` -------------------------------- ### SingleInstance Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/windows-wiring.md Documentation for the SingleInstance class, including constructor, members, and usage example. ```APIDOC ## SingleInstance ### Description Named-mutex single-instance guard scoped to the Windows user SID. ### Constructor ```csharp public SingleInstance(string scope="ZX6DisplayControl") ``` Builds the names `Local\{scope}-{user SID}` (mutex) and `Local\{scope}-{user SID}-open` (auto-reset event used to signal the first instance). ### Members | Member | Signature | Behavior | |--------|-----------|----------| | `TryAcquire()` | `public bool TryAcquire()` | Creates the mutex (initially owned) and the event; returns whether this instance is the owner. | | `SignalExistingWindow()` | `public void SignalExistingWindow()` | Sets the open event for the running instance. | | `ConsumeOpenRequest()` | `public bool ConsumeOpenRequest()` | Owner-only, non-blocking check whether the event was signaled. | | `Dispose()` | `public void Dispose()` | Disposes the event, releases the mutex when owned, disposes the mutex. | ### Example (from `Program.cs`) ```csharp using (var instance = new SingleInstance()) { if (!instance.TryAcquire()) { instance.SignalExistingWindow(); return; } ... form.BringToFrontRequested += () => { if (instance.ConsumeOpenRequest()) form.ShowWindow(); }; } ``` ``` -------------------------------- ### Get profile description Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/profile-library.md Returns the one-line preset description for the eight built-ins (e.g., "Both bars fill at a steady 2 fps." for Classic) or "Your saved temperature sources and animation settings." for anything else. ```csharp public static string Description(string name) ``` -------------------------------- ### ProfilesControl usage example Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/ui-pickers.md Creates a ProfilesControl, subscribes to the Requested event, populates it with settings, and prints the selected profile name. ```csharp var profiles = new ProfilesControl(); profiles.Requested += action => Console.WriteLine("Action: " + action); profiles.SetProfiles(settings, settings.ActiveProfileName); Console.WriteLine(profiles.SelectedName); // current selection ``` -------------------------------- ### Power off with error handling Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/packet-codec.md Example turning off the display with error handling, catching any exception during the write. ```csharp try { if (powerOff) port.Write(PacketCodec.Control(108)); } catch (Exception e) { Console.WriteLine("Display power-off failed: " + e.Message); } ``` -------------------------------- ### Wiring the default application with SessionController Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/session-controller.md Creates a SessionController with a HolderSession that wires the default components (SharedMemoryReader, DeviceDiscovery, SerialTransport, MonotonicClock) and a default SessionConfiguration (TCPU / TGPU1, display on). Starts the controller with controller.Start(). ```csharp var controller = new SessionController( () => new HolderSession(new SharedMemoryReader(), new DeviceDiscovery(), new SerialTransport(), new MonotonicClock(), s => log.Write("session", s)), new SessionConfiguration(), // TCPU / TGPU1, display on (message, error) => log.Write("controller failure", message, error)); controller.Start(); ``` -------------------------------- ### Example: consuming SensorReadResult in a session loop Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/sensor-types.md Shows how to read a sensor snapshot and handle success or failure. If the snapshot is null, the error code and message are stored and the read is counted as an error; otherwise the snapshot is used and the read is counted as valid. ```csharp var result = reader.Read(clock.ElapsedMilliseconds); currentSnapshot = result.Snapshot; if (currentSnapshot == null) { aidaStatus = result.ErrorCode; aidaMessage = result.Message; readErrors++; return; } snapshot = currentSnapshot; validReads++; ``` -------------------------------- ### Sensor-driven animation with smoothing and inversion Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/animation-engine.md Non-trivial example with sensor-driven speed, smoothing, and inversion. Uses PingPong sequence, sensor ID "SCPUUTI", input range 0-100, FPS range 1-4, filter window of 2 seconds, and invert flag. Iterates over sensor readings and prints raw, filtered, normalized, fps, and frame values. ```csharp var engine = new AnimationEngine(new Random()); var settings = new AnimationSettings { Mode = AnimationMode.Cycle, Sequence = SequenceKind.PingPong, RateMode = AnimationRateMode.Sensor, SensorId = "SCPUUTI", InputMin = 0, InputMax = 100, MinFps = 1, MaxFps = 4, FilterSeconds = 2, Invert = true }; AnimationOutput output = null; foreach (var reading in new[] { 10.0, 20.0, 45.0, 70.0, 90.0 }) { output = engine.Advance(settings, reading, 0.125, paused: false); Console.WriteLine("raw={0:0.0} filtered={1:0.0} norm={2:0.00} fps={3:0.0} frame={4}", output.RawValue, output.FilteredValue, output.NormalizedValue, output.FramesPerSecond, output.Frame); } ``` -------------------------------- ### Basic call to SharedMemoryReader.Read Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/aida-reader.md Example of a basic call to Read. Creates a SharedMemoryReader, reads a snapshot, and prints the number of sensors or the error code and message. ```csharp var reader = new SharedMemoryReader(); var result = reader.Read(clock.ElapsedMilliseconds); if (result.Snapshot != null) { Console.WriteLine("Exported " + result.Snapshot.Values.Count + " sensors"); } else { Console.WriteLine(result.ErrorCode + ": " + result.Message); } ``` -------------------------------- ### ChannelControl Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/ui-channel.md Complete API documentation for the ChannelControl class, including constructor, members, and usage examples. ```APIDOC ## ChannelControl ### Description Per-side editor (temperature picker + animation controls) used by MainForm for CPU and GPU channels. ### Constructor ```csharp public ChannelControl(bool gpu=false) ``` | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | `gpu` | `bool` | no | `false` | Side flag; selects `SGPU1UTI`/`SGPU1UTI` defaults and usage explanations. | ### Members | Member | Signature | Behavior | |--------|-----------|----------| | `DraftChanged` | `public event EventHandler DraftChanged` | Raised by `Changed()` on any edit (after re-matching the preset label and re-validating). | | `GetDraft()` | `public ChannelSettings GetDraft()` | Reads all controls into a `ChannelSettings`. **Throws `FormatException`** (`"Enter a valid number for {field}."`) when a numeric field cannot be parsed with `NumberStyles.Float` + current culture or is non-finite. | | `ValidationErrors()` | `public IReadOnlyList ValidationErrors()` | `GetDraft()` errors plus: `"Choose a display temperature."` for blank `TemperatureId`, every `AnimationSettings.Validate()` message, and `"Review and confirm this sensor range in Advanced settings."` when a source is needed, the unit has no preset (`unknownRange`, i.e. not `%` or `°C`) and `confirmRange` is unchecked. | | `Load(settings, catalog)` | `public new void Load(ChannelSettings settings,SensorSnapshot catalog)` | Populates the controls from settings/catalog without raising `DraftChanged`; resets `unknownRange`, advanced and confirmation state; selects the matching preset. | | `SetCatalog(catalog, deferStructure)` | `public void SetCatalog(SensorSnapshot catalog,bool deferStructure=false)` | Refreshes both sensor pickers; when not deferred, updates the input-unit captions (`"Minimum input (°C)"` etc.) and explanation. | | `SetAvailable(value)` | `public void SetAvailable(bool value)` | Forwards availability to both pickers (`"Last reading:"` state). | | `SetPreview(output)` | `public void SetPreview(AnimationOutput output)` | Renders the response label: `"Control sensor unavailable · holding the last level"`, `"Paused · temperatures continue updating"`, `"{fps} fps · Level {n} of 7"`, or with Advanced open the full `Input → Smoothed` + fps + level + `"· at range limit"` (Clamped) text; `null` → `"No display output"`. | ### Control vocabulary (accessible names used by `UiHelp`) `Mode` (Loop/Sensor level/Fixed level), `Sequence` (Fill/Empty/Bounce/Random), `RateMode` (Fixed/Sensor-driven), `FilterSeconds` (Off/1/2/5 seconds), `SimplePreset`, `AdvancedOptions`, `InputMin`, `InputMax`, `FixedFps`, `MinFps`, `MaxFps`, `HysteresisPercent`, `FixedFrame` (0–7), `Invert sensor response`, `Pause animation`, `Use this sensor range`, `TemperatureSource`, `AnimationSource`. ### Example — wiring an editor to the session (as `MainForm` does) ```csharp var cpuChannel = new ChannelControl(); // CPU side cpuChannel.DraftChanged += (s, e) => RefreshStatus(); cpuChannel.Load(config.Cpu, snapshot); // snapshot may be null cpuChannel.SetCatalog(state.Snapshot); // new readings every 125 ms cpuChannel.SetAvailable(state.AidaStatus == "Ready"); cpuChannel.SetPreview(state.Cpu); ChannelSettings draft = cpuChannel.GetDraft(); // may throw FormatException if (cpuChannel.ValidationErrors().Count == 0) { Console.WriteLine("CPU: " + draft.TemperatureId + " level per " + draft.Animation.Mode); } ``` **Example — reading the draft with validation (error handling):** ```csharp try { var draft = cpuChannel.GetDraft(); foreach (var error in cpuChannel.ValidationErrors()) { Console.WriteLine("CPU: " + error); // e.g. "CPU: Speed must be between 1 and 4 fps..." } } catch (FormatException ex) { Console.WriteLine(ex.Message); // "Enter a valid number for Minimum input." } ``` ``` -------------------------------- ### DiagnosticsControl usage example Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/ui-pickers.md Instantiates DiagnosticsControl, updates its state and log, and enables or disables actions based on busy and failed flags. ```csharp var diagnostics = new DiagnosticsControl(); diagnostics.UpdateState(controller.State ?? new SessionState()); diagnostics.UpdateLog(log, operationName); diagnostics.SetActionsEnabled(!busy, !failed); ``` -------------------------------- ### DeviceDiscovery.Find method Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/windows-wiring.md Queries WMI Win32_PnPEntity (scope root\CIMV2, class GUID {4d36e978-e325-11ce-bfc1-08002be10318}, EnumerationOptions timeout 2 s) for PNPDeviceID, Name and ConfigManagerErrorCode; keeps rows where ConfigManagerErrorCode == 0 and FromPnp returns a device. Returns IReadOnlyList; empty when no holder is present. ```csharp public IReadOnlyList Find() ``` -------------------------------- ### ProfileDocument import/export JSON example Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/configuration.md Example of a ProfileDocument used for profile import/export. The SchemaVersion must be 1 and the embedded profile must validate; exports write value.PersonalCopy(value.Name) without BuiltInId. ```json { "SchemaVersion": 1, "Profile": { "Name": "...", "Cpu": {...}, "Gpu": {...} } } ``` -------------------------------- ### SensorPicker usage example Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/ui-pickers.md Example usage of SensorPicker: creates a temperature-only picker, subscribes to SelectionChanged to print the selected sensor's value, then sets the snapshot and selects the sensor with ID "TCPU". ```csharp var temperature = new SensorPicker(temperaturesOnly: true); temperature.SelectionChanged += (s, e) => { var sensor = temperature.SelectedSensor; Console.WriteLine("Selected " + temperature.SelectedId + " = " + (sensor != null && sensor.Number.HasValue ? sensor.Number.Value.ToString() : "n/a")); }; temperature.SetSnapshot(snapshot); temperature.SelectedId = "TCPU"; ``` -------------------------------- ### AppSettings.ActiveProfile Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/settings-model.md Gets the active profile based on the ActiveProfileName property. ```APIDOC ## AppSettings.ActiveProfile ### Description Returns the profile from the Profiles list whose Name matches ActiveProfileName (case-insensitive). ### Method GET ### Endpoint AppSettings.ActiveProfile ### Parameters None ### Request Example N/A ### Response #### Success Response (200) - **Profile** - The active profile. #### Response Example { "name": "Classic", "displayEnabled": true } ``` -------------------------------- ### SettingsStore Constructor Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/settings-store.md Initializes a new SettingsStore instance with a data directory. ```APIDOC ## Constructor ### Description Initializes a new instance of the SettingsStore class with the specified data directory. ### Method Constructor ### Endpoint `public SettingsStore(string directory)` ### Parameters - **directory** (string) - Required - Data directory; `Path.GetFullPath` applied at construction. ### Response No return value. ``` -------------------------------- ### Build and package with Build.ps1 and Package.ps1 Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/README.md Run from the source folder to build the project and then package it using the existing build. The build produces the Core DLL, GUI executable, and test executable in bin/, and writes build-info.json with SHA-256 fingerprints. Package.ps1 requires Git and a clean, committed working tree; -UseExistingBuild uses the matching build without running tests. ```powershell .\Build.ps1 .\Package.ps1 -UseExistingBuild ``` -------------------------------- ### IStartupStore interface Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/windows-wiring.md Defines the storage seam for startup registration. The production implementation is RegistryStartupStore (internal). ```csharp public interface IStartupStore {string Read();void Write(string value);void Delete();} ``` -------------------------------- ### Encode with error handling Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/packet-codec.md Example showing temperature validation before encoding, catching ArgumentOutOfRangeException for unclamped temperatures. ```csharp double cpu = reading.GetValueOrDefault(); try { port.Write(PacketCodec.Encode(cpu, gpu, cpuLevel, gpuLevel)); } catch (ArgumentOutOfRangeException ex) { // e.g. temperature above 99 °C was not clamped by the caller Console.WriteLine("Cannot encode: " + ex.Message); } ``` -------------------------------- ### Load settings and show warning Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/settings-model.md Loads settings via SettingsStore.Load, retrieves the Settings property, and displays a warning message box if a warning is present. ```csharp var loaded = store.Load(); var settings = loaded.Settings; if (loaded.Warning != null) MessageBox.Show(loaded.Warning, "Settings recovered", MessageBoxButtons.OK, MessageBoxIcon.Warning); ``` -------------------------------- ### Load() Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/settings-store.md Loads settings with recovery from backup and defaults. ```APIDOC ## Load ### Description Loads settings from the data directory with recovery from backup and defaults. ### Method Load ### Endpoint `public SettingsLoadResult Load()` ### Parameters None. ### Response #### Success Response - **Settings** - The loaded AppSettings. - **Warning** - A warning message if recovery occurred. - **UsedBackup** - Boolean indicating if backup was used. #### Response Example ```json { "Settings": {}, "Warning": "Previous settings restored. The original file has been retained.", "UsedBackup": true } ``` ### Throws Rethrows non-recoverable exceptions: IOException, InvalidDataException, UnauthorizedAccessException, SerializationException, ArgumentException. ``` -------------------------------- ### Create SessionConfiguration from settings Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/settings-model.md Initializes a SessionConfiguration using copies of the active profile's CPU and GPU settings and the DisplayEnabled flag. ```csharp var initial = new SessionConfiguration { Cpu = settings.ActiveProfile.Cpu.Copy(), Gpu = settings.ActiveProfile.Gpu.Copy(), DisplayEnabled = settings.DisplayEnabled }; ``` -------------------------------- ### AppSettings.Copy() Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/settings-model.md Creates a deep copy of the AppSettings instance, including a deep copy of the Profiles list. ```APIDOC ## AppSettings.Copy() ### Description Returns a new AppSettings instance that is a deep copy of the current instance, including a deep copy of all profiles. ### Method GET ### Endpoint AppSettings.Copy() ### Parameters None ### Request Example N/A ### Response #### Success Response (200) - **AppSettings** - A deep copy of the current AppSettings. #### Response Example { "schemaVersion": 1, "profiles": [], "activeProfileName": "Classic" } ``` -------------------------------- ### Error/edge handling example (cleared states) Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/ui-preview.md Handles cleared states: when display is off or disconnected, shows appropriate captions with null values and draft: false. ```csharp if (!saved.DisplayEnabled || state.DisplayOff) { preview.ShowOutput("Display off", null, null, null, null, draft: false); } else if (!state.Connected) { preview.ShowOutput("Display disconnected", null, null, null, null, draft: false); } ``` -------------------------------- ### Direct wiring with HolderSession (Program.cs pattern) Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/holder-session.md Direct wiring pattern: constructs a HolderSession with required dependencies, applies a SessionConfiguration for CPU/GPU channels, then runs a loop calling Tick() every 25 ms until stopping, followed by Stop(). ```csharp var session = new HolderSession( new SharedMemoryReader(), new DeviceDiscovery(), new SerialTransport(), new MonotonicClock(), s => log.Write("session", s)); session.Apply(new SessionConfiguration { Cpu = new ChannelSettings { TemperatureId = "TCPU", Animation = new AnimationSettings() }, Gpu = new ChannelSettings { TemperatureId = "TGPU1", Animation = new AnimationSettings() }, DisplayEnabled = true }); while (!stopping) { session.Tick(); Thread.Sleep(25); } session.Stop(); ``` -------------------------------- ### Version comparison with ReleaseVersion.Parse Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/update-service.md Compares release versions using ReleaseVersion.Parse and CompareTo. Shows that pre-release versions sort before stable releases and that beta.10 is greater than beta.2. ```csharp Console.WriteLine(ReleaseVersion.Parse("v0.1.0-beta.2").CompareTo(ReleaseVersion.Parse("0.1.0"))); // -1 Console.WriteLine(ReleaseVersion.Parse("0.1.0-beta.10").CompareTo(ReleaseVersion.Parse("0.1.0-beta.2"))); // 1 ``` -------------------------------- ### AppSettings.Defaults() Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/settings-model.md Creates a new AppSettings instance with default values, including a default profile library and active profile name. ```APIDOC ## AppSettings.Defaults() ### Description Creates a fresh default AppSettings document with SchemaVersion=1, a default profile library, ActiveProfileName="Classic", DisplayEnabled=true, CloseToTray=true, CheckUpdatesOnStartup=true, and PresetLibraryVersion=2. ### Method GET ### Endpoint AppSettings.Defaults() ### Parameters None ### Request Example N/A ### Response #### Success Response (200) - **AppSettings** - A new AppSettings instance with default values. #### Response Example { "schemaVersion": 1, "profiles": [], "activeProfileName": "Classic", "displayEnabled": true, "closeToTray": true, "checkUpdatesOnStartup": true, "presetLibraryVersion": 2 } ``` -------------------------------- ### GitHub Releases API request Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/update-service.md The only network request in the app, a GET to the public GitHub Releases API. Uses headers User-Agent, Accept, and X-GitHub-Api-Version, with a 15 s timeout and 1 MiB response buffer. ```http GET https://api.github.com/repos/XxYouDeaDPunKxX/zx6-display-control/releases?per_page=100 ``` -------------------------------- ### ISessionController interface definition Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/session-controller.md Defines the public interface for controlling a session. Includes properties for state, failure message, and completion task, along with methods for starting, applying configuration, retrying, suspending, resuming, and stopping. ```csharp public interface ISessionController { SessionState State {get;} string FailureMessage {get;} Task Completion {get;} void Start();void Apply(SessionConfiguration configuration);void Retry();void Suspend();void Resume();void Stop(); } ``` -------------------------------- ### Error handling with ProfileEditor.Apply Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/profile-editor.md Error handling example: sets an invalid Draft name, calls Apply, and catches the InvalidOperationException. Shows that Apply throws with message "Fix the profile settings before applying." when validation fails. ```csharp var editor = new ProfileEditor(active); editor.Draft.Name = ""; // validation error: "Enter a profile name." try { editor.Apply(); } catch (InvalidOperationException ex) { // "Fix the profile settings before applying." Console.WriteLine(ex.Message); Console.WriteLine(string.Join("; ", editor.Validate(null))); } ``` -------------------------------- ### AnimationPresets.Create method signature Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/ui-channel.md Creates an AnimationSettings preset by name. Throws ArgumentException("Preset unavailable.") for unknown names. ```csharp public static AnimationSettings Create(string name,AnimationMode mode,SequenceKind sequence,bool gpu,string temperatureId) ``` -------------------------------- ### Generate unique profile name Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/profile-library.md Returns the proposed name, or appends " N" (starting at 2) until the name is not used by any profile (case-insensitive). Powers "Save as new profile", Duplicate, Import, and migration naming. ```csharp public static string UniqueName(IEnumerable profiles,string proposed) ``` -------------------------------- ### ProfileLibrary.Create() Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/profile-library.md Creates and returns the canonical eight built-in profiles with BuiltInId set to the lowercased name with spaces replaced by hyphens. ```APIDOC ## ProfileLibrary.Create() ### Description Creates the canonical eight built-in profiles with `BuiltInId` set to the lowercased name with spaces replaced by `-` (e.g., `"classic"`, `"activity"`, `"thermal"`, `"random"`, `"sensor-levels"`, `"breathe"`, `"counterflow"`, `"split-tempo"`). ### Method Create ### Signature ```csharp public static List Create() ``` ### Returns List - The list of eight built-in profiles. ### Preset Behaviors | Preset | Behavior | |--------|----------| | `Classic` | Both bars fill at steady `FixedFps=2` (`SequenceKind.Forward`). | | `Activity` | CPU/GPU usage (`SCPUUTI`/`SGPU1UTI`) drives speed; `RateMode=Sensor`, `FilterSeconds=2`. | | `Thermal` | Each side's `TemperatureId` drives speed over `InputMin=30`/`InputMax=80`, `FilterSeconds=2`. | | `Random` | `SequenceKind.Random` at `FixedFps=2`. | | `Sensor levels` | Copy of Activity with `Mode=SensorLevel` (usage controls level, `HysteresisPercent` default 2). | | `Breathe` | `SequenceKind.PingPong` at `FixedFps=1` on both sides. | | `Counterflow` | CPU fills (`Forward`), GPU `SequenceKind.Reverse`, both at `FixedFps=2`. | | `Split tempo` | CPU `FixedFps=1`, GPU `FixedFps=4` (both fill). | ### Example ```csharp var library = ProfileLibrary.Create(); foreach (var profile in library) Console.WriteLine("{0}\t{1} [{2}]", profile.BuiltInId, profile.Name, ProfileLibrary.Group(profile)); ``` ``` -------------------------------- ### SelectRelease static method signature Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/update-service.md Static method that parses GitHub releases JSON and selects the newest valid release. Throws InvalidDataException for null, empty, or oversized JSON or null entries. The current parameter is the installed version used for comparison. ```csharp public static UpdateResult SelectRelease(byte[] json,string current) ``` -------------------------------- ### Create built-in profiles Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/profile-library.md Returns the canonical eight built-in profiles with BuiltInId set to the lowercased name with spaces replaced by '-' (e.g., "classic", "activity", "thermal", "random", "sensor-levels", "breathe", "counterflow", "split-tempo"). ```csharp public static List Create() ``` -------------------------------- ### Basic AnimationEngine call with fixed FPS Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/animation-engine.md Basic usage of AnimationEngine with fixed frame rate. Creates an engine with a Random seed, configures a cycle mode with forward sequence at 2 fps, and advances one second to get the frame and fps output. ```csharp var engine = new AnimationEngine(new Random()); var settings = new AnimationSettings { Mode = AnimationMode.Cycle, Sequence = SequenceKind.Forward, RateMode = AnimationRateMode.Fixed, FixedFps = 2 }; var output = engine.Advance(settings, sourceValue: null, elapsedSeconds: 1.0, paused: false); Console.WriteLine("Frame: " + output.Frame); // 2 after one second at 2 fps Console.WriteLine("Fps: " + output.FramesPerSecond);// 2 ``` -------------------------------- ### Constructor: HolderPreview Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/ui-preview.md Creates the control with Name="HolderPreview", AccessibleName="Display preview", AccessibleRole=AccessibleRole.Graphic, double-buffered painting, Caption="Waiting for display output" and MinimumSize=new Size(200,140). ```csharp public HolderPreview() ``` -------------------------------- ### CheckAsync method signature Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/update-service.md Asynchronously checks for updates. The current parameter is the installed version tag, automatic indicates a startup check subject to the 24-hour cache window, and token cancels the request and gates the semaphore. Returns a Task that never throws for network/cache failures but propagates OperationCanceledException. ```csharp public Task CheckAsync(string current,bool automatic,CancellationToken token) ``` -------------------------------- ### ProfileLibrary.Upgrade() Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/profile-library.md Migrates older libraries to PresetLibraryVersion=2, performing several steps to update stored profiles. ```APIDOC ## ProfileLibrary.Upgrade() ### Description Migrates older libraries to `PresetLibraryVersion=2` (no-op when already ≥ 2). Called by every successful `SettingsStore.Load` path. ### Signature ```csharp public static void Upgrade(AppSettings settings) ``` ### Parameters - **settings** (AppSettings) - Required - The application settings to migrate. ### Behavior 1. Strips `BuiltInId` from every stored profile (they were mutable before). 2. For `PresetLibraryVersion < 1`, remaps legacy localized names (`Classico`→Classic, `Attività`/`Attivita`→Activity, `Termico`→Thermal, `Casuale`→Random, with numeric suffixes on collision) via `settings.Rename`, and adds missing `Sensor levels`, `Breathe`, `Counterflow`, `Split tempo`. 3. Rebuilds the canonical library: profiles matching a canonical preset by name and content keep the built-in name; others become personal copies named `" (custom)"` (unique-ified) with `BuiltInId=null`. 4. Repoints `ActiveProfileName` to the migrated equivalent, sets `PresetLibraryVersion=2` and calls `settings.Validate()`. ### Returns void ``` -------------------------------- ### Custom availability check and injected clock Source: https://github.com/xxyoudeadpunkxx/zx6-display-control/blob/main/_autodocs/api-reference/aida-reader.md Example with a custom availability check and injected clock. Uses a MonotonicClock and a processAvailable callback that always returns true to skip the process check in tests or headless runs. Catches exceptions and converts them to a SensorReadResult.Failure with ErrorCode "ReadError". Also checks for AccessDenied and prints a message. ```csharp var clock = new MonotonicClock(); var reader = new SharedMemoryReader( mapName: "AIDA64_SensorValues", processAvailable: () => true); // skip the process check in tests/headless runs SensorReadResult result; try { result = reader.Read(clock.ElapsedMilliseconds); } catch (Exception e) { result = SensorReadResult.Failure("ReadError", "AIDA64 read failed: " + e.Message); } if (result.ErrorCode == "AccessDenied") { Console.WriteLine("Reopen both apps under the same Windows account."); } ```