feat: expand oracle and topology evidence gates

This commit is contained in:
2026-08-10 18:27:56 -04:00
parent d727375561
commit 3907f50ff1
16 changed files with 17629 additions and 364 deletions

View File

@@ -8,6 +8,20 @@ import FreeCAD as App
def property_metadata(obj):
def json_value(value):
if value is None or isinstance(value, (bool, int, float, str)):
return value
if isinstance(value, (list, tuple)):
return [json_value(item) for item in value]
if isinstance(value, dict):
return {str(key): json_value(item) for key, item in value.items()}
# FreeCAD quantities and placements keep a stable textual form while
# the report remains valid JSON for every registered property type.
try:
return str(value)
except Exception:
return "<unserializable>"
properties = []
for name in obj.PropertiesList:
try:
@@ -22,23 +36,61 @@ def property_metadata(obj):
status = list(obj.getPropertyStatus(name))
except Exception:
status = []
properties.append({"name": name, "typeId": type_id, "group": group, "status": status})
try:
default = json_value(getattr(obj, name))
except Exception:
default = "<unreadable>"
properties.append({"name": name, "typeId": type_id, "group": group, "status": status, "default": default})
return properties
def probe_object(document, type_id):
object_name = "Reference" + type_id.replace(":", "_")
try:
obj = document.addObject(type_id, "Reference" + type_id.replace(":", "_"))
obj = document.addObject(type_id, object_name)
except Exception as error:
return {"typeId": type_id, "available": False, "error": str(error)}
return {"typeId": type_id, "available": False, "probeStatus": "unavailable", "error": str(error)}
return {
"typeId": type_id,
"available": True,
"probeStatus": "available",
"name": obj.Name,
"runtimeTypeId": obj.TypeId,
"properties": property_metadata(obj),
}
def probe_registered_objects(document):
try:
registered = sorted(set(str(type_id) for type_id in document.supportedTypes()))
except Exception as error:
return {"available": False, "types": [], "error": str(error)}
records = []
for index, type_id in enumerate(registered):
record = probe_object(document, type_id)
record["candidateIndex"] = index
record["candidateSource"] = "Document.supportedTypes"
records.append(record)
# A few GUI-only TechDraw providers dereference their view during
# removeObject(). Keep those instances alive until FreeCAD exits so a
# probe cannot turn a valid metadata observation into a process crash.
if not bool(getattr(App, "GuiUp", False)) and record.get("available") and record.get("name"):
try:
document.removeObject(record["name"])
except Exception as error:
record["cleanupError"] = str(error)
return {
"available": True,
"candidateSource": "Document.supportedTypes",
"candidateCount": len(records),
"availableCount": sum(1 for record in records if record.get("available")),
"unavailableCount": sum(1 for record in records if not record.get("available")),
"types": records,
}
ALL_REFERENCE_MODULES = [
"AddonManager", "Assembly", "BIM", "CAM", "Cloud", "Draft", "Fem", "Help",
"Idf", "Import", "Inspection", "JtReader", "Material", "Measure", "Mesh",
@@ -180,6 +232,9 @@ def probe_gui_commands(document, selected_object):
document = App.newDocument("ReferenceProbe")
box_object = document.addObject("Part::Box", "CommandProbeBox")
modules = [probe_module(name) for name in ALL_REFERENCE_MODULES]
gui_commands = probe_gui_commands(document, box_object)
runtime_objects = probe_registered_objects(document)
version = App.Version()
result = {
"schemaVersion": 1,
@@ -191,12 +246,13 @@ result = {
"guiUp": bool(getattr(App, "GuiUp", False)),
"buildPurpose": "desktop-reference-oracle" if bool(getattr(App, "GuiUp", False)) else "headless-reference-oracle",
"moduleCount": len(ALL_REFERENCE_MODULES),
"modules": [probe_module(name) for name in ALL_REFERENCE_MODULES],
"modules": modules,
"objects": [probe_object(document, type_id) for type_id in [
"Part::Box", "Part::Cylinder", "Part::Sphere", "Part::Cone",
"Part::Feature", "PartDesign::Feature", "Sketcher::SketchObject",
]],
"guiCommands": probe_gui_commands(document, box_object),
"runtimeObjects": runtime_objects,
"guiCommands": gui_commands,
}
result["moduleStatusSummary"] = {
status: sum(1 for module in result["modules"] if module["runtimeStatus"] == status)