Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Key Concepts

Tip
iconfalse

Related docs
SetLocalData
Using JavaScript
Schedule and unschedule TQL code
Mirror ThingFacet


Background

Modeling and Simulation are central to the IoT Applications

...

and

...

employ a wide range of technologies and methodologies. Most of

...

these technologies focus on

...

rather specific

...

aspects and a

...

narrow purpose (e.g., discrete event simulation in industrial systems vs. continuous simulation in electrical and computer engineering). There is a need for an all-encompassing approach, especially in the IoT arena. Atomiton TQL provides right set of hooks to create an all-encompassing approach to simulation.


Greenhouse Problem Statement

For the purpose of this tutorial to explain implementation approaches to simulation we look at the Greenhouse monitoring system with following scenario.

Image Modified

For the purpose of this tutorial let's model the Greenhouse monitoring system with following configuration.

  • Greenhouse: size: 40 feet by 110 feet
  • crop types, tomatoes and peppers
  • 3 lanes, each lane has 10 zones
  • Total 30 zones (each zone is 10 feet by 10 feet. Lay out is 3 by 10): 20 zones (2 by 10) are tomatoes, 10 zones are peppers

Each zone subdivided into 9 grids. Each grid is 3.33 feet by 3.33 feet. Total 270 grids

 Zone Content: 

  • 1 temperature sensor (total 30)
  • 1 humidity sensor (total 30)
  • 1 camera (total 30)
  • 1 irrigation motor (total 30)
  • 1 heater (total 30)

 Zone Grid Content:

  • 1 ambient light sensor (total 270)
  • 1 light (total 270)
  • 1 irrigation nozzle (total 270)
  • 1 soil moisture sensor (total 270)

 External Conditions:

  • 1 ambient light sensor
  • 1 temperature sensor
  • 1 humidity sensor

Simulation Design Patterns

...

The existing Atomic domain languages capabilities can be exploited to achieve simple to complex simulation. The simulation design patterns can be classified as follows:

...


Pattern #NameDescriptionUse CasesImplementation Flow
1@ThingFacet LevelSeamless integration within the attribute's associate action
  • Simple Sensors and Actuators
  • JavaScript to capture simulation logic
  • Emulate automatic trigger for sensors
2@ThingFacet LevelSeparate Action to start the simulation
  • Simple Sensors and Actuators
  • JavaScript to capture simulation logic
  • Schedule frequency of simulation
3@Protocol Handler LevelPush the simulation logic down into protocol handler level
  • Complex simulation profiles can be passed
  • Automatically available at the Invoke level
  • Within Protocol Handler.
4@Behavior TreeSimulation logic is driven primarily by Behavior Trees.
  • Simulate a behavior
  • Within BDL Facet Instance

...

 

 

Create DataModel to store non-thing related data

TQLEngine provides DataModel to store "pure" data, DataModels do not combine facets. They are simply models to store application data that is essential to create a meaningful real-world IoT application.

Expand
titleDataModel
Image Removed

Go to page

Custom Types

We create two custom types to store GeoLocation and BoundingBox.

...

GeoLocation - To Store GeoLocation

Code Block
languagexml
titleGeoLocation Type
linenumberstrue
<Def Name="GeoLocation">
  <Number Name="latitude"/>
  <Number Name="longitude"/>
</Def>	

...

BoundingBox - To Store Geo Boundary of Zone, etc

Code Block
languagexml
titleBoundingBox Type
linenumberstrue
<Def Name="BoundingBox">
  <GeoLocation Name="Vertex1"/>
  <GeoLocation Name="Vertex2"/>
  <GeoLocation Name="Vertex3"/>
  <GeoLocation Name="Vertex4"/>
</Def>

...

Simulating Temperature Sensor

Simulation Logic

Image Added

  • GHCurrentTemp is the general temperature inside the greenhouse
  • It is affected by CurrentTemp (external), the vent and fans, and all the heaters
  • The heaters are controlled by the application
  • The vent and fans controls are not exposed to the application. So vent and fan behavior are simulated by BT, described in the next section.
  • GHCurrentTemp is the default value for ZoneTemp if the heater is not on in that zone (if the heater is on in that zone, ZoneTemp will be higher than GHCurrentTemp)
  • Value updates every 5 minutes
  • GHTempAdj On Sunnyday: formula to calculate GHTempAdj in Celcius: GHTempAdj = (100/(CurrentTemp+237.1))3 * 100

    For example, when (external) CurrenTemp = -5 Celcius, GHTempAdj = 5.186; when CurrentTemp = 28 Celcius, GHTempAdj = 3.661

    On NOT Sunnyday: formula to calculate GHTempAdj in Celcius: GHTempAdj = (100/(CurrentTemp+237.1))3 * 60

    For example, when (external) CurrenTemp = -5 Celcius, GHTempAdj = 3.111; when CurrentTemp = 28 Celcius, GHTempAdj = 2.197
  • Since temperature has an effect on humidity, always determine temperature value before determining humidity value

Add Greenhouse and Heater Model

Code Block
languagexml
title

...

Greenhouse DataModel and Types
linenumberstrue
<Def Name="EnvInfo">
   

...

 

...

  <Number Name="Temperature"/>
      <Number Name="Humidity"/>
      <Number Name="Light"/>
      <Number Name="Pressure"/>
      <Number Name="SoilMoisture"/>
      <Number Name="Wind"/>
      <String Name="LastUpdatedByProvider"/>
   

...

List of DataModels

Greenhouse : Overall DataModel holder for Greenhouse. GHLanes is defined of type reference. There are two types of references - simple reference and smart reference. Smartness can be defined by providing a "Find" Query at the time of model definition or at the time of instantiating the model. GHLanes is defined as Reference to include all the Lanes from Lane model.

...

languagexml
titleGreenhouse DataModel
linenumberstrue

...

 </Def>

    <Def Name="BoundingBox">
      <GeoLocation Name="Vertex1"/>
      <GeoLocation Name="Vertex2"/>
      <GeoLocation Name="Vertex3"/>
      <GeoLocation Name="Vertex4"/>
  

...

  </Def>

    

...

<Def Name="

...

GeoLocation"

...

>
  

...

    <Number Name="

...

latitude"/>
  

...

    <Number Name="

...

longitude"/>
  

...

  </Def>

    

...

<DataModel Name="

...

Greenhouse"

...

>
  

...

    <Sid Name="

...

GreenhouseID"/>
  

...

    <String Name="

...

GreenhouseName"/>
    

...

  <GeoLocation Name="

...

Location"/>
  

...

 

...

   <String Name="

...

LocationName"/>
  

...

 

...

   

...

<BoundingBox Name="

...

Boundary"/>
    

...

  <Integer Name="LaneCount"/>
   

...

   <Double Name="LaneWidth"/>
      

...

<Double 

...

Name="ZoneLength"/>
      <Integer 

...

Name="ZoneInLane"/>
    

...

  

...

Grid - DataModel for storing Grid information.

...

languagexml
titleGrid DataModel
linenumberstrue

...

<Integer Name="

...

ZoneCount"/>
  

...

    <Double Name="

...

GHLength"/>
  

...

    <Double Name="

...

GHWidth"/>
      

...

<EnvInfo Name="

...

ExternalEnv"/>
   

...

   <EnvInfo Name="

...

InternalEnv"/>
  

...

    <String Name="

...

VentOnOffState"/>
  

...

 

...

   

...

<String Name="

...

FansOnOffState"/>
  

...

 

...

   

...

<Boolean Name="

...

SunnyDay"/>
  

...

 

...

   

...

<Boolean Name="

...

Zone - DataModel for storing Zone information. Does a Smart Reference  to Grid at instantiation time by simply defining ZoneGrids as Reference type. The Find query can be add at the instantiation time.

Code Block
languagexml
titleZone DataModel
linenumberstrue
<DataModel Name="Zone">
  <Sid Name="ZoneID"/>
  <String Name="LaneID"/>
  <String Name="ZoneName"/>
  <BoundingBox Name="ZoneLocation"/>
  <Double Name="ZoneWidth"/>
  <Double Name="ZoneLength"/>
  <Integer Name="GridOnLength"/>
  <Integer Name="GridOnWidth"/>
  <Integer Name="GridCount"/>
  <Double Name="GridLength"/>
  <Double Name="GridWidth"/>
  <String Name="EndZone"/>
  <Double Name="ZoneTemperature"/>
  <Double Name="ZoneHumidity"/>
  <Double Name="ZoneAvgSM"/>
  <Double Name="ZoneAvgLight"/>
  <String Name="CropType"/>
  <Date Name="SeedingDate" format="$SimpleDateFormat(dd-MM-yyyy)"/>
  <Date Name="LastFertilisationDate" format="$SimpleDateFormat(dd-MM-yyyy)"/>
  <String Name="HarvestingSeason"/>
  <Integer Name="NoOfPlants"/>
  <Double Name="VPD"/>
  <Reference Name="LaneZone" Type="Lane" Cardinality="1"/>
  <Reference Name="ZoneGrids">
     <Find>
      <Grid>
        <GridID ne=""/>
      </Grid>
    </Find>
  </Reference>
</DataModel>

Lane - DataModel to hold Lane information. Defines Smart Reference to Zones.

...

languagexml
titleLane DataModel
linenumberstrue

...

Running" default="false"/> <!-- true / false -->
    </DataModel>

    <ThingModel Name="Heater">
      <Sid Name="HeaterID"/>
      <String Name="OnOffState"/>
      <Integer Name="HeatingLevel"/>
    </ThingModel>

Pipeline Macros

  • Execute Internal Query
  • Schedule and UnSchedule TQL Code


Code Block
languagexml
titlePipeline Macros
linenumberstrue
<Macro Name="executeQuery">
  <Argument>
    <QueryString>
      <Query/>
    </QueryString>
  </Argument>
  <Result>
    <OnRequest Target="[:RuntimeParams.FacetIDName:]" Disable="CMD_SERVER">
      <Process Return="CMD_NOP">
        <Message>
          <Value>
            [:$Macro.Argument.QueryString:]
          </Value>
        </Message>
      </Process>
    </OnRequest>
  </Result>
</Macro>


<Macro Name="DeleteScheduleJob">
  <Argument>
    <JobName/>
  </Argument>
  <Result>
    <DoRequest target="[:RuntimeParams.SequenceFacetIDName:]">
      

...

<Process>
       

...

 

...

<Message type="

...

xml"

...

>
       

...

 

...

  <Value>
    

...

 

...

       

...

<Remove 

...

eid="

...

[:$Macro.Argument.JobName:]"/>
      

...

    </Value>
       

...

 

...

</Message>
      

...

</Process>
    </DoRequest>
 

...

 </Result>
</Macro>

<Macro Name="ScheduleJob">
  <Argument>
   

...

 <ScheduleInterval>
    </ScheduleInterval>
    

...

<StartTime>
      0
    </StartTime>

...

    <EndTime>
    </EndTime>
    <ActionCode>
   

...

 

...

</ActionCode>
    <Name>
    </Name>
  </

...

Argument>
  <Result>
    <Log Message="Inside ScheduleJob 

...

macro"/

...

>
    

...

Data + Things

We have to assign 1 temperature sensor per zone. We do this by:

  1. Use the content of MultipleSensors tutorial project and bring in all the model content into this tutorial.
  2. Use Simple Reference in TempSensorModel to a particular Zone using ZoneID
    Other sensors and actuators can be referenced in a similar fashion.
Code Block
languagexml
titleTempSensorModel with reference to Zone
linenumberstrue
<ThingModel Name="TempSensorModel" Combines="TempSensorFacet">
  <Sid Name="TempSensorId"/>
  <Reference Name="TempSensorInZone" Type="Zone" Cardinality="1"/>
</ThingModel>

Queries

Let's start writing some queries to create and read the data. 

Create Grids

Code Block
languagexml
titleCreate Grid
linenumberstrue
<Query>
  <Find>
    <Zone>
      <ZoneName eq="Zone1"/>
    </Zone>
  </Find>
  <Create>
    <Grid>
      <GridName>
        Grid1
      </GridName>
      <GridNSPosition>
        1
      </GridNSPosition>
      <GridWEPosition>
        1
      </GridWEPosition>
      <GridLength>
        3.33
      </GridLength>
      <GridWidth>
        3.33
      </GridWidth>
      <SoilMoisture>
        0
      </SoilMoisture>
      <AmbientLight>
        0
      </AmbientLight>
      <ZoneGrid>[:$Response.Message.Value.Find.Result.Zone.ZoneID:]</ZoneGrid>
    </Grid>
  </Create>
 
</Query>

Create Zone

Code Block
languagexml
titleCreate Zone
linenumberstrue
<Query>
  <DeleteAll>
    <Zone>
      <ZoneID ne=""/>
    </Zone>
  </DeleteAll>
  <Create>
    <Zone>
      <ZoneWidth>
        10
      </ZoneWidth>
      <ZoneLength>
        10
      </ZoneLength>
      <GridOnLength>
        3
      </GridOnLength>
      <GridOnWidth>
        3
      </GridOnWidth>
      <GridCount>
        9
      </GridCount>
      <GridLength>
        3.33
      </GridLength>
      <GridWidth>
        3.33
      </GridWidth>
      <EndZone>
        No
      </EndZone>
      <ZoneTemperature>
        0
      </ZoneTemperature>
      <ZoneHumidity>
        0
      </ZoneHumidity>
      <CropType>Tomato</CropType>
    </Zone>
  </Create>
</Query>

Create Lane

Code Block
languagexml
titleCreate Lane
linenumberstrue
<Query>
  <Create>
    <Lane>
      <LaneWidth>
        110
      </LaneWidth>
      <ZoneInLane>
        10
      </ZoneInLane>
      <BorderLane>
        Yes
      </BorderLane>
      <LaneZones>
      </LaneZones>
    </Lane>
  </Create>
</Query>

Create Greenhouse

Code Block
languagexml
titleCreate Greenhouse
linenumberstrue
<Query>
  <Create>
    <Greenhouse>
      <LaneCount>
        3
      </LaneCount>
      <LaneWidth>
        110
      </LaneWidth>
      <ZoneLength>
        10
      </ZoneLength>
      <ZoneInLane>
        10
      </ZoneInLane>
      <ZoneCount>
        90
      </ZoneCount>
      <ExtEnvInfo>
        <Temperature>
          0
        </Temperature>
        <Humidity>
          0
        </Humidity>
        <Light>
          0
        </Light>
      </ExtEnvInfo>
      <GHLanes></GHLanes>
    </Greenhouse>
  </Create>
</Query>

Create Temperature Sensor

TempSensorInZone value is the ZoneID value of the Zone. This is dynamically assigned by querying the Zone Model.

...

<DoRequest target="[:RuntimeParams.SequenceFacetIDName:]">
      <Process>
        <Message>
          <Value>
            <Execute eid="[:$Macro.Argument.Name:]" schedule="[[:$Macro.Argument.StartTime:]..[:$Macro.Argument.EndTime:]/[:$Macro.Argument.ScheduleInterval:]]">
              <Action>
                <ExecuteQuery>
                  <QueryString>
                    [:$Macro.Argument.ActionCode:]
                  </QueryString>
                </ExecuteQuery>
              </Action>
            </Execute>
          </Value>
        </Message>
      </Process>
    </DoRequest>
  </Result>
</Macro>	


Macro to Simulate Sensor

Code Block
languagexml
titleSimulate Sensor Logic
linenumberstrue
<Macro Name="SetGHCurrentTemp">
      <Argument>
        <TempSensorId></TempSensorId>
        <GreenhouseID></GreenhouseID>
      </Argument>
      <Result>
        <Log Message="*********Calculating Temperature..."/>
        <executeQuery>
          <QueryString>
            <Query>
              <Find>
                <Greenhouse>
                  <GreenhouseID>[:$Macro.Argument.GreenhouseID:]</GreenhouseID>
                </Greenhouse>
              </Find>
            </Query>
          </QueryString>
        </executeQuery>
        <if condition="$Response.Message.Value/Find/Status eq 'Success'">
          <then>
            <SetLocalData key="CurrentTemp">
              <Value>[:$Response.Message.Value.Find.Result.Greenhouse.ExternalEnv.Temperature:]</Value>
            </SetLocalData>
            <SetLocalData key="GHCurrentTemp">
              <Value>[:$Response.Message.Value.Find.Result.Greenhouse.InternalEnv.Temperature:]</Value>
            </SetLocalData>
            <SetLocalData key="Vent">
              <Value>[:$Response.Message.Value.Find.Result.Greenhouse.VentOnOffState:]</Value>
            </SetLocalData>
            <SetLocalData key="Fans">
              <Value>[:$Response.Message.Value.Find.Result.Greenhouse.FansOnOffState:]</Value>
            </SetLocalData>
            <SetLocalData key="SunnyDay">
              <Value>[:$Response.Message.Value.Find.Result.Greenhouse.SunnyDay:]</Value>
            </SetLocalData>
            <executeQuery>
              <QueryString>
                <Query>
                  <Find>
                    <Heater>
                      <OnOffState eq='On'/>
                    </Heater>
                  </Find>
                </Query>
              </QueryString>
            </executeQuery>

            <Javascript>
              <resp>
                <Include>$Response.Message.Value</Include>
              </resp>
              var resultList = resp.Find;
              var heaterLevels = [];
              var heaterLevelsSum = 0;
              for each (var result in resultList.iterEntries("Result")){
              var heaters =
              result.getValue();
              heaterLevels.push(heaters.Heater.HeatingLevel);
              heaterLevelsSum = heaterLevelsSum + heaters.Heater.HeatingLevel;
              }
              var GHTempAdj =
              0;
              if([:$LocalData.SunnyDay:]){
              GHTempAdj = Math.pow((100/([:$LocalData.CurrentTemp:]+237.1)), 3) * 100;
              }else{
              GHTempAdj =
              Math.pow((100/([:$LocalData.CurrentTemp:]+237.1)), 3) * 60;
              }
              var GHBaseTemp = [:$LocalData.CurrentTemp:] + GHTempAdj;
              var TempVentImp =
              ([:$LocalData.CurrentTemp:] - [:$LocalData.GHCurrentTemp:]) * 0.03;
              var HeaterEffectRatio = heaterLevelsSum / 90;
              var TempHeaterImp =
              HeaterEffectRatio * (Math.sqrt(Math.max(0,(45-[:$LocalData.GHCurrentTemp:]))));
              var GHCurrentTemp = GHBaseTemp + TempVentImp + TempHeaterImp;
              sffContext.execute("SetContextData","key","GHCurrentTemp","value",GHCurrentTemp);
            </Javascript>

            <executeQuery>
              <QueryString>
                <Query>
                  <Find format="Version">
                    <TempSensor>
                      <sensorId>[:$Macro.Argument.TempSensorId:]</sensorId>
                    </TempSensor>
                  </Find>
                  <SetResponseData>
                    <Key>Message.Value.Find.Result.TempSensor.TempValue.Value</Key>
                    <Value>[:$ContextData.GHCurrentTemp:]</Value>
                  </SetResponseData>
                  <Save>
                    <from>Result</from>
                    <Include>$Response.Message.Value.Find</Include>
                  </Save>
                </Query>
              </QueryString>
            </executeQuery>

            <executeQuery>
              <QueryString>
                <Query>
                  <Find format="Version">
                    <Greenhouse>
                      <GreenhouseID>[:$Macro.Argument.GreenhouseID:]</GreenhouseID>
                    </Greenhouse>
                  </Find>
                  <SetResponseData>
                    <Key>Message.Value.Find.Result.Greenhouse.InternalEnv.Temperature.Value</Key>
                    <Value>[:$ContextData.GHCurrentTemp:]</Value>
                  </SetResponseData>
                  <Save>
                    <from>Result</from>
                    <Include>$Response.Message.Value.Find</Include>
                  </Save>
                </Query>
              </QueryString>
            </executeQuery>
          </then>
        </if>
      </Result>
    </Macro>	

Add Action to Start Simulate

Code Block
languagexml
titleAction to Start Simulation
linenumberstrue
<Action Name="SimulatedReadAction" Documentation="Start reading temperature from simulated env">
        <Workflow Limit="1" Live="1" Timeout="-1">
          <Task Name="Main1" While="true">
            <Event Name="Argument" As="ActionArgument"/>
            <Invoke Name="StartSimulation" waitFor="Argument">
              <FacetScript>
                <If condition="/'[%:Event.Argument.Simulated.Value:%]' eq 'true'">
                  <then>
                   <Log Message="Starting simulation.."/>
                    <SetGHCurrentTemp>
                      <TempSensorId>
                        [%:Event.Argument.sensorId:%]
                      </TempSensorId>
                      <GreenhouseID>
                        [%:Event.Argument.GreenhouseID.Value:%]
                      </GreenhouseID>
                    </SetGHCurrentTemp>
                    <ScheduleJob>
                      <Name>
                        SetGHCurrentTemperature
                      </Name>
                      <ScheduleInterval>
                        5min
                      </ScheduleInterval>
                      <ActionCode>
                        <SetGHCurrentTemp>
                          <TempSensorId>
                            [%:Event.Argument.sensorId:%]
                          </TempSensorId>
                          <GreenhouseID>
                            [%:Event.Argument.GreenhouseID.Value:%]
                          </GreenhouseID>
                        </SetGHCurrentTemp>
                      </ActionCode>
                    </ScheduleJob>
                  </then>
                  <else>
                     <Log Message="Stopping simulation.."/>
                    <DeleteScheduleJob>
                      <JobName>
                        SetGHCurrentTemperature
                      </JobName>
                    </DeleteScheduleJob>
                  </else>
                </If>
              </FacetScript>
            </Invoke>
            <Output Name="Result" As="ActionResult">
              <Value>
                <Simulated>
                  [%:Event.Argument.Simulated.Value:%]
                </Simulated>
              </Value>
            </Output>
          </Task>
        </Workflow>
      </Action>

Changes to TempFacetSerial


Code Block
languagexml
titleChanges to Thing Facet
linenumberstrue
<ThingFacet Name="TempFacetSerial">
      <String Name="TempValue"/>
      <String Name="Unit" default="Celsius"/>
      <String Name="Peripheral"/>
      <String Name="InterfacePort"/>
      <String Name="Interface"/>
      <String Name="UniqueId" default="56789"/>
      <String Name="Baudrate"/>
      <String Name="Format" default="ascii"/>
      <String Name="Operation"/>
      <String Name="Payload"/>
      <Reference Name="GreenhouseID" Type="Greenhouse"/>
      <String Name="Simulated" KnownBy="SimulatedReadAction"/>
</ThingFacet>


Queries

Let's start writing some queries to create and read the data. 

Create Greenhouse

Code Block
languagexml
titleCreate Greenhouse
linenumberstrue
<Query>
  <Create>
    <Greenhouse>
      <GreenhouseName>Khan Farmhouse</GreenhouseName>
      <BoundingBox>
        <Vertex1>0</Vertex1>
        <Vertex2>0</Vertex2>
        <Vertex3>110</Vertex3>
        <Vertex4>40</Vertex4>
      </BoundingBox>
      <LaneCount>
        3
      </LaneCount>
      <LaneWidth>110</LaneWidth>
      <ZoneLength>10</ZoneLength>
      <ZoneInLane>
        10
      </ZoneInLane>
      <ZoneCount>3</ZoneCount>
      <GHLength>110</GHLength>
      <GHWidth>40</GHWidth>
      <ExternalEnv>
        <Temperature>1</Temperature>
        <Humidity>1</Humidity>
        <Light>1</Light>
        <SoilMoisture>1</SoilMoisture>
        <Wind>19.5</Wind>
      </ExternalEnv>
      <InternalEnv>
        <Temperature>1</Temperature>
        <Humidity>1</Humidity>
        <Light>1</Light>
        <SoilMoisture>1</SoilMoisture>
      </InternalEnv>
      <SunnyDay>True</SunnyDay>
      <Location>
        <latitude>-122.1</latitude>
        <longitude>32.0</longitude>
      </Location>
    </Greenhouse>
  </Create>
</Query>

Add Heater

Code Block
languagexml
titleAdd Heater
linenumberstrue
<Query>
  <Create>
    <Heater>
      <OnOffState>
        on
      </OnOffState>
      <HeatingLevel>
        2
      </HeatingLevel>
    </Heater>
  </Create>
</Query>

Start Simulation

Code Block
languagexml
titleStart Simulator
linenumberstrue
<Query>
  <DeleteAll>
    

...

<TempSensor>
      

...

<sensorId ne=""/>
    </

...

TempSensor>
  </DeleteAll>
  <Find format="Version">

...

    <Greenhouse>

...

      <GreenhouseID 

...

ne 

...

="

...

"/>
    </Greenhouse>
  </Find>
  <Create>
    <TempSensor>
      <Simulated>
        true
   

...

   </

...

Simulated>
  

...

    <GreenhouseID>

...

        

...

[:$Response.Message.Value.Find.Result.Greenhouse.GreenhouseID:]
      </GreenhouseID>
 

...

   </TempSensor>
  </Create>
</

...

Query>

Find Greenhouse / TempSensor

Code Block
languagexml
titleFind Greenhouse
linenumberstrue
<Query>
  <Find>
    <Greenhouse>
  

...

    <GreenhouseID ne=""/>
   

...

 </Greenhouse>
  </Find>

...

</

...

Query>
 
<!-- Custom container -->

...

<Query as="Khans.GreenDen">
  <Find>
    

...

<Greenhouse>
      <GreenhouseID 

...

ne=""/>
    </

...

Greenhouse>
  </

...

Find Temperature above x degree C

...

languagexml
titleFind Temperature
linenumberstrue

...

Find>
</Query>
 
<!-- Filter the result -->
<Query as="Khans.GreenDen">
  <Find only="Greenhouse:ExternalEnv">
    

...

<Greenhouse>
      

...

<GreenhouseID ne=""

...

/>
    </

...

Greenhouse>
  </Find>
</Query>


Source Code

Import into TQLStudio

ProjectNameImport Link
Temp Sensor SerialSimulateTempSensor