Copyright © 2016 W3C® (MIT, ERCIM, Keio, Beihang). W3C liability, trademark and document use rules apply.
The W3C Vehicle Signal Server Specification defines a WebSocket based API that enables client applications to GET, SET, SUBSCRIBE and UNSUBSCRIBE to vehicle signals and data attributes.
The purpose of the specification is to promote a Server API that enables application development in a consistent manner across participating automotive manufacturers.
It is recommended that this Vehicle Signal Server Specification (VSSS) is read in conjunction with the W3C Vehicle Signal Client Specification (VSCS) and the GENIVI Vehicle Signal Specification (VSS).
The Vehicle Signal Client Specification defines an API that wraps the data access and security primitives defined in this Server Specification. It has been defined to support the development of 'standards-compliant' JavaScript libraries that can be used by web based clients.
It is assumed that native clients (written for example in C/C++) and managed runtime clients (written in languages like Java or C#) invoke the WebSocket Server API using standard WebSocket libraries that have been created specifically for those languages.
The GENIVI Vehicle Signal Specification (VSS) defines the set of vehicle signals and data that are exposed via this Server Specification. The term 'signal' is used here to represent an item of data that can vary over time, for example vehicle speed, whilst the term 'static data' is used to denote a temporally unchanging property like vehicle width.
This Server Specification describes a discovery mechanism that defines the set of signals and data that a particular client can access at a particular point in time. It is recognized that some automobile manufacturers may expose more signals and data than others. The Vehicle Signal Specification supports both extensibility and the ability to define private branches.
In addition, the 'tree' of signals that is accessible at any point in time may also vary depending on standard access control principles. That is, it can vary based on the identity of the user (person or organisation) requesting the data and/or the device (e.g. vehicle) where the request originates.
To support this, the Server Specification describes a token based mechanism that can optionally be used to pass tokens to the WebSocket Server to represent the user of an application and the device that the application is running on.
The target platform supported by the specification is exclusively passenger vehicles. Use of this specification for non-passenger applications (for example heavy machinery, marine and airline infotainment) is not prohibited, but is not covered in the design or content of the API and therefore may be insufficient.
An example use case could be the implementation of a 'Home Mechanic' application that provides warnings if any the following need attention: tire pressure, engine oil level, washer fluid level and battery charge level. Future use case innovations in transportation, safety, navigation, smart energy grids, smart transportation, consumer infotainment and personalisation are all possible through this specification.
Web developers building interoperable applications based upon this API, will help empower a common web platform across consumer devices and passenger vehicles within the Web of Things.
This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at https://www.w3.org/TR/.
This document was published by the Automotive Working Group as a First Public Working Draft. This document is intended to become a W3C Recommendation. If you wish to make comments regarding this document, please send them to public-automotive@w3.org (subscribe, archives). All comments are welcome.
Publication as a First Public Working Draft does not imply endorsement by the W3C Membership. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.
This document was produced by a group operating under the 5 February 2004 W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.
This document is governed by the 1 September 2015 W3C Process Document.
The vehicle SHALL expose vehicle signals via a WebSocket. This enables a client to GET or SET vehicle signals and data; to SUBSCRIBE to receive notifications and to UNSUBSCRIBE from receiving notifications.
A future revision of this specification may consider additionally exposing vehicle signals via a RESTful web service but this is out of scope for this version of the specification.
The W3C WebSocket API is defined here and the WebSocket Protocol is defined in RFC6455.
A component or module running on the vehicle is required to create a WebSocket and listen for inbound connections from application clients to enable secure access to vehicle signals. In this specification this module are referred to as the ‘WebSocket Vehicle Signal Server’ (WVSS) or for simplicity as ‘the server’.
This specification assumes that a single WebSocket is used to enable communication between the client application and the server in order to reduce processing overhead.
It is not explicitly prohibited for the client to request that the server opens more than one WebSocket. However, the server MAY refuse to open a subsequent WebSocket connection and the client is responsible for handling this gracefully.
If more than one WebSocket connection is established between a Client Application and the server then each connection is managed independently. For example, subscriptions created using a particular WebSocket connection only trigger notifications via that connection and the client must use that WebSocket instance to unsubscribe.
If more than one WebSocket connection has been established between one or more clients and a particular server instance, there is a risk that race conditions and concurrency issues could occur. An example of this would be, where two or more WebSocket connections are used to update a particular setting at the same time.
Unless explicitly stated otherwise, the client can only assume that the server implements a simple concurrency model where lost updates and dirty reads could potentially occur if the server has more than one WebSocket connection open.
The following example illustrates how a client could invoke the API. This simplified example is for illustrative purposes only, it does not show error handling and is not intended to be commercial code.
// open WebSocket
var vehicle = new WebSocket("wss://wwwivi", "wvss1.0");
// establish authorization
vehicle.onopen = function () {
vehicle.send('{"action": "authorize",
"tokens": {"authorization": "<user_token_value>"},
"requestId": 103}');
};
// request a signal
if (vehicle.readyState === OPEN) {
vehicle.send('{"action": "get", "path": "Signal.Drivetrain.Transmission.Speed"}');
// process messages from the server
vehicle.onmessage = function(event){
var msg = JSON.parse(event.data);
if(msg.path == "Signal.Drivetrain.Transmission.Speed" && msg.value){
console.log("The current speed is " + msg.value);
}
};
}
// close the WebSocket
vehicle.close();
The following example shows a JavaScript client subscription request and response.
// set a subscription, assuming the same authorization and set up from Example 1
if (vehicle.readyState === OPEN) {
vehicle.send('{"action": "subscribe", "path": "Signal.Drivetrain.InternalCombustionEngine.RPM",
"requestId": 104}');
vehicle.onmessage = function(event){
var msg = JSON.parse(event.data);
if(msg.hasOwnProperty("requestId") && msg.requestId == 104){
console.log("Latest RPM is " + msg.value);
}
};
}
As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.
The key words MAY, MUST, and SHALL are to be interpreted as described in [RFC2119].
This specification defines conformance criteria that apply to a single product: specifically the 'in-vehicle' WebSocket Vehicle Signal Server that implements the interfaces, semantics and behaviour defined in this document. For simplicity, this usually is referred to as the 'WebSocket Server' or just 'the server'
The term 'WebSocket' when used in this document, is as defined in the W3C WebSocket API (see here) and the WebSocket Protocol (see RFC6455).
This section is non-normative.
In a typical vehicle design, signals and data are transmitted between Electronic Control Units (ECUs) connected via internal vehicle networks. These include Controller Area Networks (CAN), Media Oriented Systems Transport (MOST) and Local Interconnect Networks (LIN). ECUs on these networks broadcast messages on network buses, and other ECUs on the bus respond to the messages.
In the component diagram included below, the internal vehicle CAN, MOST and LIN networks and the ECUs that communicate via these networks are abstracted and for simplicity, represented by the System component.
For safety, security and commercial reasons not all clients should be able to GET, SET or SUBSCRIBE to particular vehicle signals and data attributes. As a consequence, access-control must be managed so clients cannot simply connect directly to ECUs or to CAN, MOST or LIN network buses.
In this specification, the term 'signal' refers to a value that can change over time, for example, vehicle speed, whilst 'static data' or simply 'data' is used for a value that does not change e.g. vehicle length.
The vehicle signals and static data that are available within the vehicle System are exposed in a controlled manner to the Web Socket Server. The interface and communication mechanisms that are used between the vehicle System and the Web Socket Server are outside of the scope of this specification.
The WebSocket Server is the vehicle system that is responsible for exposing vehicle signals and data to its clients by implementing the interface and behaviours defined in this Vehicle Signal Server Specification (VSSS).
The WebSocket Server exposes signals and data in a manner that is consistent with the Vehicle Signal Specification (VSS). This defines a 'tree-like' logical taxonomy of the vehicle, (formally a Directed Acyclic Graph), where major vehicle structures (e.g. body, engine) are near the top of the tree and the logical assemblies and components that comprise them, are defined as their child nodes. Each of the child nodes in the tree is further decomposed into its logical constituents, and the process is repeated until leaf nodes are reached. A 'leaf node' is a node at the end of a branch that cannot be decomposed because it represents a single signal or data attribute value. An example VSS tree is shown below, its contents are illustrative:
Signals are named according to their path using the dot notation e.g. engine.rpm.
The client MAY invoke the WebSocket Server getVSS method to request that the server returns metadata that describes which signals and data attributes could potentially be accessed provided that the user and/or device is suitably authorised. This and other valid WebSocket Server actions are defined in more detail here.
This section is non-normative.
In-vehicle clients include both those clients that are running on an ECU in the vehicle itself, e.g. an ECU that implements an In Vehicle Infotainment (IVI) system, but also includes clients running on a user's device, e.g. a laptop, phone or tablet, that is connected via the vehicle's WiFi client (if one exists).
The methods defined on the WebSocket Server interface may be invoked by different types of on-board and off-board clients. On-board clients, running on the vehicle fall into one of two major categories:
Applications can access vehicle signals and data and have a User Interface that the driver or passengers in the vehicle can interact with.
Agents typically have no user interface. They can also invoke methods exposed on the WebSocket Server but their purpose is to connect to one or more off-board e.g. Vehicle to Everything (V2X) internet Servers in order to send data off-board.
Both applications and agents can be subdivided into those that are web-based and which run in a 'Web Runtime' and are implemented using Web Standards like HTML, CSS and JavaScript; those that run in their own native process written using e.g. C, C++ or Objective-C; and those that run in a Managed Runtime process written using languages like C# or Java.
Web applications and web agents MAY directly invoke the Vehicle Signal Server interface exposed by the WebSocker server, but it is expected that they typically access vehicle signals and data via a JavaScript Library that implements the W3C Vehicle Signal Client API (TODO Add Link). The Client API defines a standard approach to encapsulate or 'wrap' the W3C Vehicle Server Specification API to make it easier for Web based clients to access vehicle signals and data in a controlled way.
In addition to local, in-vehicle applications and agents, a variety of internet-based clients and servers may be interested in accessing vehicle signals and data. However, when a vehicle is not being used, most electrical systems are shut down in order to maximise battery life. This may include the systems that enable Wireless or Mobile connectivity. Then, when the vehicle is powered up, various systems start up, including those that provide off-board connectivity and the vehicle typically connects either to a local WiFi network or to a Mobile Network Operator (MNO) and dynamically be assigned an IP address.
At this point, internet based clients and servers do not know the dynamic IP address that was assigned to a specific vehicle. So normally, a vehicle has to connect to a well known endpoint, generally using a URL to connect to a V2X Server. The vehicle and the internet server typically mutually authenticate and the vehicle 'registers' with the server over an encrypted channel passing it a unique identifier e.g. its Vehicle Identification Number (VIN). From that point on, the server has the IP address that is currently assigned to a vehicle with a particular VIN, and can share this information with other internet based clients and servers, which are then able to send messages to the vehicle.
However, if the vehicle loses connectivity and is dynamically assigned a new IP address, it needs to re-register its new IP address and the new address needs to be communicated to any interested parties. For simplicity, this is not shown in the component diagram, primarily because this connectivity only becomes possible after the vehicle has registered with at least one off-board server (which is shown) but also because these scenarios do not affect the interface and behaviours defined in this WebSocket Server Specification
The component diagram shows four different types of internet based clients. A user may use a Web Page or a Web Application running on a phone or tablet to request access to vehicle signals on a particular vehicle. Alternatively these signals could be accessed using a Native or Managed Runtime Application or via another automated Client System or service e.g. the Traffic Management System in a 'Smart City'.
The server implementation MAY optionally restrict access to one or more vehicle signals so that they can only be accessed in response to a request from an authorized user and/or device. This could be for a variety of reasons, including safety, privacy or commercial considerations.
Hence, a request to GET, SET, SUBSCRIBE or UNSUBSCRIBE to data may require the client to demonstrate to the server that the request is from one or more suitably authorized Security Principals.
The different types of Security Principal, the approach taken to control access to signals and the importance of data privacy are described in the sections that follow.
The types of security principal include:
Type | Description |
---|---|
User | A person, system or organisation responsible for making the request e.g. driver, Emergency Services, Smart City Traffic Management System. |
Device | Vehicle or device where the request originates. Could for example be a user's Consumer Electronics (CE) device connected to the vehicle's WiFi hotspot or another vehicle in a convoy; an Electronic Control Unit (ECU) on the same vehicle or any internet connected system e.g. a Web of Things (WoT) device. |
When a client makes a request to access signal data it is performing the request on behalf of one or more Security Principals, that is, for a particular user and/or vehicle/device.
Access to signals is managed and controlled by the server. The server MAY elect not to enforce access controls on a particular signal or set of signals and to enforce different access controls on other signals.
For each security principal that must be authorised by the server, the client SHALL obtain and pass a security token e.g. an OAuth 2.0 token (see RFC6749) to the server using a message containing an 'authorize' action as defined here.
A server implementation MAY require that a request for a particular signal includes a security token for both the user (e.g. driver of the vehicle) and for the device (e.g. the vehicle) that is hosting the client. A request for a different set of vehicle signals MAY require that only the user is authorized or that only the device is authorized to access particular signals.
The client MAY send a message with an 'authorize' action to modify the access-control state of the WebSocket channel. The message structure is defined in detail here.
The following diagram illustrates a scenario where the client requests vehicle speed. In this example scenario, this signal is not under access control and so the server returns a message containing the requested data. The client then requests that the server sets the vehicle trunk status to open and the server demands that the client passes access-control credentials before satisfying the request. The steps are shown in more detail in the diagram below:
TODO: Update diagram to reflect auth token structure and review including other diagrams from Wiki
After receiving a message with an 'authorize' action the server attempts to verify the tokens e.g. by checking with the issuing Security Authority. If all of the tokens that are passed to the server are valid, it returns a success response and all subsequent requests received by the WebSocket instance have elevated access control privileges. Specifically, each of the GET, SET, SUBSCRIBE and UNSUBSCRIBE actions have the access control rights that the server deems to be appropriate for the security principals represented by the token(s).
If the client sends a subsequent 'authorize' message to the server with different token value(s). If one or more of these are invalid, then the server returns an error response and the WebSocket access control status remain unchanged. However, if the new token values are valid, then the server returns a success response and the access control privileges associated with the new token(s) only, applies for all requests from that point.
If the client or the server close the WebSocket connection and a new WebSocket instance is opened, then it is opened without elevated access control privileges.
This server specification defines a standardized, token based approach for access control that includes specific error responses for common user and device access control scenarios. It is important however, that the security model is extensible. Hence, the server MAY implement any type of token that is consistent with this standardized approach and MAY optionally define additional token(s) for other Security Principal type(s). If this is the case, it is expected that the precise type of security tokens that are supported by a particular server implementation, the format of those tokens and all additional error codes and reasons (e.g. to indicate that an additional token type has expired and needs to be renewed) is defined in the Server's documentation.
To support a layered security model and to help establish a 'defence in depth', all vehicle signal communications between the client and server MUST be strongly encrypted. This is to make it more difficult for an attacker to eavesdrop or tamper with the security tokens; the request data or the response payload.
One way in which this may be implemented is for the client and the server to use a Public Key Infrastructure (PKI) approach, where the client verifies the server's identity by checking the server's X.509 certificate and the client and the server negotiate to establish a secure Transport Layer Security (TLS) 'tunnel'.
The WebSocket protocol mandates that if a client requests that the server opens a WebSocket connection and the request is received over HTTPS, then the WebSocket is established over TLS, that is, a secure 'wss' connection is created.
Each security token SHALL have a specified lifetime during which it is valid. If on receiving a request for signals that are subject to access-control, the server determines that the request is unauthorised because the token has expired, the server returns an error response indicating the fact and the client requests a new token from the Security Authority and repeat the request.
If the server returns an error response indicating that the request is forbidden, renewing the security token does not make the request valid. In this case, the client should not repeat the request unless some other change has been made that may mean the request is now valid.
If the client application is an HTML Application running in a web runtime or is a web page running in a browser, the WebSocket instance may either be instantiated natively or be created using a 'standards compliant' WebSocket JavaScript library.
A WebSocket can also be initiated from a native (e.g. C++) Application or from an Application written using a 'Managed Runtime' language like Java or C#. It is assumed that native and managed clients use a suitable standards compliant WebSocket library to request that a WebSocket connection is opened on the server.
A client running on the vehicle is able to connect to the WebSocket Server instance using the hostname 'wwwivi' and uses the default port 443. The hostname 'wwwivi' may locally be mapped to the localhost IP address 127.0.0.1 e.g. by adding an entry to the /etc/hosts file.
The sub-protocol name always is 'wvss' with a version number suffix, e.g. wvss1.0
var vehicle = new WebSocket("wss://wwwivi", "wvss1.0");
RESTful web services are out of scope for the first revision of this specification, but could be considered for addition in a later version.
To support ‘defence in depth’ and a layered security approach, connections between clients and servers are strongly encrypted. This is to make it more difficult for an attacker that has succeeded in installing malicious code on a vehicle to eavesdrop, hijack security tokens or impersonate valid security principals in order to get and set sensitive vehicle signals.
The client SHALL connect to the server over HTTPS and request that the server opens a WebSocket. All WebSocket communications between the client and server MUST be over ‘wss’. Non encrypted communication is not supported, hence the server MUST refuse ‘ws’ connection requests.
For security reasons, clients are not able to connect directly to ECUs or to CAN, MOST or LIN networks. All access is via WebSocket. This allows the server to securely control access to vehicle signals.
The client MUST use the WebSocket send method, defined here, to pass request messages to the server. The message signature SHALL be:
WebSocket.send(request)
The request message MUST be comprised of one the request objects defined in this section. The client SHALL receive responses from the server using the WebSocket onmessage method, as follows:
WebSocket.onmessage = function(obj){
// process data
}
The message returned by the server MUST be one of the response objects defined in the table below.
The request and response parameters contain a limited number of attributes, defined in the table below.
Attribute | Type | Description |
---|---|---|
action | Action |
The action that the server is requested to perform. |
path | DOMString | The path to the desired vehicle signal(s), as defined by the Vehicle Signal Specification (VSS). |
requestId | DOMString | Unique id value specified by the client. Returned by the server in the response and used by client to link the request and response messages. The value MAY be an integer or a Universally Unique Identifier (UUID). |
subscriptionId | DOMString | Value returned by the server to uniquely identify each subscription. The value MAY be an integer or a Universally Unique Identifier (UUID). |
tokens | object | Structure containing one or more security token (e.g OAuth2) name/value pairs. |
timestamp | DOMTimestamp | The Coordinated Universal Time (UTC) time that the server returned the response (expressed as number of milliseconds). |
value | any | The data value returned by the server. This could either be a basic type, or a complex type comprised of nested name/value pairs in JSON format. |
TTL | int | Returns the time to live of the authentication token in milliseconds. |
filters | object | Provides a filtering mechanism to reduce the demands of a subscription on the server. |
error |
Error |
Returns an error code, reason and message. |
The Action enumeration is used to define the type of action requested by the client. All client messages MUST contain a JSON structure that has an 'action' name/value pair and the value of the 'action' property MUST be one of the values specified in the enumeration:
enum Action {
"authorize",
"getVSS",
"get",
"set",
"subscribe",
"unsubscribe",
"unsubscribeAll"
};
Enumeration description | |
---|---|
authorize |
Enables client to pass security tokens for Security Principals to the server to support access-control. |
getVSS |
Allows the client to request metadata describing signals and data attributes that are potentially accessible. |
get |
Enables the client to get a value once. |
set |
Enables the client to set a value once. |
subscribe |
Enables the client to receive a notification containing a JSON data structure with values for one or more vehicle signals and/or data attributes. The client requests that it is notified when the signal changes on the server. |
unsubscribe |
Allows the client to notify the server that it should no longer receive notifications based on that subscription. |
unsubscribeAll |
Allows the client to notify the server that it should no longer receive notifications for any active subscription. |
To enable access to signals and data attributes that are under access control, the client MAY optionally pass a message with an 'authorize' action to the server. The structure of the message and the associated success and error responses are defined below.
interface authorizeRequest { attributeAction
action; attribute string tokens; attribute string requestId; }; interface authorizeSuccessResponse { attributeAction
action; attribute int TTL; attribute string requestId; }; interface authorizeErrorResponse { attributeAction
action; attributeError
error; attribute string requestId; };
The server SHALL provide support the following security token types and names:
Security Principal | Token Name | Description |
---|---|---|
User | authorization | The user that the client is making requests on behalf of. This MAY be a person e.g. driver or passenger, it MAY be an organisation e.g. Emergency Services or MAY be any other legal entity. |
Device | www-vehicle-device | The originating device that is making the request to the server. This MAY be an ECU in the vehicle that is hosting the WebSocket Server or MAY be a device that is connected to the vehicle via a WiFi hotspot or MAY be any other device. |
The 'tokens' JSON fragment MAY contain an 'authorization' structure that contains just a single name/value pair, for example, to pass only the user token. Or it may contain only the 'www-vehicle-device' name/value pair to pass the just the vehicle token; alternatively it MAY include name/value pairs for both the 'authorization' and 'www-vehicle-device' tokens. These alternatives are illustrated in the following example:
if(userTokenOnly){
// Pass user token only
vehicle.send('{ "action": "authorize",
"tokens": { "authorization": "<user_token_value>" },
"requestId": "<some_unique_value>" }');
} else if (deviceTokenOnly) {
// Pass vehicle/device token only
vehicle.send('{ "action": "authorize",
"tokens": { "www-vehicle-device": "<device_token_value>" },
"requestId": "<some_unique_value>" }');
} else if (userAndDeviceToken) {
// Pass tokens for user and device
vehicle.send('{ "action": "authorize",
"tokens": { "authorization": "<user_token_value>",
"www-vehicle-device": "<device_token_value>" },
"requestId": "<some_unique_value>" }');
}
This specification purposely does not define the token structure and the methods to secure tokens. Providers of the server may choose their own preferred formats and security methods. The server may also treat tokens as opaque structures and pass them on to underlying software layers for evaluation.
While this specification does not mandate the token format and structure it SHALL at least contain the following elements to provide meaningful authorization:
Element | Description |
---|---|
Path | The signal path (defined here) the token authorizes. The path may be a branch name or contain wildcards to authorize entire branches. |
Actions | List of actions that the token authorizes for the path. The list contains at least one of the actions getVSS, get, set, subscribe and unsubscribe. |
Valid From | Timestamp in UTC indicating the date and time from which on the token is valid. |
Valid Until | Tmestamp in UTC indicating the date and time until which the token is valid. |
It is expected that client and server use the same token format. If a client presents a token using a format that is not understood by the server, ther server rejects the token.
The client MAY use the 'getVSS' action to request metadata describing the potentially available VSS tree. It does this by sending a 'vssRequest' message to the server. If the server is able to return the VSS metadata, then this is returned using a 'vssSuccessResponse' message. If an error occurs, the server returns a 'vssErrorResponse' message to the client.
The client is able to request VSS metadata from any point in the VSS tree, such that only the metadata for the signals within the given branch of the tree is returned. For example, only metadata for the chassis branch of the tree is returned when the chassis path is specified. If the path is not set, the response contains the metadata for the entire VSS tree.
If more than one 'getVSS' call is made with the same 'vssRequest' message content but at different times, the metadata in the 'vssSuccessResponse' SHALL be the same provided the access-control state of the WebSocket channel has not changed.
interface vssRequest { attributeAction
action; attribute string? path; }; interface vssSuccessResponse { attributeAction
action; attribute string path; attribute object vss; }; interface vssErrorResponse { attributeAction
action; attribute string path; attributeError
error; };
The following data flow example shows a request for the signal structure within the Signal.Body branch containing signals related to the vehicle body.
client -> {
"action": "getVSS",
"path": "Signal.Body",
}
receive <- {
"action": "getVSS",
"path": "Signal.Body",
"vss": { },
}
The client MAY send a 'getRequest' message to the server to get the value of one or more vehicle signals and data attributes. If the server is able to satisfy the request it SHALL return a 'getSuccessResponse' message. If the server is unable to fulfil the request, e.g. because the client is not authorised to retrieve one or more of the signals, then the server SHALL return a 'getErrorResponse'. The structure of these message objects is defined below:
interface getRequest { attributeAction
action; attribute DOMString path; }; interface getSuccessResponse { attributeAction
action; attribute DOMString path; attribute any value; attribute DOMTimeStamp timestamp; }; interface getErrorResponse { attributeAction
action; attribute DOMString path; attributeError
error; attribute DOMTimeStamp timestamp; };
It is important to note that all examples involving paths are illustrative. Valid path values and the signals and data attributes that correspond to a particular path are defined in the Vehicle Signal Specification.
The example below shows the JSON structure for a 'getRequest' message sent by the client to obtain the engine RPM value and a 'getSuccessResponse' returned by the server.
client -> {
"action": "get",
"path": "Signal.Drivetrain.InternalCombustionEngine.RPM"
}
receive <- {
"action": "get",
"path": "Signal.Drivetrain.InternalCombustionEngine.RPM",
"value": 2372,
"timestamp": <DOMTimeStamp>
}
In the case where the server returns a value that is a complex type, i.e. a value that is not a single basic JavaScript type (e.g. string, number, boolean), the value SHALL be returned as a set of name/value pairs in a JSON object structure. The format MUST be as defined by the Vehicle Signal Specification.
The following shows an example of a 'getRequest' that results in the server returning a 'getSuccessResponse' with a value that is a complex type:
client -> {
"action": "get",
"path": "Signal.Body.Trunk"
}
receive <- {
"action": "get",
"path": "Signal.Body.Trunk",
"value": { "Signal.Body.Trunk.IsLocked": false,
"Signal.Body.Trunk.IsOpen": true },
"timestamp": <DOMTimeStamp>
}
One or more wildcards (denoted by asterisk '*') MAY be included at any level in the path to specify that all nodes at that level are to be included.
In the example below, the path in the 'getRequest' includes a wildcard at the levels above the leaf (signal) node, in order to request just the 'IsLocked' state for all doors.
client -> {
"action": "get",
"path": "Signal.Cabin.Door.*.IsLocked"
}
receive <- {
"action": "get",
"path": "Signal.Cabin.Door.*.IsLocked ",
"value": [ {"Signal.Cabin.Door.Row1.Right.IsLocked" : true },
{"Signal.Cabin.Door.Row1.Left.IsLocked" : true },
{"Signal.Cabin.Door.Row2.Right.IsLocked" : false },
{"Signal.Cabin.Door.Row2.Left.IsLocked" : true } ],
"timestamp": <DOMTimeStamp>
}
In this example, a complex type with a nested array is returned in response to the Path: "Signal.Cabin.Door.*" which denotes: 'Return all signals and data attributes for all doors'. For simplicity, the example assumes that the VSS definition for each door only has two attributes 'IsLocked' and 'Window.Position'.
client -> {
"action": "get",
"path": "Signal.Cabin.Door.*"
}
receive <- {
"action": "get",
"path": "Signal.Cabin.Door.*",
"value": [ {"Signal.Cabin.Door.Row1.Right.IsLocked" : true, "Signal.Cabin.Door.Row1.Right.Window.Position": 50},
{"Signal.Cabin.Door.Row1.Left.IsLocked" : true, "Signal.Cabin.Door.Row1.Left.Window.Position": 23},
{"Signal.Cabin.Door.Row2.Right.IsLocked" : false, "Signal.Cabin.Door.Row2.Right.Window.Position": 100 },
{"Signal.Cabin.Door.Row2.Left.IsLocked": true, "Signal.Cabin.Door.Row2.Left.Window.Position": 0 } ],
"timestamp": <DOMTimeStamp>
}
The following shows a request for non-existent data
client -> {
"action": "get",
"path": "Body.Flux.Capacitor"
}
receive <- {
"action": "get",
"path": "Body.Flux.Capacitor",
"error": { "number":404,
"reason": "invalid_path",
"message": "The specified data path does not exist." },
"timestamp": <DOMTimeStamp>
}
The client may request that the server sets the value of a signal e.g. to lock a door or open a window by sending a 'setRequest' message to the server. If the server is able to satisfy the request it SHALL return a 'setSuccessResponse' message. If an error occurs e.g. because the client is not authorised to set the requested value, or the value is read-only, the server SHALL return a 'setErrorResponse' message.
interface setRequest { attributeAction
action; attribute DOMString path; attribute any value; }; interface setSuccessResponse { attributeAction
action; attribute DOMString path; attribute any value; attribute DOMTimeStamp timestamp; }; interface setErrorResponse { attributeAction
action; attribute DOMString path; attributeError
error; attribute DOMTimeStamp timestamp; };
Successfully set a signal.
client -> {
"action": "set",
"path": "Signal.Cabin.Door.*.IsLocked",
"value":{ [ {"Row1.Right.IsLocked" : true },
{"Row1.Left.IsLocked" : true },
{"Row2.Right.IsLocked" : true },
{"Row2.Left.IsLocked" : true } ] }
}
receive <- {
"action": "set",
"path": " Signal.Cabin.Door.*.IsLocked",
"value":{ [ {"Signal.Cabin.Door.Row1.Right.IsLocked" : true },
{"Signal.Cabin.Door.Row1.Left.IsLocked" : true },
{"Signal.Cabin.Door.Row2.Right.IsLocked" : true },
{"Signal.Cabin.Door.Row2.Left.IsLocked" : true } ] },
"timestamp": <DOMTimeStamp>
}
Unsuccessful set. The value cannot be set.
client -> {
"action": "set",
"path": "Signal.Drivetrain.InternalCombustionEngine.RPM",
"value": 2000
}
receive <- {
"action": "set",
"path": "Signal.Drivetrain.InternalCombustionEngine.RPM",
"error": { "number": 403,
"reason": "device_forbidden",
"message": "User is not authorised to set this value"},
"timestamp": <DOMTimeStamp>
}
Unsuccessful set. The value does not exist in the specified path.
client -> {
"action": "set",
"path": "Signal.Drivetrain.InternalCombustionEngine.RPM",
"value": { "locked" : true }
}
receive <- {
"action": "set",
"path": "Signal.Drivetrain.InternalCombustionEngine.RPM",
"error": { "number": 400,
"reason": "bad_request" ,
"message": "The server is unable to fulfil the client
request because the request is malformed."},
"timestamp": <DOMTimeStamp>
}
Vehicle data subscriptions provide data to the client whenever the signal changes on the server, unless otherwise specified using Server Side Filtering. The server MAY reduce the number of notifications sent to the client in order to reduce processing demands, particularly when the client has subscribed to continuously varying signals.
When the client makes a request to the server to create a new subscription, a JSON data object is returned. This object contains the attributes that were passed to the server to make the subscription and a 'subscriptionId' integer handle value which is used to uniquely identify the subscription.
interface subscribeRequest { attributeFor example when client need to continuously receive "Signal.Drivetrain.Transmission.TripMeter" information from the server, it can use "subscribe" action with the target property like below.Action
action; attribute DOMString path; attribute object? filters; attribute string requestId; }; interface subscribeSuccessResponse { attributeAction
action; attribute string requestId; attribute string subscriptionId; attribute DOMTimeStamp timestamp; }; interface subscribeErrorResponse { attribute DOMString path; attribute string requestId; attributeError
error; attribute DOMTimeStamp timestamp; }; interface subscriptionNotification { attribute string subscriptionId; attribute DOMString path; attribute any value; attribute DOMTimeStamp timestamp; }; interface subscriptionNotificationError { attribute string subscriptionId; attribute DOMString path; attribute object filters; attributeError
error; attribute DOMTimeStamp timestamp; };
client -> {
"action": "subscribe",
"path": "Signal.Drivetrain.Transmission.TripMeter",
"requestId": 1004
}
receive <- {
"action": "subscribe",
"requestId": 1004,
"subscriptionId": 35472,
"timestamp": <DOMTimeStamp>
}
When this "subscribe" request has been successed, client will receive a notificaton with changing value continuously as below JSON structure.
receive <- {
"subscriptionId": 35472,
"path": "Signal.Drivetrain.Transmission.TripMeter",
"value": 36912,
"timestamp": <DOMTimeStamp>
}
The client can use the 'requestId' value to associate the successful subscription response with the original request.
The 'subscriptionId' value is a unique value, created by the server and which is used internally by the server to manage subscriptions on that WebSocket instance.
The subscription id value may be used by the client to unsubscribe from receiving future notifications, by passing the handle value to the server with the unsubscribe action.
To differentiate subscription response from responses for ‘GET’ requests, subscription responses shall additionally include the subscription id value that identifies the subscription that triggered that notification.
The server ensures that a new unique subscription id value is returned for each successful subscription request on a particular WebSocket connection. However the server does not guarantee that subscription handle values are unique between different WebSocket instances.
Once the subscription is successfully registered with the server, the client receives subscription notifications containing the requested data, at a rate defined either by the server or by the server side filter. If there is an error with an existing subscription a subscriptionNotificationError is received by the client. This allows the client to handle the error, such as to reduce the subscription frequency if the server is unable to satisfy the initial demands of the client.
An example of a subscription can be found here.
To unsubscribe from a subscription, the client SHALL send an 'unsubscribeRequest' message to the server. This is comprised of a JSON structure which contains an action property set to 'unsubscribe' and a string containing the 'subscriptionId'. If the server is able to satisfy the request it returns an 'unsubscribeSuccessResponse'. If an error occurs, for example because an invalid subscriptionId is passed to the server, an 'unsubscribeErrorResponse' is returned.
The client MAY unsubscribe from all of its subscriptions by sending an 'unsubscribeRequest' with the action property set to 'unsubscribeAll'. This does not require a subscriptionId value.
If the client has created more than one WebSocket instance, it MUST always unsubscribe using the same WebSocket instance that was originally used to create the subscription.
The client should always unsubscribe from receiving notifications when it is no longer using the data. Over a long vehicle journey, this significantly reduces the processing load on the server and allow the server to free memory. It therefore makes it more likely that the server remains responsive to future requests from the client.
The 'unsubscribe' message structures are defined below:
interface unsubscribeRequest { attributeUnsubscribe from a single subscription.Action
action; attribute string? subscriptionId; attribute string requestId; }; interface unsubscribeSuccessResponse { attributeAction
action; attribute string? subscriptionId; attribute string requestId; attribute DOMTimeStamp timestamp; }; interface unsubscribeErrorResponse { attributeAction
action; attribute string? subscriptionId; attributeError
error; attribute string requestId; attribute DOMTimeStamp timestamp; };
client -> {
"action": "unsubscribe",
"subscriptionId": 102,
"requestId": 5264
}
receive <- {
"action": "unsubscribe",
"subscriptionId": 102,
"requestId": 5264,
"timestamp": <DOMTimeStamp>
}
Unsubscribe from all subscriptions.
client -> {
"action": "unsubscribeAll",
"requestId": 3468
}
receive <- {
"action": "unsubscribeAll",
"subscriptionId": null,
"requestId": 3468,
"timestamp": <DOMTimeStamp>
}
Error case - invalid ID
client -> {
"action": "unsubscribe",
"subscriptionId": 3542,
"requestId": 7846
}
receive <- {
"action": "unsubscribe",
"subscriptionId": 3542,
"error": { "number":404,
"reason": "invalid_subscriptionId",
"message": "The specified subscription was not found." },
"requestId": 7846,
"timestamp": <DOMTimeStamp>
}
// send unsubscribe message
vehicle.send('{ "action": "unsubscribe", "subscriptionId": 102, "requestId": 5429 }');
// set handler
vehicle.onmesssage(function(event){
var msg = JSON.parse(event.data);
// success case
if(msg.hasOwnProperty("requestId") && msg.requestId == 5429){
console.log("Successfully unsubscribed for id " + msg.subscriptionId);
// error case
} else if (msg.hasOwnProperty("error")) {
console.log("Unsuccessful unsubscribe. " + msg.error.message)
}
});
'Filters' may be specified to enable Server side filtering to be used in order to throttle the demands of subscriptions on the server. This may enable the reduction of traffic if the developer has received a 429 - Too Many Requests error message. This can be implementation dependent allowing a number of potential filtering mechanisms, such as ranges, intervals and minimum changes. This can be implemented using the "filters" option.
Filters can only be applied for nodes in the VSS tree, not entire branches. For example, a filter cannot be set on engine.*, however it can be set on engine.rpm.
The subscription currently defaults to sending values to the client only onchange, however this may cause unnecessary processing demands on the vehicle server. A filter object can be included in the subscription request:
{ "action": "subscribe", "path": "<any_path>",
"filters": "{ <insert_custom_tags> }"
"requestId": "<some_unique_value>" }
Potential tags could include:
If the filter is not set, or is unsupported by the server, the notification frequency is determined by the server.
//client receives data every 100ms
{ "action": "subscribe", "path": "<any_path>",
"filters": { "interval": 100 },
"requestId": "<some_unique_value>" }
//client receives data when the value is between 100 and 200 (inclusive)
{ "action": "subscribe", "path": "<any_path>",
"filters": { "range": { "above": 100, "below": 200 } },
"requestId": "<some_unique_value>" }
//client receives data when the value is below 100 (inclusive)
{ "action": "subscribe", "path": "<any_path>",
"filters": { "range": { "below": 100 } },
"requestId": "<some_unique_value>" }
//client receives data when the value changes by 100 units
{ "action": "subscribe", "path": "<any_path>",
"filters": { "minChange": 100 },
"requestId": "<some_unique_value>" }
//client receives data when the value changes by 100 units
{ "action": "subscribe", "path": "<any_path>",
"filters": { "minChange": 100 },
"requestId": "<some_unique_value>" }
//client receives data when the value is above 200 (inclusive)
//and the value changes by 20 units
{ "action": "subscribe", "path": "<any_path>",
"filters": { "range": { "below": 200 }, "minChange": 20},
"requestId": "<some_unique_value>" }
When the range filter is used a final message is sent when the value returned is outside of the specified range. For example, if the range states { "below": 100 }, a final value may be received at 101 to indicate that the value is now out of range.
The client should not specify a minimum change amount that is smaller than it needs - in order to prevent adding unnecessary load on the server. The server shall return a '429 - Too Many Request' error response if it is unable to fulfil the request made by the client.
The section that follows defines the error responses that shall be supported by the server.
The WebSocket may be closed by either the client or the server by invoking the ‘close()’ method on the WebSocket instance.
The following example shows the lifetime of a WebSocket on the client:
// Open the WebSocket
var vehicle = new WebSocket("wss://localhost:4343", "wvss1.0");
// WebSocket is used to GET, SET, SUBSCRIBE and UNSUBSCRIBE
…
// Close the WebSocket
vehicle.close();
The WebSocket server may terminate the WebSocket connection if it has not received a request for a period determined by the server. It is the client’s responsibility to handle this gracefully and to recover and request new subscriptions, where required.
If there is an error with any of the client’s requests, the server responds with an error number, reason and message.
interface Error
{
attribute int number;
attribute string reason;
attribute string message;
};
For some error codes, for example '401 (Unauthorised)' there can be more than one cause. The error number that is returned is the HTTP Status Code Number e.g. 401. An error reason is also returned, this contains a pre-defined string value that can be used to distinguish between errors that have the same code (e.g. '401 Unauthorized)' but a difference cause. The error message is used to provide message text describing the cause in more detail.
client -> { "action": "subscribe",
"filters": { "<filter_expression>" },
"path": "<any_vss_path>",
"requestId": "<some_unique_value>" }
receive on error <- {
"error":{ "number": "<error_num>",
"reason": "<error_reason>",
"message": "<error_message>" },
"path": "<any_vss_path>",
"requestId": "<some_unique_value>" }
The server implementation supports at least the error numbers and reasons listed in the table below.
Error Number (Code) | Error Reason | Error Message |
---|---|---|
304 (Not Modified) | not_modified | No changes have been made by the server. |
400 (Bad Request) | bad_request | The server is unable to fulfil the client request because the request is malformed. |
401 (Unauthorised) | user_token_expired | User token has expired. |
401 (Unauthorised) | user_token_invalid | User token is invalid. |
401 (Unauthorised) | user_token_missing | User token is missing. |
401 (Unauthorised) | device_token_expired | Device token has expired. |
401 (Unauthorised) | device_token_invalid | Device token is invalid. |
401 (Unauthorised) | device_token_missing | Device token is missing. |
403 (Forbidden) | user_forbidden | The user is not permitted to access the requested resource. Retrying does not help. |
403 (Forbidden) | user_unknown | The user is unknown. Retrying does not help. |
403 (Forbidden) | device_forbidden | The device is not permitted to access the requested resource. Retrying does not help. |
403 (Forbidden) | device_unknown | The device is unknown. Retrying does not help. |
404 (Not Found) | invalid_path | The specified data path does not exist. |
404 (Not Found) | private_path | The specified data path is private and the request is not authorised to access signals on this path. |
404 (Not Found) | invalid_subscriptionId | The specified subscription was not found. |
406 (Not Acceptable) | not_acceptable | The server is unable to generate content that is acceptable to the client |
429 (Too Many Requests) | too_many_requests | The client has sent the server too many requests in a given amount of time. |
502 (Bad Gateway) | bad_gateway | The server was acting as a gateway or proxy and received an invalid response from an upstream server. |
503 (Service Unavailable) | service_unavailable | The server is currently unable to handle the request due to a temporary overload or scheduled maintenance (which may be alleviated after some delay). |
504 (Gateway Timeout) | gateway_timeout | The server did not receive a timely response from an upstream server it needed to access in order to complete the request. |
The server may optionally return additional error codes. It is expected that if this is the case, they are defined in the Server Documentation. Wherever possible the Server returns a standard HTTP error code where one has been defined for the error condition. See for example RFC7231, RFC7235, and RFC6585.