Phase 0: wipe PrimeBAT template, SimHarness (sim+probe), lint + schema harvest tools
Gate G0 green: scan clean, lint 0 failures, clients 200, probe P0 facts captured, simulator journaling. Probe locked: uuid row grouping, compound AlarmState strings, java-exception bare-except gotcha, millis dates OK, ackUserName field. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
# probe - gateway-side API ground truth (SimHarness, dev-only, Jython 2.7).
|
||||
#
|
||||
# Runs inside the gateway via the ProbeTick timer (enable it, scan, wait <=20s,
|
||||
# read the output, disable it). Writes JSON to the bind-mounted repo path:
|
||||
# container: /usr/local/bin/ignition/data/projects/.probe/out.json
|
||||
# host repo: ignition/gateway/projects/.probe/out.json
|
||||
#
|
||||
# Edit PROBE_NAME / add sections as needed; every section is try-wrapped so a
|
||||
# single API surprise never kills the whole probe.
|
||||
|
||||
import json
|
||||
|
||||
OUT_DIR = "/usr/local/bin/ignition/data/projects/.probe"
|
||||
PROBE_NAME = "P0"
|
||||
|
||||
|
||||
def _safe(fn):
|
||||
# Bare except is deliberate: in Jython, JAVA exceptions (e.g. bad kwargs
|
||||
# into system.* calls) do NOT subclass Python's Exception and would fly
|
||||
# straight past 'except Exception'.
|
||||
try:
|
||||
return {"ok": True, "value": fn()}
|
||||
except:
|
||||
import sys
|
||||
et, ev = sys.exc_info()[0], sys.exc_info()[1]
|
||||
return {"ok": False, "error": "%s: %s" % (getattr(et, "__name__", et), ev)}
|
||||
|
||||
|
||||
def _write(result):
|
||||
from java.io import File
|
||||
File(OUT_DIR).mkdirs()
|
||||
f = open(OUT_DIR + "/out.json", "w")
|
||||
try:
|
||||
f.write(json.dumps(result, indent=1, default=lambda o: repr(o)))
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
|
||||
def _prop(evt, names):
|
||||
out = {}
|
||||
for n in names:
|
||||
try:
|
||||
v = evt.get(n)
|
||||
out[n] = {"cls": v is not None and v.__class__.__name__ or None,
|
||||
"repr": repr(v)[:200]}
|
||||
except Exception, e:
|
||||
out[n] = {"error": "%s" % e}
|
||||
return out
|
||||
|
||||
|
||||
def _entry_facts(evt):
|
||||
d = {}
|
||||
d["cls"] = evt.__class__.__name__
|
||||
try:
|
||||
d["id"] = str(evt.getId())
|
||||
except Exception, e:
|
||||
d["id_error"] = "%s" % e
|
||||
try:
|
||||
s = evt.getState()
|
||||
d["state_str"] = str(s)
|
||||
d["state_cls"] = s is not None and s.__class__.__name__ or None
|
||||
except Exception, e:
|
||||
d["state_error"] = "%s" % e
|
||||
try:
|
||||
d["source"] = str(evt.getSource())
|
||||
except Exception, e:
|
||||
d["source_error"] = "%s" % e
|
||||
try:
|
||||
d["displayPath"] = repr(evt.getDisplayPath())
|
||||
except Exception, e:
|
||||
d["displayPath_error"] = "%s" % e
|
||||
try:
|
||||
p = evt.getPriority()
|
||||
d["priority_cls"] = p is not None and p.__class__.__name__ or None
|
||||
d["priority_str"] = str(p)
|
||||
try:
|
||||
d["priority_int"] = int(p.ordinal())
|
||||
except Exception:
|
||||
try:
|
||||
d["priority_int"] = int(p)
|
||||
except Exception:
|
||||
d["priority_int"] = None
|
||||
except Exception, e:
|
||||
d["priority_error"] = "%s" % e
|
||||
d["props"] = _prop(evt, ["eventTime", "eventType", "eventtype", "ackUser",
|
||||
"ackUserName", "isSystemEvent", "systemEvent",
|
||||
"eventValue", "name", "label"])
|
||||
for getter in ("getActiveData", "getClearedData", "getAckData"):
|
||||
try:
|
||||
v = getattr(evt, getter)()
|
||||
d[getter] = v is not None and repr(v)[:200] or None
|
||||
except Exception, e:
|
||||
d[getter] = "ERR %s" % e
|
||||
try:
|
||||
from java.util import Date
|
||||
t = evt.get("eventTime")
|
||||
if t is not None and isinstance(t, Date):
|
||||
d["eventTime_ms"] = t.getTime()
|
||||
except Exception:
|
||||
pass
|
||||
return d
|
||||
|
||||
|
||||
def _p0():
|
||||
result = {}
|
||||
end = system.date.now()
|
||||
start = system.date.addHours(end, -8)
|
||||
|
||||
def q_basic():
|
||||
r = system.alarm.queryJournal(startDate=start, endDate=end)
|
||||
entries = [e for e in r]
|
||||
by_id = {}
|
||||
for e in entries:
|
||||
try:
|
||||
k = str(e.getId())
|
||||
except Exception:
|
||||
k = "?"
|
||||
by_id.setdefault(k, []).append(e)
|
||||
multi = {}
|
||||
n = 0
|
||||
for k, evts in by_id.items():
|
||||
if len(evts) > 1 and n < 5:
|
||||
states = []
|
||||
for e in evts:
|
||||
try:
|
||||
states.append(str(e.getState()))
|
||||
except Exception:
|
||||
states.append("?")
|
||||
multi[k] = states
|
||||
n += 1
|
||||
return {
|
||||
"result_cls": r.__class__.__name__,
|
||||
"len_works": _safe(lambda: len(r)),
|
||||
"dataset_attr": hasattr(r, "getDataset"),
|
||||
"entry_count": len(entries),
|
||||
"distinct_ids": len(by_id),
|
||||
"multi_state_id_samples": multi,
|
||||
"first_entries": [_entry_facts(e) for e in entries[:6]],
|
||||
"system_rows": [_entry_facts(e) for e in entries
|
||||
if str(_safe(lambda: str(e.getSource()))["value"]).startswith("evt:")][:3],
|
||||
}
|
||||
result["basic"] = _safe(q_basic)
|
||||
|
||||
result["no_kwargs_at_all"] = _safe(
|
||||
lambda: len([e for e in system.alarm.queryJournal(
|
||||
startDate=start, endDate=end)]))
|
||||
result["with_journal_name"] = _safe(
|
||||
lambda: len([e for e in system.alarm.queryJournal(
|
||||
journalName="Journal", startDate=start, endDate=end)]))
|
||||
result["bad_journal_name"] = _safe(
|
||||
lambda: len([e for e in system.alarm.queryJournal(
|
||||
journalName="NoSuchJournal", startDate=start, endDate=end)]))
|
||||
result["long_millis_dates"] = _safe(
|
||||
lambda: len([e for e in system.alarm.queryJournal(
|
||||
startDate=system.date.toMillis(start),
|
||||
endDate=system.date.toMillis(end))]))
|
||||
result["include_system_false"] = _safe(
|
||||
lambda: len([e for e in system.alarm.queryJournal(
|
||||
startDate=start, endDate=end, includeSystemEvents=False)]))
|
||||
result["include_system_false_alt"] = _safe(
|
||||
lambda: len([e for e in system.alarm.queryJournal(
|
||||
startDate=start, endDate=end, isSystem=False)]))
|
||||
|
||||
def q_status():
|
||||
r = system.alarm.queryStatus(state=["ActiveUnacked", "ActiveAcked"])
|
||||
entries = [e for e in r]
|
||||
return {"count": len(entries),
|
||||
"first_entries": [_entry_facts(e) for e in entries[:5]]}
|
||||
result["status"] = _safe(q_status)
|
||||
result["shelved"] = _safe(lambda: repr(system.alarm.getShelvedPaths()))
|
||||
return result
|
||||
|
||||
|
||||
def run():
|
||||
result = {"probe": PROBE_NAME,
|
||||
"ranAt": str(system.date.now())}
|
||||
result["payload"] = _safe(_p0)
|
||||
_write(result)
|
||||
system.util.getLogger("probe").info("probe %s written to %s" % (PROBE_NAME, OUT_DIR))
|
||||
Reference in New Issue
Block a user