# -*- coding: utf-8 -*- # LPC 0140650 web UI,运行于 Ubuntu 192.168.58.7:8090 # LPC 自治架构:Modbus 轮询+闭环在 LPC Lua(触发器 #14/#15),本程序只做监控/记录/HTTP 转发 import json import re import threading import time import urllib.request from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from urllib.parse import urlparse, parse_qs LPC = "http://192.168.58.109" PORT = 8090 HIST_FILE = "/home/tonycao/rs485-lpc/history.jsonl" SAMPLE_INTERVAL = 30 # 秒 HIST_KEEP = 48 * 3600 # 48 小时 TRIG = {"BRT": 2, "LUX": 8, "LXT": 9, "LX2": 10, "FX": 11, "SC": 12, "FAD": 18, "PER": 19, "DBD": 20} TRIG_LINK_ON = 16 TRIG_LINK_OFF = 17 _hist = [] # [[t_ms, brt, out, lux], ...] lux 可为 None _hist_lock = threading.Lock() _samples_since_compact = 0 def lpc_json(path): with urllib.request.urlopen(LPC + path, timeout=5) as r: return json.loads(r.read().decode("utf-8", "replace")) def lpc_lua(names): d = lpc_json("/api/lua?variables=" + ",".join(names)) return d def lua_num(v): try: return int(round(float(v))) except Exception: return None def read_lux(): try: return lua_num(lpc_lua(["lux_value"]).get("lux_value")) except Exception: return None def link_on(): try: return bool(lpc_lua(["lux_link_on"]).get("lux_link_on")) except Exception: return False def fire_trigger(num, var=None): url = LPC + "/api/trigger?num=%d" % num if var is not None: url += "&var=%d" % var req = urllib.request.Request(url, data=b"", method="POST") with urllib.request.urlopen(req, timeout=5) as r: r.read() def sample_once(): t = int(time.time() * 1000) brt = None out = None try: g = lpc_json("/api/group") brt = g["groups"][0]["level"] except Exception: pass try: o = lpc_json("/api/output?universe=dmx:1") vals = [] for c in o["channels"][:32]: m = re.search(r"\d+", str(c)) vals.append(int(m.group(0)) if m else 0) out = round(sum(vals) * 100.0 / (255 * len(vals)), 1) except Exception: pass lux = read_lux() row = [t, brt, out, lux] with _hist_lock: _hist.append(row) cutoff = t - HIST_KEEP * 1000 while _hist and _hist[0][0] < cutoff: _hist.pop(0) try: with open(HIST_FILE, "a") as f: f.write(json.dumps(row, separators=(",", ":")) + "\n") except Exception: pass global _samples_since_compact _samples_since_compact += 1 if _samples_since_compact >= 120: # 每小时压实一次文件 _samples_since_compact = 0 compact_history() def compact_history(): with _hist_lock: data = list(_hist) try: with open(HIST_FILE, "w") as f: for r in data: f.write(json.dumps(r, separators=(",", ":")) + "\n") except Exception: pass def load_history(): try: cutoff = int(time.time() * 1000) - HIST_KEEP * 1000 with open(HIST_FILE) as f: for line in f: line = line.strip() if not line: continue try: r = json.loads(line) except Exception: continue if isinstance(r, list) and len(r) == 4 and r[0] >= cutoff: _hist.append(r) except Exception: pass def sampler_loop(): while True: try: sample_once() except Exception: pass time.sleep(SAMPLE_INTERVAL) PAGE = r"""