diff --git a/bricksrc/collections.py b/bricksrc/collections.py index 2da6e474..3bf1ac3a 100644 --- a/bricksrc/collections.py +++ b/bricksrc/collections.py @@ -1,4 +1,4 @@ -from .namespaces import TAG, OWL, BRICK +from .namespaces import TAG, REC, BRICK system_subclasses = { "Automatic_Tint_Window_Array": { @@ -62,7 +62,9 @@ }, }, }, - "VRF_System": {"tags": [TAG.Variable, TAG.Refrigerant, TAG.Flow, TAG.System]}, + "VRF_System": { + "tags": [TAG.Variable, TAG.Refrigerant, TAG.Flow, TAG.System] + }, "Refrigeration_System": {"tags": [TAG.Refrigeration, TAG.System]}, "Steam_System": {"tags": [TAG.Steam, TAG.System]}, "Water_System": { @@ -159,6 +161,10 @@ "tags": [TAG.Collection, TAG.Portfolio], "constraints": {BRICK.hasPart: [BRICK.Site]}, }, + "Point_Group": { + "tags": [TAG.Collection, TAG.Point_Group], + "constraints": {BRICK.hasPart: [BRICK.Point, BRICK.Point_Group]}, + }, "System": { "tags": [TAG.Collection, TAG.System], "subclasses": system_subclasses, diff --git a/bricksrc/definitions.csv b/bricksrc/definitions.csv index bef3dba1..0ed996a5 100644 --- a/bricksrc/definitions.csv +++ b/bricksrc/definitions.csv @@ -203,7 +203,7 @@ https://brickschema.org/schema/Brick#Conference_Room,A space dedicated in which https://brickschema.org/schema/Brick#Constant_Air_Volume_Box,A terminal unit for which supply air flow rate is constant and the supply air temperature is varied to meet thermal load,https://en.wikipedia.org/wiki/Constant_air_volume https://brickschema.org/schema/Brick#Contact_Sensor,"Senses or detects contact, such as for determining if a door is closed.", https://brickschema.org/schema/Brick#Control_Room,A space from which operations are managed, -https://brickschema.org/schema/Brick#Controller,, +https://brickschema.org/schema/Brick#Controller,An ICT device that exposes Points on a network and implements software to control or monitor equipment or systems using those Points., https://brickschema.org/schema/Brick#Cooling_Coil,"A cooling element made of pipe or tube that removes heat from equipment, machines or airflows. Typically filled with either refrigerant or cold water.", https://brickschema.org/schema/Brick#Cooling_Command,Controls the amount of cooling to be delivered (typically as a proportion of total cooling output), https://brickschema.org/schema/Brick#Cooling_Demand_Sensor,Measures the amount of power consumed by a cooling process; typically found by multiplying the tonnage of a unit (e.g. RTU) by the efficiency rating in kW/ton, @@ -937,6 +937,7 @@ https://brickschema.org/schema/Brick#Plate_Heat_Exchanger,A heat exchanger that https://brickschema.org/schema/Brick#PlugStrip,A device containing a block of electrical sockets allowing multiple electrical devices to be powered from a single electrical socket.,https://en.wikipedia.org/wiki/Power_strip https://brickschema.org/schema/Brick#Plumbing_Room,A service room devoted to the operation and routing of water in a building. Usually distinct from the HVAC subsystems., https://brickschema.org/schema/Brick#Point,, +https://brickschema.org/schema/Brick#Point_Group,"Nestable collections of associated points with a label, e.g. to model associations of points for display, configuration, packaging, and other user-defined needs", https://brickschema.org/schema/Brick#Portfolio,A collection of sites, https://brickschema.org/schema/Brick#Position,The fraction of the full range of motion, https://brickschema.org/schema/Brick#Position_Command,Controls or reports the position of some object, diff --git a/bricksrc/equipment.py b/bricksrc/equipment.py index 00fa549c..544098e5 100644 --- a/bricksrc/equipment.py +++ b/bricksrc/equipment.py @@ -8,6 +8,7 @@ equipment_subclasses = { "ICT_Equipment": { "tags": [TAG.ICT, TAG.Equipment], + BRICK.hostsPoint: [BRICK.Point], "subclasses": { "ICT_Hardware": { "tags": [TAG.Equipment, TAG.ICT, TAG.Hardware], @@ -57,6 +58,7 @@ }, "Controller": { "tags": [TAG.Equipment, TAG.ICT, TAG.Controller], + BRICK.controls: [BRICK.Equipment], "subclasses": { "BACnet_Controller": { "tags": [TAG.Equipment, TAG.BACnet, TAG.Controller], diff --git a/bricksrc/recpatches.ttl b/bricksrc/recpatches.ttl index 0702285d..a0bb682d 100644 --- a/bricksrc/recpatches.ttl +++ b/bricksrc/recpatches.ttl @@ -1179,10 +1179,6 @@ brick:Collection sh:property [ rdf:type sh:PropertyShape ; sh:path rec:includes ; - sh:or ( - [ sh:class brick:Equipment ] - [ sh:class brick:Collection ] - ) ; sh:minCount 1 ; sh:name "includes" ; sh:nodeKind sh:IRI ; @@ -1773,4 +1769,4 @@ rec:AudioVisualEquipment brick:deprecatedInVersion "1.4.0" ; brick:deprecationMitigationMessage "REC ICT classes are being phased out in favor of Brick classes. For a replacement, consider brick:Audio_Visual_Equipment" ; brick:isReplacedBy brick:Audio_Visual_Equipment ; -. \ No newline at end of file +. diff --git a/bricksrc/relationships.py b/bricksrc/relationships.py index 67bc0211..075a8b42 100644 --- a/bricksrc/relationships.py +++ b/bricksrc/relationships.py @@ -117,6 +117,7 @@ A: [OWL.ObjectProperty, OWL.AsymmetricProperty, OWL.IrreflexiveProperty], OWL.inverseOf: BRICK["isPartOf"], RDFS.label: Literal("Has part", lang="en"), + RDFS.subPropertyOf: REC.includes, }, "isPartOf": { A: [OWL.ObjectProperty, OWL.AsymmetricProperty, OWL.IrreflexiveProperty], @@ -162,6 +163,34 @@ "domain": BRICK.Point, RDFS.label: Literal("Has unit", lang="en"), }, + "controls": { + A: [OWL.ObjectProperty, OWL.AsymmetricProperty, OWL.IrreflexiveProperty], + OWL.inverseOf: BRICK["isControlledBy"], + "range": BRICK.Equipment, + "domain": BRICK.Controller, + RDFS.label: Literal("Controls", lang="en"), + }, + "isControlledBy": { + A: [OWL.ObjectProperty, OWL.AsymmetricProperty, OWL.IrreflexiveProperty], + OWL.inverseOf: BRICK["controls"], + "range": BRICK.Controller, + "domain": BRICK.Equipment, + RDFS.label: Literal("Is controlled by", lang="en"), + }, + "hostsPoint": { + A: [OWL.ObjectProperty, OWL.AsymmetricProperty, OWL.IrreflexiveProperty], + OWL.inverseOf: BRICK["isHostedBy"], + "range": BRICK.Point, + "domain": BRICK.ICT_Equipment, + RDFS.label: Literal("Hosts point", lang="en"), + }, + "isHostedBy": { + A: [OWL.ObjectProperty, OWL.AsymmetricProperty, OWL.IrreflexiveProperty], + OWL.inverseOf: BRICK["hostsPoint"], + "range": BRICK.ICT_Equipment, + "domain": BRICK.Point, + RDFS.label: Literal("Is hosted by", lang="en"), + }, "meters": { A: [OWL.ObjectProperty, OWL.AsymmetricProperty, OWL.IrreflexiveProperty], OWL.inverseOf: BRICK.isMeteredBy, @@ -238,4 +267,8 @@ if row["datatype"]: relationships[row["path"]][A] = [OWL.DatatypeProperty] if row["nodeKind"]: - relationships[row["path"]][A] = [OWL.ObjectProperty, OWL.AsymmetricProperty, OWL.IrreflexiveProperty] + relationships[row["path"]][A] = [ + OWL.ObjectProperty, + OWL.AsymmetricProperty, + OWL.IrreflexiveProperty, + ] diff --git a/bricksrc/root_class_shapes.ttl b/bricksrc/root_class_shapes.ttl index f0b62a7c..0296e9eb 100644 --- a/bricksrc/root_class_shapes.ttl +++ b/bricksrc/root_class_shapes.ttl @@ -79,6 +79,11 @@ brick:Equipment a sh:NodeShape ; ); sh:message "A piece of Equipment can feed a Equipment or Location/Space." ]; + sh:property [ + sh:path brick:isControlledBy ; + sh:class brick:Controller ; + sh:message "Equipment can be controlled only by Controllers." + ] ; . brick:Point a sh:NodeShape; @@ -93,7 +98,13 @@ brick:Point a sh:NodeShape; sh:maxCount 0 ; sh:message "Points cannot have locations; use 'isPointOf' instead" ; ] ; - . + sh:property [ + sh:path brick:isHostedBy ; + sh:maxCount 1 ; + sh:class brick:ICT_Equipment ; + sh:message "A Point can be hosted by at most one ICT Equipment." + ] ; +. brick:Collection a sh:NodeShape; sh:node [sh:not [ sh:class brick:Equipment ] ; sh:message "Collection is an exclusive top class." ], diff --git a/bricksrc/rules.ttl b/bricksrc/rules.ttl index 5c282a0e..f276a02a 100644 --- a/bricksrc/rules.ttl +++ b/bricksrc/rules.ttl @@ -1,4 +1,5 @@ @prefix brick: . +@prefix rec: . @prefix qudt: . @prefix ref: . @prefix rdfs: . @@ -571,3 +572,27 @@ bsh:InheritEVSEChargerDirection a sh:NodeShape ; """ ; ] ; . + +# if point group rec:includes a point or point group, add brick:hasPart relationship +bsh:PointGroupIncludesHasPart a sh:NodeShape ; + sh:targetClass brick:Point_Group ; + sh:rule [ + a sh:SPARQLRule ; + sh:construct """ + CONSTRUCT { + $this brick:hasPart ?part . + } + WHERE { + $this rec:includes ?part . + { + ?part rdf:type/rdfs:subClassOf* brick:Point . + } + UNION + { + ?part rdf:type/rdfs:subClassOf* brick:Point_Group . + } + } + """ ; + sh:prefixes ; + ] ; +. diff --git a/examples/controller/controller.ttl b/examples/controller/controller.ttl new file mode 100644 index 00000000..14cf9c9b --- /dev/null +++ b/examples/controller/controller.ttl @@ -0,0 +1,40 @@ +@prefix bldg: . +@prefix rdfs: . +@prefix rec: . +@prefix brick: . +@prefix bacnet: . +@prefix unit: . +@prefix xsd: . +@prefix ref: . +@prefix owl: . + + a owl:Ontology ; + owl:imports . + + +bldg:Controller_1 a brick:Controller ; + rdfs:label "Main Building Controller" ; + brick:hostsPoint bldg:VAV1_Temperature_Sensor, bldg:VAV1_Occupancy_Sensor ; + brick:controls bldg:VAV1 ; +. + +bldg:VAV1PointGroup a brick:Point_Group ; + rdfs:label "VAV1 Point Group" ; + brick:hasPart bldg:VAV1_Temperature_Sensor, bldg:VAV1_Occupancy_Sensor ; +. + +bldg:VAV1_Temperature_Sensor a brick:Temperature_Sensor ; + rdfs:label "VAV1 Temperature Sensor" . + +bldg:VAV1_Occupancy_Sensor a brick:Occupancy_Sensor ; + rdfs:label "VAV1 Occupancy Sensor" . + +bldg:VAV1 a brick:Variable_Air_Volume_Box ; + rdfs:label "VAV1" ; + brick:hasPoint bldg:VAV1_Temperature_Sensor, bldg:VAV1_Occupancy_Sensor ; + brick:feeds bldg:Zone1 ; +. + +bldg:Zone1 a rec:HVACZone ; + rdfs:label "Zone 1" ; +.