Skip to content

Commit 8585de1

Browse files
authored
EVALG-75: Add Application Design tutorial (#693)
1 parent 0e3e9b1 commit 8585de1

File tree

7 files changed

+534
-1
lines changed

7 files changed

+534
-1
lines changed

.github/workflows/lint_python.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
- name: Set up Python
3535
uses: actions/setup-python@v2
3636
with:
37-
python-version: 3.7
37+
python-version: 3.8
3838
- name: Install necessary tools
3939
run: pip install black==22.3.0
4040
- name: Perform format check in a pull request
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Tutorial: Application Design
2+
3+
This code is part of the Connext Application Design tutorial.
4+
5+
There are multiple subfolders which contain a toolchain-specific tutorial.
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<?xml-model href="https://community.rti.com/schema/7.3.0/rti_dds_profiles.xsd" type="application/xml" schematypens="http://www.w3.org/2001/XMLSchema"?>
3+
<dds>
4+
<types>
5+
<const name="VIN_LENGTH" type="uint8" value="17"/>
6+
<typedef name="VIN" type="string" stringMaxLength="VIN_LENGTH"/>
7+
8+
<struct name="Coord" extensibility="final" nested="true">
9+
<member name="lat" type="float64"/>
10+
<member name="lon" type="float64"/>
11+
</struct>
12+
<struct name="VehicleTransit" extensibility="appendable">
13+
<member name="vehicle_vin"
14+
type="nonBasic"
15+
nonBasicTypeName="VIN"
16+
key="true" />
17+
<member name="current_position"
18+
type="nonBasic"
19+
nonBasicTypeName="Coord" />
20+
<member name="current_route"
21+
type="nonBasic"
22+
nonBasicTypeName="Coord"
23+
sequenceMaxLength="-1"
24+
optional="true" /> <!-- 'no route' == standby -->
25+
</struct>
26+
27+
<typedef name="Percentage" type="float64" min="0.0" max="100.0"/>
28+
<struct name="VehicleMetrics" extensibility="appendable">
29+
<member name="vehicle_vin"
30+
type="nonBasic"
31+
nonBasicTypeName="VIN"
32+
key="true"/>
33+
<member name="fuel_level"
34+
type="nonBasic"
35+
nonBasicTypeName="Percentage"/>
36+
</struct>
37+
</types>
38+
39+
<qos_library name="VehicleModeling_Library">
40+
<qos_profile name="VehicleMetrics_Profile">
41+
<datawriter_qos base_name="BuiltinQosLib::Generic.BestEffort">
42+
<deadline>
43+
<period>
44+
<sec>10</sec>
45+
<nanosec>0</nanosec>
46+
</period>
47+
</deadline>
48+
</datawriter_qos>
49+
<datareader_qos base_name="BuiltinQosLib::Generic.BestEffort">
50+
<deadline>
51+
<period>
52+
<sec>15</sec>
53+
<nanosec>0</nanosec>
54+
</period>
55+
</deadline>
56+
</datareader_qos>
57+
</qos_profile>
58+
<qos_profile name="VehicleTransit_Profile">
59+
<datawriter_qos base_name="BuiltinQosLib::Generic.StrictReliable">
60+
<durability>
61+
<kind>TRANSIENT_LOCAL_DURABILITY_QOS</kind>
62+
</durability>
63+
</datawriter_qos>
64+
<datareader_qos base_name="BuiltinQosLib::Generic.KeepLastReliable">
65+
<durability>
66+
<kind>TRANSIENT_LOCAL_DURABILITY_QOS</kind>
67+
</durability>
68+
</datareader_qos>
69+
</qos_profile>
70+
</qos_library>
71+
72+
<domain_library name="DomainLibrary">
73+
<domain name="VehicleDomain" domain_id="0">
74+
<topic name="VehicleMetricsTopic" register_type_ref="VehicleMetrics" />
75+
<topic name="VehicleTransitTopic" register_type_ref="VehicleTransit" />
76+
</domain>
77+
</domain_library>
78+
79+
<domain_participant_library name="ParticipantLibrary">
80+
<domain_participant name="PublisherApp" domain_ref="DomainLibrary::VehicleDomain">
81+
<publisher name="Publisher">
82+
<data_writer name="MetricsWriter" topic_ref="VehicleMetricsTopic">
83+
<datawriter_qos base_name="VehicleModeling_Library::VehicleMetrics_Profile" />
84+
</data_writer>
85+
86+
<data_writer name="TransitWriter" topic_ref="VehicleTransitTopic">
87+
<datawriter_qos base_name="VehicleModeling_Library::VehicleTransit_Profile" />
88+
</data_writer>
89+
</publisher>
90+
</domain_participant>
91+
92+
<domain_participant name="SubscriberApp" domain_ref="DomainLibrary::VehicleDomain">
93+
<subscriber name="Subscriber">
94+
<data_reader name="MetricsReader" topic_ref="VehicleMetricsTopic">
95+
<datareader_qos base_name="VehicleModeling_Library::VehicleMetrics_Profile" />
96+
</data_reader>
97+
<data_reader name="TransitReader" topic_ref="VehicleTransitTopic">
98+
<datareader_qos base_name="VehicleModeling_Library::VehicleTransit_Profile" />
99+
</data_reader>
100+
</subscriber>
101+
</domain_participant>
102+
</domain_participant_library>
103+
</dds>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Tutorial: Data Persistence
2+
3+
This code is part of the Connext Application Design tutorial and is included
4+
here in full for convenience.
5+
Please see the tutorial for instructions.
6+
7+
This code targets Python 3.8 and uses the Connext Python API.
8+
9+
## How to build
10+
11+
The example already provides generated types (`VehicleModeling.py`), so nothing
12+
needs to be done for Python applications.
13+
14+
## How to run
15+
16+
The publisher can be run as a file or module. The expected output contains a
17+
description of the application. It will end once the vehicle has run out of fuel.
18+
19+
```console
20+
$ python publisher.py
21+
Running simulation: simulation=Simulation(self._metrics_writer=<rti.connextdds.DataWriter object at 0x7f7e693cdb30>, self._transit_writer=<rti.connextdds.DataWriter object at 0x7f7e697d83f0>, self._vehicle_vin='3P524A8JB256YZOQY', self._vehicle_fuel=100.0, self._vehicle_route=[Coord(lat=10.851231171123665, lon=4.549741897319626), Coord(lat=-19.336436875210726, lon=45.5761709545348), Coord(lat=-28.473080284108043, lon=-3.3714508132107524), Coord(lat=-44.45875114267031, lon=11.536506807784141), Coord(lat=-30.68510788813299, lon=3.7515235253391954), Coord(lat=-45.485688894791096, lon=-19.45044753805537)], self._vehicle_position=Coord(lat=-9.361148743978752, lon=10.532865637410172))
22+
Vehicle '3P524A8JB256YZOQY' has reached its destination, now moving to a new location...
23+
Vehicle '3P524A8JB256YZOQY' has reached its destination, now moving to a new location...
24+
Vehicle '3P524A8JB256YZOQY' ran out of fuel!
25+
26+
```
27+
28+
The subscriber can be run as a file or a module. The expected output contains a
29+
description of the application, followed by periodic updates on the understanding
30+
of the system. It will run until interrupted.
31+
32+
```console
33+
$ python subscriber.py
34+
[[ DASHBOARD: 2024-07-11 13:28:39.503181 ]]
35+
Online vehicles: 1
36+
- Vehicle F01UV8KCSAY5EVBL3:
37+
Fuel updates: 4
38+
Last known destination: Coord(lat=-29.739379575147705, lon=8.811412382546946)
39+
Last known fuel level: 90.24212596289948
40+
Offline vehicles: 1
41+
- Vehicle V93YZUZIYQB71P77Z:
42+
Mean fuel consumption: 2.5009597229252654
43+
Known reached destinations: 3
44+
- Coord(lat=-19.42303588068077, lon=4.543146614919191)
45+
- Coord(lat=-23.903966240016082, lon=-1.5759817176247415)
46+
- Coord(lat=-27.18890271220428, lon=-23.879609290719095)
47+
```
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#
2+
# (c) 2024 Copyright, Real-Time Innovations, Inc. All rights reserved.
3+
#
4+
# RTI grants Licensee a license to use, modify, compile, and create derivative
5+
# works of the Software solely for use with RTI products. The Software is
6+
# provided "as is", with no warranty of any type, including any warranty for
7+
# fitness for any purpose. RTI is under no obligation to maintain or support
8+
# the Software. RTI shall not be liable for any incidental or consequential
9+
# damages arising out of the use or inability to use the software.
10+
#
11+
12+
import os
13+
import sys
14+
from dataclasses import field
15+
from enum import IntEnum
16+
from typing import Optional, Sequence, Union
17+
18+
import rti.idl as idl
19+
20+
VIN_LENGTH = 17
21+
22+
VIN = str
23+
24+
25+
@idl.struct(type_annotations=[idl.final])
26+
class Coord:
27+
lat: float = 0.0
28+
lon: float = 0.0
29+
30+
31+
VehicleMetricsTopic = "VehicleMetrics"
32+
33+
34+
@idl.struct(
35+
member_annotations={
36+
"vehicle_vin": [idl.key, idl.bound(VIN_LENGTH)],
37+
"current_route": [idl.bound(100)],
38+
}
39+
)
40+
class VehicleTransit:
41+
vehicle_vin: str = ""
42+
current_position: Coord = field(default_factory=Coord)
43+
current_route: Optional[Sequence[Coord]] = None
44+
45+
46+
Percentage = float
47+
48+
VehicleTransitTopic = "VehicleTransit"
49+
50+
51+
@idl.struct(
52+
member_annotations={
53+
"vehicle_vin": [idl.key, idl.bound(VIN_LENGTH)],
54+
}
55+
)
56+
class VehicleMetrics:
57+
vehicle_vin: str = ""
58+
fuel_level: float = 0.0
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
#
2+
# (c) 2024 Copyright, Real-Time Innovations, Inc. All rights reserved.
3+
#
4+
# RTI grants Licensee a license to use, modify, compile, and create derivative
5+
# works of the Software solely for use with RTI products. The Software is
6+
# provided "as is", with no warranty of any type, including any warranty for
7+
# fitness for any purpose. RTI is under no obligation to maintain or support
8+
# the Software. RTI shall not be liable for any incidental or consequential
9+
# damages arising out of the use or inability to use the software.
10+
#
11+
12+
import random
13+
import time
14+
import typing
15+
16+
import rti.connextdds as dds
17+
18+
from VehicleModeling import Coord, VehicleMetrics, VehicleTransit
19+
20+
21+
def new_route(
22+
n: int = 5,
23+
start: typing.Optional[Coord] = None,
24+
end: typing.Optional[Coord] = None,
25+
):
26+
def new_random_coord():
27+
return Coord(
28+
(0.5 - random.random()) * 100,
29+
(0.5 - random.random()) * 100,
30+
)
31+
32+
start = start or new_random_coord()
33+
intermediate = (new_random_coord() for _ in range(n))
34+
end = end or new_random_coord()
35+
36+
return [start, *intermediate, end]
37+
38+
39+
class PublisherSimulation:
40+
def __init__(
41+
self,
42+
metrics_writer: "dds.DataWriter",
43+
transit_writer: "dds.DataWriter",
44+
):
45+
self._metrics_writer = metrics_writer
46+
self._transit_writer = transit_writer
47+
self._vehicle_vin: str = "".join(
48+
random.choices("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", k=17)
49+
)
50+
self._vehicle_fuel = 100.0
51+
self._vehicle_route = new_route()
52+
self._vehicle_position = self._vehicle_route.pop(0)
53+
54+
def __repr__(self):
55+
return (
56+
f"Simulation("
57+
f"{self._metrics_writer=}, "
58+
f"{self._transit_writer=}, "
59+
f"{self._vehicle_vin=}, "
60+
f"{self._vehicle_fuel=}, "
61+
f"{self._vehicle_route=}, "
62+
f"{self._vehicle_position=})"
63+
)
64+
65+
@property
66+
def has_ended(self):
67+
return self._is_out_of_fuel
68+
69+
@property
70+
def _is_out_of_fuel(self):
71+
return self._vehicle_fuel <= 0.0
72+
73+
@property
74+
def _is_on_standby(self):
75+
return not self._vehicle_route
76+
77+
def run(self):
78+
while not self.has_ended:
79+
self._metrics_writer.write(
80+
VehicleMetrics(
81+
self._vehicle_vin,
82+
self._vehicle_fuel,
83+
)
84+
)
85+
86+
self._transit_writer.write(
87+
VehicleTransit(
88+
self._vehicle_vin,
89+
current_route=self._vehicle_route,
90+
current_position=self._vehicle_position,
91+
)
92+
)
93+
94+
time.sleep(1)
95+
96+
if self._is_on_standby:
97+
print(
98+
f"Vehicle '{self._vehicle_vin}' has reached its destination, now moving to a new location..."
99+
)
100+
self._vehicle_route = new_route(start=self._vehicle_position)
101+
102+
self._vehicle_position = self._vehicle_route.pop(0)
103+
self._vehicle_fuel -= 10 * random.random()
104+
105+
if self._is_out_of_fuel:
106+
self._vehicle_fuel = 0.0
107+
108+
print(f"Vehicle '{self._vehicle_vin}' ran out of fuel!")
109+
110+
111+
def main():
112+
dds.DomainParticipant.register_idl_type(VehicleMetrics, "VehicleMetrics")
113+
dds.DomainParticipant.register_idl_type(VehicleTransit, "VehicleTransit")
114+
115+
qos_provider = dds.QosProvider("../VehicleModeling.xml")
116+
117+
with qos_provider.create_participant_from_config(
118+
"ParticipantLibrary::PublisherApp"
119+
) as participant:
120+
metrics_writer = dds.DataWriter(
121+
participant.find_datawriter("Publisher::MetricsWriter")
122+
)
123+
transit_writer = dds.DataWriter(
124+
participant.find_datawriter("Publisher::TransitWriter")
125+
)
126+
127+
simulation = PublisherSimulation(
128+
metrics_writer=metrics_writer, transit_writer=transit_writer
129+
)
130+
print(f"Running simulation: {simulation=}")
131+
simulation.run()
132+
133+
134+
if __name__ == "__main__":
135+
try:
136+
main()
137+
except KeyboardInterrupt:
138+
pass

0 commit comments

Comments
 (0)