pytest 31/31. In-gateway end-to-end: 559 activations correlated (uuid mode), 3 chatterers, 34 fleeting, 1 flood, health F(22) on the deliberately-bad sim plant. Gate G1 green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
233 lines
8.7 KiB
Python
233 lines
8.7 KiB
Python
# 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 = "P1"
|
|
|
|
|
|
def _p1():
|
|
"""Exec PrimeBAT's real PrimeControls modules (calc/fmt/alarms) in this scope
|
|
and run getDashboardBundle against the live journal - end-to-end data-layer
|
|
smoke with zero dev footprint inside PrimeBAT."""
|
|
import imp
|
|
base = "/usr/local/bin/ignition/data/projects/PrimeBAT/ignition/script-python/PrimeControls/"
|
|
pkg = imp.new_module("PrimeControls")
|
|
pkg.__path__ = [base]
|
|
sys_modules = __import__("sys").modules
|
|
sys_modules["PrimeControls"] = pkg
|
|
for name in ("calc", "fmt", "alarms"):
|
|
m = imp.new_module("PrimeControls." + name)
|
|
m.__dict__["system"] = system
|
|
code = open(base + name + "/code.py").read()
|
|
compiled = compile(code, name, "exec")
|
|
# py2 qualified-exec statement form: required because _p1 contains
|
|
# closures, which forbid the unqualified exec(...) call form in Jython.
|
|
exec compiled in m.__dict__
|
|
sys_modules["PrimeControls." + name] = m
|
|
setattr(pkg, name, m)
|
|
al = pkg.alarms
|
|
end = system.date.toMillis(system.date.now())
|
|
start = end - 4 * 3600 * 1000
|
|
b = al.getDashboardBundle(start, end, None)
|
|
page = al.getJournalPage(start, end, None, 0, 5)
|
|
shift = al.getShiftReportText()
|
|
return {
|
|
"meta": {"event_count": b["meta"]["event_count"],
|
|
"activation_count": b["meta"]["activation_count"],
|
|
"areas": b["meta"]["areas"],
|
|
"correlation_mode": b["meta"]["correlation_mode"],
|
|
"standing_mode": b["meta"]["standing_mode"],
|
|
"error": b["meta"]["error"],
|
|
"dropped": b["meta"]["dropped_rows"]},
|
|
"health": {"grade": b["health"]["grade"], "score": b["health"]["score"],
|
|
"subs": [(s["key"], s["score"]) for s in b["health"]["subs"]]},
|
|
"kpis": dict((k, b["kpis"][k]["value"]) for k in b["kpis"]),
|
|
"counts": {"chattering": len(b["chattering"]),
|
|
"standing": b["standing"]["count"],
|
|
"fleeting": b["fleeting"]["total"],
|
|
"floods": len(b["floods"]["episodes"]),
|
|
"pareto_rows": len(b["pareto"]["rows"]),
|
|
"bins": len(b["rate"]["bins"]),
|
|
"active_now": len(b["active_now"])},
|
|
"insights": [i["text"] for i in b["insights"]],
|
|
"journal_page": {"total": page["total"], "rows": len(page["rows"]),
|
|
"error": page["error"],
|
|
"first": page["rows"][0] if page["rows"] else None},
|
|
"shift_label": shift["label"],
|
|
"shift_text_head": shift["text"][:400],
|
|
}
|
|
|
|
|
|
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(_p1)
|
|
_write(result)
|
|
system.util.getLogger("probe").info("probe %s written to %s" % (PROBE_NAME, OUT_DIR))
|