Difference between revisions of "WICE RP How-To"
Line 273: | Line 273: | ||
To transfer a Node web-app to one ore more WCUs, it should be put in a zip file and uploaded to the WICE Portal and an RP module. | To transfer a Node web-app to one ore more WCUs, it should be put in a zip file and uploaded to the WICE Portal and an RP module. | ||
The zip file must have a top level directory called <code>rp</code>, and in this directory a Node application entry point file called <code>server.js</code> | The zip file must have a top level directory called <code>rp</code>, and in this directory a Node application entry point file called <code>server.js</code> | ||
A complete example of a Node web-application RP zip-file, including source code, is available for download [http://www.alkit.se/rp/example here]. | A complete example of a Node web-application RP zip-file, including source code, is available for download [http://www.alkit.se/rp/node-example here]. |
Revision as of 14:08, 4 September 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_frame
which 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);
You can find more information about the signal broker API in WICE Signal Broker API section.
Rapid Prototyping using Node.js
To develop Rapid Prototyping applications with graphical user interfaces, the Node.js framework can be used. Node.js is a cross-platform JavaScript run-time environment that executes JavaScript code on the server side. In the WICE RP concept, a Node.js application can be downloaded to a WCU where it is executed. The user interface developed can be accessed from a web browser connected to the WCU through Ethernet or WiFi. The setup described here is built with Node.js Express. However, other frameworks can be utilized using the same methodology, with small modifications.
The following example demonstrates how to access vehicle signals from a Node.js application running on a WCU.
In the Node.js application server set-up script, add the following code segment to include the Express framework and socket.io
handling socket communication.
// Setup server environment
const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server);
// Run internal util
require('/path/to/script/socketHandler.js')(io)
The socketHandler.js
script contains the code that handles the socket communication, both with the web-client and with the WICE Signal Broker. The function waits for a client to connect, by accessing the web page. It then connects to the Signal Broker and subscribes to the desired signals (in this example Engine Speed and Vehicle Speed on the MS CAN bus). The application then listens for incoming messages on the connected socket and processes the data, i.e. signal names and values. The data is then transferred to the client by socket.io
communication in order to present the information in the web-browser. An example socketHandler.js
script is outlined here:
const net = require('net');
const PORT = 31415;
module.exports = function(io) {
io.on('connection', function(client) { // Await client connection
console.log('Client connected...');
// Setup TCP socket to Signal Broker.
var tcpSocket = new net.Socket();
tcpSocket.connect(PORT, 'localhost', function () {
console.log('Connected to signal broker... ');
// Get a list of available signals (optional)
tcpSocket.write('list\n');
// Subscribe to desired signals.
tcpSocket.write('subscribe CAN_MS.EngineSpeed CAN_MS.VehicleSpeed\n');
});
// Listener triggered by messages from sigread.
tcpSocket.on('data', function(message) {
console.log(message.toString());
// Handle received messages here...
});
// Triggered if the tcpSocket is closed.
tcpSocket.on('close', function () {
console.log('TCP connection closed!');
});
// Triggered upon error events.
tcpSocket.on('error', function(error) {
console.log('TCP error: %s', error);
});
// Triggered if the client refreshes or closes the page
client.on('disconnect', function (){
console.log("Client disconnected");
tcpSocket.destroy(); // Disconnect from Signal Broker
});
});
}
Each received message is a newline-terminated text line consisting of signal name followed by signal value, separated by a space character (see Signal Broker section above). The message is transferred to the client through socket communication established in the first part of the script above.
Send data to client:
client.emit('speed', speed);
Receive data in HTML-embedded client script:
// Setup client socket.
var socket = io.connect('/'); // url path for the current page
socket.on('connect', function (data) {
console.log("Client connected to server");
});
// Triggered by ‘speed’ tagged messages from server.
socket.on('speed', function (value) {
console.log(value);
});
To transfer a Node web-app to one ore more WCUs, it should be put in a zip file and uploaded to the WICE Portal and an RP module.
The zip file must have a top level directory called rp
, and in this directory a Node application entry point file called server.js
A complete example of a Node web-application RP zip-file, including source code, is available for download here.