2.3.3. Component Implementation

Before implementing the component, the code generator has to be started. To do so, right click on the component model and select "Run SmartMDSD Code Generator" (cf. figure 2.20).

Code generation

Figure 2.20. Code generation


2.3.3.1. Generated Files

Depending on the elements of the model, files are created during the code generation. For every component the files

  • CompHandler.cc

  • CompHandler.hh

  • <component name>Core.cc

  • <component name>Core.hh

are generated into the src-folder.

The CompHandler.* files contain code which is executed after the component is started or terminated. By default all services, tasks and timer of the component are started in these files. Afterwards the component is notified that the setup/initialization is finished. The <component name>Core.* files can be used to declare variables and methods which should be accessible from all other files inside the component.

For SmartTasks the following files are generated into the src-folder:

  • <Task name>.cc

  • <Task name>.hh

The <Task name>.* files contain a constructor, destructor and the methods on_entry(), on_execute() and on_exit(). The method on_entry() is called once, each time the task is started and can be used to initialize procedures. The method on_exit() is called once at the end of the thread and is typically used to clean-up resources which were initialized in the on_entry() method. The method on_execute() is called periodically in the thread and contains the logic of the component.

For a SmartEventClient the following files are generated into the src-folder:

  • <eventHandler name>.cc

  • <eventHandler name>.hh

The <eventHandler name>.* files contain the method handleEvent(const CHS::EventId id, const <Communication Object> &r) which is called as soon as an event is received.

For a SmartEventServer the files

  • <eventTestHandler name>.cc

  • <eventTestHandler name>.hh

are generated into the src-folder. The <eventTestHandler name>.* files contain the method testEvent(<Communication Object> &p, <Communication Object> &r, const <Communication Object> &s) which is used to check whether the event condition is true and the event fires. The specific check has to be added by the user. Thereby the Communication object p is the event parameter, the communication object r is the event result and the communication object s is the event state. If the event condition is true the method must set the event result and return the value 'true'. In contrast, if the event condition is false the method must return the value 'false'.

For a SmartSendServer the folowing files are generated into the src-folder:

  • <sendHandler name>.cc

  • <sendHandler name>.hh

The <sendHandler name>.* files contain the method handleSend(const <Communication Object> &r) which is called as soon as a communication object is received.

For a SmartQueryServer the files

  • <queryHandler name>.cc

  • <queryHandler name>.hh

are generated into the src-folder. The <queryHandler name>.* files contain the method handleQuery(CHS::QueryServer<<Communication Object>, <Communication Object>> &server, const CHS::QueryId id, const <Communication Object> &request) which is called as soon as a request is received.

For a SmartPushTimedServer the following files are generated into the src-folder:

  • <pushTimedHandler name>.cc

  • <pushTimedHandler name>.hh

The <pushTimedHandler name>.* files contain the method handlePushTimer(CHS::PushTimedServer<<Communication Object>> &server) which is used to send data periodically.

For a SmartStateSlave the files

  • SmartStateChangedHandler.cc

  • SmartStateChangedHandler.hh

are generated into the src-folder. The SmartStateChangedHandler.* files contain the methods handleEnterState(const std::string &substate) which is called as soon as a substate is entered and handleQuitState(const std::string &substate) which is called as soon as a substate is left.

For a SmartComponentParameter the files

  • ParameterStateStruct.cc

  • ParameterStateStruct.hh

are generated into the src-folder. The ParameterStateStruct.* files contain the method handleCOMMIT(const ParameterStateStruct &commitState) which is used to implement consistency checks which ensure that the incoming parameter meets internal constraints.

2.3.3.2. Start Services and Tasks

Services and tasks are connected and started in the CompHandler.cc file. By default all services and tasks of the component are started in the method onStartup(). However, this code can be adapted to your needs. If, for example, a component has several services, but only the specific service "imuDataPushTimedClient" should be started the line

	COMP->connectAndStartAllServices();
					

has to be replaced with

	COMP->connectImuDataPushTimedClient(
					COMP->connections.imuDataPushTimedClient.serverName, 
					COMP->connections.imuDataPushTimedClient.serviceName
					);
					

Look at the generated implementation of onStartup() for more examples. Often the start of a service should be configurable with an internal parameter. To do so, surround the start of the service with an IF-statement, e.g. only call COMP->connectImuDataPushTimedClient() if a specific condition is true (cf. fig. 2.21).

The easiest way to change the code of the CompHandler is to copy the code of the default method which contains the startup code of all services and tasks and adjust this copied code to your needs. The default method can be found in the <component name>.cc file which is located in the src-gen folder.

Start services

Figure 2.21. Start services


2.3.3.3. Using Communication Objects

To be able to use communication objects inside a C++ implementation the corresponding SmartSoft communication/coordination repository project has to be compiled (cf. section 2.2.4) and the *.hh file of the communication object has to be included. If, for example, the communication object CommNavigationVelocity should be used in the C++ implementation of a component, it has to be included as follows:

	#include "CommBasicObjects/CommNavigationVelocity.hh"
					

Now, the communication object can be instantiated:

	CommBasicObjects::CommNavigationVelocity vel;
					

The attributes of the communication object can be accessed via getter and setter methods:

	vel.set_vX(x);
	vel.set_omega(omega);
	// using the builder pattern, set multiple values in one line:
	vel.set_vX(x).set_omega(omega);
					

2.3.3.4. Using Services

There are different methods to exchange data for the different communication patterns. In the following the most important methods are presented. Further information can be found in the SmartSoft/ACE reference at http://servicerobotik-ulm.de/drupal/doxygen/aceSmartSoft/. For an example how to use a service from your implementation, see fig. 2.22

Example implementation of a task using a service. SmartMDSDToolchain2016

Figure 2.22. Example implementation of a task using a service. [4]


event. The SmartEventClient subscribes for an event and receives the event result, which is provided by the SmartEventServer.

SmartEventClient:

  • StatusCode activate(const EventMode mode , const P& parameter, EventId& id): Activate an event with the provided parameters in either "single" or "continuous" mode.

  • StatusCode deactivate(const EventId id): Deactivate the event with the specified identifier. An event must always be deactivated, even if it has already fired in single mode. This is just necessary for cleanup procedures and provides a uniform user API independently of the event mode. Calling deactivate() while there are blocking calls aborts them with the appropriate status code.

  • StatusCode tryEvent(const EventId id): Check whether event has already fired and return immediately with status information. This method does not consume an available event.

  • StatusCode getEvent(const EventId id, E& event): Blocking call which waits for the event to fire and then consumes the event. This method consumes an event. Returns immediately if an unconsumed event is available. Blocks otherwise till event becomes available. If method is called concurrently from several threads with the same id and thod is blocking, then every call returns with the same event once the event fired. If there is however already an unconsumed event available, then only one out of the concurrent calls consumes the event and the other calls return with appropriate status codes.

  • StatusCode getNextEvent(const EventId id, E& event): Blocking call which waits for the next event. This methods waits for the next arriving event to make sure that only events arriving after entering the method are considered. Method consumes event. An old event that has been fired is ignored (in contrary to getEvent()). If method is called concurrently from several threads with the same id, then every call returns with the same event once the event fired.

SmartEventServer:

  • StatusCode put(const S& state): Initiate testing the event conditions for the activations.

pushNewest. The SmartPushNewestServer provides data for SmartPushNewestClients whenever new data is available.

SmartPushNewestServer:

  • StatusCode put(const T& d): Send updated data to all subscribed clients

SmartPushNewestClient:

  • StatusCode getUpdate(T& d): Non-blocking call to immediately return the latest available data buffered at the client side from the most recent update. No data is returned as long as no update is received since subscription. To avoid returning old data, no data is returned after the client got unsubscribed.

  • StatusCode getUpdateWait(T& d): Blocking call which waits until the next update is received. Blocking is aborted with the appropriate status if either the client gets unsubscribed or disconnected or if blocking is not allowed anymore at the client.

pushTimed. The SmartPushNewestServer provides data in specified time intervals for SmartPushTimedClients.

SmartPushTimedServer:

  • StatusCode put(const T& d): Provide new data which is sent to all subscribed clients taking into account their individual update cycles. Update cycles are always whole-numbered multiples of the server update cycle.

SmartPushTimedClient:

  • StatusCode getUpdate(T& d): Non-blocking call to immediately return the latest available data buffered at the client side from the most recent update. No data is returned as long as no update is received since subscription. To avoid returning old data, no data is returned after the client is unsubscribed or when the server is not active.

  • StatusCode getUpdateWait(T& d): Blocking call which waits until the next update is received. Blocking is aborted with the appropriate status if either the server gets deactivated, the client gets unsubscribed or disconnected or if blocking is not allowed any more at the client.

query. The SmartQueryClient sends a request containing individual parameters and receives an individual result from the SmartQueryServer.

SmartQueryClient:

  • StatusCode query(const R& request, A& answer): Perform a blocking query and return only when the query answer is available. Member function is thread safe and thread reentrant.

  • StatusCode queryRequest(const R& request, QueryId& id): Perform a query and receive the answer later, returns immediately. Member function is thread safe and reentrant.

  • StatusCode queryReceive(const QueryId id, A& answer): Check if answer is available. Non-blocking call to fetch the answer belonging to the given identifier. Returns immediately. Member function is thread safe and reentrant.

  • StatusCode queryReceiveWait(const QueryId id, A& answer): Wait for reply. Blocking call to fetch the answer belonging to the given identifier. Waits until the answer is received.

  • StatusCode queryDiscard(const QueryId id): Discard the pending answer with the identifier id. Call this member function if you do not want to get the answer of a request anymore which was invoked by queryRequest(). This member function invalidates the identifier id.

SmartQueryServer:

  • StatusCode answer(const QueryId id, const A& answer): Provide answer to be sent back to the requestor. Member function is thread safe and thread reentrant.

send. The SmartSendClient provides data which is received by a SmartSendServer.

SmartSendClient:

  • StatusCode send(const C& c): Perform a one-way communication. Appropriate status codes make sure that the information has been transferred.

state and parameter. For descriptions on state and parameter, please refer to the doxygen reference of SmartSoft/ACE. For an example how to use the parameter, please refer to the tutorial within this handbook.

2.3.3.5. Status Codes

Status codes are used for the status and error handling when using SmartSoft methods, especially when interacting with services. To convert a status code into readable ASCII representation the following method can be used:

	std::string StatusCodeConversion(StatusCode code)
					

The method can be used as follows:

	CHS::StatusCode status = COMP->connectAndStartAllServices();
	if(status != CHS::SMART_OK) {
		std::cout << "Error connecting services: " << CHS::StatusCodeConversion(status);
	}
					

2.3.3.6. Component Wide Variables

Variables which should be accessible from all files of the component project can be declared in the <component name>Core.hh file. To prevent the access of several threads at the same time a Mutex should be added to this variables. The variable can be accessed as follows:

	COMP-><variable name>
					

Add Component wide variables

Figure 2.23. Add Component wide variables


A SmartMutex can be used to prevent simultaneous access of different threads. An example of a mutex for a global variable could be found in the component SmartBluetoothLocalization (SmartBluetoothLocalizationCore.hh):

	CommIndoorOutdoorNavigationObjects::CommiBeaconList componentWideBeaconList;
	CHS::SmartMutex componentWideBeaconListMutex;
					

To acquire or release the lock ownership of the component wide variable componentWideBeaconList, the following code has to be used:

	COMP->componentWideBeaconListMutex.acquire();
	COMP->componentWideBeaconList.clearBeaconList();
	COMP->componentWideBeaconListMutex.release();
					

2.3.3.7. Using Parameters Within the Component

To access an attribute of a parameter within the component the following code is used:

	COMP->getGlobalState().get<parameter name>().get<attribute name>();
					

The settings.max_velocity, for example, is accesed as follows:

	COMP->getGlobalState().getSettings().getMax_velocity();
					

In case these parameters change at runtime, we strongly recommend to work on a copy of the global state:

	ParameterStateStruct localState = COMP->getGlobalState();
	localState.getSettings().getMax_velocity();
					

WordPress Appliance - Powered by TurnKey Linux