You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Real-time monitoring platform for mining equipment. Collects GPS telemetry from fleet vehicles via MQTT, processes through an alert engine with configurable rules (speed, geo-fence, idle), and exposes data via REST API with SignalR real-time push.
This starts 6 services: PostgreSQL, Mosquitto, LocalStack, API (auto-migration + seed data + admin user), Worker, and TruckMocker. The API is available at http://localhost:5211/swagger.
Verify the pipeline
./verify-pipeline.sh | tee pipeline-verification.log
Manual startup (for development)
Prerequisites: .NET 9 SDK, Docker
After cloning, set up the git hooks (auto-format on commit):
git config core.hooksPath .githooks
# 1. Start infrastructure only
docker compose up -d postgres mosquitto localstack
# 2. Run API (auto-migrates on startup)
dotnet run --project src/MineWatch.Api
# 3. Run Worker (MQTT subscriber + SQS consumer + alert engine)
dotnet run --project src/MineWatch.Worker
# 4. Start simulator
dotnet run --project TruckMocker
Kubernetes-style readiness probe (verifies DB connectivity)
Console output
Structured JSON logs via Serilog (enriched with machine name, environment)
Configuration
Configuration is via appsettings.json and environment variables (overridden in docker-compose):
Key
Description
ConnectionStrings:DefaultConnection
PostgreSQL connection string
Jwt:Key
JWT signing key (env var in production, User Secrets in development)
Jwt:Issuer / Jwt:Audience
JWT token issuer and audience
AWS:ServiceURL
SQS endpoint (LocalStack or real AWS)
AWS:Region
AWS region
Sqs:QueueName
SQS queue name
Sqs:DlqName
Dead-letter queue name
Sqs:MaxReceiveCount
DLQ threshold (default: 3)
Mqtt:Server / Mqtt:Port
MQTT broker address
Default Seed Data
On first startup, the system seeds:
Roles: Admin, Operator, Viewer
Admin user: username admin, password Admin@123
Devices: Truck-001, Truck-002, Truck-003
Alert rules:
Speed Limit — Trucks: 40 km/h threshold, 300s cooldown
Restricted Zone — Office Area: geo-fence circle (Perth CBD), no cooldown
Idle Timeout — Trucks: 5 min stationary, 600s cooldown
Known Trade-offs
These are deliberate simplifications for the demo/portfolio context. A production deployment would address each one:
Area
Current Implementation
Production Approach
SQS message deletion
SqsConsumerWorker deletes from SQS immediately after writing to the in-memory Channel<T>, before TelemetryBatchWriter persists to DB. If the worker crashes between channel write and DB write, that data is lost.
Delete-after-persist: acknowledge the message only after the batch writer confirms the DB write. Alternatives: outbox pattern, or use the batch writer callback to signal completion.
Channel backpressure
Bounded Channel<T>(1000) with FullMode = DropOldest. Suitable for live dashboards where stale positions are low-value, but inappropriate for audit/reporting pipelines that require every sample.
For audit-critical data, use FullMode.Wait and let SQS absorb backpressure, or scale out consumers. Consider separate pipelines for live vs. historical data.
Demo credentials
docker-compose.yml contains a demo JWT signing key and DbSeeder creates admin / Admin@123. These are for local development only.
Production uses AWS Secrets Manager or equivalent for JWT keys and connection strings. No seed credentials in production — admin accounts created via a secure bootstrapping flow.
Device type cache
AlertEngine caches rules (30s TTL) and device types (5min TTL) in-memory. Changes propagate on cache expiry, not immediately.
PostgreSQL NOTIFY/LISTEN or MQTT pub/sub for real-time cache invalidation.