Implementing a Device Interface: Command

The Command provides a template of a common method intended to trigger in your device a certain action, e.g. to start an engine. Commands can also be used to query the device status or to receive data from the device. In order to perform a Command the GUI application just needs to invoke the corresponding method. In the method invocation the GUI application can pass arguments and the method can return a value received from the device. As such, Commands build an integral element of the interface separating the GUI application and the underlying device. Furthermore, Commands abstract the device operations reducing them to simple commands like Start Engine.

Usually, you use the Command template when implementing the Device Interface class. In such case, you add as many Commands to the Interface as operations possible in your device. For example, if your device controls a pump, you can add one Command to start the pump, second Command to stop it and eventually a third Command to query the actual status of the pump. It's up to you what the Commands do. You are free in the implementation and declaration of the respective methods. Accordingly, your implementation can access the GPIOs, use operating system services and communicate with other software modules responsible for the function of the device.

The figure below demonstrates the principle concept of a Command as interface between the GUI application and the device. On the left you see a GUI component containing two push buttons. Each time the user activates a button, the associated Command method implemented in the Device Interface class is invoked. Thereupon the implementation of the method is executed. The implementation, on the other hand, contains a section of native code, where some C function (here an imaginary function gpio_write_engine_pin()) is called. Assuming, the corresponding GPIO pin controls the power supply of the engine in your device, modifying its state causes the engine to be started or stopped:

The following sections are intended to provide you an introduction and useful tips of how to add, implement and use Commands. To better understand the underlying concepts we also recommend you to read the chapter Implementing a Device Interface as well as Integrating with the device.

Add a new Command

Commands are usually used when implementing an interface between the GUI application and the device. Accordingly, you add the Commands to a Device Interface class:

If you have not done it yet, add a new Device Interface to your project.

Ensure that Composer shows the Device Interface class where you want to add the new Command.

Then ensure that the Templates window is visible.

In Templates window switch to the folder Device.

In the folder locate the template Command.

Drag & Drop the template into the Composer window:

As you see in the screenshot above, the new added Command appears as a method brick accompanied by an annotation providing helpful tips how to proceed. If undesired, you can select and delete the annotation.

Name the Command method

The names of newly added Commands start per default with Command. That can lead to confusion especially when your Device Interface consists of many Commands. We recommend thus immediately after adding a new Command to name it according to the corresponding function it will trigger in the device (e.g. to StartEngine). For this purpose:

First ensure, that the method brick representing the Command is selected.

Press the key F2 or select the menu item EDITRename ....

Enter the new name in the Inspector window.

Open the Command method in Code Editor

The newly added Command methods don't provide any meaningful default implementation. Thus, it's up to you to implement the methods to correctly access the hardware of your device or to call software routines controlling it. The methods are edited in the Code Editor window. For this purpose:

First ensure, that the method brick representing the Command is selected.

Press the key ENTER ...

... or double click on the method with the mouse.

Once the method is opened in the Code Editor you see that the method templates are extensively commented providing you diverse tips how to adapt and implement it. If you don't want the comments, simply select the text in the Code Editor and delete it.

Implement the Command method

The main aim of a Command is to perform an operation in the device. For example, to start an engine or to query the status of the device. These operations require the implementation of the Command method to access hardware specific GPIOs, use operating system services or call other software routines existing outside of the GUI application. You implement this functionality in the Code Editor window.

Being a highly platform independent development tool, Embedded Wizard does not allow the GUI application to simply access the hardware or any other external software routines. To achieve such effect, you have to implement the desired hardware access or invocation of external routines directly in the native syntax of your target system. This is usually C, C++ or it is JavaScript if you are developing a GUI application for the WebGL platform.

Embedded Wizard, however, can understand the syntax of its own programming language Chora only. The unique possibility to integrate the native code with the platform independent Chora is thus to enclose it explicitly within a native statement. Then, during Code Generation the native code is simply taken over and mixed together with all the other generated code. The following example demonstrates the implementation of a Command method intended to start an engine:

native { gpio_write_engine_pin( 1 ); }

In this example, we assumed that the engine is powered when the correct GPIO pin is set high by using an imaginary C function gpio_write_engine_pin(). In the practice you will of course implement native code using real functions or you will access real CPU register existing in your target device. With the native statement you are thus flexible to perform any device specific operation. It's finally up to you what is performed in the Command method.

When implementing native code in C or C++ syntax, it is obligatory to have the used functions being declared in advance. Similarly when the native code is using type definitions available in your device software, these definitions need to be known when the code snippet enclosed in native statement is compiled by the C compiler. In the simplest case you can add the necessary declarations inline together with the native statement. The following example declares the gpio_write_engine_pin() C function. The C compiler knows thereupon what to do with the invocation of this function:

native { /* Declaration of an external C function */ extern void gpio_write_engine_pin( int aValue ); /* Invocation of this function */ gpio_write_engine_pin( 1 ); }

In more sophisticated cases, it is suitable to include complete header files containing all the necessary function and data type declarations. This approach is solved ideally by using the inline code member accompanying per default the Device Interface. This is explained in the section Add middleware and other external code to the project.

Please note, during Prototyping the Chora compiler ignores all native statements and reports only a warning. To avoid this Chora warning you can use the conditional compilation directives like $if .. $endif to conditionally exclude the native statement:

$if !$prototyper native { [...] } $endif

Specify the Command parameters

Newly added Command methods comes with one int32 and second bool parameter named consequently aParameter1 and aParameter2. These parameters exist mainly for demonstration purpose and for your convenience to simplify the creation of new Commands. In the practice, however, after adding a new Command you should adapt the declaration of the method to the needs of the particular operation represented by it. Following figure demonstrates the per default existing parameters:

For example, if the Command is intended to start the brewing process in a coffee machine, you could adapt the method declaration to have a parameter indicating the desired coffee strength. The data type of this parameter would be usefully int32 or float. Later, when invoking the Command method you provide in this parameter a number corresponding to the amount of coffee beans to grind. Providing a large number would result in a strong coffee. With a small number, less coffee beans are ground and the coffee is weak.

If the coffee machine can be started to brew one or two coffee cups at once, you could adapt the Command by a further parameter named e.g. aDoubleCup and configure it with bool as data type. Later, when the Command method is invoked, you can pass in this parameter the value true or false depending on whether the coffee machine should prepare two or one coffee cups. The following figure shows the possible declaration of such StartBrewing method:

The editing of method parameters takes place in the declaration area of the Code Editor window. Following operations are possible:

You can add new parameters.

You can edit existing parameters.

You can delete existing parameters.

You can reorder existing parameters.

Once declared, you can evaluate the parameters in the implementation of the Command method. You can even pass the parameters to the native code and evaluate them there directly. Following figure demonstrates the usage of the both approaches in a finished StartBrewing method:

It is not obligatory to implement each Command having parameters. The above coffee machine examples should help you understand that you are very well able to provide values when executing a Command. In practice, however, you can also implement Commands without any parameter. These represent then a simple action like Start Engine. In such case simply delete the parameters existing actually in the method:

Specify the Command return value

The newly added Command methods are per default declared to return an int32 value. This return value exists mainly for demonstration purpose and for your convenience to simplify the creation of new Commands. In the practice, however, after adding a new Command you should adapt the declaration of the method to the needs of the particular operation represented by it. Following figure demonstrates the per default existing return value:

The possibility to return a value is an interesting feature for all Commands intended to query the device status or to read any other information stored in the hardware or managed by the external software. For example, a GUI application controlling a pump could implement a Command to query whether the pump is actually running or not. Accordingly, the respective Command method would be declared to return a bool value. It would return true if the pump is active and false if it doesn't. The caller of the method (e.g. a GUI component) can thereupon evaluate the returned value and update its appearance according to the current status of the pump. The following figure shows the possible declaration of such IsPumpActive method:

The editing of the method return data type takes place in the declaration area of the Code Editor window. How you do this exactly is explained in the section Edit the return data type of the method. Once declared the return data type, you have to adapt the implementation of the Command method to return a value matching this data type. In case of the above mentioned example of an IsPumpActive method, the implementation could look as shown in the figure below:

Let's take a look at another case implementing a simple audio player. The GUI application of such player allows the user to select a song from a play list and to start the playback. All songs are stored on the file system of the device and as such they are not directly accessible from the GUI. In order to display the title of the actually played song, it is thus necessary to implement a Command GetCurrentTitle, which determines the actually played song and returns its title as string value. The GUI application can thereupon display this string within a Text view.

The following figure demonstrates the declaration and the implementation of such method to query the current song title. Please note, the method uses imaginary C functions and type definitions to determine which song is actually played and to query its title. You can imagine, that in a real project you would need to implement similar code to get the respective information:

If a Command is not intended to return any value, you should adapt its method declaration so that its return data type is void. For this purpose you edit the return type and modify it to void. Following figure shows a method declared to not return any values:

Use the Command in the GUI application

As explained in the section above, Commands are always implemented within a Device Interface class. Consequently, the corresponding methods can be used (invoked) only in context of an instance of such class. Before a Command can be executed the caller has thus to create a new or estimate an existing Device Interface instance. For your convenience, such instance is prepared automatically when you add a new Device Interface to your project. You recognize it as a global autoobject named per default Device:

The advantage of a global autoobject is that it can be accessed from everywhere in your application. You should only take care of the correct addressing the instance by using its complete name composed of the unit containing the autoobject, the :: (double colon) delimiter and the name of the autoobject itself. For example, the autoobject shown in the screenshot above is addressed by the expression Application::Device. A GUI component containing a push button could thereupon implement following code to execute the StartEngine method every time the user activates the push button:

Application::Device.StartEngine();

Following example project demonstrates the just explained approach of using the Device Interface Commands. The project consists of a GUI component containing two push buttons and a Device Interface class implementing two Commands StartEngine and StopEngine. Activating one of the buttons causes the corresponding Command to be invoked. Accordingly, code to start or stop the engine is executed. Since the example is detached from a real device, it limits to log a message that the Command has been executed. In practice the implementation could modify GPIO pins, etc. and so control the power supply of a real engine:

DOWNLOAD EXAMPLE

Please note, the example presents eventually features available as of version 9.00

If the Command method expects any parameters, you have to provide them when invoking the method. The number of operands, their data types and the order in which they are passed in the invocation have to match the declaration of the method. In the sections above we spoke about an imaginary Command to start the coffee brewing process. Following could be the invocation of the corresponding method with the first parameter specifying the coffee strength and the second determining whether we want to brew two coffee cups at once:

Application::Device.StartBrewing( 1.0, false );

In case of a Command method implemented to query a device status or to read a value stored in the external software, you evaluate the value returned by the invoked method. In particular you can assign the value to a variable, involve it in expressions or compare it with other values. In the sections above we analyzed an example of a simple audio player. The interface to the audio player device had a Command to query the title of the actually played song. The following code could be used to invoke the Command method and to display the title in a Text view:

// Query the current song title and store it in a local variable var string title = Application::Device.GetCurrentTitle(); // If title is known, display it ... if ( title != "" ) TextView.String = "Playing song '" + title + "'"; // ... otherwise the playback is stopped. else TextView.String = "Playback stopped.";

Implement asynchronous, non blocking Commands

Whether a Command is blocking or not does depend on the implementation of its method. If the method calls a C function which blocks the execution waiting for the completion of an operation, then the Command will block too. Accordingly, the GUI application will stop handling user inputs and performing screen updates unless the blocking function is finished and the Command method returns to the caller.

We recommend to implement the Commands, whenever possible, to not block the execution. You can consider them just as triggers initiating respective operations in your device. For example, the command to start the coffee brewing could trigger a separate task responsible for the heater and the pump. The Command method itself can thus return immediately to the caller while the started task supervises the brewing process.

The concept of non blocking, asynchronous Commands is also very common for software exchanging data via software buses or network connections. In such case, the Command sends a message via software bus or the network to another device and returns immediately to the caller. The method does not need to wait for the answer on this message. For example:

native { /* Declaration of an imaginary function to send CAN-Bus messages */ extern void CAN_send_message( int aId, void* aMsg, int aMsgSize ); /* Some message content to send via CAN-Bus */ unsigned char msg[8] = { 0x01, 0x02, ... }; /* Send message to id 0x1234 and return immediately */ CAN_send_message( 0x1234, msg, sizeof( msg )); }

In case, the Command is designed to return a value, the implementation will become problematic if the desired information can not be be obtained immediately. Concrete, if the Command method has to wait for a C function to return the requested data, then the method will block inevitably the entire GUI application. It is up to you to decide whether such delay is acceptable or not. If it is not, then you have to redesign the application to avoid such blocking query Commands.

You will concrete need to limit the Commands to trigger the respective query operations without waiting for the resulting data. As soon as the data is available, the software in your device should notify the GUI application by triggering a dedicated System Event. The application can thereupon react to the event and complete the request. The available information can either be provided together with the event, or the application uses a separate query Command to read the latest version of the available data.

Also applicable is the approach to store the requested data in Device Interface Properties. As soon as the data is available, the software in your device updates the corresponding Properties, which thereupon broadcast notifications within the GUI application. The GUI application can then react to the notifications and complete the query operation or simply refresh the display showing the new data to the user. While the application is waiting for the requested data the user can still interact with the GUI.