From 1933cc365f9647d8b76b79c03461d15c97f7a239 Mon Sep 17 00:00:00 2001 From: Tyler Kron Date: Sat, 16 May 2026 21:15:44 -0600 Subject: [PATCH 1/8] docs: rewrite README with marketing positioning and cross-platform emphasis Co-Authored-By: Claude Sonnet 4.6 --- README.md | 161 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 108 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index a6fcfe9..ccdceb9 100644 --- a/README.md +++ b/README.md @@ -1,80 +1,135 @@ # DAQiFi Desktop -Windows desktop application (.NET) that is used to communicate with DAQiFi hardware. +> "Revolutionizing the data collection experience with convenient, portable device connectivity." +> +> The official Windows desktop application for DAQiFi hardware — real-time visualization, session logging, and firmware updates, all in one place. -## Tech Stack +[![Build](https://github.com/daqifi/daqifi-desktop/actions/workflows/build.yaml/badge.svg)](https://github.com/daqifi/daqifi-desktop/actions/workflows/build.yaml) +[![License: MIT](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](LICENSE) +[![Platform](https://img.shields.io/badge/platform-Windows-blue?style=flat-square)](https://github.com/daqifi/daqifi-desktop/releases) +[![.NET](https://img.shields.io/badge/.NET-10.0-purple?style=flat-square)](https://dotnet.microsoft.com/) -- .NET 10.0 -- WPF -- SQLite +[daqifi.com](https://daqifi.com) · [daqifi-core SDK](https://github.com/daqifi/daqifi-core) · [Issues](https://github.com/daqifi/daqifi-desktop/issues) · [Discussions](https://github.com/daqifi/daqifi-desktop/discussions) -## Dependencies +--- -- EntityFramework (ORM) -- Google Protocol Buffers (read incoming data from DAQiFi hardware) -- MahApps (UI components) -- Oxyplot (for graphing) +## What is DAQiFi Desktop? -## CI/CD +DAQiFi hardware is designed to get out of the way so you can focus on the data, not the collection process. DAQiFi Desktop is the application that makes that possible — connect a Nyquist device over WiFi or USB, configure your channels, start a logging session, and watch your data arrive in real time. No custom scripting required. -The project uses GitHub Actions for continuous integration and deployment: +If you are building automated pipelines or integrating DAQiFi devices into your own software, the [daqifi-core](https://github.com/daqifi/daqifi-core) .NET SDK gives you programmatic access to the same hardware. -- **Build & Test**: Automated build, testing, and code coverage on every pull request -- **Static Analysis**: .NET SDK analyzers and Roslynator for code quality enforcement -- **Code Coverage**: ReportGenerator creates HTML reports and posts summaries to PRs (80% minimum required) -- **MSI Installer**: Automated Windows installer builds using Wix Toolset -- **Release**: Automatic release asset publishing when GitHub releases are created -- **Dependency Updates**: Dependabot manages NuGet and GitHub Actions dependencies weekly +## Quick install / first run -All workflows run on .NET 10.0 with Windows runners for WPF compatibility. +1. Download the latest `DAQifiDesktop_Setup.msi` from the [Releases page](https://github.com/daqifi/daqifi-desktop/releases). +2. Run the installer — no prerequisites beyond the included .NET runtime. +3. Launch **DAQiFi Desktop**. +4. Click **Connect** and let the app discover your Nyquist device on the local network, or enter its IP address manually. +5. Enable the channels you want to log and press **Start Logging**. -## Observability +## Common applications -Exceptions are sent to [Sentry](https://o4511134234640384.sentry.io/). The DSN is configured in `Daqifi.Desktop/App.config`. +- **Space research** — sensor data acquisition for moon regolith testing +- **Medical R&D** — prosthetic socket pressure monitoring +- **Industrial monitoring** — continuous analog and digital I/O logging +- **Engineering education** — hands-on data acquisition with SCPI-compliant hardware -## Documentation +## Where DAQiFi Desktop fits -How data goes from the device to the database. +| Layer | Repo | What it does | +|---|---|---| +| Hardware | Nyquist 1 / Nyquist 3 | Wireless DAQ devices (WiFi + USB, battery-powered) | +| SDK | [daqifi-core](https://github.com/daqifi/daqifi-core) | .NET library for device communication and data streaming | +| **App** | **daqifi-desktop** | **GUI for device connection, visualization, logging, and firmware updates** | +| User code | Your project | Custom dashboards, test rigs, or automated pipelines built on daqifi-core | + +## What you can do + +| Capability | What it means for you | +|---|---| +| Auto-discovery over WiFi (UDP, port 30303) | Devices appear automatically when they are on the same network | +| Manual IP connection | Connect directly to a known device address when broadcast discovery is not available | +| USB / Serial connection | Use USB as an alternative to WiFi — same data, no network configuration needed | +| Multiple simultaneous devices | Connect and log from more than one Nyquist at the same time | +| Real-time channel visualization | Analog and digital channels plotted live with a viewport-aware minimap for large datasets | +| Start / stop logging sessions | Record data to a local SQLite database; sessions are preserved between runs | +| Per-channel formula scaling | Apply a custom NCalc expression (e.g. `x * 0.001 + 273.15`) to convert raw values before display and logging | +| CSV export with optional averaging | Export one or more sessions to `.csv`; optionally downsample by averaging N consecutive samples | +| Named profiles | Save and restore channel and device configurations across sessions | +| Firmware update via USB HID | Update Nyquist firmware from within the app — no separate tool needed | +| Error reporting via Sentry | Unhandled exceptions are captured automatically to help the team diagnose issues | + +## Supported devices + +| Device | Analog inputs | Resolution | Input range | Digital I/O | Transport | Power | +|---|---|---|---|---|---|---| +| Nyquist 1 | 16 | 12-bit | 0 – 5 V | Yes | 802.11n WiFi + USB | Battery | +| Nyquist 3 | 8 | 18-bit | ±10 V | Yes | 802.11n WiFi + USB | Battery | + +Both devices are SCPI-compliant and compatible with LabVIEW. + +## How data flows ```mermaid sequenceDiagram -DAQiFiHardware->>IStreamingDevice: Protobuf Message -IStreamingDevice->>StreamMessageConsumer: Protobuf Message -StreamMessageConsumer->>ProtobufMessageParser: Decode Message -StreamMessageConsumer->>IDevice:OnMessageReceived() -IDevice->>IChannel:Set Active Sample -IChannel->>IChannel:Scale Sample(Expression) -IChannel->>LoggingManager:OnChannelUpdated() -LoggingManager->>DatabaseLogger:HandleChannelUpdate() -DatabaseLogger->>DatabaseLogger:Add to Buffer -DatabaseLogger->>DatabaseLogger:ConsumerThread -DatabaseLogger->>Database:Bulk Insert Buffer + DAQiFiHardware->>IStreamingDevice: Protobuf message + IStreamingDevice->>StreamMessageConsumer: Protobuf message + StreamMessageConsumer->>ProtobufMessageParser: Decode + StreamMessageConsumer->>IDevice: OnMessageReceived() + IDevice->>IChannel: Set active sample + IChannel->>IChannel: Apply scale expression + IChannel->>LoggingManager: OnChannelUpdated() + LoggingManager->>DatabaseLogger: HandleChannelUpdate() + DatabaseLogger->>DatabaseLogger: Add to buffer + DatabaseLogger->>Database: Bulk insert ``` -## Installer +## WiFi connectivity + +**Requirements:** + +- Computer and DAQiFi device must be on the same subnet +- UDP port 30303 must be reachable (configured automatically when the app runs with administrator privileges) +- Virtual machines: use bridged networking, not NAT + +**Troubleshooting:** + +1. Run DAQiFi Desktop as administrator so it can configure the firewall rule automatically. +2. Confirm the computer and device are on the same WiFi network. +3. Test reachability with `ping `. +4. If discovery does not find the device, use **Manual Connection** and enter the IP address directly. + +**Port reference:** + +| Protocol | Port | Purpose | +|---|---|---| +| UDP | 30303 | Device discovery broadcasts | +| TCP | Device-specific (typically 9760) | Data streaming | + +## Requirements + +- **OS**: Windows 10 or later (x64) +- **Runtime**: .NET 10.0 for Windows (bundled in the MSI installer) +- **Privileges**: Administrator rights recommended for automatic firewall configuration during initial setup + +## Community & support + +- **Bug reports and feature requests**: [GitHub Issues](https://github.com/daqifi/daqifi-desktop/issues) +- **Questions and discussion**: [GitHub Discussions](https://github.com/daqifi/daqifi-desktop/discussions) +- **Commercial inquiries and custom hardware**: [daqifi.com](https://daqifi.com) -- Uses [Wix Toolset](https://wixtoolset.org/) -- Separate solution `Daqifi.Desktop.Setup` +## Contributing -## WiFi Device Connectivity +Please read the [Contributing Guidelines](CONTRIBUTING.md) before opening a pull request. All PRs require a conventional commit title (`feat:`, `fix:`, `docs:`, `deps:`, `chore:`) and at least one approving review from a DAQiFi core member. -DAQiFi Desktop discovers and connects to DAQiFi devices over WiFi using UDP broadcasts and TCP connections. +## For maintainers -### Network Requirements -- **Same Network**: Computer and DAQiFi device must be on the same network/subnet -- **Firewall**: UDP port 30303 must be allowed (configured automatically with admin privileges) -- **Virtual Machines**: Use bridged networking mode for VM environments +Releases are created by pushing a GitHub Release tag. The `release.yaml` workflow builds the MSI via WiX Toolset and attaches `DAQifiDesktop_Setup.msi` to the release automatically. The app version is set in `Daqifi.Desktop/Daqifi.Desktop.csproj` (``). Follow [semantic versioning](https://semver.org/); breaking changes should use the `feat!:` prefix in the PR title. -### Troubleshooting WiFi Discovery -1. **Run as Administrator** - Required for automatic firewall configuration -2. **Check Network Connection** - Ensure computer and device are on same WiFi network -3. **Verify Connectivity** - Test with `ping ` from command prompt -4. **Manual Connection** - Use manual IP connection if discovery fails +--- -### Port Configuration -- **UDP Discovery**: Port 30303 (device discovery broadcasts) -- **TCP Data**: Device-specific port (varies by device, typically 9760) +
-## Contribution +Built by [DAQiFi](https://daqifi.com) · Licensed under [MIT](LICENSE) -Please read [Contributing Guidelines](CONTRIBUTING.md) before contributing. +
From ad09ff5c4d626abd0ce3e27e7248b89788536b12 Mon Sep 17 00:00:00 2001 From: Tyler Kron Date: Sat, 16 May 2026 21:22:53 -0600 Subject: [PATCH 2/8] docs: fix runtime prerequisite, move Sentry to maintainers, tighten profiles description Co-Authored-By: Claude Sonnet 4.6 --- README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index ccdceb9..e62d2dc 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,12 @@ If you are building automated pipelines or integrating DAQiFi devices into your ## Quick install / first run -1. Download the latest `DAQifiDesktop_Setup.msi` from the [Releases page](https://github.com/daqifi/daqifi-desktop/releases). -2. Run the installer — no prerequisites beyond the included .NET runtime. -3. Launch **DAQiFi Desktop**. -4. Click **Connect** and let the app discover your Nyquist device on the local network, or enter its IP address manually. -5. Enable the channels you want to log and press **Start Logging**. +1. Install the [.NET 10.0 Desktop Runtime for Windows](https://dotnet.microsoft.com/download/dotnet/10.0) if you don't already have it. +2. Download the latest `DAQifiDesktop_Setup.msi` from the [Releases page](https://github.com/daqifi/daqifi-desktop/releases). +3. Run the installer. +4. Launch **DAQiFi Desktop**. +5. Click **Connect** and let the app discover your Nyquist device on the local network, or enter its IP address manually. +6. Enable the channels you want to log and press **Start Logging**. ## Common applications @@ -55,9 +56,8 @@ If you are building automated pipelines or integrating DAQiFi devices into your | Start / stop logging sessions | Record data to a local SQLite database; sessions are preserved between runs | | Per-channel formula scaling | Apply a custom NCalc expression (e.g. `x * 0.001 + 273.15`) to convert raw values before display and logging | | CSV export with optional averaging | Export one or more sessions to `.csv`; optionally downsample by averaging N consecutive samples | -| Named profiles | Save and restore channel and device configurations across sessions | +| Named profiles | Save and restore device connections, active channels, and sampling rate — switch between setups without reconfiguring | | Firmware update via USB HID | Update Nyquist firmware from within the app — no separate tool needed | -| Error reporting via Sentry | Unhandled exceptions are captured automatically to help the team diagnose issues | ## Supported devices @@ -126,6 +126,8 @@ Please read the [Contributing Guidelines](CONTRIBUTING.md) before opening a pull Releases are created by pushing a GitHub Release tag. The `release.yaml` workflow builds the MSI via WiX Toolset and attaches `DAQifiDesktop_Setup.msi` to the release automatically. The app version is set in `Daqifi.Desktop/Daqifi.Desktop.csproj` (``). Follow [semantic versioning](https://semver.org/); breaking changes should use the `feat!:` prefix in the PR title. +Unhandled exceptions are captured via Sentry. The DSN is in `Daqifi.Desktop/App.config`. + ---
From 115a786e0d0f91185a29d3347c113cd0de73dbc1 Mon Sep 17 00:00:00 2001 From: Tyler Kron Date: Sat, 16 May 2026 21:26:11 -0600 Subject: [PATCH 3/8] docs: fix release trigger description (GitHub Release created event, not tag push) Co-Authored-By: Claude Sonnet 4.6 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e62d2dc..489e730 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ Please read the [Contributing Guidelines](CONTRIBUTING.md) before opening a pull ## For maintainers -Releases are created by pushing a GitHub Release tag. The `release.yaml` workflow builds the MSI via WiX Toolset and attaches `DAQifiDesktop_Setup.msi` to the release automatically. The app version is set in `Daqifi.Desktop/Daqifi.Desktop.csproj` (``). Follow [semantic versioning](https://semver.org/); breaking changes should use the `feat!:` prefix in the PR title. +Releases are created by publishing a GitHub Release (via the GitHub web UI or API — the `release.yaml` workflow triggers on the `release: created` event, not on a tag push alone). Once a release is published, the workflow builds the MSI via WiX Toolset and attaches `DAQifiDesktop_Setup.msi` automatically. The app version is set in `Daqifi.Desktop/Daqifi.Desktop.csproj` (``). Follow [semantic versioning](https://semver.org/); breaking changes should use the `feat!:` prefix in the PR title. Unhandled exceptions are captured via Sentry. The DSN is in `Daqifi.Desktop/App.config`. From c32483b05cbcfe46fcc338fbc0a042ab4416569c Mon Sep 17 00:00:00 2001 From: Tyler Kron Date: Sun, 17 May 2026 22:12:46 -0600 Subject: [PATCH 4/8] docs: pin build badge to main branch, remove Discussions link Co-Authored-By: Claude Sonnet 4.6 --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 489e730..56e0caa 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,12 @@ > > The official Windows desktop application for DAQiFi hardware — real-time visualization, session logging, and firmware updates, all in one place. -[![Build](https://github.com/daqifi/daqifi-desktop/actions/workflows/build.yaml/badge.svg)](https://github.com/daqifi/daqifi-desktop/actions/workflows/build.yaml) +[![Build](https://github.com/daqifi/daqifi-desktop/actions/workflows/build.yaml/badge.svg?branch=main)](https://github.com/daqifi/daqifi-desktop/actions/workflows/build.yaml) [![License: MIT](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](LICENSE) [![Platform](https://img.shields.io/badge/platform-Windows-blue?style=flat-square)](https://github.com/daqifi/daqifi-desktop/releases) [![.NET](https://img.shields.io/badge/.NET-10.0-purple?style=flat-square)](https://dotnet.microsoft.com/) -[daqifi.com](https://daqifi.com) · [daqifi-core SDK](https://github.com/daqifi/daqifi-core) · [Issues](https://github.com/daqifi/daqifi-desktop/issues) · [Discussions](https://github.com/daqifi/daqifi-desktop/discussions) +[daqifi.com](https://daqifi.com) · [daqifi-core SDK](https://github.com/daqifi/daqifi-core) · [Issues](https://github.com/daqifi/daqifi-desktop/issues) --- @@ -115,7 +115,6 @@ sequenceDiagram ## Community & support - **Bug reports and feature requests**: [GitHub Issues](https://github.com/daqifi/daqifi-desktop/issues) -- **Questions and discussion**: [GitHub Discussions](https://github.com/daqifi/daqifi-desktop/discussions) - **Commercial inquiries and custom hardware**: [daqifi.com](https://daqifi.com) ## Contributing From c813238ee67408d47a1e7aceec8d743ba7273cd3 Mon Sep 17 00:00:00 2001 From: Tyler Kron Date: Sun, 17 May 2026 22:20:17 -0600 Subject: [PATCH 5/8] docs: add architecture.md with C4 diagrams, verified streaming flow Pulls the data-flow diagram out of the README into docs/architecture.md and rewrites it against the actual code: - StreamMessageConsumer / ProtobufMessageParser no longer exist; protobuf decoding now happens in Daqifi.Core's ProtobufProtocolHandler - IChannel.ActiveSample setter is what applies NCalc scaling and fires OnChannelUpdated - LoggingManager iterates a list of ILoggers (DatabaseLogger, PlotLogger, SummaryLogger), not just DatabaseLogger - DatabaseLogger uses a BlockingCollection consumer thread that bulk- inserts via EFCore.BulkExtensions every ~100 ms Adds C4 System Context and Container diagrams so contributors can see where DAQiFi Desktop sits relative to Nyquist hardware, Sentry, GitHub Releases, and the Windows Firewall. Co-Authored-By: Claude Sonnet 4.6 --- README.md | 18 +------- docs/architecture.md | 102 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 17 deletions(-) create mode 100644 docs/architecture.md diff --git a/README.md b/README.md index 56e0caa..de03d75 100644 --- a/README.md +++ b/README.md @@ -68,22 +68,6 @@ If you are building automated pipelines or integrating DAQiFi devices into your Both devices are SCPI-compliant and compatible with LabVIEW. -## How data flows - -```mermaid -sequenceDiagram - DAQiFiHardware->>IStreamingDevice: Protobuf message - IStreamingDevice->>StreamMessageConsumer: Protobuf message - StreamMessageConsumer->>ProtobufMessageParser: Decode - StreamMessageConsumer->>IDevice: OnMessageReceived() - IDevice->>IChannel: Set active sample - IChannel->>IChannel: Apply scale expression - IChannel->>LoggingManager: OnChannelUpdated() - LoggingManager->>DatabaseLogger: HandleChannelUpdate() - DatabaseLogger->>DatabaseLogger: Add to buffer - DatabaseLogger->>Database: Bulk insert -``` - ## WiFi connectivity **Requirements:** @@ -119,7 +103,7 @@ sequenceDiagram ## Contributing -Please read the [Contributing Guidelines](CONTRIBUTING.md) before opening a pull request. All PRs require a conventional commit title (`feat:`, `fix:`, `docs:`, `deps:`, `chore:`) and at least one approving review from a DAQiFi core member. +Please read the [Contributing Guidelines](CONTRIBUTING.md) before opening a pull request. See [docs/architecture.md](docs/architecture.md) for the streaming data pipeline and system overview, and [docs/design-philosophy.md](docs/design-philosophy.md) for UI/UX principles. All PRs require a conventional commit title (`feat:`, `fix:`, `docs:`, `deps:`, `chore:`) and at least one approving review from a DAQiFi core member. ## For maintainers diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..ca64a29 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,102 @@ +# Architecture + +This document is for contributors. It explains how DAQiFi Desktop is structured and how a single sample makes its way from a Nyquist device into the SQLite database. For visual/interaction principles, see [design-philosophy.md](design-philosophy.md). For specific design decisions, see [adr/](adr/). + +## System context + +How DAQiFi Desktop relates to the world outside it. + +```mermaid +C4Context + title System Context — DAQiFi Desktop + + Person(user, "Operator", "Engineer, researcher, or educator collecting data") + + System(desktop, "DAQiFi Desktop", "WPF app: discovery, live visualization, session logging, firmware updates") + + System_Ext(nyquist, "Nyquist Device", "DAQiFi hardware (Nyquist 1 or 3) over WiFi and/or USB") + System_Ext(github, "GitHub Releases API", "Source of update notifications") + System_Ext(sentry, "Sentry", "Unhandled-exception telemetry") + System_Ext(firewall, "Windows Firewall", "Host firewall — UDP 30303 rule for device discovery") + + Rel(user, desktop, "Connects devices, configures channels, starts logging") + Rel(desktop, nyquist, "UDP discovery (30303), TCP streaming, USB-Serial, USB HID for firmware") + Rel(desktop, github, "Checks latest release", "HTTPS") + Rel(desktop, sentry, "Reports unhandled exceptions", "HTTPS") + Rel(desktop, firewall, "Creates inbound UDP 30303 rule on first run (requires admin)") +``` + +## Containers + +What lives inside the DAQiFi Desktop process and on the user's machine. + +```mermaid +C4Container + title Container View — DAQiFi Desktop + + Person(user, "Operator") + System_Ext(nyquist, "Nyquist Device") + + Container_Boundary(app, "DAQiFi Desktop (WPF process)") { + Container(ui, "WPF UI", "XAML + ViewModels (CommunityToolkit.Mvvm)", "Channels pane, plot, profiles, dialogs") + Container(domain, "Device & Logging Domain", "C# (.NET 10)", "AbstractStreamingDevice, Channel, LoggingManager, DatabaseLogger") + Container(core, "Daqifi.Core", "NuGet package", "Transport (TCP/Serial/HID), ProtobufProtocolHandler, discovery, SCPI") + } + + ContainerDb(sqlite, "SQLite", "Local file (EF Core)", "Sessions, samples, device metadata") + Container_Ext(config, "App.config + Profiles XML", "Files in CommonApplicationData", "Settings, named profiles") + + Rel(user, ui, "Uses") + Rel(ui, domain, "Commands and observable state") + Rel(domain, core, "Sends SCPI, subscribes to MessageReceived") + Rel(core, nyquist, "Wire protocol") + Rel(domain, sqlite, "Bulk inserts via EF Core") + Rel(ui, config, "Reads/writes") +``` + +## Streaming data flow + +One sample's journey from the device wire to the database. Every step below was verified against the current code; the layer that owns each step is in parentheses. + +```mermaid +sequenceDiagram + participant HW as Nyquist Device + participant Core as Daqifi.Core
(DaqifiDevice + ProtobufProtocolHandler) + participant Dev as AbstractStreamingDevice
(WiFi or Serial subclass) + participant Ch as AbstractChannel + participant LM as LoggingManager + participant DBL as DatabaseLogger + participant DB as SQLite + + HW->>Core: Bytes (TCP or USB-Serial) + Core->>Core: Decode protobuf → DaqifiOutMessage + Core->>Dev: MessageReceived event + Dev->>Dev: HandleInboundMessage → ProtobufProtocolHandler.HandleAsync + Dev->>Dev: OnStreamMessageReceived(DaqifiOutMessage) + Note over Dev: For each active analog channel:
scale raw ADC (WiFi) or
use pre-scaled float (USB) + Dev->>Ch: channel.ActiveSample = new DataSample(...) + Ch->>Ch: Apply NCalc scale expression (if set) + Ch->>LM: OnChannelUpdated event + LM->>DBL: logger.Log(sample) // iterates all registered ILoggers + DBL->>DBL: _buffer.Add(sample) // BlockingCollection + Note over DBL: Background Consumer thread
polls every 100 ms + DBL->>DB: context.BulkInsert(samples) in EF Core transaction +``` + +### Notes on the flow + +- **WiFi vs USB scaling.** WiFi firmware sends raw ADC counts; USB firmware sends pre-scaled floats already in volts. `OnStreamMessageReceived` branches on this — see `AnalogInData` vs `AnalogInDataFloat` in [AbstractStreamingDevice.cs](../Daqifi.Desktop/Device/AbstractStreamingDevice.cs). +- **Two parallel paths per protobuf message.** The same `DaqifiOutMessage` produces per-channel `DataSample`s (the path above) *and* a single `DeviceMessage` carrying device-level state (battery, status, target frequency). The latter is dispatched via `LoggingManager.HandleDeviceMessage`. +- **Loggers are a list, not a single class.** `LoggingManager.Loggers` is an `ILogger` collection. `DatabaseLogger` is the persistent one, but `PlotLogger` and `SummaryLogger` also subscribe. +- **Backpressure.** `DatabaseLogger` uses a `BlockingCollection` producer/consumer split so the UI/device threads never wait on disk. The consumer drains in ~100 ms ticks and bulk-inserts via `EFCore.BulkExtensions.Sqlite`. +- **Timestamps.** `Daqifi.Core.TimestampProcessor` handles hardware-counter rollover and provides the firmware-measured inter-message delta (immune to TCP jitter). + +## Key entry points + +| Concern | Start here | +|---|---| +| New device transport | `Daqifi.Desktop/Device/AbstractStreamingDevice.cs` and the subclasses under `WiFiDevice/`, `SerialDevice/` | +| Channel behavior or scaling | `Daqifi.Desktop/Channel/AbstractChannel.cs` (`ActiveSample` setter) | +| Logging pipeline | `Daqifi.Desktop/Loggers/LoggingManager.cs` and `DatabaseLogger.cs` | +| Plot rendering and downsampling | See [adr/001-viewport-aware-downsampling.md](adr/001-viewport-aware-downsampling.md) | +| Visual and interaction design | See [design-philosophy.md](design-philosophy.md) | From f5147725851367b2514ddf2cde195147618070d8 Mon Sep 17 00:00:00 2001 From: Tyler Kron Date: Fri, 22 May 2026 07:40:25 -0600 Subject: [PATCH 6/8] docs: tighten C4 diagram labels with line breaks for readability Long descriptions and relationship labels were overflowing their boxes and overlapping arrows when rendered. Adds explicit \n breaks in descriptions, shortens verbose labels, and splits the device-transport relationship into two arrows (data streaming vs. firmware update) since they use different transports anyway. Co-Authored-By: Claude Sonnet 4.6 --- docs/architecture.md | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index ca64a29..48e7c9b 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -10,20 +10,21 @@ How DAQiFi Desktop relates to the world outside it. C4Context title System Context — DAQiFi Desktop - Person(user, "Operator", "Engineer, researcher, or educator collecting data") + Person(user, "Operator", "Engineer, researcher,\nor educator") - System(desktop, "DAQiFi Desktop", "WPF app: discovery, live visualization, session logging, firmware updates") + System(desktop, "DAQiFi Desktop", "WPF app: discovery,\nvisualization, logging,\nfirmware updates") - System_Ext(nyquist, "Nyquist Device", "DAQiFi hardware (Nyquist 1 or 3) over WiFi and/or USB") - System_Ext(github, "GitHub Releases API", "Source of update notifications") - System_Ext(sentry, "Sentry", "Unhandled-exception telemetry") - System_Ext(firewall, "Windows Firewall", "Host firewall — UDP 30303 rule for device discovery") + System_Ext(nyquist, "Nyquist Device", "DAQiFi hardware\n(Nyquist 1 or 3)") + System_Ext(github, "GitHub Releases", "Update check source") + System_Ext(sentry, "Sentry", "Exception telemetry") + System_Ext(firewall, "Windows Firewall", "Host firewall —\nUDP 30303 rule") - Rel(user, desktop, "Connects devices, configures channels, starts logging") - Rel(desktop, nyquist, "UDP discovery (30303), TCP streaming, USB-Serial, USB HID for firmware") + Rel(user, desktop, "Connects devices,\nlogs data") + Rel(desktop, nyquist, "Discovery & streaming\n(WiFi UDP/TCP, USB Serial)") + Rel(desktop, nyquist, "Firmware updates\n(USB HID)") Rel(desktop, github, "Checks latest release", "HTTPS") - Rel(desktop, sentry, "Reports unhandled exceptions", "HTTPS") - Rel(desktop, firewall, "Creates inbound UDP 30303 rule on first run (requires admin)") + Rel(desktop, sentry, "Reports exceptions", "HTTPS") + Rel(desktop, firewall, "Adds UDP 30303 rule\n(first run, admin)") ``` ## Containers @@ -38,20 +39,20 @@ C4Container System_Ext(nyquist, "Nyquist Device") Container_Boundary(app, "DAQiFi Desktop (WPF process)") { - Container(ui, "WPF UI", "XAML + ViewModels (CommunityToolkit.Mvvm)", "Channels pane, plot, profiles, dialogs") - Container(domain, "Device & Logging Domain", "C# (.NET 10)", "AbstractStreamingDevice, Channel, LoggingManager, DatabaseLogger") - Container(core, "Daqifi.Core", "NuGet package", "Transport (TCP/Serial/HID), ProtobufProtocolHandler, discovery, SCPI") + Container(ui, "WPF UI", "XAML + MVVM", "Channels pane,\nplot, profiles,\ndialogs") + Container(domain, "Device & Logging\nDomain", "C# (.NET 10)", "AbstractStreamingDevice,\nChannel,\nLoggingManager,\nDatabaseLogger") + Container(core, "Daqifi.Core", "NuGet package", "Transport,\nProtobufProtocolHandler,\ndiscovery, SCPI") } - ContainerDb(sqlite, "SQLite", "Local file (EF Core)", "Sessions, samples, device metadata") - Container_Ext(config, "App.config + Profiles XML", "Files in CommonApplicationData", "Settings, named profiles") + ContainerDb(sqlite, "SQLite", "Local file\n(EF Core)", "Sessions, samples,\ndevice metadata") + Container_Ext(config, "App.config +\nProfiles XML", "CommonApplicationData", "Settings,\nnamed profiles") Rel(user, ui, "Uses") - Rel(ui, domain, "Commands and observable state") - Rel(domain, core, "Sends SCPI, subscribes to MessageReceived") + Rel(ui, domain, "Commands &\nobservable state") + Rel(domain, core, "Sends SCPI,\nreads messages") Rel(core, nyquist, "Wire protocol") - Rel(domain, sqlite, "Bulk inserts via EF Core") - Rel(ui, config, "Reads/writes") + Rel(domain, sqlite, "Bulk inserts\n(EF Core)") + Rel(ui, config, "Reads / writes") ``` ## Streaming data flow From 5d31fdc43cac755bf390d58957aaaa57cb975992 Mon Sep 17 00:00:00 2001 From: Tyler Kron Date: Fri, 22 May 2026 07:43:05 -0600 Subject: [PATCH 7/8] docs: switch architecture diagrams from C4 to flowchart for reliable rendering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mermaid C4 doesn't interpret \n as a line break — it renders the literal characters in the box. Switching to flowchart syntax (where
works reliably) with C4-style classDef colors. Visually equivalent, but text now wraps cleanly. Co-Authored-By: Claude Sonnet 4.6 --- docs/architecture.md | 91 +++++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 39 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index 48e7c9b..4d16eba 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -7,24 +7,29 @@ This document is for contributors. It explains how DAQiFi Desktop is structured How DAQiFi Desktop relates to the world outside it. ```mermaid -C4Context - title System Context — DAQiFi Desktop - - Person(user, "Operator", "Engineer, researcher,\nor educator") - - System(desktop, "DAQiFi Desktop", "WPF app: discovery,\nvisualization, logging,\nfirmware updates") - - System_Ext(nyquist, "Nyquist Device", "DAQiFi hardware\n(Nyquist 1 or 3)") - System_Ext(github, "GitHub Releases", "Update check source") - System_Ext(sentry, "Sentry", "Exception telemetry") - System_Ext(firewall, "Windows Firewall", "Host firewall —\nUDP 30303 rule") - - Rel(user, desktop, "Connects devices,\nlogs data") - Rel(desktop, nyquist, "Discovery & streaming\n(WiFi UDP/TCP, USB Serial)") - Rel(desktop, nyquist, "Firmware updates\n(USB HID)") - Rel(desktop, github, "Checks latest release", "HTTPS") - Rel(desktop, sentry, "Reports exceptions", "HTTPS") - Rel(desktop, firewall, "Adds UDP 30303 rule\n(first run, admin)") +flowchart TB + user(["Operator
Person
Engineer, researcher,
or educator"]) + + desktop["DAQiFi Desktop
System
WPF app: discovery,
visualization, logging,
firmware updates"] + + nyquist["Nyquist Device
External
DAQiFi hardware
(Nyquist 1 or 3)"] + github["GitHub Releases
External
Update check"] + sentry["Sentry
External
Exception telemetry"] + firewall["Windows Firewall
External
UDP 30303 rule"] + + user -- "Connects devices,
logs data" --> desktop + desktop -- "Streaming
(WiFi UDP/TCP,
USB Serial)" --> nyquist + desktop -- "Firmware
(USB HID)" --> nyquist + desktop -- "Latest release
(HTTPS)" --> github + desktop -- "Exceptions
(HTTPS)" --> sentry + desktop -- "Adds rule
(first run, admin)" --> firewall + + classDef person fill:#08427b,stroke:#073b6f,color:#fff + classDef system fill:#1168bd,stroke:#0b4884,color:#fff + classDef external fill:#999,stroke:#6b6b6b,color:#fff + class user person + class desktop system + class nyquist,github,sentry,firewall external ``` ## Containers @@ -32,27 +37,35 @@ C4Context What lives inside the DAQiFi Desktop process and on the user's machine. ```mermaid -C4Container - title Container View — DAQiFi Desktop - - Person(user, "Operator") - System_Ext(nyquist, "Nyquist Device") - - Container_Boundary(app, "DAQiFi Desktop (WPF process)") { - Container(ui, "WPF UI", "XAML + MVVM", "Channels pane,\nplot, profiles,\ndialogs") - Container(domain, "Device & Logging\nDomain", "C# (.NET 10)", "AbstractStreamingDevice,\nChannel,\nLoggingManager,\nDatabaseLogger") - Container(core, "Daqifi.Core", "NuGet package", "Transport,\nProtobufProtocolHandler,\ndiscovery, SCPI") - } - - ContainerDb(sqlite, "SQLite", "Local file\n(EF Core)", "Sessions, samples,\ndevice metadata") - Container_Ext(config, "App.config +\nProfiles XML", "CommonApplicationData", "Settings,\nnamed profiles") - - Rel(user, ui, "Uses") - Rel(ui, domain, "Commands &\nobservable state") - Rel(domain, core, "Sends SCPI,\nreads messages") - Rel(core, nyquist, "Wire protocol") - Rel(domain, sqlite, "Bulk inserts\n(EF Core)") - Rel(ui, config, "Reads / writes") +flowchart TB + user(["Operator"]) + nyquist["Nyquist Device
External"] + + subgraph app["DAQiFi Desktop (WPF process)"] + direction TB + ui["WPF UI
XAML + MVVM
Channels pane,
plot, profiles,
dialogs"] + domain["Device & Logging Domain
C# (.NET 10)
AbstractStreamingDevice,
Channel, LoggingManager,
DatabaseLogger"] + core["Daqifi.Core
NuGet package
Transport,
ProtobufProtocolHandler,
discovery, SCPI"] + end + + sqlite[("SQLite
EF Core, local file
Sessions, samples,
device metadata")] + config["App.config +
Profiles XML

CommonApplicationData
Settings,
named profiles"] + + user -- "Uses" --> ui + ui -- "Commands &
observable state" --> domain + domain -- "SCPI out,
MessageReceived in" --> core + core -- "Wire protocol" --> nyquist + domain -- "Bulk inserts" --> sqlite + ui -- "Reads / writes" --> config + + classDef person fill:#08427b,stroke:#073b6f,color:#fff + classDef container fill:#438dd5,stroke:#2e6295,color:#fff + classDef external fill:#999,stroke:#6b6b6b,color:#fff + class user person + class ui,domain,core container + class sqlite container + class nyquist,config external + style app stroke:#888,stroke-dasharray:5 5,fill:transparent ``` ## Streaming data flow From 324ac23299e1da3d6c7da0db744ce7504e081648 Mon Sep 17 00:00:00 2001 From: Tyler Kron Date: Fri, 22 May 2026 07:51:37 -0600 Subject: [PATCH 8/8] docs: replace empty Build badge with latest-release badge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit build.yaml only runs on pull_request, so the badge has no status to display outside an active PR. A latest-release badge is more useful for an end-user app anyway — it tells visitors what version they can download. Also fixes a leftover stale claim in the Requirements section that said .NET 10.0 was bundled in the MSI, which contradicts the install steps (the publish is framework-dependent — runtime must be installed separately). Co-Authored-By: Claude Sonnet 4.6 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index de03d75..49a2a22 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ > > The official Windows desktop application for DAQiFi hardware — real-time visualization, session logging, and firmware updates, all in one place. -[![Build](https://github.com/daqifi/daqifi-desktop/actions/workflows/build.yaml/badge.svg?branch=main)](https://github.com/daqifi/daqifi-desktop/actions/workflows/build.yaml) +[![Latest release](https://img.shields.io/github/v/release/daqifi/daqifi-desktop?style=flat-square&label=release&color=brightgreen)](https://github.com/daqifi/daqifi-desktop/releases/latest) [![License: MIT](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](LICENSE) [![Platform](https://img.shields.io/badge/platform-Windows-blue?style=flat-square)](https://github.com/daqifi/daqifi-desktop/releases) [![.NET](https://img.shields.io/badge/.NET-10.0-purple?style=flat-square)](https://dotnet.microsoft.com/) @@ -93,7 +93,7 @@ Both devices are SCPI-compliant and compatible with LabVIEW. ## Requirements - **OS**: Windows 10 or later (x64) -- **Runtime**: .NET 10.0 for Windows (bundled in the MSI installer) +- **Runtime**: [.NET 10.0 Desktop Runtime for Windows](https://dotnet.microsoft.com/download/dotnet/10.0) (install separately before running the MSI) - **Privileges**: Administrator rights recommended for automatic firewall configuration during initial setup ## Community & support