forked from zakaria/chicken-counting-sukawarna-det
fix: make chicken-dashboard.service dynamic, add install_service.sh, and add sibling VIDEOS startup checks
This commit is contained in:
1 parent
acd9c9265b
commit
9f4fc138ba
5 files changed
+83
-13
No files matched your search
@@ -1,16 +1,16 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Chicken Counter Dashboard
|
Description=Chicken Counter Live Dashboard
|
||||||
After=network.target
|
After=network.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
User=asus
|
# Uses dynamic %h (user home directory) so it works on any user account (asus, jetson, ubuntu, etc.)
|
||||||
WorkingDirectory=/home/asus/.Codes/chicken-counting-sukawarna-det
|
WorkingDirectory=%h/.Codes/chicken-counting-sukawarna-det
|
||||||
ExecStart=/home/asus/.Codes/chicken-counting-sukawarna-det/start_dashboard.sh
|
ExecStart=%h/.Codes/chicken-counting-sukawarna-det/start_dashboard.sh
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=5
|
RestartSec=5
|
||||||
Environment=PYTHONUNBUFFERED=1
|
Environment=PYTHONUNBUFFERED=1
|
||||||
Environment=PORT=8080
|
Environment=PORT=8080
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=default.target
|
||||||
Executable
+45
@@ -0,0 +1,45 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Portable Systemd Service Installer for Chicken Counter Dashboard
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
CURRENT_USER="$(id -un)"
|
||||||
|
SERVICE_NAME="chicken-dashboard.service"
|
||||||
|
|
||||||
|
echo "=== Chicken Dashboard Service Installer ==="
|
||||||
|
echo "Target User: $CURRENT_USER"
|
||||||
|
echo "Project Dir: $SCRIPT_DIR"
|
||||||
|
|
||||||
|
# Check if user wants user-level systemd (no root required) or system-level systemd
|
||||||
|
USER_SERVICE_DIR="$HOME/.config/systemd/user"
|
||||||
|
mkdir -p "$USER_SERVICE_DIR"
|
||||||
|
|
||||||
|
cat <<EOF > "$USER_SERVICE_DIR/$SERVICE_NAME"
|
||||||
|
[Unit]
|
||||||
|
Description=Chicken Counter Live Dashboard
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
WorkingDirectory=$SCRIPT_DIR
|
||||||
|
ExecStart=$SCRIPT_DIR/start_dashboard.sh
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
Environment=PYTHONUNBUFFERED=1
|
||||||
|
Environment=PORT=8080
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "✅ Installed user service to: $USER_SERVICE_DIR/$SERVICE_NAME"
|
||||||
|
echo ""
|
||||||
|
echo "To enable and start the dashboard service (runs on boot without sudo):"
|
||||||
|
echo " systemctl --user daemon-reload"
|
||||||
|
echo " systemctl --user enable --now $SERVICE_NAME"
|
||||||
|
echo " loginctl enable-linger $CURRENT_USER # Ensures service runs even if logged out"
|
||||||
|
echo ""
|
||||||
|
echo "To check status:"
|
||||||
|
echo " systemctl --user status $SERVICE_NAME"
|
||||||
|
echo ""
|
||||||
+6
-1
@@ -23,7 +23,12 @@ for cfg in $(ls -1 "$SCRIPT_DIR/configs/floor_config/"K*-L*.yaml 2>/dev/null | s
|
|||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
CONFIGS+=("configs/floor_config/$(basename "$cfg")")
|
CONFIGS+=("configs/floor_config/$(basename "$cfg")")
|
||||||
done
|
# Startup check: Ensure sibling VIDEOS directory exists
|
||||||
|
VIDEOS_BASE="$SCRIPT_DIR/../VIDEOS"
|
||||||
|
if [ ! -d "$VIDEOS_BASE" ]; then
|
||||||
|
echo "[setup] 📁 Sibling directory '$VIDEOS_BASE' not found; creating directory skeleton..."
|
||||||
|
mkdir -p "$VIDEOS_BASE/cycle7"
|
||||||
|
fi
|
||||||
|
|
||||||
echo "================================================================="
|
echo "================================================================="
|
||||||
echo " Starting Multi-Floor Batch Processing for Date: $DATE"
|
echo " Starting Multi-Floor Batch Processing for Date: $DATE"
|
||||||
|
|||||||
@@ -16,7 +16,15 @@ class CameraDiscoveryResult:
|
|||||||
|
|
||||||
def discover_camera_videos(day_dir: Path, settings: BatchSettings) -> CameraDiscoveryResult:
|
def discover_camera_videos(day_dir: Path, settings: BatchSettings) -> CameraDiscoveryResult:
|
||||||
if not day_dir.is_dir():
|
if not day_dir.is_dir():
|
||||||
raise FileNotFoundError(f"Daily input folder does not exist: {day_dir}")
|
# Startup check: Auto-create skeleton root_dir if missing
|
||||||
|
root_p = Path(settings.batch.root_dir)
|
||||||
|
if not root_p.exists():
|
||||||
|
root_p.mkdir(parents=True, exist_ok=True)
|
||||||
|
print(f"[batch] 📁 Created missing video root directory: {root_p}")
|
||||||
|
raise FileNotFoundError(
|
||||||
|
f"Daily input folder does not exist: {day_dir}\n"
|
||||||
|
f"👉 Place camera video files (e.g. {settings.batch.location}_cam1_*.mp4 .. cam4_*.mp4) inside: {day_dir}"
|
||||||
|
)
|
||||||
|
|
||||||
result = CameraDiscoveryResult()
|
result = CameraDiscoveryResult()
|
||||||
total_configured = len(settings.cameras)
|
total_configured = len(settings.cameras)
|
||||||
|
|||||||
+18
-6
@@ -19,18 +19,30 @@ DATE_ARG="${DATE_ARG:-}"
|
|||||||
|
|
||||||
CMD=("$PYTHON" "$SCRIPT_DIR/dashboard.py" --port "$PORT" --db "$DB_PATH")
|
CMD=("$PYTHON" "$SCRIPT_DIR/dashboard.py" --port "$PORT" --db "$DB_PATH")
|
||||||
|
|
||||||
# Auto-discover mortality directories (any folder containing mortality_report.json)
|
# Startup check: Ensure sibling VIDEOS directory structure exists
|
||||||
# Override with MORTALITY_DIRS="dir1,dir2" env variable or let it auto-discover
|
VIDEOS_BASE="$SCRIPT_DIR/../VIDEOS"
|
||||||
|
if [ ! -d "$VIDEOS_BASE" ]; then
|
||||||
|
echo "[dashboard] 📁 Sibling directory '$VIDEOS_BASE' not found; creating skeleton directory..."
|
||||||
|
mkdir -p "$VIDEOS_BASE/cycle7"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Auto-discover mortality directories (any folder containing mortality_report.json or mortality folders)
|
||||||
if [ -n "${MORTALITY_DIRS:-}" ]; then
|
if [ -n "${MORTALITY_DIRS:-}" ]; then
|
||||||
IFS=',' read -ra MDIRS <<< "$MORTALITY_DIRS"
|
IFS=',' read -ra MDIRS <<< "$MORTALITY_DIRS"
|
||||||
for mdir in "${MDIRS[@]}"; do
|
for mdir in "${MDIRS[@]}"; do
|
||||||
CMD+=(--mortality-dir "$mdir")
|
CMD+=(--mortality-dir "$mdir")
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
# Default: check adjacent VIDEOS folder for any mortality subdirectory
|
# Recursively discover all mortality subdirectories under ../VIDEOS
|
||||||
DEFAULT_MORTALITY="$SCRIPT_DIR/../VIDEOS/cycle7/kandang-atas/mortality"
|
FOUND_MDIRS=()
|
||||||
if [ -d "$DEFAULT_MORTALITY" ]; then
|
while IFS= read -r -d '' mdir; do
|
||||||
CMD+=(--mortality-dir "$(realpath "$DEFAULT_MORTALITY")")
|
FOUND_MDIRS+=("$mdir")
|
||||||
|
done < <(find "$VIDEOS_BASE" -type d -name "mortality" -print0 2>/dev/null)
|
||||||
|
|
||||||
|
if [ ${#FOUND_MDIRS[@]} -gt 0 ]; then
|
||||||
|
for mdir in "${FOUND_MDIRS[@]}"; do
|
||||||
|
CMD+=(--mortality-dir "$mdir")
|
||||||
|
done
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user