# Field Logger Cross-platform .NET MAUI app (Windows, macOS, iOS, Android) for logging utility locate points from an **Underground Magnetics locating receiver** paired with RTK GPS positions from a **Maglink (H11) RTK receiver**, both over BLE. ## Solution layout ``` FieldLogger/ Models/ Device protocol models (UmLogPacket, GnssFix) and DB entities (Job, LoggedPoint) Services/ Ble/ BleSerialClient (line-oriented GATT serial) and BleScanner (Plugin.BLE) UmReceiverService.cs $UMPBDL protocol, PBDL schema-1 packet parsing MaglinkService.cs $GNPOS/$GNDEV NMEA parsing + AT commands DeviceConnectionManager.cs Auto-connect on launch, reconnect with backoff PointLogger.cs Joins locator packets with the latest GNSS fix, saves to SQLite Data/AppDatabase.cs sqlite-net-pcl store (jobs + points) Sync/IMqttSyncService.cs Stub for the future MQTT job/point synchronization ViewModels/ + Views/ Shell tabs: Home, Jobs, Map, Settings (+ DeviceScan, JobDetail) Resources/Raw/map.html Google Maps JS page used by the Windows map view doc/ Device protocol documentation ``` ## How it works - **First launch:** the Home page redirects to the device scan page. Pick your locating receiver (names starting with `UMRX` or `DT100`) and, from Settings or Home, the Maglink RTK receiver. Selections are persisted. - **Subsequent launches:** `DeviceConnectionManager` auto-connects to both saved devices and keeps retrying/reconnecting with backoff. Devices can be changed or forgotten in Settings. - **Logging:** create a job (Home or Jobs tab) — it becomes the active job. When the operator presses the log button on the UM receiver, the app receives the PBDL packet, attaches the most recent RTK fix (if fresher than 5 s), and stores the combined record with all metadata in SQLite (`fieldlogger.db3` in app data). - **Map:** plots the active (or selected) job's points. Native map control on iOS/Android/macOS (Google Maps on Android, Apple Maps on iOS/macOS); Google Maps JavaScript in a WebView on Windows. ## Setup required before running 1. **Google Maps key (Android):** the manifest key is **injected at build time**, never committed (SEC-1). Provide it one of three ways: pass `-p:MapsApiKey=` to `dotnet build` (how CI supplies it from a secret), set the `MAPS_API_KEY` environment variable, or copy `maps.key.props.example` to `maps.key.props` (gitignored) at the repo root and put your key there. Use a key restricted to the app package + release SHA-1 (Google Cloud Console → Maps SDK for Android). Without a key, maps render blank but the app builds. 2. **Google Maps key (Windows):** create a Maps JavaScript API key and paste it into the Settings page of the app. 3. **Maglink GATT UUIDs:** the Maglink docs describe the serial protocol but not its GATT table. `MaglinkService` currently assumes the Nordic UART Service (`6e400001-b5a3-f393-e0a9-e50e24dcca9e`). Verify against the actual hardware (any BLE scanner app will show the service UUIDs) and update the constants at the top of `FieldLogger/Services/MaglinkService.cs` if they differ. ## Building and running All commands are run from the repository root. (`dotnet run` does not support MAUI projects — use `dotnet build -t:Run` to deploy and launch instead.) ### Windows ```powershell # Build dotnet build FieldLogger/FieldLogger.csproj -f net9.0-windows10.0.19041.0 # Run (unpackaged; WindowsPackageType=None) dotnet build FieldLogger/FieldLogger.csproj -f net9.0-windows10.0.19041.0 -t:Run # ...or launch the built exe directly: .\FieldLogger\bin\Debug\net9.0-windows10.0.19041.0\win10-x64\FieldLogger.exe ``` ### Android ```powershell # Build (APK is produced under bin/Debug/net9.0-android) dotnet build FieldLogger/FieldLogger.csproj -f net9.0-android # Deploy + launch on the connected device or running emulator dotnet build FieldLogger/FieldLogger.csproj -f net9.0-android -t:Run # If several devices/emulators are attached, pick one by adb serial (see `adb devices`) dotnet build FieldLogger/FieldLogger.csproj -f net9.0-android -t:Run -p:AdbTarget="-s emulator-5554" ``` Note: BLE does not work in the Android emulator — use a physical device (enable USB debugging, then `adb devices` to confirm it is attached). ### iOS (requires a Mac; run these on the Mac, or from Windows via Visual Studio's paired Mac) ```bash # Build dotnet build FieldLogger/FieldLogger.csproj -f net9.0-ios # Run in the default iOS Simulator (note: the simulator has no Bluetooth) dotnet build FieldLogger/FieldLogger.csproj -f net9.0-ios -t:Run # Run on a specific simulator by UDID (list with: xcrun simctl list devices) dotnet build FieldLogger/FieldLogger.csproj -f net9.0-ios -t:Run -p:_DeviceName=:v2:udid= # Run on a physical iPhone (requires provisioning profile / signing set up in Xcode first) dotnet build FieldLogger/FieldLogger.csproj -f net9.0-ios -t:Run -p:RuntimeIdentifier=ios-arm64 -p:_DeviceName= ``` ### macOS (Mac Catalyst — requires a Mac) ```bash # Build dotnet build FieldLogger/FieldLogger.csproj -f net9.0-maccatalyst # Build + launch dotnet build FieldLogger/FieldLogger.csproj -f net9.0-maccatalyst -t:Run ``` ### Visual Studio Open `FieldLogger.sln`, pick the target framework/device in the debug-target dropdown, and press F5. This is the most convenient route for iOS from Windows (via a paired Mac) and for Android device debugging. ## Roadmap - **MQTT sync** (`Services/Sync/IMqttSyncService.cs` is the seam): receive configured jobs from the server and publish logged points. `Job.RemoteId` / `Synced` and `LoggedPoint.Synced` columns are already in the schema. Suggested client: MQTTnet. - The Maglink can also upload positions directly via MQTT (`AT+UPLOADDATA_*` commands, see `doc/AT_UPLOADDATA_MQTT_Configuration_EN.md`) if server-side ingestion is preferred.