Python version of LBMS
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-11 20:37:14 -04:00
items 2610.1 - First commit 2026-10-11 20:37:14 -04:00
lbms 2610.1 - First commit 2026-10-11 20:37:14 -04:00
.gitignore Initial commit 2026-10-11 20:34:49 -04:00
LICENSE Initial commit 2026-10-11 20:34:49 -04:00
main.py 2610.1 - First commit 2026-10-11 20:37:14 -04:00
README.md 2610.1 - First commit 2026-10-11 20:37:14 -04:00
requirements.txt 2610.1 - First commit 2026-10-11 20:37:14 -04:00

LBMS

Laser gun monitoring system: listens on a serial port for XBee packets from 44 tracked guns, decodes fire counts and "seen" beacons, estimates on-time (battery use), and displays a live grid of gun tiles. Includes a history dialog (per-gun stats, battery-change log) and a simulate mode that replays captured log files.

This repository contains:

  • LBMS/ — the original VB.NET WinForms project (reference, unchanged)
  • lbms/, main.py — the Python port (documented below)

Python port

Requirements

  • Python 3.8+
  • tkinter (standard library; on Debian/Ubuntu: sudo apt install python3-tk)
  • pyserial
pip install -r requirements.txt

Running

python3 main.py [options]

Options (all optional, key=value pairs):

Option Default Description
com — Serial port to open automatically on start (e.g. /dev/ttyUSB0). The main window is minimized when this is used.
admin false admin=true unlocks the SetFired/SetTime controls in the history dialog.
fired 11000 Fire-count threshold; reaching it flags the gun for a battery change.
battery 660 On-time (minutes) threshold; reaching it flags the gun for a battery change.

If no port is given, enter one in the text box (e.g. COM4 / /dev/ttyUSB0) and click Button1 to open/close it. Status shows in Label1 (Open/Closed).

Interface

  • LBMS Console (main window) — port controls, live decode stats (lines read, valid packets, queue/decode lengths, raw hex), and a scrolling per-gun summary.
  • LBMS (gun grid) — one tile per gun, 8x6 grid. Tile color:
    • green — seen in the last second
    • orange — seen in the last second but flagged for battery change
    • fading green — seen within the last 30 seconds
    • default grey — no recent contact
    • gun icon appears for ~3 s after a fire event; battery icon shows estimated remaining time/shots; both flash when a battery change is due.
    • Click a tile to open its History dialog: current + historical fired/on-time totals, change log, and Change Batteries (rolls current totals into history and zeroes them). Admin mode adds SetFired/SetTime.
    • Double-click the grid window to exit the app (with confirmation).
  • Simulate — pick a capture log (timestamp,hex per line, e.g. the files inside LBMS/220619.zip), and packets are replayed through the normal decode pipeline at their original timing.

Serial protocol

The port runs 115200, 8N1. Data is uppercased to hex and fed into a queue; the 75 ms main timer drains it into a decode buffer and scans for:

Marker Length (hex chars) Layout Action
7E002480 80 head(8) + MAC(16) + payload(34) + fire count(4) + tail(18) add fire count, mark seen
7E002A80 92 head(8) + MAC(16) + payload(36) + tail(32) mark seen

Fire count is big-endian (hi*256 + lo). When a count ever decreases (gun rebooted), the previous count is added to the gun's overall total and battery time is rolled over. Every time a known gun is seen and the last count is older than CountInterval (10 min), that interval is added to its on-time — which is how battery life is estimated. Junk data: if no packet is found and the buffer exceeds 200 chars, the first half is discarded.

Persistence

State is saved to ./items/<gun_id>.cfg (one JSON file per gun) on close and every ~61 s. On start, if ./items/ exists it is loaded; otherwise all 44 guns are created fresh from the MAC list in lbms/main_window.py (_create_id_list).

Project layout

main.py              # entry point
requirements.txt
lbms/
├── globals.py       # shared state: gun list, message queue, helpers
├── gun_object.py    # GunObject model + JSON save/load
├── cls_com_ports.py # ComPorts serial wrapper (pyserial)
├── resources.py     # PNG image loader (lbms/resources/)
├── gun_button.py    # one gun tile (text, colors, battery/gun icons)
├── frm_guns.py      # gun grid window
├── frm_history.py   # per-gun history dialog
└── main_window.py   # console window: timers, serial reader, packet decoder

Threading model

  • A daemon reader thread polls the serial port and enqueues hex strings into lbms.globals.message_queue.
  • All decoding and UI updates happen on the tkinter thread via after() timers (75 ms main tick, 5 ms simulate, 1000 ms summary, 250/500 ms per tile) — the same architecture as the original WinForms event/timer design.
  • Unrecoverable decode errors are appended to err.log and the app exits (matching the VB End behavior).

Differences from the VB.NET original

  • GUI: WinForms → tkinter; serial: System.IO.Ports → pyserial.
  • ./items/*.cfg files are plain JSON instead of base64-wrapped BinaryFormatter blobs, so .NET-era .cfg files are not readable by the Python port (state starts fresh on first run).
  • The VB Form1.vb referenced a Button2 missing from its designer file; the port implements it as the Button2 "save now" button next to Button1.
  • The simulate log format YYYY-MM-DD HH:MM:SS.mmm,<hex> is parsed with datetime.fromisoformat.