plc-checkweigher 1.32.3 → 1.33.1
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 +169 -25
- package/package.json +1 -1
- package/setup.sh +30 -2
package/bin/plc_checkweigher
CHANGED
|
@@ -174,12 +174,13 @@ restart)
|
|
|
174
174
|
need_sudo
|
|
175
175
|
banner "Restarting services"
|
|
176
176
|
echo ""
|
|
177
|
-
spin_start "Restarting plc_watcher and
|
|
178
|
-
sudo systemctl restart plc_watcher plc_web
|
|
177
|
+
spin_start "Restarting plc_watcher, plc_web and plc_selfheal"
|
|
178
|
+
sudo systemctl restart plc_watcher plc_web plc_selfheal 2>/dev/null \
|
|
179
|
+
|| sudo systemctl restart plc_watcher plc_web
|
|
179
180
|
sleep 2
|
|
180
181
|
spin_ok
|
|
181
182
|
echo ""
|
|
182
|
-
systemctl status plc_watcher plc_web --no-pager | grep -E 'Active|Main PID'
|
|
183
|
+
systemctl status plc_watcher plc_web plc_selfheal --no-pager 2>/dev/null | grep -E 'Active|Main PID'
|
|
183
184
|
echo ""
|
|
184
185
|
;;
|
|
185
186
|
|
|
@@ -187,8 +188,9 @@ start)
|
|
|
187
188
|
need_sudo
|
|
188
189
|
banner "Starting services"
|
|
189
190
|
echo ""
|
|
190
|
-
spin_start "Starting plc_watcher and
|
|
191
|
-
sudo systemctl start plc_watcher plc_web
|
|
191
|
+
spin_start "Starting plc_watcher, plc_web and plc_selfheal"
|
|
192
|
+
sudo systemctl start plc_watcher plc_web plc_selfheal 2>/dev/null \
|
|
193
|
+
|| sudo systemctl start plc_watcher plc_web
|
|
192
194
|
sleep 1
|
|
193
195
|
spin_ok
|
|
194
196
|
echo ""
|
|
@@ -198,12 +200,15 @@ stop)
|
|
|
198
200
|
need_sudo
|
|
199
201
|
banner "Stopping services"
|
|
200
202
|
echo ""
|
|
201
|
-
|
|
203
|
+
# Stop the self-healing daemon FIRST — otherwise it would detect the
|
|
204
|
+
# watcher/web going down and immediately restart them, fighting this stop.
|
|
205
|
+
spin_start "Stopping plc_selfheal, plc_watcher and plc_web"
|
|
206
|
+
sudo systemctl stop plc_selfheal 2>/dev/null || true
|
|
202
207
|
sudo systemctl stop plc_watcher plc_web
|
|
203
208
|
spin_ok
|
|
204
209
|
echo ""
|
|
205
210
|
warn "Services stopped — will restart automatically on next boot"
|
|
206
|
-
warn "To disable auto-start: sudo systemctl disable plc_watcher plc_web"
|
|
211
|
+
warn "To disable auto-start: sudo systemctl disable plc_watcher plc_web plc_selfheal"
|
|
207
212
|
echo ""
|
|
208
213
|
;;
|
|
209
214
|
|
|
@@ -235,6 +240,87 @@ queue)
|
|
|
235
240
|
echo ""
|
|
236
241
|
;;
|
|
237
242
|
|
|
243
|
+
# ── Self-healing daemon ────────────────────────────────────────────────────────
|
|
244
|
+
selfheal)
|
|
245
|
+
SUBCMD="${1:-status}"
|
|
246
|
+
shift || true
|
|
247
|
+
case "$SUBCMD" in
|
|
248
|
+
status)
|
|
249
|
+
banner "Self-Healing Daemon"
|
|
250
|
+
echo ""
|
|
251
|
+
ACTIVE=$(systemctl is-active plc_selfheal 2>/dev/null || true)
|
|
252
|
+
ENABLED=$(systemctl is-enabled plc_selfheal 2>/dev/null || true)
|
|
253
|
+
if [[ "$ACTIVE" == "active" ]]; then
|
|
254
|
+
ok "plc_selfheal: RUNNING (auto-start: ${ENABLED})"
|
|
255
|
+
else
|
|
256
|
+
warn "plc_selfheal: ${ACTIVE} (auto-start: ${ENABLED})"
|
|
257
|
+
info "Start it: sudo systemctl start plc_selfheal"
|
|
258
|
+
fi
|
|
259
|
+
echo ""
|
|
260
|
+
# Last-cycle / throttle state
|
|
261
|
+
_ST="/home/pi/reports/health/.selfheal_state.json"
|
|
262
|
+
if [[ -f "$_ST" ]]; then
|
|
263
|
+
"${PYTHON}" - "$_ST" << 'PYEOF' 2>/dev/null || true
|
|
264
|
+
import json, sys, time
|
|
265
|
+
try:
|
|
266
|
+
d = json.load(open(sys.argv[1]))
|
|
267
|
+
except Exception:
|
|
268
|
+
sys.exit(0)
|
|
269
|
+
lc = d.get("last_cycle", 0)
|
|
270
|
+
if lc:
|
|
271
|
+
age = int(time.time() - lc)
|
|
272
|
+
print(f" Last self-heal sweep: {age}s ago")
|
|
273
|
+
rep = d.get("reported", {})
|
|
274
|
+
if rep:
|
|
275
|
+
print(f" Active unresolved problems: {len(rep)}")
|
|
276
|
+
for k in rep: print(f" • {k}")
|
|
277
|
+
else:
|
|
278
|
+
print(" No unresolved problems")
|
|
279
|
+
PYEOF
|
|
280
|
+
else
|
|
281
|
+
info "No sweep has run yet (daemon may have just started)"
|
|
282
|
+
fi
|
|
283
|
+
echo ""
|
|
284
|
+
# Pending health reports on disk
|
|
285
|
+
_HD="/home/pi/reports/health"
|
|
286
|
+
if [[ -d "$_HD" ]]; then
|
|
287
|
+
_N=$(find "$_HD" -maxdepth 1 -name 'health_*.txt' 2>/dev/null | wc -l)
|
|
288
|
+
[[ "${_N:-0}" -gt 0 ]] && info "${_N} health report(s) in ${_HD}"
|
|
289
|
+
fi
|
|
290
|
+
echo ""
|
|
291
|
+
info "Recent activity: plc_checkweigher selfheal logs"
|
|
292
|
+
echo ""
|
|
293
|
+
;;
|
|
294
|
+
logs)
|
|
295
|
+
banner "Self-Heal Logs"
|
|
296
|
+
echo ""
|
|
297
|
+
exec journalctl -u plc_selfheal -n 50 -f --no-pager
|
|
298
|
+
;;
|
|
299
|
+
now|run)
|
|
300
|
+
banner "Self-Heal — Immediate Sweep"
|
|
301
|
+
need_sudo
|
|
302
|
+
echo ""
|
|
303
|
+
spin_start "Running one self-heal cycle"
|
|
304
|
+
sudo "${PYTHON}" -c "
|
|
305
|
+
import sys; sys.path.insert(0, '${INSTALL_DIR}')
|
|
306
|
+
import selfheal
|
|
307
|
+
healed, failed, env = selfheal.run_cycle()
|
|
308
|
+
selfheal.maybe_report(healed, failed, env)
|
|
309
|
+
print('HEALED:', len(healed), '| UNRESOLVED:', len(failed))
|
|
310
|
+
for k,d in healed: print(' [HEALED]', k, '-', d)
|
|
311
|
+
for k,d in failed: print(' [FAIL] ', k, '-', d)
|
|
312
|
+
" && spin_ok || spin_warn "Cycle completed with issues"
|
|
313
|
+
echo ""
|
|
314
|
+
;;
|
|
315
|
+
*)
|
|
316
|
+
echo "Usage: plc_checkweigher selfheal [status|logs|now]"
|
|
317
|
+
echo " status — daemon state, last sweep, unresolved problems (default)"
|
|
318
|
+
echo " logs — follow the self-heal journal"
|
|
319
|
+
echo " now — run one self-heal sweep immediately"
|
|
320
|
+
;;
|
|
321
|
+
esac
|
|
322
|
+
;;
|
|
323
|
+
|
|
238
324
|
# ── Push test ─────────────────────────────────────────────────────────────────
|
|
239
325
|
push-test)
|
|
240
326
|
banner "SMB Push Test"
|
|
@@ -612,20 +698,23 @@ console-passwd)
|
|
|
612
698
|
|
|
613
699
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
614
700
|
# FIX — auto-detect and repair common issues
|
|
615
|
-
# Usage: fix [-wifi] [-health] [-programs] [-errors]
|
|
701
|
+
# Usage: fix [-wifi] [-health] [-programs] [-errors] [-registers] (no flags = all but registers)
|
|
616
702
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
617
703
|
fix)
|
|
618
|
-
FIX_WIFI=0; FIX_HEALTH=0; FIX_PROGRAMS=0; FIX_ERRORS=0
|
|
704
|
+
FIX_WIFI=0; FIX_HEALTH=0; FIX_PROGRAMS=0; FIX_ERRORS=0; FIX_REGISTERS=0
|
|
619
705
|
if [[ $# -eq 0 ]]; then
|
|
706
|
+
# -registers is opt-in only: it talks to the PLC and may rewrite the
|
|
707
|
+
# register map, so it is excluded from the default "fix everything" run.
|
|
620
708
|
FIX_WIFI=1; FIX_HEALTH=1; FIX_PROGRAMS=1; FIX_ERRORS=1
|
|
621
709
|
else
|
|
622
710
|
for _flag in "$@"; do
|
|
623
711
|
case "$_flag" in
|
|
624
|
-
-wifi)
|
|
625
|
-
-health)
|
|
626
|
-
-programs)
|
|
627
|
-
-errors)
|
|
628
|
-
|
|
712
|
+
-wifi) FIX_WIFI=1 ;;
|
|
713
|
+
-health) FIX_HEALTH=1 ;;
|
|
714
|
+
-programs) FIX_PROGRAMS=1 ;;
|
|
715
|
+
-errors) FIX_ERRORS=1 ;;
|
|
716
|
+
-registers) FIX_REGISTERS=1 ;;
|
|
717
|
+
*) warn "Unknown flag: $_flag (valid: -wifi -health -programs -errors -registers)" ;;
|
|
629
718
|
esac
|
|
630
719
|
done
|
|
631
720
|
fi
|
|
@@ -634,10 +723,11 @@ fix)
|
|
|
634
723
|
mkdir -p "$LOG_DIR" 2>/dev/null || true
|
|
635
724
|
# Build mode tag: "all" or hyphen-joined active scopes
|
|
636
725
|
_LOG_MODES=""
|
|
637
|
-
[[ $FIX_WIFI
|
|
638
|
-
[[ $FIX_HEALTH
|
|
639
|
-
[[ $FIX_PROGRAMS
|
|
640
|
-
[[ $FIX_ERRORS
|
|
726
|
+
[[ $FIX_WIFI -eq 1 ]] && _LOG_MODES="${_LOG_MODES:+${_LOG_MODES}-}wifi"
|
|
727
|
+
[[ $FIX_HEALTH -eq 1 ]] && _LOG_MODES="${_LOG_MODES:+${_LOG_MODES}-}health"
|
|
728
|
+
[[ $FIX_PROGRAMS -eq 1 ]] && _LOG_MODES="${_LOG_MODES:+${_LOG_MODES}-}programs"
|
|
729
|
+
[[ $FIX_ERRORS -eq 1 ]] && _LOG_MODES="${_LOG_MODES:+${_LOG_MODES}-}errors"
|
|
730
|
+
[[ $FIX_REGISTERS -eq 1 ]] && _LOG_MODES="${_LOG_MODES:+${_LOG_MODES}-}registers"
|
|
641
731
|
[[ "$_LOG_MODES" == "wifi-health-programs-errors" ]] && _LOG_MODES="all"
|
|
642
732
|
LOG_FILE="${LOG_DIR}/fix_${_LOG_MODES}_$(date '+%Y%m%d_%H%M%S').log"
|
|
643
733
|
flog() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"; }
|
|
@@ -647,7 +737,7 @@ fix)
|
|
|
647
737
|
ffix_err() { spin_err "$*"; flog "ERROR : $*"; }
|
|
648
738
|
|
|
649
739
|
FIX_COUNT=0
|
|
650
|
-
flog "=== Run started | wifi=${FIX_WIFI} health=${FIX_HEALTH} programs=${FIX_PROGRAMS} errors=${FIX_ERRORS} ==="
|
|
740
|
+
flog "=== Run started | wifi=${FIX_WIFI} health=${FIX_HEALTH} programs=${FIX_PROGRAMS} errors=${FIX_ERRORS} registers=${FIX_REGISTERS} ==="
|
|
651
741
|
|
|
652
742
|
banner "Auto Fix"
|
|
653
743
|
echo ""
|
|
@@ -1152,6 +1242,37 @@ SMBC
|
|
|
1152
1242
|
echo ""
|
|
1153
1243
|
fi
|
|
1154
1244
|
|
|
1245
|
+
# ─────────────────────────────────────────────────────────────────────────
|
|
1246
|
+
# REGISTERS — confirm the live PLC register layout and auto-correct the map
|
|
1247
|
+
# ─────────────────────────────────────────────────────────────────────────
|
|
1248
|
+
if [[ $FIX_REGISTERS -eq 1 ]]; then
|
|
1249
|
+
echo -e " ${B}▸ PLC Registers${NC}"; hr; echo ""
|
|
1250
|
+
flog "=== Register scan ==="
|
|
1251
|
+
# The scanner connects to the PLC, decodes known + candidate registers,
|
|
1252
|
+
# and — if the read weight reads 0 while an item is on the scale —
|
|
1253
|
+
# detects the real register and writes data/register_map.json so the
|
|
1254
|
+
# code self-corrects without a source edit.
|
|
1255
|
+
_REG_OUT=$("${PYTHON}" -c "
|
|
1256
|
+
import sys; sys.path.insert(0, '${INSTALL_DIR}')
|
|
1257
|
+
import regmap
|
|
1258
|
+
changed, _ = regmap.scan(write=True)
|
|
1259
|
+
sys.exit(10 if changed else 0)
|
|
1260
|
+
" 2>&1)
|
|
1261
|
+
_REG_RC=$?
|
|
1262
|
+
echo "$_REG_OUT"
|
|
1263
|
+
while IFS= read -r _rl; do flog "REG: $_rl"; done <<< "$_REG_OUT"
|
|
1264
|
+
echo ""
|
|
1265
|
+
if [[ $_REG_RC -eq 10 ]]; then
|
|
1266
|
+
ffix_ok "Register map auto-corrected — read weight remapped"
|
|
1267
|
+
# New map takes effect on the reader's next item; restart to apply now.
|
|
1268
|
+
sudo systemctl restart plc_watcher 2>/dev/null || true
|
|
1269
|
+
ffix_info "plc_watcher restarted to apply the new register map"
|
|
1270
|
+
else
|
|
1271
|
+
ffix_info "Register scan complete — no remap needed"
|
|
1272
|
+
fi
|
|
1273
|
+
echo ""
|
|
1274
|
+
fi
|
|
1275
|
+
|
|
1155
1276
|
# ── Summary ───────────────────────────────────────────────────────────────
|
|
1156
1277
|
flog "=== Complete: ${FIX_COUNT} fix(es) applied ==="
|
|
1157
1278
|
hr
|
|
@@ -1296,7 +1417,8 @@ update)
|
|
|
1296
1417
|
# ── Update systemd unit files if they changed ─────────────────────────────
|
|
1297
1418
|
_UNITS_UPDATED=0
|
|
1298
1419
|
for _svc_src in "${INSTALL_DIR}/plc_watcher.service" \
|
|
1299
|
-
"${INSTALL_DIR}/web/plc_web.service"
|
|
1420
|
+
"${INSTALL_DIR}/web/plc_web.service" \
|
|
1421
|
+
"${INSTALL_DIR}/plc_selfheal.service"; do
|
|
1300
1422
|
[[ ! -f "$_svc_src" ]] && continue
|
|
1301
1423
|
_svc_dst="/etc/systemd/system/$(basename "$_svc_src")"
|
|
1302
1424
|
if [[ ! -f "$_svc_dst" ]] || ! diff -q "$_svc_src" "$_svc_dst" &>/dev/null; then
|
|
@@ -1309,6 +1431,22 @@ update)
|
|
|
1309
1431
|
spin_ok "systemd reloaded"
|
|
1310
1432
|
fi
|
|
1311
1433
|
|
|
1434
|
+
# ── Self-healing daemon present + enabled (retrofit older installs) ────────
|
|
1435
|
+
spin_start "Self-healing daemon"
|
|
1436
|
+
if [[ -f "${INSTALL_DIR}/selfheal.py" && -f /etc/systemd/system/plc_selfheal.service ]]; then
|
|
1437
|
+
if ! systemctl is-enabled --quiet plc_selfheal 2>/dev/null; then
|
|
1438
|
+
sudo systemctl enable plc_selfheal 2>/dev/null || true
|
|
1439
|
+
spin_ok "plc_selfheal enabled"
|
|
1440
|
+
elif ! systemctl is-active --quiet plc_selfheal 2>/dev/null; then
|
|
1441
|
+
sudo systemctl start plc_selfheal 2>/dev/null || true
|
|
1442
|
+
spin_ok "plc_selfheal started"
|
|
1443
|
+
else
|
|
1444
|
+
spin_ok "plc_selfheal active"
|
|
1445
|
+
fi
|
|
1446
|
+
else
|
|
1447
|
+
spin_warn "selfheal.py or unit missing — will be installed on next full setup"
|
|
1448
|
+
fi
|
|
1449
|
+
|
|
1312
1450
|
# ── Update Plymouth boot splash ───────────────────────────────────────────
|
|
1313
1451
|
_THEME_DIR="/usr/share/plymouth/themes/saismruth"
|
|
1314
1452
|
_LOGO_SRC="${INSTALL_DIR}/assets/logo.png"
|
|
@@ -1566,6 +1704,7 @@ SUDOEOF
|
|
|
1566
1704
|
if [[ $_CODE_CHANGED -eq 1 || $_UNITS_UPDATED -eq 1 ]]; then
|
|
1567
1705
|
spin_start "Restarting services"
|
|
1568
1706
|
sudo systemctl restart plc_watcher plc_web 2>/dev/null
|
|
1707
|
+
sudo systemctl restart plc_selfheal 2>/dev/null || true
|
|
1569
1708
|
sleep 2
|
|
1570
1709
|
spin_ok "Services restarted"
|
|
1571
1710
|
else
|
|
@@ -1699,12 +1838,13 @@ uninstall)
|
|
|
1699
1838
|
# ── 1. Stop and disable services ─────────────────────────────────────────
|
|
1700
1839
|
echo ""
|
|
1701
1840
|
ustep "Stopping and disabling services"
|
|
1702
|
-
for SVC in plc_watcher plc_web; do
|
|
1841
|
+
for SVC in plc_selfheal plc_watcher plc_web; do
|
|
1703
1842
|
systemctl is-active --quiet "$SVC" 2>/dev/null && sudo systemctl stop "$SVC" 2>/dev/null || true
|
|
1704
1843
|
systemctl is-enabled --quiet "$SVC" 2>/dev/null && sudo systemctl disable "$SVC" 2>/dev/null || true
|
|
1705
1844
|
done
|
|
1706
1845
|
sudo rm -f /etc/systemd/system/plc_watcher.service \
|
|
1707
|
-
/etc/systemd/system/plc_web.service
|
|
1846
|
+
/etc/systemd/system/plc_web.service \
|
|
1847
|
+
/etc/systemd/system/plc_selfheal.service
|
|
1708
1848
|
spin_ok "Services removed"
|
|
1709
1849
|
|
|
1710
1850
|
# ── 2. System drop-ins ───────────────────────────────────────────────────
|
|
@@ -1856,13 +1996,17 @@ help|--help|-h)
|
|
|
1856
1996
|
echo " fix -health Fix disk, memory, time, dirs"
|
|
1857
1997
|
echo " fix -programs Fix services, packages, queue, unit files"
|
|
1858
1998
|
echo " fix -errors Scan journal, verify RT/permissions/PLC/config"
|
|
1999
|
+
echo " fix -registers Confirm live PLC register layout + auto-correct map"
|
|
1859
2000
|
echo " logs Stream live logs (plc_watcher + plc_web)"
|
|
1860
2001
|
echo " queue Show SMB pending queue and delivery ledger"
|
|
2002
|
+
echo " selfheal status Self-healing daemon state + unresolved problems"
|
|
2003
|
+
echo " selfheal logs Follow the self-heal journal"
|
|
2004
|
+
echo " selfheal now Run one self-heal sweep immediately"
|
|
1861
2005
|
echo ""
|
|
1862
2006
|
echo -e " ${W}Services${NC}"
|
|
1863
|
-
echo " start Start
|
|
1864
|
-
echo " stop Stop
|
|
1865
|
-
echo " restart Restart
|
|
2007
|
+
echo " start Start watcher, web and self-heal"
|
|
2008
|
+
echo " stop Stop all services"
|
|
2009
|
+
echo " restart Restart all services"
|
|
1866
2010
|
echo ""
|
|
1867
2011
|
echo -e " ${W}Network${NC}"
|
|
1868
2012
|
echo " wifi Scan and switch WiFi network"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "plc-checkweigher",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.33.1",
|
|
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"
|
package/setup.sh
CHANGED
|
@@ -432,9 +432,37 @@ WantedBy=multi-user.target
|
|
|
432
432
|
EOF
|
|
433
433
|
ok "plc_web.service (Nice=-10)"
|
|
434
434
|
|
|
435
|
+
# ── Self-healing daemon — auto-repairs services/files/configs at runtime ──
|
|
436
|
+
cat > /etc/systemd/system/plc_selfheal.service << EOF
|
|
437
|
+
[Unit]
|
|
438
|
+
Description=PLC Check-Weigher Self-Healing Daemon
|
|
439
|
+
After=network.target plc_watcher.service plc_web.service
|
|
440
|
+
Wants=plc_watcher.service plc_web.service
|
|
441
|
+
|
|
442
|
+
[Service]
|
|
443
|
+
Type=simple
|
|
444
|
+
User=root
|
|
445
|
+
WorkingDirectory=${INSTALL_DIR}
|
|
446
|
+
Environment=PYTHONUNBUFFERED=1
|
|
447
|
+
ExecStart=${VENV_DIR}/bin/python3 -u ${INSTALL_DIR}/selfheal.py
|
|
448
|
+
Restart=always
|
|
449
|
+
RestartSec=10
|
|
450
|
+
Nice=10
|
|
451
|
+
CPUAffinity=0 1 2
|
|
452
|
+
IOSchedulingClass=idle
|
|
453
|
+
StandardOutput=journal
|
|
454
|
+
StandardError=journal
|
|
455
|
+
|
|
456
|
+
[Install]
|
|
457
|
+
WantedBy=multi-user.target
|
|
458
|
+
EOF
|
|
459
|
+
cp /etc/systemd/system/plc_selfheal.service "${INSTALL_DIR}/plc_selfheal.service"
|
|
460
|
+
chown "${PI_USER}:${PI_USER}" "${INSTALL_DIR}/plc_selfheal.service"
|
|
461
|
+
ok "plc_selfheal.service (auto-repair · cores 0-2 · Nice=10)"
|
|
462
|
+
|
|
435
463
|
systemctl daemon-reload
|
|
436
|
-
systemctl enable plc_watcher.service plc_web.service
|
|
437
|
-
ok "
|
|
464
|
+
systemctl enable plc_watcher.service plc_web.service plc_selfheal.service
|
|
465
|
+
ok "All services enabled — start automatically after reboot"
|
|
438
466
|
|
|
439
467
|
cp /etc/systemd/system/plc_watcher.service "${INSTALL_DIR}/plc_watcher.service"
|
|
440
468
|
chown "${PI_USER}:${PI_USER}" "${INSTALL_DIR}/plc_watcher.service"
|