File size: 7,194 Bytes
2574e86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# solverforge-fsr WIREFRAME

This file is the architectural map for the field-service routing example.

`README.md` explains how to run and use the app. This document explains how the
pieces fit together and where each responsibility lives.

## Documentation Roles

- `README.md`
  Quick start, dependency shape, API list, and user-facing orientation.
- `WIREFRAME.md`
  Architecture, execution flow, and file-map walkthrough.
- `AGENTS.md`
  Repo-specific contribution, validation, and documentation rules.
- `Makefile`
  Local development, validation, and Space/Docker command surface.
- `Dockerfile`
  Hugging Face Docker Space image definition.

## What This Repo Is Teaching

This repo is a complete `solverforge-fsr` `1.0.1` list-variable SolverForge app
for field-service routing in Bergamo.

It shows how to combine:

- a `FieldServicePlan` solution with a list planning variable
- route-level hard and soft score rules
- precomputed travel-leg facts and `solverforge-maps` road-network geometry
- retained jobs with snapshots, analysis, cancel, pause, resume, and SSE
- a browser map workspace built on stock `solverforge-ui` assets

## SolverForge Concepts In Plain Language

- `Location`
  Input place data. Depots and customer sites are indexed so routes can refer to
  them cheaply.
- `ServiceVisit`
  Input job data. The solver places visit indexes into technician routes.
- `TravelLeg`
  Input travel data. Each leg records duration, distance, and whether the road
  graph can connect the two locations.
- `TechnicianRoute`
  Planning entity. Each technician owns one ordered `visits` list.
- `FieldServicePlan`
  Planning solution. It holds facts, route entities, and the current score.
- hard score
  Missing assignments, unreachable legs, missing skills or parts, late visits,
  and route overtime.
- soft score
  Travel cost, workload balance, territory fit, and priority slack.
- retained job
  A solve that lives in memory so the UI can stream events, fetch snapshots,
  pause/resume, cancel, analyze, and delete terminal jobs.

## Runtime Flow

1. The browser loads `static/index.html`.
2. `static/app.js` loads `static/sf-config.json` and
   `static/generated/ui-model.json`.
3. The app fetches `/demo-data/STANDARD`.
4. The backend returns a `FieldServicePlan` with seed travel legs.
5. The browser renders route cards, tables, timeline, map shell, and the visible
   REST API guide.
6. When the user clicks Solve, the browser posts the current plan to
   `POST /jobs`.
7. `src/api/routes.rs` deserializes the `PlanDto` and calls
   `prepare_routing()`.
8. `prepare_routing()` loads or fetches the Bergamo road network, computes the
   full travel matrix, and replaces seed legs with road-network legs.
9. `SolverService` starts a retained solve through
   `SolverManager<FieldServicePlan>`.
10. Solver events are converted by `src/solver/event_payload.rs` into
    UI-facing JSON.
11. The browser consumes `/jobs/{id}/events` and fetches snapshots, analysis,
    and route geometry for exact snapshot revisions.
12. `src/api/route_geometry.rs` builds map segments, preserving non-routed
    statuses so one unreachable leg does not hide the rest of a route.

## File Map

```text
.
β”œβ”€β”€ Cargo.toml
β”‚   Rust 1.95 crate metadata for app version 1.0.1 and registry dependency
β”‚   requests.
β”œβ”€β”€ solver.toml
β”‚   Embedded search policy for list construction and local search.
β”œβ”€β”€ solverforge.app.toml
β”‚   App metadata, demo IDs, model facts/entities, registry dependency sources,
β”‚   and the `solverforge 0.13.1` runtime target.
β”œβ”€β”€ Makefile
β”‚   Local build, validation, and Space/Docker commands.
β”œβ”€β”€ Dockerfile
β”‚   Multi-stage Rust 1.95 Docker image for Hugging Face Spaces.
β”œβ”€β”€ README.md
β”‚   Run guide, dependency shape, API list, and learning path.
β”œβ”€β”€ AGENTS.md
β”‚   Repo-specific rules for future edits.
β”œβ”€β”€ WIREFRAME.md
β”‚   This architectural walkthrough.
β”œβ”€β”€ docs/screenshot.png
β”‚   Current browser screenshot used by the README.
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ domain/
β”‚   β”‚   `planning_model!` manifest, facts, route entity, and solution.
β”‚   β”œβ”€β”€ constraints/
β”‚   β”‚   Route metric helpers and one score rule per file.
β”‚   β”œβ”€β”€ data/
β”‚   β”‚   Deterministic Bergamo seeds, demo entrypoints, and OSM matrix loading.
β”‚   β”œβ”€β”€ solver/
β”‚   β”‚   Retained-job service and runtime event payload formatting.
β”‚   └── api/
β”‚       Axum routes, DTOs, route geometry, and SSE endpoint.
└── static/
    β”œβ”€β”€ index.html
    β”œβ”€β”€ sf-config.json
    β”œβ”€β”€ generated/ui-model.json
    └── app*.js
        Browser controller, map rendering, route state, layout, and tables.
```

## Domain And Route Metrics

`src/domain/field_service_plan.rs` owns the public solution shape. It keeps the
SolverForge model explicit: facts are read-only inputs, while
`TechnicianRoute.visits` is the one mutable list variable.

Route-specific scoring math lives in `src/constraints/route_metrics.rs`. That
module walks a route from depot to visits to depot, advances a service clock,
and records reusable counters for the individual constraints.

`src/api/route_geometry.rs` is separate because map drawing has a different
job from scoring. Scoring consumes matrix facts already on the plan; geometry
loads the road graph to draw visible polylines for a retained snapshot.

## Demo Data

`src/data/data_seed.rs` exposes one demo ID:

- `STANDARD`

The generator is deterministic. It builds two depots, 24 customer locations, 48
visits, six technicians, and seed self-leg travel facts. Full road-network
travel facts are prepared when a job is created.

## API And Retained Runtime

The REST API handles job control and snapshot reads:

- `/jobs` creates a retained solver job.
- `/jobs/{id}` and `/jobs/{id}/status` expose summary state.
- `/jobs/{id}/snapshot` returns an exact or latest snapshot.
- `/jobs/{id}/analysis` runs constraint analysis for a snapshot.
- `/jobs/{id}/routes` returns route geometry for a snapshot.
- `/jobs/{id}/events` streams typed lifecycle events.

## Frontend Layout

`static/app.js` is the controller. It owns current plan state, retained job
state, route focus, event handlers, and analysis modal wiring.

Supporting modules split the UI by responsibility:

- `static/app-dataset.js`
  Demo catalog and plan loading.
- `static/app-layout.js`
  Page shell and stock SolverForge UI component composition.
- `static/app-route-state.js`
  Snapshot identity and route geometry cache coordination.
- `static/app-render*.js`
  Summary cards, route cards, maps, timeline, tables, and API guide.
- `static/app-utils.js`
  Plan cloning, labels, formatting, and color helpers.

## Validation Surfaces

Use the Makefile as the repo-local workflow:

- `make fmt-check`
- `make clippy`
- `make build-release`
- `make test`
- `make test-e2e`
- `make space-build`
- `make ci-local`
- `make pre-release`

`make ci-local` includes the Docker image build used by the Hugging Face Space.
The Playwright command uses the publication bundle's root Node dev dependency;
runtime UI assets are served from the declared `solverforge-ui` Cargo crate.