Update Today
This commit is contained in:
1 parent
896a07b5fa
commit
eefff48953
7 files changed
+2827
-12
No files matched your search
+2781
-7
File diff suppressed because it is too large.
Load diff
@@ -33,3 +33,23 @@ make -j$(nproc)
|
|||||||
systemctl restart bytetrack-counter
|
systemctl restart bytetrack-counter
|
||||||
journalctl -u bytetrack-counter -f
|
journalctl -u bytetrack-counter -f
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Reset via .reset file
|
||||||
|
|
||||||
|
Place a `.reset` file in `SHM_DIR` (default `/dev/shm/bytetrack-counter`) to trigger a full state reset:
|
||||||
|
|
||||||
|
- **Startup**: deleting `.reset` before service starts clears all state files on boot
|
||||||
|
- **Runtime**: dropping `.reset` while service runs calls `BatchStore::reset()` — ends active batch, clears in-memory state, deletes `STATE_FILE`, clears live stream JPEG + writes blank frame on next write
|
||||||
|
|
||||||
|
```bash
|
||||||
|
touch /dev/shm/bytetrack-counter/.reset
|
||||||
|
```
|
||||||
|
|
||||||
|
## Shared memory
|
||||||
|
|
||||||
|
`SHM_DIR` (`/dev/shm/bytetrack-counter` by default) stores:
|
||||||
|
- `LIVE_STREAM_FRAME_PATH` JPEG snapshot
|
||||||
|
- `RECORD_SHM_DIR` video recordings before move to disk
|
||||||
|
- `.reset` trigger file
|
||||||
|
|
||||||
|
Directory auto-created at startup.
|
||||||
@@ -20,12 +20,16 @@ Talenan crossing → close batch. Persist to SQLite + JSON.
|
|||||||
|
|
||||||
```
|
```
|
||||||
env: .env KEY=VALUE file — all config per config.env.example
|
env: .env KEY=VALUE file — all config per config.env.example
|
||||||
env: LIVE_STREAM_FRAME_PATH → JPEG snapshot path (/dev/shm/bytetrack-counter/live_frame.jpg)
|
env: SHM_DIR → shared memory directory (/dev/shm/bytetrack-counter)
|
||||||
|
env: LIVE_STREAM_FRAME_PATH → JPEG snapshot path
|
||||||
|
env: RECORD_VIDEO, RECORD_DELAY_SECONDS, RECORD_END_DELAY, RECORD_RETENTION_DAYS
|
||||||
cmd: bytetrack-counter <config.env>
|
cmd: bytetrack-counter <config.env>
|
||||||
db: SQLite batches(id,counting_date,batch_number,camera_name,object_label,count,start_time,end_time)
|
db: SQLite batches(id,counting_date,batch_number,camera_name,object_label,count,start_time,end_time)
|
||||||
db: SQLite daily_summaries(id,counting_date,camera_name,object_label,total_count,total_batches)
|
db: SQLite daily_summaries(id,counting_date,camera_name,object_label,total_count,total_batches)
|
||||||
file: JSON state — batch_number,count,start_time,last_detection_time,counted_event_ids[]
|
file: JSON state — batch_number,count,start_time,last_detection_time,counted_event_ids[]
|
||||||
file: CSV crossing log — batch,frame,timestamp,chicken_id
|
file: CSV crossing log — batch,frame,timestamp,chicken_id
|
||||||
|
file: SHM_DIR/.reset → trigger full state reset
|
||||||
|
file: SHM_DIR/batch_N_HHMMSS.mp4 → per-batch video recording (moved to OUTPUT_DIR/YYYY-MM-DD/)
|
||||||
sig: SIGTERM|SIGINT → shutdown_requested → graceful exit
|
sig: SIGTERM|SIGINT → shutdown_requested → graceful exit
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -42,7 +46,10 @@ V7: counting_date rolls at DAILY_CUTOFF_TIME → cutoff_watcher thread
|
|||||||
V8: ayam & talenan → separate ByteTrack instances (match_thresh_0=0.8, match_thresh_1=0.6)
|
V8: ayam & talenan → separate ByteTrack instances (match_thresh_0=0.8, match_thresh_1=0.6)
|
||||||
V9: live_stream_enabled → parent dir of LIVE_STREAM_FRAME_PATH created @ startup
|
V9: live_stream_enabled → parent dir of LIVE_STREAM_FRAME_PATH created @ startup
|
||||||
V10: stale track (> TRACKED_PRUNE_SEC unseen) → prune from external tracking maps
|
V10: stale track (> TRACKED_PRUNE_SEC unseen) → prune from external tracking maps
|
||||||
V11: DIRECTORY for LIVE_STREAM_FRAME_PATH must exist — created by code and by tmpfiles.d on boot (/dev/shm is volatile)
|
V11: all dirs (output_dir, shm_dir, live_stream parent) created @ startup
|
||||||
|
V12: SHM_DIR/.reset → BatchStore::reset() — end active batch, clear memory, delete state_file, write blank live JPEG
|
||||||
|
V13: video recording: motion → SPECULATING (≤RECORD_DELAY_SECONDS), ayam → CONFIRMED → talenan/batch-end → STOPPING (+RECORD_END_DELAY) → move to OUTPUT_DIR/YYYY-MM-DD/
|
||||||
|
V14: video retention: day-dirs > RECORD_RETENTION_DAYS old → removed @ startup & @ each CUTOFF_TIME
|
||||||
```
|
```
|
||||||
|
|
||||||
## §T — Tasks
|
## §T — Tasks
|
||||||
@@ -64,7 +71,7 @@ T12|x|CsvLogger for crossing events|I.file
|
|||||||
T13|x|Live JPEG snapshot to shared memory|V9
|
T13|x|Live JPEG snapshot to shared memory|V9
|
||||||
T14|x|systemd service with ProtectSystem + ReadWritePaths|I.cmd
|
T14|x|systemd service with ProtectSystem + ReadWritePaths|I.cmd
|
||||||
T15|x|Graceful shutdown: SIGTERM → close batch → save state|I.sig
|
T15|x|Graceful shutdown: SIGTERM → close batch → save state|I.sig
|
||||||
T16|x|Video per batch recording (config exists, impl ⊥ in C++) to shared memory for speed reason. To identify batch is between talenan (index=1), save with different name. Once completed, spawn new process (non blocking) to move recorded file to final destination (from config env). To identify first batch, record and wait X seconds (X default is 10 seconds, can be defined in config env), until first ayam (index=0) detected|-
|
T16|x|Video per-batch recording: motion-triggered → speculating in shm, confirmed when ayam detected ≤ RECORD_DELAY_SECONDS, stopped RECORD_END_DELAY after batch close, moved to OUTPUT_DIR/YYYY-MM-DD/ via fork, retention cleanup at startup & cutoff. Unconfirmed recordings deleted.|V13,V14
|
||||||
T17|~?|Python DEBUG_TRACKING equivalent (per-frame score/track dump)|-
|
T17|~?|Python DEBUG_TRACKING equivalent (per-frame score/track dump)|-
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -413,6 +413,17 @@ void BatchStore::set_batch_closed_callback(std::function<void(int)> cb) {
|
|||||||
batch_closed_cb_ = std::move(cb);
|
batch_closed_cb_ = std::move(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BatchStore::reset() {
|
||||||
|
std::lock_guard<std::mutex> lock(state_mutex_);
|
||||||
|
if (!current_state_.is_null()) {
|
||||||
|
end_batch_locked("reset");
|
||||||
|
}
|
||||||
|
current_state_ = nullptr;
|
||||||
|
batch_timer_running_ = false;
|
||||||
|
std::error_code ec;
|
||||||
|
std::filesystem::remove(state_file_, ec);
|
||||||
|
}
|
||||||
|
|
||||||
bool BatchStore::check_cutoff_reset() {
|
bool BatchStore::check_cutoff_reset() {
|
||||||
return cutoff_triggered_.exchange(false);
|
return cutoff_triggered_.exchange(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ public:
|
|||||||
int display_total();
|
int display_total();
|
||||||
|
|
||||||
void set_batch_closed_callback(std::function<void(int)> cb);
|
void set_batch_closed_callback(std::function<void(int)> cb);
|
||||||
|
void reset();
|
||||||
|
|
||||||
bool check_cutoff_reset();
|
bool check_cutoff_reset();
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -150,12 +150,13 @@ int main(int argc, char* argv[]) {
|
|||||||
std::filesystem::create_directories(live_p.parent_path());
|
std::filesystem::create_directories(live_p.parent_path());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool startup_reset = false;
|
||||||
{
|
{
|
||||||
std::string reset_file = g_config.shm_dir + "/.reset";
|
std::string reset_file = g_config.shm_dir + "/.reset";
|
||||||
if (std::filesystem::exists(reset_file)) {
|
if (std::filesystem::exists(reset_file)) {
|
||||||
std::cout << "[RESET] [INFO] .reset detected — clearing state" << std::endl;
|
std::cout << "[RESET] [INFO] .reset detected — clearing state" << std::endl;
|
||||||
|
startup_reset = true;
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
std::filesystem::remove(g_config.state_file, ec);
|
|
||||||
std::filesystem::remove(g_config.live_stream_frame_path, ec);
|
std::filesystem::remove(g_config.live_stream_frame_path, ec);
|
||||||
std::filesystem::remove(reset_file, ec);
|
std::filesystem::remove(reset_file, ec);
|
||||||
}
|
}
|
||||||
@@ -177,6 +178,7 @@ int main(int argc, char* argv[]) {
|
|||||||
g_config.min_duration_per_batch,
|
g_config.min_duration_per_batch,
|
||||||
logger);
|
logger);
|
||||||
store.start_cutoff_watcher();
|
store.start_cutoff_watcher();
|
||||||
|
if (startup_reset) store.reset();
|
||||||
|
|
||||||
std::unique_ptr<CsvLogger> cross_logger;
|
std::unique_ptr<CsvLogger> cross_logger;
|
||||||
if (g_config.export_csv) {
|
if (g_config.export_csv) {
|
||||||
@@ -258,8 +260,8 @@ int main(int argc, char* argv[]) {
|
|||||||
std::string reset_file = g_config.shm_dir + "/.reset";
|
std::string reset_file = g_config.shm_dir + "/.reset";
|
||||||
if (std::filesystem::exists(reset_file)) {
|
if (std::filesystem::exists(reset_file)) {
|
||||||
std::cout << "[" << now_str() << "] [INFO] [RESET] .reset detected — clearing state" << std::endl;
|
std::cout << "[" << now_str() << "] [INFO] [RESET] .reset detected — clearing state" << std::endl;
|
||||||
|
store.reset();
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
std::filesystem::remove(g_config.state_file, ec);
|
|
||||||
std::filesystem::remove(g_config.live_stream_frame_path, ec);
|
std::filesystem::remove(g_config.live_stream_frame_path, ec);
|
||||||
std::filesystem::remove(reset_file, ec);
|
std::filesystem::remove(reset_file, ec);
|
||||||
blank_live_frame = true;
|
blank_live_frame = true;
|
||||||
|
|||||||
Reference in new issue
Block a user