# SPDX-License-Identifier: MPL-2.0
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix bdd: <https://secorolab.github.io/metamodels/acceptance-criteria/bdd#> .
@prefix env: <https://secorolab.github.io/metamodels/environment#> .
@prefix agn: <https://secorolab.github.io/metamodels/agent#> .
@prefix obs: <https://secorolab.github.io/metamodels/observation#> .
@prefix py: <https://secorolab.github.io/metamodels/languages/python#> .
@prefix ros: <https://index.ros.org/p/> .
@prefix cstr-ext: <https://secorolab.github.io/metamodels/task/constraint#> .
@prefix sens: <https://secorolab.github.io/metamodels/robot/sensors#> .
@prefix sosa: <http://www.w3.org/ns/sosa/> .
@prefix qudt: <http://qudt.org/schema/qudt/> .
@prefix qudt-quant: <http://qudt.org/vocab/quantitykind/> .
@prefix unit: <http://qudt.org/vocab/unit/> .
@prefix geom-rel: <https://comp-rob2b.github.io/metamodels/geometry/spatial-relations#> .

obs:ObservationShape
    a sh:NodeShape ;
    sh:targetClass obs:Observation ;
    sh:property [
        sh:path obs:has-provider ;
        sh:class obs:ObservationProvider ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path obs:observes-target ;
        sh:nodeKind sh:IRI ;
        sh:node [
            sh:or (
                [ sh:class bdd:ScenarioVariable ]
                [ sh:class env:Object ]
                [ sh:class env:Workspace ]
                [ sh:class agn:Agent ]
            )
        ] ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path obs:time-extractor ;
        sh:class py:ModuleAttribute ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path obs:entity-mapper ;
        sh:maxCount 1 ;
    ] ;
    sh:sparql [
        sh:message "A targeted observation requires an entity mapper." ;
        sh:select """
            PREFIX obs: <https://secorolab.github.io/metamodels/observation#>
            SELECT $this
            WHERE {
                $this obs:observes-target ?target .
                FILTER NOT EXISTS { $this obs:entity-mapper ?mapper }
            }
        """ ;
    ] .

obs:ObservationPolicyShape
    a sh:NodeShape ;
    sh:targetClass obs:ObservationPolicy ;
    sh:xone (
        [ sh:class obs:DirectTrinaryPolicy ]
        [ sh:class obs:EvaluatedObservationPolicy ]
    ) .

obs:DirectTrinaryPolicyShape
    a sh:NodeShape ;
    sh:targetClass obs:DirectTrinaryPolicy ;
    sh:class obs:ObservationPolicy ;
    sh:property [ sh:path obs:has-observation ; sh:maxCount 0 ] ;
    sh:property [ sh:path obs:time-extractor ; sh:maxCount 0 ] ;
    sh:property [ sh:path obs:entity-mapper ; sh:maxCount 0 ] ;
    sh:property [ sh:path obs:has-evaluator ; sh:maxCount 0 ] .

obs:EvaluatedObservationPolicyShape
    a sh:NodeShape ;
    sh:targetClass obs:EvaluatedObservationPolicy ;
    sh:class obs:ObservationPolicy ;
    sh:property [
        sh:path obs:has-observation ;
        sh:class obs:Observation ;
        sh:minCount 1 ;
    ] ;
    sh:property [ sh:path obs:time-extractor ; sh:maxCount 0 ] ;
    sh:property [ sh:path obs:entity-mapper ; sh:maxCount 0 ] ;
    sh:property [
        sh:path obs:has-evaluator ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
        sh:node obs:EvaluatorShape ;
    ] .

obs:EvaluatorShape
    a sh:NodeShape ;
    sh:or (
        [ sh:class py:ModuleAttribute ]
        [ sh:class obs:LinearDistanceEvaluator ]
    ) .

obs:LinearDistanceEvaluatorShape
    a sh:NodeShape ;
    sh:targetClass obs:LinearDistanceEvaluator ;
    sh:class geom-rel:LinearDistance ;
    sh:property [
        sh:path geom-rel:between-entities ;
        sh:class obs:Observation ;
        sh:minCount 2 ;
        sh:maxCount 2 ;
    ] ;
    sh:property [
        sh:path cstr-ext:has-constraint ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path obs:max-time-offset ;
        sh:maxCount 1 ;
        sh:node [
            sh:class qudt:Quantity ;
            sh:property [
                sh:path qudt:hasQuantityKind ;
                sh:hasValue qudt-quant:Time ;
            ] ;
            sh:property [
                sh:path qudt:unit ;
                sh:hasValue unit:SEC ;
            ] ;
            sh:property [
                sh:path qudt:value ;
                sh:datatype xsd:double ;
                sh:minInclusive 0.0 ;
                sh:minCount 1 ;
                sh:maxCount 1 ;
            ] ;
        ] ;
    ] ;
    sh:sparql [
        sh:message "A linear evaluator can only reference observations of its policy." ;
        sh:select """
            PREFIX obs: <https://secorolab.github.io/metamodels/observation#>
            PREFIX geom-rel: <https://comp-rob2b.github.io/metamodels/geometry/spatial-relations#>
            SELECT $this
            WHERE {
                ?policy obs:has-evaluator $this .
                $this geom-rel:between-entities ?observation .
                FILTER NOT EXISTS { ?policy obs:has-observation ?observation }
            }
        """ ;
    ] .

# A provider is named after the quantity it yields, which is the cheapest way to ask whether an
# observation carries a pose. Which wire it arrives on is the provider's own type, not this.
obs:QuantityProviderShape
    a sh:NodeShape ;
    sh:targetClass
        obs:PoseProvider ,
        obs:VelocityTwistProvider ,
        obs:AccelerationTwistProvider ,
        obs:WrenchProvider ,
        obs:JointPositionProvider ,
        obs:JointVelocityProvider ,
        obs:JointAccelerationProvider ,
        obs:JointForceProvider ;
    sh:class obs:ObservationProvider .

ros:TopicProviderShape
    a sh:NodeShape ;
    sh:targetClass ros:Topic ;
    sh:property [
        sh:path ros:channel-name ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path ros:type-name ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] .

ros:SimulationEntityStateProviderShape
    a sh:NodeShape ;
    sh:targetClass ros:SimulationEntityStateProvider ;
    sh:class obs:PoseProvider ;
    sh:class sosa:Sensor ;
    sh:property [
        sh:path sens:update-rate ;
        sh:node sens:FrequencyQuantityShape ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] .
