diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 38edd12b..6f639c28 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -49,3 +49,14 @@ install( COMPONENT dev) add_subdirectory(roundtrip) + +install( + FILES atc/publisher.cpp + atc/subscriber.cpp + atc/ATCDataModel.idl + atc/CMakeLists.txt + atc/readme.rst + DESTINATION "${CMAKE_INSTALL_EXAMPLESDIR}/atc" + COMPONENT dev) + +add_subdirectory(atc) diff --git a/examples/atc/ATCDataModel.idl b/examples/atc/ATCDataModel.idl new file mode 100644 index 00000000..89bd1d39 --- /dev/null +++ b/examples/atc/ATCDataModel.idl @@ -0,0 +1,32 @@ +/* + * Copyright(c) 2006 to 2020 ZettaScale Technology and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +module ATCDataModule +{ + struct Position + { + short x; // latitude + short y; // longitude + short z; // altitude + }; + + struct Flight + { + /** Flight Call ID */ + long ID; //@Key + + Position pos; + // float Speed; + /** Current Region we are flying into*/ + string CurrentRegion; + }; + #pragma keylist Flight ID +}; diff --git a/examples/atc/CMakeLists.txt b/examples/atc/CMakeLists.txt new file mode 100644 index 00000000..ec0b3d0f --- /dev/null +++ b/examples/atc/CMakeLists.txt @@ -0,0 +1,40 @@ +# +# Copyright(c) 2020 to 2022 ZettaScale Technology and others +# +# This program and the accompanying materials are made available under the +# terms of the Eclipse Public License v. 2.0 which is available at +# http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License +# v. 1.0 which is available at +# http://www.eclipse.org/org/documents/edl-v10.php. +# +# SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause +# +cmake_minimum_required(VERSION 3.16) +project(atc LANGUAGES C CXX) + +set(CMAKE_CXX_STANDARD 17) + +if(NOT TARGET CycloneDDS-CXX::ddscxx) + find_package(CycloneDDS-CXX REQUIRED) +endif() + +idlcxx_generate(TARGET ATCDataModel FILES ATCDataModel.idl WARNINGS no-implicit-extensibility) + +add_executable(pubATC publisher.cpp) +add_executable(subATC subscriber.cpp) + +# Link both executables to idl data type library and ddscxx. +target_link_libraries(pubATC CycloneDDS-CXX::ddscxx ATCDataModel) +target_link_libraries(subATC CycloneDDS-CXX::ddscxx ATCDataModel) + +# Disable the static analyzer in GCC to avoid crashing the GNU C++ compiler +# on Azure Pipelines +if(DEFINED ENV{SYSTEM_TEAMFOUNDATIONSERVERURI}) + if(CMAKE_C_COMPILER_ID STREQUAL "GNU" AND ANALYZER STREQUAL "on") + target_compile_options(pubATC PRIVATE -fno-analyzer) + target_compile_options(subATC PRIVATE -fno-analyzer) + endif() +endif() + +set_property(TARGET pubATC PROPERTY CXX_STANDARD ${cyclonedds_cpp_std_to_use}) +set_property(TARGET subATC PROPERTY CXX_STANDARD ${cyclonedds_cpp_std_to_use}) diff --git a/examples/atc/publisher.cpp b/examples/atc/publisher.cpp new file mode 100644 index 00000000..cb364a06 --- /dev/null +++ b/examples/atc/publisher.cpp @@ -0,0 +1,144 @@ +/* + * Copyright(c) 2006 to 2020 ZettaScale Technology and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#include +#include "dds/dds.hpp" +#include "ATCDataModel.hpp" + + +int main() { + std::cout << "Cyclone- ATC Pub" << std::endl; + + /** A dds::domain::DomainParticipant is created for the default domain. */ + dds::domain::DomainParticipant dp(0); + dds::topic::qos::TopicQos topicQos = dp.default_topic_qos(); + + /** A dds::topic::Topic is created for our sample type on the domain participant. */ + dds::topic::Topic topic(dp, "FlightData", topicQos); + + /** 5 dds Publishers are associated to 5 different partitions. */ + dds::pub::qos::PublisherQos PubGotalandQoS = dp.default_publisher_qos(); + dds::pub::qos::PublisherQos PubSardegnaQoS = dp.default_publisher_qos(); + dds::pub::qos::PublisherQos PubNorrlandQoS = dp.default_publisher_qos(); + dds::pub::qos::PublisherQos PubLombardiaQoS = dp.default_publisher_qos(); + dds::pub::qos::PublisherQos pubSiciliaQoS = dp.default_publisher_qos(); + dds::pub::qos::PublisherQos pubZeelandQoS = dp.default_publisher_qos(); + dds::pub::qos::PublisherQos pubDrentheQoS = dp.default_publisher_qos(); + + PubGotalandQoS << dds::core::policy::Partition("/Europe/Sweden/Gotaland"); + PubNorrlandQoS << dds::core::policy::Partition("/Europe/Sweden/Norrland"); + PubSardegnaQoS << dds::core::policy::Partition("/Europe/Italy/Sardegna"); + PubLombardiaQoS << dds::core::policy::Partition("/Europe/Italy/Lombardia"); + pubSiciliaQoS << dds::core::policy::Partition("/Europe/Italy/Sicilia"); + pubZeelandQoS << dds::core::policy::Partition("/Europe/Netherlands/Zeeland"); + pubDrentheQoS << dds::core::policy::Partition("/Europe/Netherlands/Drenthe"); + + dds::pub::Publisher pubGotaland(dp, PubGotalandQoS); + dds::pub::Publisher pubNorrland(dp, PubNorrlandQoS); + dds::pub::Publisher pubSardegna(dp, PubSardegnaQoS); + dds::pub::Publisher pubLombardia(dp, PubLombardiaQoS); + dds::pub::Publisher pubSicilia(dp, pubSiciliaQoS); + dds::pub::Publisher pubZeeland(dp, pubZeelandQoS); + dds::pub::Publisher pubDrenthe(dp, pubDrentheQoS); + + /** A dds::pub::DataWriter is created on the Publisher & Topic with the associated Partition Qos. */ + dds::pub::qos::DataWriterQos dwqos = topic.qos(); + + dds::pub::DataWriter dwGotaland(pubGotaland, topic, dwqos); + dds::pub::DataWriter dwNorrland(pubNorrland, topic, dwqos); + dds::pub::DataWriter dwSardegna(pubSardegna, topic, dwqos); + dds::pub::DataWriter dwLombardia(pubLombardia, topic, dwqos); + dds::pub::DataWriter dwsicilia(pubSicilia, topic, dwqos); + dds::pub::DataWriter dwzeeland(pubZeeland, topic, dwqos); + dds::pub::DataWriter dwdrenthe(pubDrenthe, topic, dwqos); + + // std::cout << "@ With ddsi, Let the Apps discover each other a bit ZZZzzz:" << std::endl; + //dds_sleepfor (DDS_MSECS (5000)); + + std::cout << "===> Waiting for a subscriber. " << std::endl; + while (dwdrenthe.publication_matched_status().current_count() == 0 && + dwzeeland.publication_matched_status().current_count() == 0 && + dwsicilia.publication_matched_status().current_count() == 0 && + dwLombardia.publication_matched_status().current_count() == 0 && + dwSardegna.publication_matched_status().current_count() == 0 && + dwNorrland.publication_matched_status().current_count() == 0 && + dwGotaland.publication_matched_status().current_count() == 0) + { + dds_sleepfor (DDS_MSECS (20)); + } + std::cout << "===> Found a matching one ... " << std::endl; + + for (int16_t i = 0; i < 5; i++) { + ATCDataModule::Position p1(i+800, i+20, i+93 ); + ATCDataModule::Flight FlightInstance(i, p1,"** /Europe/Sweden/Gotaland partition ** "); + dwGotaland << FlightInstance; + std::cout << "=> [Radar Simulator] detecting FlightID: "<< FlightInstance.ID() << " in the /Europe/Sweden/Gotaland Region " << std::endl; + + dds_sleepfor (DDS_MSECS (500)); + } + + for (int16_t i = 5; i < 10; i++) { + ATCDataModule::Position p2(i+7900, i+20, i+93 ); + ATCDataModule::Flight FlightInstance(i, p2, "** /Europe/Sweden/Norrland partition ++"); + dwNorrland << FlightInstance; + std::cout << "=> [Radar Simulator] detecting FlightID: "<< FlightInstance.ID() << " in the /Europe/Sweden/Norrland Region " << std::endl; + + dds_sleepfor (DDS_MSECS (500)); + } + + for (int16_t i = 10; i < 15; i++) { + ATCDataModule::Position p3(i+800, i+20, i+93 ); + ATCDataModule::Flight FlightInstance(i, p3, "** /Europe/Italy/Sardegna partition &&"); + dwSardegna << FlightInstance; + std::cout << "=> [Radar Simulator] detecting FlightID: "<< FlightInstance.ID() << " in the /Europe/Italy/Sardegna Region " << std::endl; + + dds_sleepfor (DDS_MSECS (500)); + } + + for (int16_t i = 15; i < 20; i++) { + ATCDataModule::Position p4(i+99, i+97, i+93 ); + ATCDataModule::Flight FlightInstance(i, p4, "** /Europe/Italy/Lombardia partition %%"); + dwLombardia << FlightInstance; + std::cout << "=> [Radar Simulator] detecting FlightID: "<< FlightInstance.ID() << " in the /Europe/Italy/Lombardia Region " << std::endl; + + dds_sleepfor (DDS_MSECS (500)); + } + + for (int16_t i = 20; i < 25; i++) { + ATCDataModule::Position p5(i+242, i+240, i+930 ); + ATCDataModule::Flight FlightInstance(i,p5, "** /Europe/Italy/Sicilia partition $$"); + dwsicilia << FlightInstance; + std::cout << "=> [Radar Simulator] detecting FlightID: "<< FlightInstance.ID() << " in the /Europe/Italy/Sicilia Region " << std::endl; + + dds_sleepfor (DDS_MSECS (500)); + } + + for (int16_t i = 25; i < 30; i++) { + ATCDataModule::Position p6(i+22, i+20, i+930 ); + ATCDataModule::Flight FlightInstance(i,p6, "** /Europe/Netherlands/Zeeland partition &&"); + dwzeeland << FlightInstance; + std::cout << "=> [Radar Simulator] detecting FlightID: "<< FlightInstance.ID() << " in the /Europe/Netherlands/Zeeland Region " << std::endl; + + dds_sleepfor (DDS_MSECS (500)); + } + + for (int16_t i = 30; i < 35; i++) { + ATCDataModule::Position p7(i+22, i+20, i+930 ); + ATCDataModule::Flight FlightInstance(i,p7, "** /Europe/Netherlands/Drenthe partition &&"); + dwdrenthe << FlightInstance; + std::cout << "=> [Radar Simulator] detecting FlightID: "<< FlightInstance.ID() << " in the /Europe/Netherlands/Drenthe Region " << std::endl; + + dds_sleepfor (DDS_MSECS (500)); + } + + dds_sleepfor (DDS_MSECS (5000)); + return 0; +} diff --git a/examples/atc/readme.rst b/examples/atc/readme.rst new file mode 100644 index 00000000..c60706f6 --- /dev/null +++ b/examples/atc/readme.rst @@ -0,0 +1,69 @@ +.. + Copyright(c) 2006 to 2022 ZettaScale Technology and others + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v. 2.0 which is available at + http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + v. 1.0 which is available at + http://www.eclipse.org/org/documents/edl-v10.php. + + SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + +ATC +========== + +Description +*********** + +This example mimics, in its most simplistic form, an Air Traffic Controller System , made of two subsystems: + +A **Radar System** that, detects and publishes Flights in different region in Europe, within a dedicated Global data space, +and an Air **Flight Visualiser system** that can build *different views* of that space, either Per country, Per Region or Per the Entire continent. + +Design +****** + +The Radar system is modelled by the Publishing program ( **pubATC** ). This program + +- Get involved in the European Global Data Space DDS domain (domainId=0), +- Creates 6 DDS partitions that corresponds to the 6 regions shown on the Map (see Fig below), then it +- Publishes 5 Flights with different Call signs (CIDs) as well as their respective positions . + +The European Global Data Space is organized hierarchically following this pattern: `///`. + +The Air Flight Visualizer is modelled by the Subscribing program (**subATC**). +This program allows you to Subscribe to a specific region, for example, to the region `/Europe/Sweden/Norrland` or +at the scale of the country (`/Europe/Italy/*`) or the scale of the entire continent (`/*`), Regular expressions can be used to specify the scopes . + +The subATC program will create one subscriber to get all the data corresponding to the view you are building using the DDS concept of Partition. + +The subATC program takes at most one argument. This argument specifies the absolute name of the region i.e `///`. + +Both programs use the default DDS QoS but the PartitionQoS. + +The example helps you understand the concept of partition in DDS and how it can be used to creates different +views on the data or split the dds domain in smaller logical groups. + + +Running the examples +******************** + +It is recommended to run one Publishing program (pubATC) and several Subscribing programs in different terminals . + +**Scenario1** : Subscribing to all the flights to have a global view on all the flights + +`./pubATC` + +`./subATC "*"` + +**Scenario2** : Gross grain Subscription, subscribe to an entire country view + +`./pubATC` + +`./subATC /Europe/Netherlands/*` + +**Scenario3** : Fine grain Subscription, subscribe to a give region + +`./pubATC` + +`./subATC /Europe/Netherlands/Zeeland` diff --git a/examples/atc/subscriber.cpp b/examples/atc/subscriber.cpp new file mode 100644 index 00000000..04f6f27d --- /dev/null +++ b/examples/atc/subscriber.cpp @@ -0,0 +1,71 @@ +/* + * Copyright(c) 2006 to 2020 ZettaScale Technology and others + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License + * v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +#include +#include "dds/dds.hpp" +#include "ATCDataModel.hpp" + + +int main(int argc, char* argv[]) { + std::cout << " Cyclone- ATC Subscriber, Flight Visualiser per Country or Region *** " << std::endl; + + /** A domain participant and topic are created identically as in the ::publisher */ + dds::domain::DomainParticipant dp(0); + dds::topic::qos::TopicQos topicQos = dp.default_topic_qos(); + dds::topic::Topic topic(dp, "FlightData", topicQos); + + /** A dds::pub::Sub is created on the domain participant. */ + std::string PartitionName; + + if (argc<2) { + std::cout << "=< Specify the Region Name (Partition) , a regular expression with * or ? " << std::endl; + std::cout << "=< for examples :*;/* ; /E*; /Europe/*; or ..." << std::endl; + std::cout << "=< : /Europe/Italy/*; /Europe/Italy/Sardegna; /Europe/Italy/Sicilia; /Europe/Netherlands/*, ..." << std::endl; + std::cout << "=< Enter the Region Name or Expression:" << std::endl; + + std::cin >> PartitionName; + std::cout << " Thanks !" << std::endl; + } else { + PartitionName=argv[1]; + } + //std::cout << " The partition is..:" << PartitionName < dr(sub, topic, drqos); + + bool sampleReceived = false; + // The Subscriber is going to wait during 120 seconds then leaves , to allow receiving all the instances and their samples + int count = 0; + do { + dds::sub::LoanedSamples samples = dr.take(); + for (dds::sub::LoanedSamples::const_iterator sample = samples.begin(); sample < samples.end(); ++sample) { + if (sample->info().valid()) { + std::cout << "=== [Flight Visualiser] Flight with ID :" << sample->data().ID() <<" Visualized"<data().CurrentRegion() << "\"" << std::endl; + sampleReceived = true; + } else { + // std::cout << "data disposed, explicitely or the DW lost his liveliness" << std::endl; + } + } + + dds_sleepfor (DDS_MSECS (1)); + ++count; + } while (count < 120000 ) ; + + if (!sampleReceived) { + std::cerr << "Warning: Waited too long (120 seconds) but no sample received" << std::endl; + } + return 0; +}