plc-checkweigher 1.20.0 → 1.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/plc_checkweigher +219 -6
- package/package.json +1 -1
package/bin/plc_checkweigher
CHANGED
|
@@ -588,19 +588,20 @@ smb-config)
|
|
|
588
588
|
|
|
589
589
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
590
590
|
# FIX — auto-detect and repair common issues
|
|
591
|
-
# Usage: fix [-wifi] [-health] [-programs] (no flags = run all)
|
|
591
|
+
# Usage: fix [-wifi] [-health] [-programs] [-errors] (no flags = run all)
|
|
592
592
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
593
593
|
fix)
|
|
594
|
-
FIX_WIFI=0; FIX_HEALTH=0; FIX_PROGRAMS=0
|
|
594
|
+
FIX_WIFI=0; FIX_HEALTH=0; FIX_PROGRAMS=0; FIX_ERRORS=0
|
|
595
595
|
if [[ $# -eq 0 ]]; then
|
|
596
|
-
FIX_WIFI=1; FIX_HEALTH=1; FIX_PROGRAMS=1
|
|
596
|
+
FIX_WIFI=1; FIX_HEALTH=1; FIX_PROGRAMS=1; FIX_ERRORS=1
|
|
597
597
|
else
|
|
598
598
|
for _flag in "$@"; do
|
|
599
599
|
case "$_flag" in
|
|
600
600
|
-wifi) FIX_WIFI=1 ;;
|
|
601
601
|
-health) FIX_HEALTH=1 ;;
|
|
602
602
|
-programs) FIX_PROGRAMS=1 ;;
|
|
603
|
-
|
|
603
|
+
-errors) FIX_ERRORS=1 ;;
|
|
604
|
+
*) warn "Unknown flag: $_flag (valid: -wifi -health -programs -errors)" ;;
|
|
604
605
|
esac
|
|
605
606
|
done
|
|
606
607
|
fi
|
|
@@ -612,7 +613,8 @@ fix)
|
|
|
612
613
|
[[ $FIX_WIFI -eq 1 ]] && _LOG_MODES="${_LOG_MODES:+${_LOG_MODES}-}wifi"
|
|
613
614
|
[[ $FIX_HEALTH -eq 1 ]] && _LOG_MODES="${_LOG_MODES:+${_LOG_MODES}-}health"
|
|
614
615
|
[[ $FIX_PROGRAMS -eq 1 ]] && _LOG_MODES="${_LOG_MODES:+${_LOG_MODES}-}programs"
|
|
615
|
-
[[
|
|
616
|
+
[[ $FIX_ERRORS -eq 1 ]] && _LOG_MODES="${_LOG_MODES:+${_LOG_MODES}-}errors"
|
|
617
|
+
[[ "$_LOG_MODES" == "wifi-health-programs-errors" ]] && _LOG_MODES="all"
|
|
616
618
|
LOG_FILE="${LOG_DIR}/fix_${_LOG_MODES}_$(date '+%Y%m%d_%H%M%S').log"
|
|
617
619
|
flog() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"; }
|
|
618
620
|
ffix_ok() { spin_ok "$*"; flog "FIXED : $*"; FIX_COUNT=$((FIX_COUNT+1)); }
|
|
@@ -621,7 +623,7 @@ fix)
|
|
|
621
623
|
ffix_err() { spin_err "$*"; flog "ERROR : $*"; }
|
|
622
624
|
|
|
623
625
|
FIX_COUNT=0
|
|
624
|
-
flog "=== Run started | wifi=${FIX_WIFI} health=${FIX_HEALTH} programs=${FIX_PROGRAMS} ==="
|
|
626
|
+
flog "=== Run started | wifi=${FIX_WIFI} health=${FIX_HEALTH} programs=${FIX_PROGRAMS} errors=${FIX_ERRORS} ==="
|
|
625
627
|
|
|
626
628
|
banner "Auto Fix"
|
|
627
629
|
echo ""
|
|
@@ -878,6 +880,216 @@ SMBC
|
|
|
878
880
|
echo ""
|
|
879
881
|
fi
|
|
880
882
|
|
|
883
|
+
# ─────────────────────────────────────────────────────────────────────────
|
|
884
|
+
# ERRORS — journal scan, RT scheduling, permissions, PLC reach, config validity
|
|
885
|
+
# ─────────────────────────────────────────────────────────────────────────
|
|
886
|
+
if [[ $FIX_ERRORS -eq 1 ]]; then
|
|
887
|
+
echo -e " ${B}▸ Programmatic Errors${NC}"; hr; echo ""
|
|
888
|
+
|
|
889
|
+
# ── 1. Service restart / crash count ─────────────────────────────────
|
|
890
|
+
spin_start "Service stability"
|
|
891
|
+
_SVC_STABLE=1
|
|
892
|
+
for _svc in plc_watcher plc_web; do
|
|
893
|
+
_RC=$(systemctl show -p NRestarts --value "$_svc" 2>/dev/null || echo "0")
|
|
894
|
+
_RC="${_RC:-0}"
|
|
895
|
+
if [[ "$_RC" =~ ^[0-9]+$ && "$_RC" -gt 10 ]]; then
|
|
896
|
+
_spin_kill
|
|
897
|
+
ffix_warn "${_svc}: ${_RC} restarts — likely crash loop"
|
|
898
|
+
flog "WARN: ${_svc} restart count=${_RC}"
|
|
899
|
+
_SVC_STABLE=0
|
|
900
|
+
elif [[ "$_RC" =~ ^[0-9]+$ && "$_RC" -gt 3 ]]; then
|
|
901
|
+
_spin_kill
|
|
902
|
+
ffix_warn "${_svc}: ${_RC} restart(s) since last boot"
|
|
903
|
+
flog "WARN: ${_svc} restart count=${_RC}"
|
|
904
|
+
_SVC_STABLE=0
|
|
905
|
+
fi
|
|
906
|
+
done
|
|
907
|
+
[[ $_SVC_STABLE -eq 1 ]] && ffix_info "Both services stable (low restart count)"
|
|
908
|
+
|
|
909
|
+
# ── 2. Journal error scan — last 2 hours ─────────────────────────────
|
|
910
|
+
spin_start "Scanning journal for errors (last 2 h)"
|
|
911
|
+
_J_RAW=$(journalctl -u plc_watcher -u plc_web --since "2 hours ago" \
|
|
912
|
+
--no-pager -q 2>/dev/null \
|
|
913
|
+
| grep -iE \
|
|
914
|
+
'(Traceback|ImportError|ModuleNotFoundError|SyntaxError|PermissionError|FileNotFoundError|JSONDecodeError|OSError|ConnectionRefusedError|TimeoutError|CRITICAL|FATAL)' \
|
|
915
|
+
| grep -v '^-- Logs begin' \
|
|
916
|
+
| tail -30 || true)
|
|
917
|
+
|
|
918
|
+
if [[ -n "$_J_RAW" ]]; then
|
|
919
|
+
_spin_kill
|
|
920
|
+
warn "Errors found in recent journal:"
|
|
921
|
+
while IFS= read -r _line; do
|
|
922
|
+
# Trim to 110 chars for display
|
|
923
|
+
printf " ${R}│${NC} ${D}%.110s${NC}\n" "$_line"
|
|
924
|
+
flog "JOURNAL: $_line"
|
|
925
|
+
done <<< "$_J_RAW"
|
|
926
|
+
echo ""
|
|
927
|
+
|
|
928
|
+
# ── Auto-fix: missing Python module ──────────────────────────────
|
|
929
|
+
_MISSING_MOD=$(echo "$_J_RAW" \
|
|
930
|
+
| grep -oP "No module named ['\"]?\K[a-zA-Z0-9_]+" | head -1 || true)
|
|
931
|
+
if [[ -n "$_MISSING_MOD" ]]; then
|
|
932
|
+
spin_start "Auto-fix: installing missing module '${_MISSING_MOD}'"
|
|
933
|
+
if "${PYTHON}" -m pip install "${_MISSING_MOD}" -q 2>/dev/null; then
|
|
934
|
+
ffix_ok "pip installed: ${_MISSING_MOD}"
|
|
935
|
+
sudo systemctl restart plc_watcher plc_web 2>/dev/null || true
|
|
936
|
+
ffix_ok "Services restarted after module install"
|
|
937
|
+
else
|
|
938
|
+
ffix_warn "pip install failed for: ${_MISSING_MOD} — try manually"
|
|
939
|
+
fi
|
|
940
|
+
fi
|
|
941
|
+
|
|
942
|
+
# ── Auto-fix: corrupt delivery_queue.json ─────────────────────────
|
|
943
|
+
if echo "$_J_RAW" | grep -q "JSONDecodeError"; then
|
|
944
|
+
_QFILE="${DATA_DIR}/delivery_queue.json"
|
|
945
|
+
if [[ -f "$_QFILE" ]]; then
|
|
946
|
+
spin_start "Auto-fix: resetting corrupt delivery queue"
|
|
947
|
+
cp "$_QFILE" "${_QFILE}.broken.$(date +%s)" 2>/dev/null || true
|
|
948
|
+
echo "[]" > "$_QFILE"
|
|
949
|
+
ffix_ok "delivery_queue.json reset (broken copy saved)"
|
|
950
|
+
fi
|
|
951
|
+
fi
|
|
952
|
+
|
|
953
|
+
# ── Auto-fix: PermissionError on data/ files ──────────────────────
|
|
954
|
+
if echo "$_J_RAW" | grep -q "PermissionError"; then
|
|
955
|
+
spin_start "Auto-fix: correcting data/ permissions"
|
|
956
|
+
sudo chown pi:pi "${DATA_DIR}" 2>/dev/null || true
|
|
957
|
+
sudo chmod 755 "${DATA_DIR}" 2>/dev/null || true
|
|
958
|
+
sudo chown pi:pi "${DATA_DIR}"/* 2>/dev/null || true
|
|
959
|
+
sudo chmod 644 "${DATA_DIR}"/* 2>/dev/null || true
|
|
960
|
+
ffix_ok "data/ ownership set to pi:pi 755/644"
|
|
961
|
+
fi
|
|
962
|
+
|
|
963
|
+
# ── Auto-fix: FileNotFoundError on a data/ file ───────────────────
|
|
964
|
+
if echo "$_J_RAW" | grep -q "FileNotFoundError.*data"; then
|
|
965
|
+
spin_start "Auto-fix: recreating missing data files"
|
|
966
|
+
mkdir -p "${DATA_DIR}" 2>/dev/null || true
|
|
967
|
+
[[ -f "${DATA_DIR}/delivery_queue.json" ]] \
|
|
968
|
+
|| echo "[]" > "${DATA_DIR}/delivery_queue.json"
|
|
969
|
+
[[ -f "${DATA_DIR}/delivery_sent.log" ]] \
|
|
970
|
+
|| touch "${DATA_DIR}/delivery_sent.log"
|
|
971
|
+
chown pi:pi "${DATA_DIR}"/* 2>/dev/null || true
|
|
972
|
+
ffix_ok "Missing data files recreated"
|
|
973
|
+
fi
|
|
974
|
+
|
|
975
|
+
else
|
|
976
|
+
ffix_info "No Python exceptions in recent logs"
|
|
977
|
+
fi
|
|
978
|
+
|
|
979
|
+
# ── 3. RT scheduling verification ─────────────────────────────────────
|
|
980
|
+
spin_start "RT scheduling (plc_watcher)"
|
|
981
|
+
_WPD=$(systemctl show -p MainPID --value plc_watcher 2>/dev/null || echo "0")
|
|
982
|
+
_WPD="${_WPD//[^0-9]/}"
|
|
983
|
+
if [[ "${_WPD:-0}" -gt 0 ]]; then
|
|
984
|
+
_SCHED=$(chrt -p "$_WPD" 2>/dev/null || echo "")
|
|
985
|
+
if echo "$_SCHED" | grep -q "SCHED_FIFO"; then
|
|
986
|
+
_PRIO=$(echo "$_SCHED" | grep -oP 'priority: \K\d+' || echo "?")
|
|
987
|
+
_AFF=$(taskset -cp "$_WPD" 2>/dev/null | grep -oP 'list: \K.*' || echo "?")
|
|
988
|
+
ffix_info "SCHED_FIFO:${_PRIO} cpus:${_AFF} pid:${_WPD}"
|
|
989
|
+
flog "INFO: plc_watcher SCHED_FIFO:${_PRIO} cpu:${_AFF}"
|
|
990
|
+
else
|
|
991
|
+
ffix_warn "RT scheduling not active — reloading unit and restarting"
|
|
992
|
+
flog "WARN: plc_watcher not SCHED_FIFO: ${_SCHED}"
|
|
993
|
+
sudo systemctl daemon-reload 2>/dev/null || true
|
|
994
|
+
sudo systemctl restart plc_watcher 2>/dev/null && ffix_ok "Service restarted" || ffix_err "Restart failed"
|
|
995
|
+
fi
|
|
996
|
+
else
|
|
997
|
+
ffix_warn "plc_watcher not running — cannot check RT scheduling"
|
|
998
|
+
fi
|
|
999
|
+
|
|
1000
|
+
# ── 4. data/ directory writability ────────────────────────────────────
|
|
1001
|
+
spin_start "data/ directory"
|
|
1002
|
+
if [[ ! -d "${DATA_DIR}" ]]; then
|
|
1003
|
+
sudo mkdir -p "${DATA_DIR}" 2>/dev/null || true
|
|
1004
|
+
sudo chown pi:pi "${DATA_DIR}" && sudo chmod 755 "${DATA_DIR}"
|
|
1005
|
+
ffix_ok "Created missing data/"
|
|
1006
|
+
elif [[ ! -w "${DATA_DIR}" ]]; then
|
|
1007
|
+
sudo chown pi:pi "${DATA_DIR}" && sudo chmod 755 "${DATA_DIR}"
|
|
1008
|
+
ffix_ok "Fixed data/ permissions (was not writable by pi)"
|
|
1009
|
+
flog "FIXED: data/ was not writable — corrected to pi:pi 755"
|
|
1010
|
+
else
|
|
1011
|
+
ffix_info "data/ writable by pi"
|
|
1012
|
+
fi
|
|
1013
|
+
|
|
1014
|
+
# ── 5. smb_config.py syntax ───────────────────────────────────────────
|
|
1015
|
+
spin_start "smb_config.py syntax"
|
|
1016
|
+
if [[ -f "${SMB_CFG}" ]]; then
|
|
1017
|
+
_SMB_ERR=$("${PYTHON}" -c "import ast; ast.parse(open('${SMB_CFG}').read())" 2>&1 || true)
|
|
1018
|
+
if [[ -n "${_SMB_ERR}" ]]; then
|
|
1019
|
+
flog "ERROR: smb_config.py syntax: ${_SMB_ERR}"
|
|
1020
|
+
ffix_warn "smb_config.py has syntax errors — resetting to disabled"
|
|
1021
|
+
printf 'SMB_ENABLED = False\nSMB_HOST = ""\nSMB_SHARE = "Reports"\nSMB_USERNAME = ""\nSMB_PASSWORD = ""\nSMB_SUBDIR = ""\n' \
|
|
1022
|
+
> "${SMB_CFG}" 2>/dev/null || true
|
|
1023
|
+
ffix_ok "smb_config.py reset — run: plc_checkweigher smb-config"
|
|
1024
|
+
else
|
|
1025
|
+
_SMB_EN=$(grep "^SMB_ENABLED" "${SMB_CFG}" 2>/dev/null \
|
|
1026
|
+
| grep -qi "true" && echo "enabled" || echo "disabled")
|
|
1027
|
+
_SMB_H=$(smb_get "SMB_HOST")
|
|
1028
|
+
ffix_info "smb_config.py valid (SMB: ${_SMB_EN}${_SMB_H:+ → ${_SMB_H}})"
|
|
1029
|
+
fi
|
|
1030
|
+
else
|
|
1031
|
+
ffix_warn "smb_config.py missing — run: plc_checkweigher smb-config"
|
|
1032
|
+
flog "WARN: smb_config.py not found at ${SMB_CFG}"
|
|
1033
|
+
fi
|
|
1034
|
+
|
|
1035
|
+
# ── 6. Source file protection (root:root 644) ─────────────────────────
|
|
1036
|
+
spin_start "Source file protection"
|
|
1037
|
+
_WRONG=$(find "${INSTALL_DIR}" -maxdepth 1 -name "*.py" \
|
|
1038
|
+
! -user root 2>/dev/null | wc -l || echo 0)
|
|
1039
|
+
if [[ "${_WRONG:-0}" -gt 0 ]]; then
|
|
1040
|
+
ffix_warn "${_WRONG} source .py file(s) not root-owned — locking"
|
|
1041
|
+
flog "WARN: ${_WRONG} source file(s) had wrong owner"
|
|
1042
|
+
sudo find "${INSTALL_DIR}" -maxdepth 1 -name "*.py" \
|
|
1043
|
+
-exec chown root:root {} \; -exec chmod 644 {} \; 2>/dev/null || true
|
|
1044
|
+
sudo find "${INSTALL_DIR}/web" -name "*.py" \
|
|
1045
|
+
-exec chown root:root {} \; -exec chmod 644 {} \; 2>/dev/null || true
|
|
1046
|
+
ffix_ok "Source files re-locked to root:root 644"
|
|
1047
|
+
else
|
|
1048
|
+
ffix_info "Source files protected (root:root 644)"
|
|
1049
|
+
fi
|
|
1050
|
+
|
|
1051
|
+
# ── 7. PLC TCP reachability ───────────────────────────────────────────
|
|
1052
|
+
spin_start "PLC TCP connection (192.168.3.250:1025)"
|
|
1053
|
+
if timeout 3 bash -c "echo >/dev/tcp/192.168.3.250/1025" 2>/dev/null; then
|
|
1054
|
+
ffix_info "PLC reachable at 192.168.3.250:1025"
|
|
1055
|
+
else
|
|
1056
|
+
ffix_warn "PLC not reachable at 192.168.3.250:1025"
|
|
1057
|
+
flog "WARN: PLC TCP connect failed (192.168.3.250:1025)"
|
|
1058
|
+
echo ""
|
|
1059
|
+
info "Possible causes:"
|
|
1060
|
+
info " • eth0 cable unplugged (check: ip link show eth0)"
|
|
1061
|
+
info " • PLC powered off / not ready"
|
|
1062
|
+
info " • SLMP not enabled in GX Works (Enable SLMP TCP port 1025)"
|
|
1063
|
+
info " • Wrong subnet on eth0 (should be 192.168.3.x)"
|
|
1064
|
+
echo ""
|
|
1065
|
+
fi
|
|
1066
|
+
|
|
1067
|
+
# ── 8. plc_live.json staleness ────────────────────────────────────────
|
|
1068
|
+
spin_start "Live state file (/tmp/plc_live.json)"
|
|
1069
|
+
if [[ -f "/tmp/plc_live.json" ]]; then
|
|
1070
|
+
_AGE=$(( $(date +%s) - $(stat -c %Y /tmp/plc_live.json 2>/dev/null || echo 0) ))
|
|
1071
|
+
if [[ "${_AGE:-0}" -gt 10 ]]; then
|
|
1072
|
+
ffix_warn "plc_live.json is ${_AGE}s old — watcher may be stuck or offline"
|
|
1073
|
+
flog "WARN: plc_live.json stale (${_AGE}s old)"
|
|
1074
|
+
else
|
|
1075
|
+
ffix_info "plc_live.json updated ${_AGE}s ago (OK)"
|
|
1076
|
+
fi
|
|
1077
|
+
else
|
|
1078
|
+
ffix_info "plc_live.json absent (normal — created when watcher connects)"
|
|
1079
|
+
fi
|
|
1080
|
+
|
|
1081
|
+
# ── 9. reader script path ─────────────────────────────────────────────
|
|
1082
|
+
spin_start "plc_reader.py present"
|
|
1083
|
+
if [[ ! -f "${INSTALL_DIR}/plc_reader.py" ]]; then
|
|
1084
|
+
ffix_err "plc_reader.py missing — watcher cannot launch it"
|
|
1085
|
+
flog "ERROR: plc_reader.py not found at ${INSTALL_DIR}/plc_reader.py"
|
|
1086
|
+
else
|
|
1087
|
+
ffix_info "plc_reader.py present and readable"
|
|
1088
|
+
fi
|
|
1089
|
+
|
|
1090
|
+
echo ""
|
|
1091
|
+
fi
|
|
1092
|
+
|
|
881
1093
|
# ── Summary ───────────────────────────────────────────────────────────────
|
|
882
1094
|
flog "=== Complete: ${FIX_COUNT} fix(es) applied ==="
|
|
883
1095
|
hr
|
|
@@ -1158,6 +1370,7 @@ help|--help|-h)
|
|
|
1158
1370
|
echo " fix -wifi Fix WiFi connectivity only"
|
|
1159
1371
|
echo " fix -health Fix disk, memory, time, dirs"
|
|
1160
1372
|
echo " fix -programs Fix services, packages, queue, unit files"
|
|
1373
|
+
echo " fix -errors Scan journal, verify RT/permissions/PLC/config"
|
|
1161
1374
|
echo " logs Stream live logs (plc_watcher + plc_web)"
|
|
1162
1375
|
echo " queue Show SMB pending queue and delivery ledger"
|
|
1163
1376
|
echo ""
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plc-checkweigher",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.21.0",
|
|
4
4
|
"description": "One-command installer for the PLC Check-Weigher system on Raspberry Pi (PREEMPT_RT kernel, Python stack, WiFi, SMB, systemd RT services)",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"postinstall": "node bin/cli.js"
|