Difference between revisions of "WICE RP How-To"

From WICE Wiki v2.89
Jump to navigation Jump to search
Line 175: Line 175:
     }
     }
   }
   }
</code>
</code>The <code>read_line()</code> helper function handles the actual reading of characters from the socket, and then the <code>handle_signal()</code> function parses the signal names and values, as follows.  
The <code>read_line()</code> helper function handles the actual reading of characters from the socket, and then the <code>handle_signal()</code> function parses the signal names and values, as follows.  
  <code>void handle_signal(char *str) {
  <code>
void handle_signal(char *str) {
   char name[1000];
   char name[1000];
   float value;
   float value;
Line 190: Line 188:
</code>
</code>
The RP program can at any time unsubscribe to signals using the <code>unsubscribe</code> command, which has the same syntax as the <code>subscribe</code> command.
The RP program can at any time unsubscribe to signals using the <code>unsubscribe</code> command, which has the same syntax as the <code>subscribe</code> command.
  <code>
  <code>unsubscribe <signal name 1> <signal name 2> ...<signal name n><CRLF>
unsubscribe <signal name 1> <signal name 2> ...<signal name n><CRLF>
</code>
</code>
After unsubscribing to a (subset) of the subscribed signals, those signals' values will no longer be transmitted on the socket. (This is not shown in the example.)
After unsubscribing to a (subset) of the subscribed signals, those signals' values will no longer be transmitted on the socket. (This is not shown in the example.)


The final part of the example shows how to terminate the subscription using the <code>quit</code> command.
The final part of the example shows how to terminate the subscription using the <code>quit</code> command.
<code>
  <code>send(pubsub_socket, "quit", 4, 0);
  send(pubsub_socket, "quit", 4, 0);
   close(pubsub_socket);
   close(pubsub_socket);
</code>
</code>

Revision as of 10:49, 25 May 2018

Getting Started with WICE Rapid Prototyping

To get started developing an RP component to be run on a WICE WCU you need the following:

  • A toolchain for building executable programs on the target WCU platform
  • A set of libraries providing APIs to the WCU hardware platform resources (e.g. CAN, diagnostics, logging, etc)
  • WICE Portal access to configure the RP module for one or more WCUs

Software Development Toolchain

The development toolchain is typically installed on a desktop Linux system and then executables are built by cross-compilation. The following WCU platform architectures are supported:

  • Host Mobility MX-4: ARM Cortex A9 or Cortex A5 processor, 32 bit
  • Fältcom Committo: ARM 926EJ-S processor, 32 bit
  • Actia Platform: MIPS processor, 32 bit

All toolchains are gcc-based. The MX-4 toolchain is availble here: http://releases.linaro.org/13.09/components/toolchain/gcc-linaro/4.7

WICE RP development API libraries

The following libraries are available:

  • Canwrapper CAN library
  • Debug logging library
  • Timers and utilities library
  • Tactalib Diagnostics library
  • OBD II library

The development libraries are available here: http://www.alkit.se/rp/libs/

Building and executing the RP program

The binary file resulting from compiling the program for the target WCU platform is transferred to one or more in-vehicle WCUs using the WICE Portal. The binary will be executed when the WCU boots, and will be terminated when the WCU shuts down.

When executed, an number of command line arguments are given to the RP binary, as follows: -vin <VIN number> -settings <filename> -logfile <filename>

The -vin parameter supplies the Vehicle Identification Number of the vehicle the WCU is installed in.

The -settings parameter supplies the path to a configuration file the user can supply through the WICE portal.

The -logfile parameter supplies the path to a debug log file which will be uploaded to WICE portal when the RP module exits.

To get access to the information supplied on the command line at star-upt, the RP program must parse the command line.

A simple example RP program

The example code rpex.c illustrates how to write a small program that can be executed as an RP module on the WICE in-vehicle WCU platform.

The example shows how to read a configuration file supplied on the command line, to write a CAN diagnostic request on a CAN bus and read a diagnostic reply, and to log data to a log file that will be uploaded to the WICE Portal.

The full source code of rpex is available here.

The first thing the example program does is to parse the command line parameters:

ret = parse_options(argc, argv);
 if (ret != 1) {
   return EXIT_FAILURE;
 }

The parse_options function sets the variables settings_filename, log_filename and vin to the strings supplied on the command line.

Next, debug output is enabled, and the output filename is set to log_filename, as specified on the command line. This ensures that the log file will be uploaded to the WICE Portal when rpex exits. The debug_enable and debug_msg functions are defined in the alkit-debug development library.

if (log_filename != NULL ) {
   debug_enable(log_filename);
}
debug_msg(1, "rpex version is %s\n", RPEX_VERSION_STRING);

Then, after some more log output, the settings file is read:

 if(settings_filename)
   read_settings(settings_filename);

The settings file is the main way (except for the command line parameters) to provide input to the RP program. It can contain any type of input data needed by the program. In the example, rpex only reads text from the settings file and prints it to the output logfile.

The program is now ready to do its main work,in this case to send a diagnostic request on a CAN bus, and read a response. This is done using the alkit-canwrapper library. The following code block shows how to set up the CAN interface, prepare a CAN frame for transmission, set up a CAN filter matching the expected response, and then send the CAN frame.

 // Initialize CAN interface
 ret = init_can(canbus, DEFAULT_CAN_DEVICE);
 can_device_fd = get_can_fd(canbus);
 
// Set up CAN filter install_CAN_filter(canbus, 0, CAN_FILTER_TYPE_PASS, can_filter_id, can_filter_mask);
// Prepare CAN frame for request memset(&req_frame, 0, sizeof(candata_t)); req_frame.id = ecu_id; req_frame.bus_nr = canbus; req_frame.frame_type = CAN_FRAME_11_BIT; memset(req_frame.databytes, 0, 8); req_frame.databytes[0] = 0x02; // SingleFrame, length = 2 req_frame.databytes[1] = 0x10; // Service 10 req_frame.databytes[2] = 0x01; // Default Diagnostic Session req_frame.databyte_count = 8; // With padding
// Send CAN frame debug_msg(1, "Sending diagnostic request on CAN bus %d...\n", canbus); n = write_can(canbus, &req_frame, &t); if(n<0) { debug_msg(1, "Error writing CAN frame\n"); return -1; }

The following code block shows how to read a response CAN frame.

// Read CAN response
 FD_ZERO(&readfds);
 FD_SET(can_device_fd, &readfds);
 timeout.tv_sec = 0;
 timeout.tv_usec = 200000; // 200 ms
 
ready = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout); if (ready > 0) { ret = read_can(canbus, &resp_frame); if (ret == CAN_STATUS_OK) { // Read successful handle_can_frame(&resp_frame); } else { debug_msg(1, "Error reading CAN frame\n"); } } else { debug_msg(1, "No response.\n"); }

The function handle_can_framewhich is called when a frame is read simply logs the CAN identifier and payload to the logfile.

Using the high-level API for reading in-vehicle signals

To avoid the intricacies of reading CAN or FlexRay signals directly from the device, having to parse and interpret the signals, there is a high-level socket based API for reading in-vehicle and WICE-internal signals. The API is based on a publish/subscribe model, wherein the RP client subscribes to signals by name, and periodically gets signal values for the selected set of signals. The communication between the RP client and the WICE signal broker component is done over a socket.

The example code rpex-pubsub.c illustrates how to use the publish/subscribe API. The full source code is available here.

To set up the communication between the RP module and the signal broker, the example program calls a function called setup_pubsub_connection() after parsing the command line parameters:

pubsub_socket = setup_pubsub_connection(ip_addr, port_num);
 if(pubsub_socket<0) {
   debug_msg(1, "Pubsub connection failed\n");
   exit(-1);
 }

The setup_pubsub_connection() function sets up a TCP socket to use for the communication.

To this socket, a subscribe command is sent with a list of names of the signals to subscribe to. The syntax of the subscribe command is as follows:

subscribe <signal name 1> <signal name 2> ...<signal name n><CRLF>

Note that the separator character has to be space, and the line terminated with Carriage Return + Line Feed (or optionally only Line Feed).

sprintf(str, "subscribe %s %s\n", RPEX_SIGNAL1, RPEX_SIGNAL2);
 n = send(pubsub_socket, str, strlen(str), 0);
 if(n <= 0) {
   debug_msg(1, "Error subscribing to signals\n");
   exit(-1);
 }

Once the signals have been subscribed, the signal broker will send the signal values on the socket, to be read by the RP program, with the following syntax:

<signal name> <value><CRLF>

Again, the separator character between signal name and value is space.

The following code segment show an example of how to read signals as the bcome available on the socket using the select() system call.

while(count < 100) {
   FD_ZERO(&readfds);
   FD_SET(pubsub_socket, &readfds);
   timeout.tv_sec = 1;
   timeout.tv_usec = 0;
   ready = select(FD_SETSIZE, &readfds, NULL, NULL, &timeout);
   if (ready > 0) {
     n = read_line(pubsub_socket, &buf);
     if (n > 0) {
       // Read successful
       handle_signal(buf);
       free(buf);
       count++;
     } else {
       debug_msg(1, "Error reading signal\n");
       break;
     }
   }
 }
The read_line() helper function handles the actual reading of characters from the socket, and then the handle_signal() function parses the signal names and values, as follows. 
void handle_signal(char *str) {
 char name[1000];
 float value;
 sscanf(str, "%s %f", name, &value);
 if(strcmp(name, RPEX_SIGNAL1)==0)
   debug_msg(1, "The value of signal 1 is %f\n", value);
 if(strcmp(name, RPEX_SIGNAL2)==0)
   debug_msg(1, "The value of signal 2 is %f\n", value);

} The RP program can at any time unsubscribe to signals using the unsubscribe command, which has the same syntax as the subscribe command.

unsubscribe <signal name 1> <signal name 2> ...<signal name n><CRLF>

After unsubscribing to a (subset) of the subscribed signals, those signals' values will no longer be transmitted on the socket. (This is not shown in the example.)

The final part of the example shows how to terminate the subscription using the quit command.

 send(pubsub_socket, "quit", 4, 0);
 close(pubsub_socket);