Removes the leaked Google Maps API key literal from README.md and the
live com.google.android.geo.API_KEY in AndroidManifest.xml (which shipped
in every built APK). The manifest value is now the build-time placeholder
${MAPS_API_KEY}, injected via AndroidManifestPlaceholders from the
MapsApiKey MSBuild property, resolved from a CI secret (-p:MapsApiKey=),
the MAPS_API_KEY env var, or a gitignored maps.key.props at the repo root
(maps.key.props.example committed as the template). maps.key.props is
gitignored so a real key is never committed.
No rotated key is included here; the human supplies it via CI secret.
Pairs with the console key rotation to close SEC-1 (decisions.md
2026-08-20; security/sec-1-gmaps-key.md). Git history intentionally not
rewritten (recorded risk-acceptance relies on revocation of the old key).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
128 lines
5.8 KiB
Markdown
128 lines
5.8 KiB
Markdown
# 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=<key>` 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=<SIMULATOR_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=<DEVICE_UDID>
|
|
```
|
|
|
|
### 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.
|