This commit is contained in:
1 parent
a12056a5ad
commit
4a6b121eaf
6 files changed
+120
-86
No files matched your search
@@ -40,7 +40,7 @@ Indonesian (`karung`=sack, `truk`=truck); YOLO class names are English
|
|||||||
office `:5721` only (403 on `:5000`); **`ocr_engine` POST both ports** (synced
|
office `:5721` only (403 on `:5000`); **`ocr_engine` POST both ports** (synced
|
||||||
via `GET /api/do/settings`). Discard batch only if **both** `net_sack==0` and
|
via `GET /api/do/settings`). Discard batch only if **both** `net_sack==0` and
|
||||||
`net_box==0`. DO helpers: `src/do_batch.py`, OCR: `src/do_ocr.py`;
|
`net_box==0`. DO helpers: `src/do_batch.py`, OCR: `src/do_ocr.py`;
|
||||||
ERD: `docs/do-erd.md`.
|
ERD: `ERD.md` (repo root).
|
||||||
- **Counting filters by class name, not ID**: `SackDetector`/`BoxDetector`/
|
- **Counting filters by class name, not ID**: `SackDetector`/`BoxDetector`/
|
||||||
`TruckDetector` filter via `BaseDetector(class_filter)` (`src/detection.py`);
|
`TruckDetector` filter via `BaseDetector(class_filter)` (`src/detection.py`);
|
||||||
tracker keeps `("sack", "truck", "box")` (`src/tracking.py`); counting uses
|
tracker keeps `("sack", "truck", "box")` (`src/tracking.py`); counting uses
|
||||||
|
|||||||
+1
-1
@@ -22,7 +22,7 @@ version tags are cut in this repo — POC stage, `main` is the release line).
|
|||||||
(`extract_do_fields` tesseract|paddle|none).
|
(`extract_do_fields` tesseract|paddle|none).
|
||||||
- Operator DO panel + OCR toggle; monitoring 3-way mode switch + model mode +
|
- Operator DO panel + OCR toggle; monitoring 3-way mode switch + model mode +
|
||||||
OCR/require-plate controls; history/export plate, DO, expected, net columns.
|
OCR/require-plate controls; history/export plate, DO, expected, net columns.
|
||||||
- `docs/do-erd.md` (Mermaid ERD); tests `tests/test_do_batch.py`.
|
- `ERD.md` (Mermaid ERD, repo root; superseded `docs/do-erd.md`); tests `tests/test_do_batch.py`.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Default batch mode seed **`auto`** (was hard-coded `manual` when
|
- Default batch mode seed **`auto`** (was hard-coded `manual` when
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
# ERD — karung counter (SQLite)
|
||||||
|
|
||||||
|
Data model for the karung feedmill counter. SQLite DB at
|
||||||
|
`$OUTPUT_DIR/jetson_counter.db` (`config.yaml` `output.dir`,
|
||||||
|
default `/opt/jetson-counter/`; env override `DB_PATH`).
|
||||||
|
|
||||||
|
**No DB foreign keys enforced** (zero `FOREIGN KEY` in code) — all relationships
|
||||||
|
are soft, by naming/best-effort writes.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
erDiagram
|
||||||
|
batches ||--o{ delivery_orders : "soft via batch_id / do_numbers"
|
||||||
|
batches ||--o{ daily_summaries : "rollup by counting_date"
|
||||||
|
|
||||||
|
batches {
|
||||||
|
INTEGER id PK
|
||||||
|
TEXT counting_date "NOT NULL, YYYY-MM-DD, cutoff-shifted"
|
||||||
|
INTEGER batch_number "NOT NULL, per date+camera+label"
|
||||||
|
TEXT camera_name "NOT NULL, default CC1"
|
||||||
|
TEXT object_label "NOT NULL, default karung-pakan"
|
||||||
|
INTEGER count "NOT NULL, gross sack loading"
|
||||||
|
TEXT start_time "NOT NULL, ISO datetime"
|
||||||
|
TEXT end_time "NOT NULL, ISO datetime"
|
||||||
|
TIMESTAMP created_at "default CURRENT_TIMESTAMP"
|
||||||
|
INTEGER box_loading "NOT NULL, default 0"
|
||||||
|
INTEGER box_unloading "NOT NULL, default 0"
|
||||||
|
TEXT model_mode "NOT NULL, default A"
|
||||||
|
TEXT plate "NOT NULL, default ''"
|
||||||
|
TEXT do_numbers "NOT NULL, JSON array of No. DO"
|
||||||
|
INTEGER expected_sack "NOT NULL, default 0"
|
||||||
|
INTEGER expected_box "NOT NULL, default 0"
|
||||||
|
INTEGER net_sack "NOT NULL, default 0"
|
||||||
|
INTEGER net_box "NOT NULL, default 0"
|
||||||
|
}
|
||||||
|
|
||||||
|
delivery_orders {
|
||||||
|
INTEGER id PK
|
||||||
|
TEXT counting_date "NOT NULL"
|
||||||
|
TEXT photo_path "NOT NULL, relative under do_photos/"
|
||||||
|
TEXT no_do "default ''"
|
||||||
|
TEXT plate "default ''"
|
||||||
|
INTEGER expected_sack "default 0"
|
||||||
|
INTEGER expected_box "default 0"
|
||||||
|
TEXT ocr_raw "nullable JSON: no_do, plate, ocr_text, engine, ocr_ok, ocr_error"
|
||||||
|
TEXT status "default 'draft'"
|
||||||
|
INTEGER batch_id "soft ref to batches.id, nullable"
|
||||||
|
TIMESTAMP created_at "default CURRENT_TIMESTAMP"
|
||||||
|
TIMESTAMP updated_at "default CURRENT_TIMESTAMP"
|
||||||
|
}
|
||||||
|
|
||||||
|
daily_summaries {
|
||||||
|
INTEGER id PK
|
||||||
|
TEXT counting_date "NOT NULL"
|
||||||
|
TEXT camera_name "NOT NULL"
|
||||||
|
TEXT object_label "NOT NULL"
|
||||||
|
INTEGER total_count "NOT NULL, default 0, SUM(batches.count)"
|
||||||
|
INTEGER total_batches "NOT NULL, default 0, COUNT(batches.id)"
|
||||||
|
TIMESTAMP updated_at "default CURRENT_TIMESTAMP"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Keys
|
||||||
|
|
||||||
|
- `batches`: `UNIQUE(counting_date, batch_number, camera_name, object_label)`
|
||||||
|
- `daily_summaries`: `UNIQUE(counting_date, camera_name, object_label)`
|
||||||
|
- `delivery_orders`: `idx_do_date_status` on `(counting_date, status)`
|
||||||
|
|
||||||
|
## Relationships (logical)
|
||||||
|
|
||||||
|
| From | To | How |
|
||||||
|
|---|---|---|
|
||||||
|
| `delivery_orders` → `batches` | N—1 | On stop: `batches.do_numbers` JSON array of `No. DO`; `delivery_orders.batch_id` set best-effort. Active batch: `current_batch.json.do_ids`. |
|
||||||
|
| `batches` → `daily_summaries` | N—1 | Same `(counting_date, camera_name, object_label)`; stop recompute `SUM(count)`, `COUNT(id)`. |
|
||||||
|
|
||||||
|
## Derived columns
|
||||||
|
|
||||||
|
- `net_sack = count − sack unloading`, `net_box = box_loading − box_unloading`
|
||||||
|
(`src/do_batch.py:37`).
|
||||||
|
- **Sack unloading has no column** — it only survives inside `net_sack`
|
||||||
|
(legacy rows with `net_sack=0` from the `ALTER TABLE ... DEFAULT 0`
|
||||||
|
migration are not backfillable).
|
||||||
|
- History UI shows gross `count` / `box_loading` only (no Net).
|
||||||
|
|
||||||
|
## Status (`delivery_orders.status`)
|
||||||
|
|
||||||
|
`draft` (photo upload) → `staged` (saved/edited) → `attached` (in
|
||||||
|
active/stopped batch). Discard stop with both nets 0 returns DOs to
|
||||||
|
`staged`. `discarded` appears in older notes but **no code writes it** —
|
||||||
|
discard is a `DELETE`.
|
||||||
|
|
||||||
|
## Hazards
|
||||||
|
|
||||||
|
- `INSERT OR REPLACE` on `batches` (UNIQUE match = delete+insert) changes
|
||||||
|
`id` on re-stop → can orphan `delivery_orders.batch_id`.
|
||||||
|
- Schema divergence: `predict.py init_db()` creates only `batches` +
|
||||||
|
`daily_summaries` (3 extra cols, no `delivery_orders`); full 3-table
|
||||||
|
schema (9 extra `batches` cols) comes from `counter_dashboard.py`
|
||||||
|
`_ensure_db()` at import. Fresh DB touched only by `predict.py` is
|
||||||
|
incomplete; its DO updates fail silently (`try/except: pass`).
|
||||||
|
|
||||||
|
## JSON sidecars + photo tree (not in SQLite)
|
||||||
|
|
||||||
|
| Path | Role |
|
||||||
|
|---|---|
|
||||||
|
| `$OUTPUT_DIR/batch_mode.json` | `{mode: auto\|do_manual\|manual}` |
|
||||||
|
| `$OUTPUT_DIR/current_batch.json` | active batch + `do_ids`, plate, expected_*, nets inputs |
|
||||||
|
| `$OUTPUT_DIR/do_settings.json` | `require_plate`, `require_do`, `ocr_engine` |
|
||||||
|
| `$OUTPUT_DIR/do_photos/YYYY-MM-DD/do_<uuid>.jpg` | source photos (7-day retention) |
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
- DDL: `counter_dashboard.py:123-198` (`_ensure_db`, superset),
|
||||||
|
`predict.py:117-166` (`init_db`, partial).
|
||||||
|
- Writes: `predict.py:249-306`, `counter_dashboard.py:610-668`.
|
||||||
|
- Doc flow: `docs/do-erd.md` (merged into this file).
|
||||||
@@ -101,7 +101,7 @@ Office (`:5721`) switches mode / model mode / require_plate (403 on operator
|
|||||||
POST). **DO manual flow:** open `http://<jetson>:5000/operator` on a phone →
|
POST). **DO manual flow:** open `http://<jetson>:5000/operator` on a phone →
|
||||||
Ambil Foto DO → review/edit OCR fields → Mulai Batch → count → Selesai
|
Ambil Foto DO → review/edit OCR fields → Mulai Batch → count → Selesai
|
||||||
(soft-warn if zone busy). Discard only when both sack and box nets are 0.
|
(soft-warn if zone busy). Discard only when both sack and box nets are 0.
|
||||||
ERD: [`docs/do-erd.md`](docs/do-erd.md).
|
ERD: [`ERD.md`](ERD.md).
|
||||||
|
|
||||||
## Repository layout
|
## Repository layout
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ Script-by-script reference: [`docs/scripts.md`](docs/scripts.md).
|
|||||||
- [`docs/models.md`](docs/models.md) — model inventory & detection-mode support matrix
|
- [`docs/models.md`](docs/models.md) — model inventory & detection-mode support matrix
|
||||||
- [`docs/configuration.md`](docs/configuration.md) — all config files/variables
|
- [`docs/configuration.md`](docs/configuration.md) — all config files/variables
|
||||||
- [`docs/deployment.md`](docs/deployment.md) — Jetson services, TensorRT, deploy flow
|
- [`docs/deployment.md`](docs/deployment.md) — Jetson services, TensorRT, deploy flow
|
||||||
- [`docs/do-erd.md`](docs/do-erd.md) — delivery-order / batch ERD (Mermaid)
|
- [`ERD.md`](ERD.md) — SQLite ERD (Mermaid): batches / delivery_orders / daily_summaries
|
||||||
- [`docs/operator-tiles-history-plan.md`](docs/operator-tiles-history-plan.md) — operator tiles + history cleanup plan
|
- [`docs/operator-tiles-history-plan.md`](docs/operator-tiles-history-plan.md) — operator tiles + history cleanup plan
|
||||||
- [`docs/scripts.md`](docs/scripts.md) — entry points & utility scripts
|
- [`docs/scripts.md`](docs/scripts.md) — entry points & utility scripts
|
||||||
- [`CHANGELOG.md`](CHANGELOG.md) — release history
|
- [`CHANGELOG.md`](CHANGELOG.md) — release history
|
||||||
@@ -147,7 +147,7 @@ dashboard integration is file-based.)
|
|||||||
|
|
||||||
DO pipeline (dashboard): smartphone upload → OCR draft → edit/stage → start
|
DO pipeline (dashboard): smartphone upload → OCR draft → edit/stage → start
|
||||||
gates → active batch → stop-preview soft-warn → stop → `batches` + DO columns.
|
gates → active batch → stop-preview soft-warn → stop → `batches` + DO columns.
|
||||||
ERD: [`docs/do-erd.md`](do-erd.md). UI plan: [`docs/operator-tiles-history-plan.md`](operator-tiles-history-plan.md).
|
ERD: [`../ERD.md`](../ERD.md). UI plan: [`docs/operator-tiles-history-plan.md`](operator-tiles-history-plan.md).
|
||||||
|
|
||||||
## Retired (`archive/`, not imported)
|
## Retired (`archive/`, not imported)
|
||||||
|
|
||||||
|
|||||||
@@ -1,81 +0,0 @@
|
|||||||
# ERD — DO-gated manual batch (delivery orders)
|
|
||||||
|
|
||||||
Mermaid ER for karung feedmill counter. Soft FK: no DB foreign keys enforced;
|
|
||||||
`delivery_orders.batch_id` is informational once a batch row exists. While a
|
|
||||||
batch is active, membership lives in `current_batch.json` (`do_ids`).
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
erDiagram
|
|
||||||
batches ||--o{ delivery_orders : "soft via do_numbers / batch_id"
|
|
||||||
batches ||--o{ daily_summaries : "rolls up by counting_date"
|
|
||||||
|
|
||||||
batches {
|
|
||||||
INTEGER id PK
|
|
||||||
TEXT counting_date
|
|
||||||
INTEGER batch_number
|
|
||||||
TEXT camera_name
|
|
||||||
TEXT object_label
|
|
||||||
INTEGER count
|
|
||||||
TEXT start_time
|
|
||||||
TEXT end_time
|
|
||||||
INTEGER box_loading
|
|
||||||
INTEGER box_unloading
|
|
||||||
TEXT model_mode
|
|
||||||
TEXT plate
|
|
||||||
TEXT do_numbers
|
|
||||||
INTEGER expected_sack
|
|
||||||
INTEGER expected_box
|
|
||||||
INTEGER net_sack
|
|
||||||
INTEGER net_box
|
|
||||||
TIMESTAMP created_at
|
|
||||||
}
|
|
||||||
|
|
||||||
delivery_orders {
|
|
||||||
INTEGER id PK
|
|
||||||
TEXT counting_date
|
|
||||||
TEXT photo_path
|
|
||||||
TEXT no_do
|
|
||||||
TEXT plate
|
|
||||||
INTEGER expected_sack
|
|
||||||
INTEGER expected_box
|
|
||||||
TEXT ocr_raw
|
|
||||||
TEXT status
|
|
||||||
INTEGER batch_id
|
|
||||||
TIMESTAMP created_at
|
|
||||||
TIMESTAMP updated_at
|
|
||||||
}
|
|
||||||
|
|
||||||
daily_summaries {
|
|
||||||
INTEGER id PK
|
|
||||||
TEXT counting_date
|
|
||||||
TEXT camera_name
|
|
||||||
TEXT object_label
|
|
||||||
INTEGER total_count
|
|
||||||
INTEGER total_batches
|
|
||||||
TIMESTAMP updated_at
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Relationships (logical)
|
|
||||||
|
|
||||||
| From | To | How |
|
|
||||||
|---|---|---|
|
|
||||||
| `delivery_orders` → `batches` | N—1 | On stop: `batches.do_numbers` JSON array of `No. DO`; `delivery_orders.batch_id` set best-effort. Active batch: `current_batch.json.do_ids`. |
|
|
||||||
| `batches` → `daily_summaries` | N—1 | Same counting_date/camera/label; stop recompute `SUM(count)`. |
|
|
||||||
|
|
||||||
## Status (`delivery_orders.status`)
|
|
||||||
|
|
||||||
`draft` → `staged` (saved/edited) → `attached` (in active/stopped batch) |
|
|
||||||
`discarded` (deleted). Discarded stop (both nets 0) returns DOs to `staged`.
|
|
||||||
|
|
||||||
## JSON sidecars + photo tree (not in SQLite)
|
|
||||||
|
|
||||||
| Path | Role |
|
|
||||||
|---|---|
|
|
||||||
| `$OUTPUT_DIR/batch_mode.json` | `{mode: auto\|do_manual\|manual}` |
|
|
||||||
| `$OUTPUT_DIR/current_batch.json` | active batch + `do_ids`, plate, expected_*, nets inputs |
|
|
||||||
| `$OUTPUT_DIR/do_settings.json` | `require_plate`, `require_do`, `ocr_engine` |
|
|
||||||
| `$OUTPUT_DIR/do_photos/YYYY-MM-DD/do_<uuid>.jpg` | source photos (7-day retention) |
|
|
||||||
|
|
||||||
Schema DDL: `counter_dashboard.py` `_ensure_db()` (additive PRAGMA migrations
|
|
||||||
on `batches` + `CREATE TABLE delivery_orders`).
|
|
||||||
Reference in new issue
Block a user