diff --git a/4_keys_instances/c++98/application.h b/4_keys_instances/c++98/application.h index 86a95b2..81429e3 100644 --- a/4_keys_instances/c++98/application.h +++ b/4_keys_instances/c++98/application.h @@ -122,9 +122,6 @@ inline void print_station_kind(StationKind station_kind) case COCOA_BUTTER_CONTROLLER: std::cout << "COCOA_BUTTER_CONTROLLER"; break; - case COCOA_LIQUOR_CONTROLLER: - std::cout << "COCOA_LIQUOR_CONTROLLER"; - break; case VANILLA_CONTROLLER: std::cout << "VANILLA_CONTROLLER"; break; diff --git a/4_keys_instances/chocolate_factory.idl b/4_keys_instances/chocolate_factory.idl index 25c9cb2..525ccd7 100644 --- a/4_keys_instances/chocolate_factory.idl +++ b/4_keys_instances/chocolate_factory.idl @@ -28,11 +28,10 @@ struct Temperature { // Kind of station processing the chocolate enum StationKind { INVALID_CONTROLLER, - SUGAR_CONTROLLER, COCOA_BUTTER_CONTROLLER, - COCOA_LIQUOR_CONTROLLER, - VANILLA_CONTROLLER, + SUGAR_CONTROLLER, MILK_CONTROLLER, + VANILLA_CONTROLLER, TEMPERING_CONTROLLER }; diff --git a/5_basic_qos/c++11/monitoring_ctrl_application.cxx b/5_basic_qos/c++11/monitoring_ctrl_application.cxx index 32ccca0..7019e33 100644 --- a/5_basic_qos/c++11/monitoring_ctrl_application.cxx +++ b/5_basic_qos/c++11/monitoring_ctrl_application.cxx @@ -87,7 +87,8 @@ void monitor_temperature(dds::sub::DataReader& reader) // returned when LoanedSamples destructor called. dds::sub::LoanedSamples samples = reader.take(); - // Receive updates from stations about the state of current lots + // Receive updates from tempering station about chocolate temperature. + // Only an error if over 32 degrees Fahrenheit. for (const auto& sample : samples) { if (sample.info().valid()) { if (sample.data().degrees() > 32) { diff --git a/5_basic_qos/c++98/application.h b/5_basic_qos/c++98/application.h index 86a95b2..81429e3 100644 --- a/5_basic_qos/c++98/application.h +++ b/5_basic_qos/c++98/application.h @@ -122,9 +122,6 @@ inline void print_station_kind(StationKind station_kind) case COCOA_BUTTER_CONTROLLER: std::cout << "COCOA_BUTTER_CONTROLLER"; break; - case COCOA_LIQUOR_CONTROLLER: - std::cout << "COCOA_LIQUOR_CONTROLLER"; - break; case VANILLA_CONTROLLER: std::cout << "VANILLA_CONTROLLER"; break; diff --git a/5_basic_qos/chocolate_factory.idl b/5_basic_qos/chocolate_factory.idl index 25c9cb2..525ccd7 100644 --- a/5_basic_qos/chocolate_factory.idl +++ b/5_basic_qos/chocolate_factory.idl @@ -28,11 +28,10 @@ struct Temperature { // Kind of station processing the chocolate enum StationKind { INVALID_CONTROLLER, - SUGAR_CONTROLLER, COCOA_BUTTER_CONTROLLER, - COCOA_LIQUOR_CONTROLLER, - VANILLA_CONTROLLER, + SUGAR_CONTROLLER, MILK_CONTROLLER, + VANILLA_CONTROLLER, TEMPERING_CONTROLLER }; diff --git a/5_content_filters/c++11/ingredient_application.cxx b/5_content_filters/c++11/ingredient_application.cxx new file mode 100644 index 0000000..bfe186b --- /dev/null +++ b/5_content_filters/c++11/ingredient_application.cxx @@ -0,0 +1,204 @@ +/* + * (c) Copyright, Real-Time Innovations, 2020. All rights reserved. + * RTI grants Licensee a license to use, modify, compile, and create derivative + * works of the software solely for use with RTI Connext DDS. Licensee may + * redistribute copies of the software provided that all such copies are subject + * to this license. The software is provided "as is", with no warranty of any + * type, including any warranty for fitness for any purpose. RTI is under no + * obligation to maintain or support the software. RTI shall not be liable for + * any incidental or consequential damages arising out of the use or inability + * to use the software. + */ + +#include +#include + +#include +#include +#include // for sleep() +#include // for logging +// Or simply include + +#include "chocolate_factory.hpp" +#include "application.hpp" // Argument parsing + +using namespace application; + +// Ingredient application: +// 1) Subscribes to the lot state +// 2) "Processes" the lot. (In this example, that means sleep for a time) +// 3) After "processing" the lot, publishes an updated lot state + +void process_lot( + const StationKind station_kind, + const std::map& next_station, + dds::sub::DataReader& lot_state_reader, + dds::pub::DataWriter& lot_state_writer) +{ + // Take all samples. Samples are loaned to application, loan is + // returned when LoanedSamples destructor called. + dds::sub::LoanedSamples samples = + lot_state_reader.take(); + + // Process lots waiting for tempering + for (const auto& sample : samples) { + if (!sample.info().valid() || shutdown_requested) { + break; + } + + // No need to check that this is the next station: content filter + // ensures that the reader only receives lots with + // next_station == this station + std::cout << "Processing lot #" << sample.data().lot_id() << std::endl; + + // Send an update that this station is processing lot + ChocolateLotState updated_state(sample.data()); + updated_state.lot_status(LotStatusKind::PROCESSING); + updated_state.next_station(StationKind::INVALID_CONTROLLER); + updated_state.station(station_kind); + lot_state_writer.write(updated_state); + + // "Processing" the lot. + rti::util::sleep(dds::core::Duration(5)); + + // Send an update that this station is done processing lot + updated_state.lot_status(LotStatusKind::COMPLETED); + updated_state.next_station(next_station.at(station_kind)); + updated_state.station(station_kind); + lot_state_writer.write(updated_state); + } +} // The LoanedSamples destructor returns the loan + +StationKind string_to_stationkind(const std::string& station_kind) +{ + if (station_kind == "SUGAR_CONTROLLER") { + return StationKind::SUGAR_CONTROLLER; + } else if (station_kind == "COCOA_BUTTER_CONTROLLER") { + return StationKind::COCOA_BUTTER_CONTROLLER; + } else if (station_kind == "MILK_CONTROLLER") { + return StationKind::MILK_CONTROLLER; + } else if (station_kind == "VANILLA_CONTROLLER") { + return StationKind::VANILLA_CONTROLLER; + } + return StationKind::INVALID_CONTROLLER; +} + +void run_example(unsigned int domain_id, const std::string& station_kind) +{ + StationKind current_station = string_to_stationkind(station_kind); + std::cout << station_kind << " station starting" << std::endl; + // The stations are in a fixed order, this defines which station is next + const std::map next_station { + { StationKind::COCOA_BUTTER_CONTROLLER, StationKind::SUGAR_CONTROLLER }, + { StationKind::SUGAR_CONTROLLER, StationKind::MILK_CONTROLLER }, + { StationKind::MILK_CONTROLLER, StationKind::VANILLA_CONTROLLER }, + { StationKind::VANILLA_CONTROLLER, StationKind::TEMPERING_CONTROLLER } + }; + + // Loads the QoS from the qos_profiles.xml file. + dds::core::QosProvider qos_provider("./qos_profiles.xml"); + + // A DomainParticipant allows an application to begin communicating in + // a DDS domain. Typically there is one DomainParticipant per application. + // Uses TemperingApplication QoS profile to set participant name. + dds::domain::DomainParticipant participant( + domain_id, + qos_provider.participant_qos( + "ChocolateFactoryLibrary::TemperingApplication")); + + // A Topic has a name and a datatype. Create Topics. + // Topic names are constants defined in the IDL file. + dds::topic::Topic lot_state_topic( + participant, + CHOCOLATE_LOT_STATE_TOPIC); + std::string filter_value = "'" + station_kind + "'"; + dds::topic::ContentFilteredTopic + filtered_lot_state_topic( + lot_state_topic, + "FilteredLot", + dds::topic::Filter("next_station = %0", { filter_value })); + + // A Publisher allows an application to create one or more DataWriters + // Create Publisher with default QoS + dds::pub::Publisher publisher(participant); + + // Create DataWriter of Topic "ChocolateLotState" + // using ChocolateLotStateProfile QoS profile for State Data + dds::pub::DataWriter lot_state_writer( + publisher, + lot_state_topic, + qos_provider.datawriter_qos( + "ChocolateFactoryLibrary::ChocolateLotStateProfile")); + + // A Subscriber allows an application to create one or more DataReaders + dds::sub::Subscriber subscriber(participant); + + // Create DataReader of Topic "ChocolateLotState". + // using ChocolateLotStateProfile QoS profile for State Data + dds::sub::DataReader lot_state_reader( + subscriber, + filtered_lot_state_topic, + qos_provider.datareader_qos( + "ChocolateFactoryLibrary::ChocolateLotStateProfile")); + + // Obtain the DataReader's Status Condition + dds::core::cond::StatusCondition reader_status_condition(lot_state_reader); + + // Contains statuses that entities can be notified about + using dds::core::status::StatusMask; + + // Enable the 'data available' and 'requested incompatible qos' statuses + reader_status_condition.enabled_statuses(StatusMask::data_available()); + + // Associate a handler with the status condition. This will run when the + // condition is triggered, in the context of the dispatch call (see below) + reader_status_condition.extensions().handler([&]() { + if ((lot_state_reader.status_changes() & StatusMask::data_available()) + != StatusMask::none()) { + process_lot( + current_station, + next_station, + lot_state_reader, + lot_state_writer); + } + }); + + // Create a WaitSet and attach the StatusCondition + dds::core::cond::WaitSet waitset; + waitset += reader_status_condition; + + while (!shutdown_requested) { + // Wait for ChocolateLotState + std::cout << "Waiting for lot" << std::endl; + waitset.dispatch(dds::core::Duration(10)); // Wait up to 10s for update + } +} + +int main(int argc, char *argv[]) +{ + // Parse arguments and handle control-C + auto arguments = parse_arguments(argc, argv); + if (arguments.parse_result == ParseReturn::PARSE_RETURN_EXIT) { + return EXIT_SUCCESS; + } else if (arguments.parse_result == ParseReturn::PARSE_RETURN_FAILURE) { + return EXIT_FAILURE; + } + setup_signal_handlers(); + + // Sets Connext verbosity to help debugging + rti::config::Logger::instance().verbosity(arguments.verbosity); + + try { + run_example(arguments.domain_id, arguments.station_kind); + } catch (const std::exception& ex) { + // This will catch DDS exceptions + std::cerr << "Exception in run_example(): " << ex.what() << std::endl; + return EXIT_FAILURE; + } + + // Releases the memory used by the participant factory. Optional at + // application shutdown + dds::domain::DomainParticipant::finalize_participant_factory(); + + return EXIT_SUCCESS; +} diff --git a/5_content_filters/c++11/monitoring_ctrl_application.cxx b/5_content_filters/c++11/monitoring_ctrl_application.cxx new file mode 100644 index 0000000..4d75355 --- /dev/null +++ b/5_content_filters/c++11/monitoring_ctrl_application.cxx @@ -0,0 +1,237 @@ +/* + * (c) Copyright, Real-Time Innovations, 2020. All rights reserved. + * RTI grants Licensee a license to use, modify, compile, and create derivative + * works of the software solely for use with RTI Connext DDS. Licensee may + * redistribute copies of the software provided that all such copies are subject + * to this license. The software is provided "as is", with no warranty of any + * type, including any warranty for fitness for any purpose. RTI is under no + * obligation to maintain or support the software. RTI shall not be liable for + * any incidental or consequential damages arising out of the use or inability + * to use the software. + */ + +#include +#include + +#include +#include +#include // for sleep() +#include // for logging +// Or simply include + +#include "chocolate_factory.hpp" +#include "application.hpp" // Argument parsing + +using namespace application; + +void publish_start_lot( + dds::pub::DataWriter lot_state_writer, + unsigned int lots_to_process) +{ + ChocolateLotState sample; + for (unsigned int count = 0; !shutdown_requested && count < lots_to_process; + count++) { + // Set the values for a chocolate lot that is going to be sent to wait + // at the tempering station + sample.lot_id(count % 100); + sample.lot_status(LotStatusKind::WAITING); + sample.next_station(StationKind::COCOA_BUTTER_CONTROLLER); + + std::cout << std::endl << "Starting lot: " << std::endl; + std::cout << "[lot_id: " << sample.lot_id() + << " next_station: " << sample.next_station() << "]" + << std::endl; + + // Send an update to station that there is a lot waiting for tempering + lot_state_writer.write(sample); + + rti::util::sleep(dds::core::Duration(30)); + } +} + +unsigned int monitor_lot_state(dds::sub::DataReader& reader) +{ + // Take all samples. Samples are loaned to application, loan is + // returned when LoanedSamples destructor called. + unsigned int samples_read = 0; + dds::sub::LoanedSamples samples = reader.take(); + + // Receive updates from stations about the state of current lots + for (const auto& sample : samples) { + std::cout << "Received Lot Update:" << std::endl; + if (sample.info().valid()) { + std::cout << sample.data() << std::endl; + samples_read++; + } else { + // Detect that a lot is complete by checking for + // the disposed state. + if (sample.info().state().instance_state() + == dds::sub::all::InstanceState::not_alive_disposed()) { + ChocolateLotState key_holder; + // Fills in only the key field values associated with the + // instance + reader.key_value(key_holder, sample.info().instance_handle()); + std::cout << "[lot_id: " << key_holder.lot_id() + << " is completed]" << std::endl; + } + } + } + + return samples_read; +} + +// Add monitor_temperature function +void monitor_temperature(dds::sub::DataReader& reader) +{ + // Take all samples. Samples are loaned to application, loan is + // returned when LoanedSamples destructor called. + dds::sub::LoanedSamples samples = reader.take(); + + // Receive updates from tempering station about chocolate temperature. + // Only an error if over 32 degrees Fahrenheit. + for (const auto& sample : samples) { + if (sample.info().valid()) { + std::cout << "Tempering temperature out of range: " + << sample.data() << std::endl; + } + } +} + +void run_example(unsigned int domain_id, unsigned int lots_to_process) +{ + // Loads the QoS from the qos_profiles.xml file. + dds::core::QosProvider qos_provider("./qos_profiles.xml"); + + // A DomainParticipant allows an application to begin communicating in + // a DDS domain. Typically there is one DomainParticipant per application. + // Load DomainParticipant QoS profile + dds::domain::DomainParticipant participant( + domain_id, + qos_provider.participant_qos( + "ChocolateFactoryLibrary::MonitoringControlApplication")); + + // A Topic has a name and a datatype. Create a Topic with type + // ChocolateLotState. Topic name is a constant defined in the IDL file. + dds::topic::Topic topic( + participant, + CHOCOLATE_LOT_STATE_TOPIC); + // Add a Topic for Temperature to this application + dds::topic::Topic temperature_topic( + participant, + CHOCOLATE_TEMPERATURE_TOPIC); + dds::topic::ContentFilteredTopic + filtered_temperature_topic( + temperature_topic, + "FilteredTemperature", + dds::topic::Filter( + "degrees > %0 or degrees < %1", + { "32", "30" })); + + // A Publisher allows an application to create one or more DataWriters + // Publisher QoS is configured in USER_QOS_PROFILES.xml + dds::pub::Publisher publisher(participant); + + // This DataWriter writes data on Topic "ChocolateLotState" + dds::pub::DataWriter lot_state_writer( + publisher, + topic, + qos_provider.datawriter_qos( + "ChocolateFactoryLibrary::ChocolateLotStateProfile")); + + // A Subscriber allows an application to create one or more DataReaders + // Subscriber QoS is configured in USER_QOS_PROFILES.xml + dds::sub::Subscriber subscriber(participant); + + // Create DataReader of Topic "ChocolateLotState". + dds::sub::DataReader lot_state_reader( + subscriber, + topic, + qos_provider.datareader_qos( + "ChocolateFactoryLibrary::ChocolateLotStateProfile")); + + // Add a DataReader for Temperature to this application + dds::sub::DataReader temperature_reader( + subscriber, + filtered_temperature_topic, + qos_provider.datareader_qos( + "ChocolateFactoryLibrary::ChocolateTemperatureProfile")); + // Obtain the DataReader's Status Condition + dds::core::cond::StatusCondition temperature_status_condition( + temperature_reader); + + // Enable the 'data available' status. + temperature_status_condition.enabled_statuses( + dds::core::status::StatusMask::data_available()); + + // Associate a handler with the status condition. This will run when the + // condition is triggered, in the context of the dispatch call (see below) + temperature_status_condition.extensions().handler( + [&temperature_reader, &temperature_status_condition]() { + monitor_temperature(temperature_reader); + }); + + // Obtain the DataReader's Status Condition + dds::core::cond::StatusCondition lot_state_status_condition( + lot_state_reader); + + // Enable the 'data available' status. + lot_state_status_condition.enabled_statuses( + dds::core::status::StatusMask::data_available()); + + // Associate a handler with the status condition. This will run when the + // condition is triggered, in the context of the dispatch call (see below) + unsigned int lots_processed = 0; + lot_state_status_condition.extensions().handler( + [&lot_state_reader, &lots_processed]() { + lots_processed += monitor_lot_state(lot_state_reader); + }); + + // Create a WaitSet and attach the StatusCondition + dds::core::cond::WaitSet waitset; + waitset += lot_state_status_condition; + // Add the new DataReader's StatusCondition to the Waitset + waitset += temperature_status_condition; + + // Create a thread to periodically start new chocolate lots + std::thread start_lot_thread( + publish_start_lot, + lot_state_writer, + lots_to_process); + + while (!shutdown_requested && lots_processed < lots_to_process) { + // Dispatch will call the handlers associated to the WaitSet conditions + // when they activate + waitset.dispatch(dds::core::Duration(10)); // Wait up to 10s each time + } + + start_lot_thread.join(); +} + +int main(int argc, char *argv[]) +{ + // Parse arguments and handle control-C + auto arguments = parse_arguments(argc, argv); + if (arguments.parse_result == ParseReturn::PARSE_RETURN_EXIT) { + return EXIT_SUCCESS; + } else if (arguments.parse_result == ParseReturn::PARSE_RETURN_FAILURE) { + return EXIT_FAILURE; + } + setup_signal_handlers(); + + // Sets Connext verbosity to help debugging + rti::config::Logger::instance().verbosity(arguments.verbosity); + + try { + run_example(arguments.domain_id, arguments.sample_count); + } catch (const std::exception& ex) { + // This will catch DDS exceptions + std::cerr << "Exception in run_example(): " << ex.what() << std::endl; + return EXIT_FAILURE; + } + + // Releases the memory used by the participant factory. Optional at + // application shutdown + dds::domain::DomainParticipant::finalize_participant_factory(); + + return EXIT_SUCCESS; +} diff --git a/5_content_filters/c++11/tempering_application.cxx b/5_content_filters/c++11/tempering_application.cxx new file mode 100644 index 0000000..bd1dce7 --- /dev/null +++ b/5_content_filters/c++11/tempering_application.cxx @@ -0,0 +1,245 @@ +/* + * (c) Copyright, Real-Time Innovations, 2020. All rights reserved. + * RTI grants Licensee a license to use, modify, compile, and create derivative + * works of the software solely for use with RTI Connext DDS. Licensee may + * redistribute copies of the software provided that all such copies are subject + * to this license. The software is provided "as is", with no warranty of any + * type, including any warranty for fitness for any purpose. RTI is under no + * obligation to maintain or support the software. RTI shall not be liable for + * any incidental or consequential damages arising out of the use or inability + * to use the software. + */ + +#include +#include + +#include +#include +#include // for sleep() +#include // for logging +// Or simply include + +#include "chocolate_factory.hpp" +#include "application.hpp" // Argument parsing + +using namespace application; + +// Tempering application: +// 1) Publishes the temperature +// 2) Subscribes to the lot state +// 3) After "processing" the lot, publishes the lot state + +void publish_temperature( + dds::pub::DataWriter temperature_writer, + const std::string sensor_id) +{ + // Create temperature sample for writing + int counter = 0; + Temperature temperature; + while (!shutdown_requested) { + counter++; + // Modify the data to be written here + temperature.sensor_id(sensor_id); + // Occasionally make the temperature high + if (counter % 100 == 0) { + std::cout << "Temperature too high, notifying controller" << std::endl; + temperature.degrees(33); + } else { + temperature.degrees(rand() % 3 + 30); // Random value between 30 and 32 + } + temperature_writer.write(temperature); + + rti::util::sleep(dds::core::Duration::from_millisecs(100)); + } +} + +void process_lot( + dds::sub::DataReader& lot_state_reader, + dds::pub::DataWriter& lot_state_writer) +{ + // Take all samples. Samples are loaned to application, loan is + // returned when LoanedSamples destructor called. + dds::sub::LoanedSamples samples = + lot_state_reader.take(); + + // Process lots waiting for tempering + for (const auto& sample : samples) { + // Exercise #1.3: Remove the check that the Tempering Application is + // the next_station. This will now be filtered automatically. + if (sample.info().valid() + && sample.data().next_station() + == StationKind::TEMPERING_CONTROLLER) { + std::cout << "Processing lot #" << sample.data().lot_id() + << std::endl; + + // Send an update that the tempering station is processing lot + ChocolateLotState updated_state(sample.data()); + updated_state.lot_status(LotStatusKind::PROCESSING); + updated_state.next_station(StationKind::INVALID_CONTROLLER); + updated_state.station(StationKind::TEMPERING_CONTROLLER); + lot_state_writer.write(updated_state); + + // "Processing" the lot. + rti::util::sleep(dds::core::Duration(5)); + + // Since this is the last step in processing, + // notify the monitoring application that the lot is complete + // using a dispose + dds::core::InstanceHandle instance_handle = + lot_state_writer.lookup_instance(updated_state); + lot_state_writer.dispose_instance(instance_handle); + } + } +} // The LoanedSamples destructor returns the loan + +template +void on_requested_incompatible_qos(dds::sub::DataReader& reader) +{ + using namespace dds::core::policy; + QosPolicyId incompatible_policy = + reader.requested_incompatible_qos_status().last_policy_id(); + // Print when this DataReader discovers an incompatible DataWriter + std::cout << "Discovered DataWriter with incompatible policy: "; + + if (incompatible_policy == policy_id::value) { + std::cout << "Reliability"; + } else if (incompatible_policy == policy_id::value) { + std::cout << "Durability"; + } + + std::cout << std::endl; +} + +void run_example(unsigned int domain_id, const std::string& sensor_id) +{ + // Loads the QoS from the qos_profiles.xml file. + dds::core::QosProvider qos_provider("./qos_profiles.xml"); + + // A DomainParticipant allows an application to begin communicating in + // a DDS domain. Typically there is one DomainParticipant per application. + // Uses TemperingApplication QoS profile to set participant name. + dds::domain::DomainParticipant participant( + domain_id, + qos_provider.participant_qos( + "ChocolateFactoryLibrary::TemperingApplication")); + + // A Topic has a name and a datatype. Create Topics. + // Topic names are constants defined in the IDL file. + dds::topic::Topic temperature_topic( + participant, + CHOCOLATE_TEMPERATURE_TOPIC); + dds::topic::Topic lot_state_topic( + participant, + CHOCOLATE_LOT_STATE_TOPIC); + + // Exercise #1.1: Create a Content-Filtered Topic that filters out + // chocolate lot state unless the next_station = TEMPERING_CONTROLLER + + // A Publisher allows an application to create one or more DataWriters + // Create Publisher with default QoS + dds::pub::Publisher publisher(participant); + + // Create DataWriter of Topic "ChocolateTemperature" + // using ChocolateTemperatureProfile QoS profile for Streaming Data + dds::pub::DataWriter temperature_writer( + publisher, + temperature_topic, + qos_provider.datawriter_qos( + "ChocolateFactoryLibrary::ChocolateTemperatureProfile")); + + // Create DataWriter of Topic "ChocolateLotState" + // using ChocolateLotStateProfile QoS profile for State Data + dds::pub::DataWriter lot_state_writer( + publisher, + lot_state_topic, + qos_provider.datawriter_qos( + "ChocolateFactoryLibrary::ChocolateLotStateProfile")); + + // A Subscriber allows an application to create one or more DataReaders + dds::sub::Subscriber subscriber(participant); + + // Create DataReader of Topic "ChocolateLotState". + // using ChocolateLotStateProfile QoS profile for State Data + // Exercise #1.2: Change the DataReader's Topic to use a + // Content-Filtered Topic + dds::sub::DataReader lot_state_reader( + subscriber, + lot_state_topic, + qos_provider.datareader_qos( + "ChocolateFactoryLibrary::ChocolateLotStateProfile")); + + // Obtain the DataReader's Status Condition + dds::core::cond::StatusCondition reader_status_condition(lot_state_reader); + + // Contains statuses that entities can be notified about + using dds::core::status::StatusMask; + + // Enable the 'data available' and 'requested incompatible qos' statuses + reader_status_condition.enabled_statuses( + StatusMask::data_available() + | StatusMask::requested_incompatible_qos()); + + // Associate a handler with the status condition. This will run when the + // condition is triggered, in the context of the dispatch call (see below) + reader_status_condition.extensions().handler([&lot_state_reader, + &lot_state_writer]() { + if ((lot_state_reader.status_changes() & StatusMask::data_available()) + != StatusMask::none()) { + process_lot(lot_state_reader, lot_state_writer); + } + if ((lot_state_reader.status_changes() + & StatusMask::requested_incompatible_qos()) + != StatusMask::none()) { + on_requested_incompatible_qos(lot_state_reader); + } + }); + + // Create a WaitSet and attach the StatusCondition + dds::core::cond::WaitSet waitset; + waitset += reader_status_condition; + + // Create a thread to periodically write the temperature + std::cout << "ChocolateTemperature Sensor with ID: " << sensor_id + << " starting" << std::endl; + std::thread temperature_thread( + publish_temperature, + temperature_writer, + sensor_id); + + while (!shutdown_requested) { + // Wait for ChocolateLotState + std::cout << "Waiting for lot" << std::endl; + waitset.dispatch(dds::core::Duration(10)); // Wait up to 10s for update + } + + temperature_thread.join(); +} + +int main(int argc, char *argv[]) +{ + // Parse arguments and handle control-C + auto arguments = parse_arguments(argc, argv); + if (arguments.parse_result == ParseReturn::PARSE_RETURN_EXIT) { + return EXIT_SUCCESS; + } else if (arguments.parse_result == ParseReturn::PARSE_RETURN_FAILURE) { + return EXIT_FAILURE; + } + setup_signal_handlers(); + + // Sets Connext verbosity to help debugging + rti::config::Logger::instance().verbosity(arguments.verbosity); + + try { + run_example(arguments.domain_id, arguments.sensor_id); + } catch (const std::exception& ex) { + // This will catch DDS exceptions + std::cerr << "Exception in run_example(): " << ex.what() << std::endl; + return EXIT_FAILURE; + } + + // Releases the memory used by the participant factory. Optional at + // application shutdown + dds::domain::DomainParticipant::finalize_participant_factory(); + + return EXIT_SUCCESS; +} diff --git a/5_content_filters/chocolate_factory.idl b/5_content_filters/chocolate_factory.idl new file mode 100644 index 0000000..525ccd7 --- /dev/null +++ b/5_content_filters/chocolate_factory.idl @@ -0,0 +1,62 @@ +/* + * (c) Copyright, Real-Time Innovations, 2020. All rights reserved. + * RTI grants Licensee a license to use, modify, compile, and create derivative + * works of the software solely for use with RTI Connext DDS. Licensee may + * redistribute copies of the software provided that all such copies are subject + * to this license. The software is provided "as is", with no warranty of any + * type, including any warranty for fitness for any purpose. RTI is under no + * obligation to maintain or support the software. RTI shall not be liable for + * any incidental or consequential damages arising out of the use or inability + * to use the software. + */ + +const string CHOCOLATE_LOT_STATE_TOPIC = "ChocolateLotState"; +const string CHOCOLATE_TEMPERATURE_TOPIC = "ChocolateTemperature"; + +const uint32 MAX_STRING_LEN = 256; + +// Temperature data type used by tempering machine +struct Temperature { + // ID of the sensor sending the temperature + @key + string sensor_id; + + // Degrees in Fahrenheit + int32 degrees; +}; + +// Kind of station processing the chocolate +enum StationKind { + INVALID_CONTROLLER, + COCOA_BUTTER_CONTROLLER, + SUGAR_CONTROLLER, + MILK_CONTROLLER, + VANILLA_CONTROLLER, + TEMPERING_CONTROLLER +}; + +// Status of the chocolate lot +enum LotStatusKind { + WAITING, + PROCESSING, + COMPLETED +}; + +struct ChocolateLotState { + // Unique ID of the chocolate lot being produced. + // rolls over each day. + @key + uint32 lot_id; + + // Which station is producing the status + StationKind station; + + // This will be the same as the current station if the station producing + // the status is currently processing the lot. + StationKind next_station; + + // Current status of the chocolate lot: Waiting/Processing/Completed + LotStatusKind lot_status; + +}; +