mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-04 18:04:14 +02:00
Merge pull request #1271 from ispyisail/feature/qet-mcp-wires-per-terminal
Add qet_check rules for crowded terminals and folio reports
This commit is contained in:
@@ -39,7 +39,7 @@ here read the model.
|
||||
| `qet_element_build` | **author a `.elmt`** — draw a new symbol, with terminals to wire it by |
|
||||
| `qet_project_new` | **start from nothing** — an empty project with a title and folios |
|
||||
| `qet_element_search` | **find a symbol** in a collection by name (any language), type or terminal count |
|
||||
| `qet_check` | **design-rule checks** — duplicate labels, unlabelled masters, unnumbered conductors, empty folios |
|
||||
| `qet_check` | **design-rule checks** — duplicate labels, unlabelled masters, unnumbered conductors, empty folios, terminals with more than four wires, folio reports with several wires |
|
||||
| `qet_layout_check` | **does the drawing read well?** — a 0–100 score; wires that jog because two symbols are a few pixels out of line, symbols off the grid, wires through symbols, overlaps, crossings; and the moves that fix them, ready for `qet_edit` |
|
||||
| `qet_query` | **ask the project database** — read-only SQL over the views and tables |
|
||||
| `qet_about` | **start here** — where QElectroTech keeps things, what is switched on, the stored scripts, the calls a script can make (from `qet-assistant.json`) |
|
||||
|
||||
@@ -2535,6 +2535,45 @@ CHECKS = {
|
||||
"WHERE NOT EXISTS (SELECT 1 FROM element e WHERE e.diagram_uuid = d.uuid) "
|
||||
"ORDER BY p.pos",
|
||||
},
|
||||
"crowded_terminals": {
|
||||
"severity": "info",
|
||||
"note": "Terminals with more than four wires: two double ferrules, one "
|
||||
"either side of the screw, is already a lot for a real terminal "
|
||||
"(discussion #1158). Cable, busbar and single-line symbols carry "
|
||||
"more on purpose; element_type and label tell them apart.",
|
||||
"sql": "SELECT d.pos AS folio, e.pos AS diagram_position, "
|
||||
"COALESCE(ei.label,'') AS label, e.type AS element_type, "
|
||||
"COALESCE(t.name,'') AS terminal, w.n AS wires FROM ("
|
||||
"SELECT tu, eu, COUNT(*) AS n FROM ("
|
||||
"SELECT terminal1_uuid AS tu, terminal1_element_uuid AS eu FROM conductor "
|
||||
"UNION ALL SELECT terminal2_uuid, terminal2_element_uuid FROM conductor"
|
||||
") GROUP BY tu, eu HAVING n > 4) AS w "
|
||||
"JOIN element e ON e.uuid = w.eu "
|
||||
"LEFT JOIN diagram d ON d.uuid = e.diagram_uuid "
|
||||
"LEFT JOIN element_info ei ON ei.element_uuid = e.uuid "
|
||||
"LEFT JOIN terminal t ON t.uuid = w.tu AND t.element_uuid = w.eu "
|
||||
"WHERE e.type NOT IN ('next_report', 'previous_report') "
|
||||
"ORDER BY w.n DESC, folio, diagram_position",
|
||||
},
|
||||
"reports_with_several_wires": {
|
||||
"severity": "info",
|
||||
"note": "Folio report arrows with more than one wire. A report is a "
|
||||
"virtual point: one wire, continued on the other folio, says "
|
||||
"where the wire really runs (discussion #1158). A third of the "
|
||||
"shipped examples' reports have several, so this is a style, "
|
||||
"not an error.",
|
||||
"sql": "SELECT d.pos AS folio, e.pos AS diagram_position, "
|
||||
"COALESCE(ei.label,'') AS label, w.n AS wires FROM ("
|
||||
"SELECT tu, eu, COUNT(*) AS n FROM ("
|
||||
"SELECT terminal1_uuid AS tu, terminal1_element_uuid AS eu FROM conductor "
|
||||
"UNION ALL SELECT terminal2_uuid, terminal2_element_uuid FROM conductor"
|
||||
") GROUP BY tu, eu HAVING n > 1) AS w "
|
||||
"JOIN element e ON e.uuid = w.eu "
|
||||
"LEFT JOIN diagram d ON d.uuid = e.diagram_uuid "
|
||||
"LEFT JOIN element_info ei ON ei.element_uuid = e.uuid "
|
||||
"WHERE e.type IN ('next_report', 'previous_report') "
|
||||
"ORDER BY w.n DESC, folio, diagram_position",
|
||||
},
|
||||
"masters_without_manufacturer_reference": {
|
||||
"severity": "info",
|
||||
"note": "Masters that will show a blank article number in the BOM.",
|
||||
|
||||
@@ -1207,6 +1207,8 @@ class CheckAndContinuityAnswers(unittest.TestCase):
|
||||
{"kind": "check", "name": "unnumbered_conductors", "rows": [{"n": 1}, {"n": 2}], "error": ""},
|
||||
{"kind": "check", "name": "duplicate_simple_labels", "rows": [], "error": ""},
|
||||
{"kind": "check", "name": "empty_folios", "rows": None, "error": "bad SQL"},
|
||||
{"kind": "check", "name": "crowded_terminals", "rows": [], "error": ""},
|
||||
{"kind": "check", "name": "reports_with_several_wires", "rows": [], "error": ""},
|
||||
{"kind": "other", "name": "masters_without_manufacturer_reference", "rows": [1]},
|
||||
]
|
||||
with self.stub(lines):
|
||||
@@ -1214,7 +1216,7 @@ class CheckAndContinuityAnswers(unittest.TestCase):
|
||||
C = m.CHECKS
|
||||
self.assertEqual(r, {
|
||||
"ok": False,
|
||||
"summary": {"errors": 1, "warnings": 1, "info": 1, "passed": 1, "check_failures": 2},
|
||||
"summary": {"errors": 1, "warnings": 1, "info": 1, "passed": 3, "check_failures": 2},
|
||||
"findings": [
|
||||
{"check": "duplicate_master_labels", "severity": "error", "count": 12,
|
||||
"note": C["duplicate_master_labels"]["note"], "rows": rows[:10]},
|
||||
@@ -1222,7 +1224,7 @@ class CheckAndContinuityAnswers(unittest.TestCase):
|
||||
"note": C["unlabelled_masters"]["note"], "rows": [{"x": 1}]},
|
||||
{"check": "unnumbered_conductors", "severity": "info", "count": 2,
|
||||
"note": C["unnumbered_conductors"]["note"], "rows": [{"n": 1}, {"n": 2}]}],
|
||||
"passed": ["duplicate_simple_labels"],
|
||||
"passed": ["duplicate_simple_labels", "crowded_terminals", "reports_with_several_wires"],
|
||||
"check_failures": [
|
||||
{"check": "empty_folios", "error": "bad SQL"},
|
||||
{"check": "masters_without_manufacturer_reference", "error": "no result came back"}]})
|
||||
@@ -5477,6 +5479,24 @@ class Integration(unittest.TestCase):
|
||||
self.assertEqual(found["masters_without_manufacturer_reference"]["count"], 2)
|
||||
self.assertFalse(c["ok"])
|
||||
|
||||
def test_check_finds_a_crowded_terminal(self):
|
||||
"""Five wires on one terminal of a coil: crowded_terminals reports
|
||||
that terminal once, with its count; four would pass."""
|
||||
base = self.sb.new()
|
||||
ids = "habcde"
|
||||
r = self.ok(self.sb.edit(base, [
|
||||
*[{"op": "add_element", "id": x, "folio": 0, "path": COIL,
|
||||
"x": 100 + i * 120, "y": 100 + i * 90} for i, x in enumerate(ids)],
|
||||
*[{"op": "add_conductor", "folio": 0, "from": "$h", "from_terminal": 0,
|
||||
"to": f"${x}", "to_terminal": 0} for x in ids[1:]]]))
|
||||
c = m.tool_check(BINARY, r["output"], checks=["crowded_terminals",
|
||||
"reports_with_several_wires"])
|
||||
found = {f["check"]: f for f in c["findings"]}
|
||||
self.assertEqual(found["crowded_terminals"]["count"], 1, c)
|
||||
self.assertEqual(found["crowded_terminals"]["rows"][0]["wires"], 5)
|
||||
self.assertEqual(found["crowded_terminals"]["rows"][0]["element_type"], "master")
|
||||
self.assertIn("reports_with_several_wires", c["passed"])
|
||||
|
||||
def test_check_passes_a_clean_project(self):
|
||||
base = self.sb.new()
|
||||
r = self.ok(self.sb.edit(base, [
|
||||
|
||||
Reference in New Issue
Block a user