Files
ExperionCrawler/AGENTS.md
2026-05-08 17:22:10 +09:00

69 lines
3.1 KiB
Markdown

# ExperionCrawler — Agent Instructions
## Build / Run / Test
| Action | Command | Working Dir |
|--------|---------|-------------|
| Build | `dotnet build src/Web/ExperionCrawler.csproj` | repo root |
| Run (dev) | `dotnet run` | `src/Web/` |
| Publish | `dotnet publish -c Release -o /opt/ExperionCrawler` | `src/Web/` |
| Tests | `dotnet test` | repo root |
Single project: `src/Web/ExperionCrawler.csproj`. Core and Infrastructure are included via `<Compile Include>` globs — there are no separate projects to build. Runtime target is `linux-arm64`.
## Architecture
```
src/
├── Core/ — Interfaces (IExperionServices.cs), Domain entities, DTOs, Application Services
├── Infrastructure/ — OpcUa/, Database/, Certificates/, Csv/, Mcp/
└── Web/ — Program.cs, Controllers/, wwwroot/ (SPA)
```
All controllers are in `src/Web/Controllers/ExperionControllers.cs` (single file). All interfaces are in `src/Core/Application/Interfaces/IExperionServices.cs` (single file).
## Database
**PostgreSQL** (NOT SQLite — README is stale). Connection strings in `src/Web/appsettings.json`. TimescaleDB extension may be enabled on `history_table` via DDL only; no app code changes needed.
## Critical Convention — JSON camelCase
`PropertyNamingPolicy = null` in Program.cs means C# PascalCase becomes JSON keys. **Frontend expects camelCase**. Every controller `Ok(...)` response MUST use explicit anonymous objects with camelCase keys:
```csharp
// ✅ Correct
return Ok(new { id = x.Id, tagName = x.TagName });
// ❌ Broken — JS gets undefined
return Ok(new { x.Id, x.TagName });
return Ok(myDto); // typed object
```
See `CODING_CONVENTIONS.md` for full details and checklist.
## Background Services (HostedServices in Program.cs)
- `ExperionRealtimeService` — OPC UA subscription, 500ms batch flush to DB
- `ExperionHistoryService` — periodic snapshot (60s) from realtime_table → history_table
- `ExperionOpcServerService` — exposes realtime data as OPC UA server (port 4841)
- `McpServerHostedService` — Python MCP server bridge
- `ExperionFastService` — high-frequency data capture sessions
- `ExperionFastCleanupService` — expired session cleanup
All registered as Singleton + HostedService. RealtimeService and OpcServerService share autostart flag files (`realtime_autostart.json`, `opcserver_autostart.json`) in the working directory.
## Frontend
Vanilla JS SPA. `wwwroot/index.html` + `js/app.js` + `css/style.css`. No build step. Tab-based navigation; tabs do NOT auto-fire API calls on entry (performance fix).
## OPC UA SDK Gotchas
- SDK v1.5.378.134 — `Session.Create()` is `[Obsolete]`; use `DefaultSessionFactory.CreateAsync()`
- `Subscription.Create()` / `Delete()` / `ApplyChanges()` also deprecated → async variants preferred
- Certificate validation must be attached AFTER `OpcUaConfigProvider.GetConfigAsync()` returns the config
- TCP connect timeout: wrap `SelectEndpointAsync` with a 10s `CancellationTokenSource` (OS default is 127s)
## Deploy
`sudo bash deploy.sh` — publishes to `/opt/ExperionCrawler`, creates systemd service `experioncrawler`, sets up PKI dirs. Service runs as `www-data`.