Repository navigation
Add a Smart Building sensor-deployment example language - #47
Closed
haphantran wants to merge 4 commits into
Closed
haphantran wants to merge 4 commits into
haphantran wants to merge 4 commits into
Conversation
Spatial geometry now honours rotationZ. A shared oriented-box module (shared/spatial-geometry.ts, mirrored in frontend/src/services/spatial) builds each placed element's rotated footprint extruded over its vertical interval. The JS/OCL spatial helpers keep their names and signatures but use separating-axis overlap tests and exact convex-polygon clearance, so rotated elements no longer get axis-aligned answers. Metamodels gain spatialRules: noOverlap, minClearance, maxDistance, within, and adjacentTo over placed instances of a source and a target metaclass (subclasses included), in plan or volume mode. A pure evaluator (shared/spatial-rules.ts plus the frontend mirror) runs in browser validation, which marks both elements of an offending pair in 2D and 3D, and in the backend validation engine used by the headless pipeline. A parity test keeps the two copies identical and checks that they report the same issues. Rules are persisted in a new metamodels.spatialRules column, validated on create and update, carried through checkpoints and metamodel evolution (a rule on a removed class blocks the evolution), removed with a deleted class, and exported to Ecore as the EPackage annotation spatialdsl/spatialRules. The metamodel editor gains a Spatial Rules section with an add/edit dialog. The Smart Warehouse example declares a FixedEquipment supertype and four rules that its bundled model satisfies, and its unbounded controller references now use '*'.
A third example shows the spatial layer outside robotics. The Smart Building metamodel has a Building with Room and Corridor zones and SmokeDetector, MotionSensor, Camera, and Gateway devices that reference their zone and gateway. Its spatial rules keep rooms from overlapping, keep every device inside a zone (smoke detectors 300 mm from the boundary), and keep every device within 15 m of a gateway in 3D. The bundled model (six rooms, a corridor, 25 devices) satisfies them. The Building Operations viewpoint has a floor-plan diagram with radio links to the gateways, a 3D diagram in which zones are grounded floor slabs and devices have adjustable mounting heights per class, and a device table. The Smart Building Commissioning generator writes device-commissioning.json with each device's ID, type, room, gateway, position in metres, and yaw. The example is bundled with the others and can be imported into a new project with an "Include Smart Building starter" switch. The headless generation engine now exposes resolvedReferences target summaries (id, name, class) like the browser generator, so templates that name referenced elements render the same in pipeline runs.
Every session is now project scoped and new projects start empty, so the bundled UML Activity example could no longer be opened. The project dialog gains an "Include UML Activity starter" switch that imports its metamodel, model, viewpoint, and both saved views, using the same starter import as the other examples.
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Several persistence paths accept invalid or dangling rules, and the commissioning template can generate malformed JSON.
5 open findings
What changed in this PR
Adds declarative spatial validation and a Smart Building example language, integrating both across browser, backend, persistence, generation, interoperability, and documentation.
Changes:
- Adds rotation-aware spatial geometry and five declarative spatial-rule kinds.
- Adds the Smart Building starter, views, commissioning generator, and project options.
- Extends backend persistence, pipelines, Ecore/Sirius export, checkpoints, and tests.
| File | Description |
|---|---|
shared/types/index.ts |
Adds shared spatial-rule and issue types. |
shared/spatial-rules.ts |
Implements shared rule validation and evaluation. |
shared/spatial-geometry.ts |
Implements oriented spatial geometry. |
README.md |
Advertises spatial rules. |
frontend/src/services/spatial/spatial-rules.ts |
Mirrors rule evaluation in the frontend. |
frontend/src/services/spatial/spatial-geometry.ts |
Mirrors geometry in the frontend. |
frontend/src/services/spatial/index.ts |
Exports spatial modules. |
frontend/src/services/smart-warehouse-project-import.service.ts |
Generalizes starter imports. |
frontend/src/services/model/model-validation.service.ts |
Runs spatial rules during validation. |
frontend/src/services/metamodel/metamodel.service.ts |
Validates imported rule definitions. |
frontend/src/services/metamodel/metamodel-api-sync.service.ts |
Synchronizes rules to the API. |
frontend/src/services/metamodel/metaclass-crud.service.ts |
Removes rules referencing deleted classes. |
frontend/src/services/metamodel/exampleData.service.ts |
Bundles Smart Building and Activity starters. |
frontend/src/services/metamodel/ecore.service.ts |
Imports and exports rule annotations. |
frontend/src/services/diagram/view-validation.service.ts |
Marks all elements involved in issues. |
frontend/src/services/constraint/spatial.helpers.ts |
Makes constraint geometry rotation-aware. |
frontend/src/models/types.ts |
Adds frontend spatial-rule types. |
frontend/src/examples/data/smart-warehouse-metamodel.json |
Adds warehouse spatial rules and superclass. |
frontend/src/examples/data/smart-building-views.json |
Defines Smart Building saved views. |
frontend/src/examples/data/smart-building-viewpoints.json |
Defines building representations and notation. |
frontend/src/examples/data/smart-building-project.json |
Defines the commissioning generator. |
frontend/src/examples/data/smart-building-metamodel.json |
Defines the Smart Building language. |
frontend/src/components/projects/ProjectPickerPage.tsx |
Adds starter selection switches. |
frontend/src/components/metamodel/SpatialRulesEditor.tsx |
Adds spatial-rule authoring UI. |
frontend/src/components/metamodel/components/sidebar/MetamodelSidebar.tsx |
Integrates the rule editor. |
frontend/src/__tests__/services/view-validation.test.ts |
Tests pair-issue visualization. |
frontend/src/__tests__/services/spatial-rules.test.ts |
Tests rule semantics and validation. |
frontend/src/__tests__/services/spatial-helpers.test.ts |
Tests rotation-aware helpers. |
frontend/src/__tests__/services/smart-warehouse-project-import.service.test.ts |
Tests starter imports. |
frontend/src/__tests__/services/smart-building-example.test.ts |
Tests the Smart Building example. |
frontend/src/__tests__/services/ecore-xmi-interop.test.ts |
Tests Ecore rule annotations. |
frontend/src/__tests__/services/codegen-example-retarget.test.ts |
Tests generator retargeting. |
frontend/src/__tests__/components/SpatialRulesEditor.test.tsx |
Tests rule editor behavior. |
examples/codegen-projects/smart-building-commissioning-project.json |
Ships the commissioning generator. |
docs/user-guide/viewpoints.md |
Documents building viewpoints. |
docs/user-guide/spatial-rules.md |
Adds the spatial-rules guide. |
docs/user-guide/metamodels.md |
Documents metamodel rule support. |
docs/user-guide/code-generation.md |
Documents resolved references. |
docs/reference/data-model.md |
Documents persisted rules. |
docs/reference/api.md |
Documents rule API fields. |
docs/README.md |
Links the new guide. |
backend/src/services/sirius-interop.service.ts |
Exports rules in Sirius Ecore. |
backend/src/services/pipeline/model-validation.engine.ts |
Runs rules in pipelines. |
backend/src/services/pipeline/headless-pipeline.service.ts |
Maps persisted rules. |
backend/src/services/pipeline/code-generation.engine.ts |
Adds resolved reference summaries. |
backend/src/services/metamodel.service.ts |
Persists and validates rules. |
backend/src/services/lifecycle/project-checkpoint.service.ts |
Checkpoints spatial rules. |
backend/src/services/lifecycle/metamodel-evolution.service.ts |
Evolves and fingerprints rules. |
backend/src/__tests__/services/spatial-rules-parity.test.ts |
Tests frontend/backend parity. |
backend/src/__tests__/services/sirius-interop.service.test.ts |
Tests Sirius rule export. |
backend/src/__tests__/services/sharing.service.test.ts |
Updates metamodel fixtures. |
backend/src/__tests__/services/project-checkpoint.service.test.ts |
Tests rule checkpoint restoration. |
backend/src/__tests__/services/pipeline-engines.test.ts |
Tests pipeline validation and generation. |
backend/src/__tests__/services/metamodel.service.test.ts |
Tests rule persistence and validation. |
backend/src/__tests__/services/metamodel-evolution.service.test.ts |
Tests rule-aware evolution. |
backend/prisma/schema.prisma |
Adds the spatial-rules column. |
backend/prisma/migrations/20261011000000_add_metamodel_spatial_rules/migration.sql |
Migrates the database schema. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| "id": "b5500000-0000-4000-8000-000000000101", | ||
| "name": "Device Commissioning List", | ||
| "language": "json", | ||
| "templateContent": "{\n \"building\": \"{{#each elementsByClassName.Building}}{{name}}{{/each}}\",\n \"units\": {\"position\": \"m\", \"yaw\": \"deg\"},\n \"devices\": [\n{{resetListComma \"devices\"}}{{#each elementsByClassName.SmokeDetector}}{{commaIfNeeded \"devices\"}}\n {\"deviceId\": \"{{deviceId}}\", \"type\": \"SmokeDetector\", \"room\": \"{{resolvedReferences.installedIn.name}}\", \"gateway\": \"{{resolvedReferences.gateway.name}}\", \"x\": {{meters X}}, \"y\": {{meters Y}}, \"z\": {{meters Z}}, \"yaw\": {{#unless RZ}}0{{else}}{{RZ}}{{/unless}} }{{/each}}\n{{#each elementsByClassName.MotionSensor}}{{commaIfNeeded \"devices\"}}\n {\"deviceId\": \"{{deviceId}}\", \"type\": \"MotionSensor\", \"room\": \"{{resolvedReferences.installedIn.name}}\", \"gateway\": \"{{resolvedReferences.gateway.name}}\", \"x\": {{meters X}}, \"y\": {{meters Y}}, \"z\": {{meters Z}}, \"yaw\": {{#unless RZ}}0{{else}}{{RZ}}{{/unless}} }{{/each}}\n{{#each elementsByClassName.Camera}}{{commaIfNeeded \"devices\"}}\n {\"deviceId\": \"{{deviceId}}\", \"type\": \"Camera\", \"room\": \"{{resolvedReferences.installedIn.name}}\", \"gateway\": \"{{resolvedReferences.gateway.name}}\", \"x\": {{meters X}}, \"y\": {{meters Y}}, \"z\": {{meters Z}}, \"yaw\": {{#unless RZ}}0{{else}}{{RZ}}{{/unless}} }{{/each}}\n{{#each elementsByClassName.Gateway}}{{commaIfNeeded \"devices\"}}\n {\"deviceId\": \"{{deviceId}}\", \"type\": \"Gateway\", \"room\": \"{{resolvedReferences.installedIn.name}}\", \"gateway\": \"{{resolvedReferences.gateway.name}}\", \"x\": {{meters X}}, \"y\": {{meters Y}}, \"z\": {{meters Z}}, \"yaw\": {{#unless RZ}}0{{else}}{{RZ}}{{/unless}} }{{/each}}\n ]\n}\n", |
| "id": "b5500000-0000-4000-8000-000000000101", | ||
| "name": "Device Commissioning List", | ||
| "language": "json", | ||
| "templateContent": "{\n \"building\": \"{{#each elementsByClassName.Building}}{{name}}{{/each}}\",\n \"units\": {\"position\": \"m\", \"yaw\": \"deg\"},\n \"devices\": [\n{{resetListComma \"devices\"}}{{#each elementsByClassName.SmokeDetector}}{{commaIfNeeded \"devices\"}}\n {\"deviceId\": \"{{deviceId}}\", \"type\": \"SmokeDetector\", \"room\": \"{{resolvedReferences.installedIn.name}}\", \"gateway\": \"{{resolvedReferences.gateway.name}}\", \"x\": {{meters X}}, \"y\": {{meters Y}}, \"z\": {{meters Z}}, \"yaw\": {{#unless RZ}}0{{else}}{{RZ}}{{/unless}} }{{/each}}\n{{#each elementsByClassName.MotionSensor}}{{commaIfNeeded \"devices\"}}\n {\"deviceId\": \"{{deviceId}}\", \"type\": \"MotionSensor\", \"room\": \"{{resolvedReferences.installedIn.name}}\", \"gateway\": \"{{resolvedReferences.gateway.name}}\", \"x\": {{meters X}}, \"y\": {{meters Y}}, \"z\": {{meters Z}}, \"yaw\": {{#unless RZ}}0{{else}}{{RZ}}{{/unless}} }{{/each}}\n{{#each elementsByClassName.Camera}}{{commaIfNeeded \"devices\"}}\n {\"deviceId\": \"{{deviceId}}\", \"type\": \"Camera\", \"room\": \"{{resolvedReferences.installedIn.name}}\", \"gateway\": \"{{resolvedReferences.gateway.name}}\", \"x\": {{meters X}}, \"y\": {{meters Y}}, \"z\": {{meters Z}}, \"yaw\": {{#unless RZ}}0{{else}}{{RZ}}{{/unless}} }{{/each}}\n{{#each elementsByClassName.Gateway}}{{commaIfNeeded \"devices\"}}\n {\"deviceId\": \"{{deviceId}}\", \"type\": \"Gateway\", \"room\": \"{{resolvedReferences.installedIn.name}}\", \"gateway\": \"{{resolvedReferences.gateway.name}}\", \"x\": {{meters X}}, \"y\": {{meters Y}}, \"z\": {{meters Z}}, \"yaw\": {{#unless RZ}}0{{else}}{{RZ}}{{/unless}} }{{/each}}\n ]\n}\n", |
Comment on lines
+378
to
+385
| asArray<SpatialRule>(next.spatialRules).forEach(rule => { | ||
| [rule?.sourceClassId, rule?.targetClassId].forEach(classId => { | ||
| if (typeof classId !== 'string' || nextClassNames.has(classId)) return; | ||
| const removedName = currentClassNames.get(classId); | ||
| blockers.push(removedName | ||
| ? `Spatial rule ${rule.name} references removed class ${removedName}; update or delete the rule before applying this evolution` | ||
| : `Spatial rule ${rule?.name || rule?.id} references unknown class ${classId}`); | ||
| }); |
Comment on lines
+216
to
+221
| if (data.spatialRules !== undefined) { | ||
| const classes = data.classes !== undefined | ||
| ? data.classes | ||
| : (await prisma.metamodel.findUnique({ where: { id }, select: { classes: true } }))?.classes; | ||
| this.assertValidSpatialRules(data.spatialRules, classes); | ||
| } |
Comment on lines
+154
to
+165
| rules.push({ | ||
| id: uuidv4(), | ||
| name, | ||
| kind: parsed.kind, | ||
| sourceClassId, | ||
| targetClassId, | ||
| ...(parsed.distanceMm !== undefined && { distanceMm: parsed.distanceMm }), | ||
| ...(parsed.mode !== undefined && { mode: parsed.mode }), | ||
| severity: parsed.severity || 'error', | ||
| ...(parsed.message !== undefined && { message: parsed.message }), | ||
| ...(parsed.enabled !== undefined && { enabled: parsed.enabled }), | ||
| }); |
Collaborator
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Summary
Depends on #45 (spatial rules); this branch contains its commit. Review the commits after "Add declarative spatial rules and yaw-aware spatial geometry". The floor plan looks best with #46 merged too (one-line device names and no link labels).
A third example language shows the spatial layer outside robotics and exercises elevation policies and containment rules.
Smart Building:BuildingcontainsRoomandCorridorzones (abstractZone) andSmokeDetector,MotionSensor,Camera,Gatewaydevices (abstractDevice, withdeviceId, coverage and radio attributes). Devices reference the zone they are installed in and their gateway.Building Operations:Floor Plandiagram (translucent zones, devices, dashed radio links to gateways),Device Placement 3Ddiagram (zones grounded floor slabs, devices adjustable: smoke 2,700 mm, motion 2,200, camera 2,500, gateway 2,400; range 0 to 4,000 mm), and aDevice Inventorytable.Smart Building Commissioning: writesdevice-commissioning.json(device ID, type, room, gateway, x/y/z in metres, yaw). Also shipped inexamples/codegen-projects/.resolvedReferencestarget summaries (id,name,className) like the browser generator, so templates naming referenced elements render the same in pipeline runs (this also affects the existing Visual Components templates that useresolvedReferences).Verification
main).