Vehicle Information Service Specification

From Auto
Jump to: navigation, search

*****NOTE*****

THIS IS A WIKI PAGE FOR EXPOSING WORKING IDEAS. THE ACTUAL WORKING SPECIFICATION CAN BE FOUND HERE: https://w3c.github.io/automotive/vehicle_data/vehicle_information_service.html.

THE FIRST PUBLIC WORKING DRAFT CAN BE FOUND HERE: https://www.w3.org/TR/2016/WD-vehicle-information-service-20161020/


Contents

Vehicle Information Service Specification

Introduction/Purpose/Overview

* Paul Boyes

Basic concepts

What this spec describes

What it means to be "compliant" (?)

Terminology

* Powell Kinney

Architecture

Component Model

Kevin Gavigan (JLR) and Adam Crofts (JLR)

The following diagram provides a component view of a vehicle system that implements the W3C Vehicle and Data APIs. This includes a JavaScript library which implements a Web IDL definition of the APIs and an on-board vehicle server that exposes vehicle data via a WebSocket and/or RESTful Web Service API. The diagram also shows a number of different types of on-board and offboard clients that can consume vehicle signal data.

W3C Vehicle API Component Diagram v1.2.png

Description of Components involved in exposing and consuming W3C Vehicle and Data APIs
Component Description
Browser Web Browser running on user's Consumer Electronic (CE) device (e.g. PC, laptop, tablet, phone) accessing a web page and/or web service exposing vehicle data provided by server(s) on the internet.
Web Runtime Web Runtime acting as host for a HTML Application running on a CE device.
Native or Managed Application Native Application written using C,C++/Qt etc. or Managed Runtime Application written using Java, C#, VB.NET etc. running on a CE device accessing vehicle data provided by server on the internet.
Internet System Automated and possibly Smart Service or System that accesses vehicle data exposed by one or more internet servers using Business to Business (B2B) interface(s).
Internet Server(s) Cloud based servers and services that receive vehicle data sent by Agent(s) running on the vehicle and which enable other internet based clients to access vehicle data.
Web Client Web Runtime (e.g. browser) based client hosted and running on the vehicle accessing vehicle signals and providing a user interface (UI) for a user in the vehicle.
Web Agent Web Agent hosted and running on vehicle in a web runtime environment (e.g. browser) that accesses vehicle signals and sends them to offboard cloud server(s)
Native or Managed Agent Agent (written in C, C++/Qt or Managed Runtime language e.g. Java, C#) that accesses vehicle signals and sends them to offboard cloud server(s)
Native or Managed Client Application (written in C, C++/Qt or Managed Runtime language e.g. Java, C#) running on the vehicle accessing vehicle signals and providing a user interface (UI) for a user in the vehicle.
JavaScript Library Code library written using JavaScript that implements the W3C Vehicle APIs defined using Web IDL. Expected that the library will generally be loaded into same process (browser/web runtime) as the Web Client or Web Agent code that accesses it.
Vehicle Server Server on vehicle that provides WebSocket and/or RESTful web service interface(s) to access vehicle signals. Interface is based on the Vehicle Signal Specification (VSS).
Vehicle System Represents the remainder of the vehicle system that exposes vehicle signals in a controlled, secure way. Signals may be under access control and only accessible by suitably authorized clients. The interface(s) between the Vehicle System and the Vehicle Server are out of scope for this specification, but could be defined using e.g. Franca IDL

Security and Privacy Considerations

* Kevin Gavigan
* Junichi Hashimoto

Introduction

The server implementation may restrict access to one or more vehicle signals so that they can only be accessed in response to a request from an authorized user. This could be for a number of reasons, including safety, privacy or commercial concerns.

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.

Security Principals and Tokens

Kevin Gavigan (JLR) and Adam Crofts (JLR)

The types of security principal could include:

Types of Security Principal
Type Description
User A person, system or organisation responsible for making the request e.g. driver, Emergency Services, Traffic Management System.
Device Vehicle or device where the request originates. Could be the current vehicle or another vehicle in a convoy; a user's Consumer Electronics (CE device) an Electronic Control Unit (ECU) on the same vehicle or any internet connected system e.g. a Web of Things (WoT) device.

For each security principal that must be authorised by the server, the client will obtain and pass a security token e.g. an OAuth 2.0 token (see here) to the server as part of the request to GET, SET, SUBSCRIBE or UNSUBSCRIBE to data.

For example, a particular server implementation may require that a request to obtain vehicle speed includes a security token for both the driver of the vehicle and for the vehicle instance. A request for a different vehicle signal may require that only the user is authorized or that a particular device is authorized to access the vehicle signal.

For each Security Principal type that must be authorised for a particular request, the client will request one or more security tokens from a Security Authority and pass these tokens as part of a request to the server. The server is responsible for checking that the tokens are valid before granting the request.

Obtaining and Renewing Security Tokens

The mechanism by which a client requests security tokens for different Security Principal types and the server validates those tokens is out of the scope of this specification.

However a possible implementation could be for the client to securely pass credentials for the security principal to an on-board Authentication Agent that securely passes the credentials onto an off-board Authentication Service.

If the credentials are valid, the Authentication Service passes the token back to the local Authentication Agent, which returns it to the client. The client may keep the token in memory or store the token in secure local storage. When the client makes a request for data that requires authorization, it includes the required token(s) in the request.

When the server receives one or more tokens; for each token, it securely requests the off-board Authentication Service to determine whether the token is valid, invalid or has expired. If all of the tokens are valid, the server implements the request and assuming no other errors occur, returns a successful response. For a request over HTTPS, the server would return a 'HTTP 200 OK' response. A request over 'wss' would include a success response packet that contains data in a JSON data structure returned from the server.

Passing Security Tokens in a Request

This section defines the mechanism used to pass security tokens on WebSocket and RESTful web service calls.

WebSocket Channel Authorization

WebSocket connections are long-lived, stateful channels that can, in contrast to HTTP, maintain authorization and authentication between requests and messages. Therefore a token transmitted from client to server is applied to all messages after that.

WebSocket Security Flow 1.png

Concepts:

  • The channel has the authority of the union aggregate of all tokens provided to that connection.
  • Server is responsible for validating/authenticating tokens and respecting/enforcing permissions policy of the tokens provided.
  • “respecting/enforcing permissions/privacy” means returning 401/403/4XX status. This is the only security-related communication from the server to client defined.
  • The only security related communication from client to server is to provide token with Authorization message.
  • The client is responsible for retrieving a token from an authorization source and how that occurs is not within scope of this specification.

TODO:

  • Client to receives TTL information back from server after token sent
  • Token expiration during subscription
  • Multiple tokens & Asynchronous token verification
HTTP Request Authorization

For RESTful web service requests that require authorization, security tokens will be included as part of the HTTP header using standard Authorization header format.

GET /api/v1/vehicle.body.speed
Host: wwwivi.local
Authorization: Bearer vScgAW2WBA2tww3DXRCZsipb0nJa6mmk7jP2zkusH1tm9sNCERcVKnEYPPaFzDLYMQECpVmT756fitS6Q6JIwWyF9FGeLhPS8Re45rzOjBl3mYQwzKWOOaJSUFTLVwqvX2849hBRHJ7t
Accepts: application/json

The server may require that just the user or just the device be authorized to access a particular signal or set of signals, in which case only the relevant token needs to be passed to the server. This requirement may be specified in the VSS tree as provided by the server, but the server must reply with a correct error code message when a request is made for a signal with a token that does not provide the required permissions or access level.

N.B. Security token does not need to be included in a request if the server does not implement access control to GET, SET, SUBSCRIBE or UNSUBSCRIBE to particular vehicle signal(s). However it is not an error to include a security token in a request that does not strictly require them.

Use of Encryption

To support a layered security model and to help establish a 'defence in depth', all vehicle signal communication between the client and server will be strongly encrypted. This makes 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 will be establised over TLS, that is, a secure 'wss' connection will be created. Hence all messages containing vehicle signals, whether passed via RESTful web services or using WebSockets will be strongly encrypted.

Token Renewal

Each security token will have a particular lifetime during which it is valid. If on receiving a request that includes a security token, the server determines that the request is unauthorised because the token has expired, the server will return an error response indicating the fact and the client will request a new token from the Security Authority and repeat the request.

If the server returns an error code indicating that the request is forbidden (and so just renewing the security token will not make the request valid), then the client should not repeat the request unless some other change has been made and the client now believes that the request is valid.

Error Handling

Where an error occurs and a standard HTTP error number has been defined for the error condition, this will be returned (see for example RFC7231,RFC7235, and RFC6585)

The error numbers listed below will be returned by the server where a user or device token is unauthorised or forbidden. In the case of a RESTful web service request, this will be as a HTTP error number returned in the response. If the request is made using a WebSocket, the HTTP error number value will be returned in the JSON response packet.

Authorization Error Numbers
Error Numbers Meaning
401 Unauthorised: User token has expired. Client should renew the token and repeat the request.
403 Forbidden: User token or other part of the request is invalid. Repeating the request without resolving the issue will not succeed.
461 Unauthorised: Vehicle/Device token has expired. Client should renew the token and repeat the request.
463 Forbidden. Vehicle/Device token or other part of the request is invalid. Repeating the request without resolving the issue will not succeed.

<TODO: Discuss requesting IANA reserves HTTP error number 461 (vehicle/device unauthorised) and 463 (vehicle/device forbidden)>

For a particular error number e.g. 403 'Forbidden' there can be more than one reason why the user is forbidden from accessing the resource. The error code will be used to specify the type of error. For a particular error type there may be more than one cause. An error code string will be used to specify the reason for the error and the error message will be used to return a detailed error description.

For example, the specification enables the client to subscribe to receive notifications for one or more signals. As a result the security principal(s) represented by tokens on a particular request may not be authorised to access ALL of the requested signals. In this case, the server will return either error 403 (if the user is forbidden) or error 463 (if the vehicle/device is forbidden) from accessing one or more of the signals. The error code will contain a value indicating the reason why one or more signals could not be accessed and the error description will contain additional information describing which signal(s) triggered the error response.

<TODO Add table with list of error codes>

Managing domains, IP addresses and Certificates

While clients communicate with on-board server, a particular domain, wwwivi.local is used to identify the server. This domain name is resolved to the server's IP address. The domain name is chosen so that in different stages of the client software's life cycle, i.e. development, staging and production, developers and OEM can deploy, test and run the client software without worrying of certificates of the domain. The server's certificate is trusted by the OS on which the client is running. OEM may choose to revoke / renew certificates via OTA updates.

W3CDomainName.png

The server is running with a self-signed certificate, which is trusted by the OS running the client. If the OS is running on a device that is managed by the OEM, the certificate is trusted by the OS. If the device is a 3rd-party device such as a mobile phone, the client running on such a device will have to trust the server's certificate. Using OEM's certificate manager is a feasible way.

W3CCerts.png

Data Model (reference the VSS or resulting spec)

* Peter Winzell
* Rudi Streif

How the model is defined

How to reference the model

How to discover the model

WebSocket

* Powell Kinney
* Adam Crofts
* Kevin Gavigan
* Rudi Streif
* Peter Winzell

Connection Setup

Protocol Overview - who says what when

Message Types

subscribe
unsubscribe
get
set

Use Cases and Sample/Reference Code

HTTP

* Adam Crofts
* Kevin Gavigan
* Powell Kinney
* Rudi Streif
* Peter Winzell

Headers

Routes=

GET /api/v1/...
PUT /api/v1/...

Use Cases an Sample/Reference Code


Data Model

Rudi Streif (JLR), building upon the Vehicle Signal Specification (VSS) authored by Magnus Feuer (JLR) and published through the GENIVI Github repository at [1].

Introduction

Multiple attempts have been made to specify and unify signals used in vehicles to represent various types of data and control points such as

  • Status Information (door open, lights on, doors locked, ...)
  • Measurements (engine rpm, outside temperature, fuel level, ...)
  • Control Points (lock doors, open sun roof, start engine, ...)

This includes the Vehicle Data specification published (currently as a draft) by this group at [2].

The main criticism about the current specifications can be summarized as

  • The definition of the data model is directly tied to an API. Consequently, if the data model evolves the API must be changed which makes it a non-trivial effort to add and modify signals.
  • A structure that makes it hard to modify the vehicle configuration. For instance, the `Vehicle` interface has a member `engineSpeed` which is based on the assumption of a single-drive vehicle. However, a hybrid vehicle can have a regular engine and an electric motor which both operate at their own speeds. Or an electric vehicle could have a motor for each axle, or one for every wheel, or two motors to drive the back wheels independently and another motor for the front axle.
  • No provision for OEMs to extend the specification with proprietary signals. While this might be somewhat controversial for this consortium, it does provide an incentive for OEMs to adopt the specification.
  • No mechanism to group signals. For example the `Vehicle` interface provides the signals `engineSpeed`, `engineOil`, and `engineCoolant`. An application that wants to show a dashboard with engine data will have to subscribe to and track these signals individually.

The Vehicle Signal Specification (VSS) addresses these concerns through the use of a signal tree with

  • Branches - A branch is an entity that groups other branches and leaves.
  • Leaves - A leaf is a either
    • Attribute - an immutable object such as the number of doors, the vehicle weight, or static configuration data.
    • Signal - a mutable object such as vehicle speed, door open status, etc.

A VSS tree provides a hierarchical definition of signals that can be extended easily.

VSS Paradigms

The goals of VSS as an open standard can be summarized in a few straight-forward paradigms:

  • Standardize signal specification using a simple and extensible description language:
    • The description language shall not require any special editing tools.
    • Versioning systems such as Git shall easily be used with the description language.
    • The specification could be split into multiple files.
    • Includes allow the reuse of component specification. For example the component specification for a door can be included multiple times for every door of the vehicle.
  • Specify a minimum set of attributes:
    • To define a signal only its name, the data type, and a descriptive text are required.
    • Optionally other attributes can be specified such as unit, minimum and maximum values, etc.
  • Provide a lightweight change process
    • The process should encourage collaboration.
  • Single Source - Multiple Targets
    • The specification written using the description language shall be transformable into other formats such as JSON with tools.

As the description language, YAML was chosen. To support collaboration the specification has been published through a GENIVI repository on Github. Besides the specification, the repository also contains a converter from YAML to JSON.

VSS Tree

Signals are structured in trees.

Vss-tree.png

Signals are named according to their path using the dot notation:

   body.mirror.left.heated
   body.mirror.left.folded
   body.mirror.right.heated
   body.mirror.right.folded
   body.door.front.left.open
   body.door.back.left.open

The last element of a signal path always specifies a leaf which is either an attribute or a signal. The other elements are branches.

VSS Description

VSS descriptions are expressed as entries using YAML directives stored in files with the extension `.vspec`.

Branch Entry

A branch entry defines a branch specification:

   - transmission:
     type: branch
     description: Transmission-specific data, stopping at the drive shafts.

The elements are:

Element Description Default Optional
type Set to `branch` to identify the entry as a branch branch yes
description Description string none no
aggregate Defines if this branch is an aggregate meaning that if a single signal changes an update will be sent for all signals whether they have changed or not. true yes

Signal Entry

A signal entry defines a mutable object:

   - chassis.transmission.speed:
     type: Unit16
     unit: km/h
     min: 0
     max: 300
     mode: r
     description: The vehicle speed, as measured by the drivetrain.

The elements are:

Element Description Default Optional
type The data type branch no
min Minimum value none yes
max Maximum value none yes
unit Unit of measurement none yes
mode Access mode (r = read, w = write, rw = read and write) r yes
description Description string none no

Enumerated Signal Entry

A signal entry defines a mutable object with discrete values:

   - chassis.transmission.gear:
     type: Int16
     enum: [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8]
     mode: r
     description: The vehicle speed, as measured by the drivetrain.

The elements are:

Element Description Default Optional
type The data type branch no
enum List with values none no
mode Access mode (r = read, w = write, rw = read and write) r yes
description Description string none no

The attributes `unit`, `min` and `max` are not permitted together with the `enum` attribute.

Attribute Entry

An attribute entry defines an immutable object:

   - chassis.gross_weight:
     type: UInt16
     unit: kg
     value: 1735
     description: Total weight of the vehicle at maximum load.

The elements are:

Element Description Default Optional
type The data type branch no
unit Unit of mesaurement none yes
value The static value none yes
description Description string none no

Inclusion

For aggregation and reuse of signal and sub-tree specification, the VSS format provides an `include` directive to include a vspec file by another vspec file.

Global Inclusion

Global inclusion simply adds the content of the included vspec files to the content of the including vspec file:

   #include engine.vspec
   #include navigation.vspec
   #include transmission.vspec

The content of the included files are added to the root of the VSS tree.

Reuse Inclusion

Reuse inclusion allows the inclusion of the same file multiple time into different branches of the VSS tree:

   #include door.vspec body.door.front.left
   #include door.vspec body.door.front.right

Given a VSS specification of a `door.vspec` as:

   - door:
     type: branch
     description: Door status
     
   - door.locked:
     type: boolean
     description: Door locked (=True) or unlocked (=False)
     
   - door.open:
     type: boolean
     description: Door open (=True) or closed (=False)
     
   - door.window_position:
     type: UInt16
     min: 0
     max: 100
     description: Window position as a percentage: 0 window all the way open, 100 window closed
     

Then the above includes result in `door.vspec` to be included two times at the branch defined by the include statement which expands to:

   - body.door.front.left:
     type: branch
     description: Door status
     
   - body.door.front.left.locked:
     type: boolean
     description: Door locked (=True) or unlocked (=False)
     
   - body.door.front.left.open:
     type: boolean
     description: Door open (=True) or closed (=False)
     
   - body.door.front.left.window_position:
     type: UInt16
     min: 0
     max: 100
     description: Window position as a percentage: 0 window all the way open, 100 window closed
   
   - body.door.front.right:
     type: branch
     description: Door status
     
   - body.door.front.right.locked:
     type: boolean
     description: Door locked (=True) or unlocked (=False)
     
   - body.door.front.right.open:
     type: boolean
     description: Door open (=True) or closed (=False)
     
   - body.door.front.right.window_position:
     type: UInt16
     min: 0
     max: 100
     description: Window position as a percentage: 0 window all the way open, 100 window closed

Signal Addressing

The VSS tree provides for addressing of individual signals as well as groups of signals according to the hierarchy of the tree.

Considering a simple `get` API that takes the name of the signal as an argument and returns a JSON string:

   string get("<signalname>")
   
  • the value of an individual signal can be retrieved with:
   get("chassis.transmission.speed") ->
       { "chassis.transmission.speed": 54 }
   
  • the status of a door can be retrieved with:
   get("body.door.front.right") ->
       { "body.door.front.right.locked: true,
         "body.door.front.right.open: false,
         "body.door.front.right.window_position": 33
       }
       
  • the `locked` status of all doors can be retrieved with:
   get("body.door.*.locked") ->
       { "body.door.front.left.locked: false,
         "body.door.front.right.locked: true,
         "body.door.back.left.locked: true,
         "body.door.back.right.locked: true
       }
       
  • the status of all doors can be retrieved with:
   get("body.door.*") ->
       { "body.door.front.left.locked: true,
         "body.door.front.left.open: false,
         "body.door.front.left.window_position": 33,
         "body.door.front.right.locked: true,
         "body.door.front.right.open: false,
         "body.door.front.right.window_position": 100,
         ...
       }
  • the `locked` status of all doors, the trunk, the hood and eventually other hatches can be retrieved with:
   get("body.*.locked") ->
       { "body.door.front.left.locked: true,
         "body.door.front.right.locked: false,
         "body.door.back.right.locked: false,
         "body.door.back.right.locked: false,
         "body.hood.locked": true,
         "body.trunk.locked": true
       }

***************** Vehicle Signal Server Specification Ends Here (within wiki) *****************

Vehicle Signal Server Specification

Adam Crofts (JLR) and Kevin Gavigan (JLR), building upon discussions in Issue 81, particularly the approach raised by Powell Kinney (Vinli).

Introduction

The vehicle will expose vehicle signals via a WebSocket, or a RESTful web service interface or both. This will enable a client to GET or SET vehicle signals; to SUBSCRIBE to receive notifications relating to one or more vehicle signals and later to UNSUBSCRIBE from receiving notifications.

The W3C WebSocket API is defined here and the WebSocket Protocol (RFC6455) is defined here.

A component or module running on the vehicle is required to open a WebSocket connection and/or to provide a RESTful web service to enable secure access to vehicle signals. In this specification this module will be referred to as the ‘Application Server’ 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.

N.B 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 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 will be managed independently. For example, subscriptions created using a particular WebSocket connection will only trigger notifications via that connection and the client must use that WebSocket instance to unsubscribe.

N.B. 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, for example when two or more WebSocket connections are used to concurrently update (SET) the same value.

Unless explicitly stated otherwise, the client can only assume that the server will implement a simple concurrency model where lost updates and dirty reads could potentially occur if the server has more than one WebSocket connection open.

Initialisation of the Web Socket

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 can either be initialised natively or created using a WebSocket standards compliant JavaScript library. A WebSocket request 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 will use a suitable standards compliant WebSocket library to request that a WebSocket connection is opened on the server.

For browser and web runtime clients, the advantage of initialising the WebSocket natively in the web runtime is that there are fewer overheads for the vehicle signal implementation.

A client running on the vehicle will be able to connect to the Application Server instance using the hostname 'wwwivi' and will default to port 443. 'wwwivi' would be mapped to 127.0.0.1 in etc/hosts file.

The sub-protocol name will always be VISS and with a version number suffix, e.g. VISS1.0

var vehicle  = new WebSocket("wss://wwwivi", "<sub-protocol>");

<TODO Remove and/or websockets and RESTful webservices elsewhere in the document.>

RESTful web services are out of scope for the first revision of this specification, but could be considered for addition in a later version.

There is little advantage to embedding this in a Vehicle object on the navigator interface.

To support ‘defence in depth’ and a layered security approach, connections between clients and servers will be 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 and impersonate valid security principals to get and set sensitive vehicle signals.

The client will connect to the server over HTTPS and request that the server opens a WebSocket. All WebSocket communications between the client and server will therefore be over ‘wss’.

Non encrypted communication will not be supported, hence the server will refuse ‘ws’ connection requests.

In many vehicle designs, vehicle signals are made available by 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). For security reasons, clients will not be able to connect directly to ECUs or to CAN, MOST or LIN networks. All access will be via WebSocket. This allows the server to securely control access to vehicle signals.

Message Structure

Clients can request vehicle signal data from the server by sending a JSON data structure to the server via the WebSocket connection.

Only the action, path and reqId attributes are required, however multiple other options can be included as described in the table below.

vehicle.send('{ "action": "subscribe", "path": "body.mirrors.left",
                "reqId": <some_unique_value> }');
Messages parameters between client and server
Attribute Description Required Default Value Source
action The desired action from the client (subscribe, unsubscribe, get, set). Required n/a Client
path The path to the desired vehicle signal(s), as defined by the Vehicle Signal Specification (VSS). Required n/a Client
reqId 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. May be a Universally Unique Identifier (UUID) Required n/a Client
security Structure containing one or more security token (e.g OAuth2) name/value pairs. Optional Null Client
onchange Request for updates when a signal changes. The value can either be ‘true’ (default) or an object can be sent specifying upper and lower values and a minimum change in the signal value. Optional True Client
subId Integer handle returned by the server to uniquely identify each new subscription. Also used to unsubscribe. Required n/a Client/Server
timestamp A DOMTimestamp value indicating the time that the server returned the response (expressed as number of milliseconds). Required n/a Server
value 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. Required n/a Server
TTL Returns the time to live of the authentication token Required n/a Server
status Returns success for the ‘set’ and ‘unsubscribe’ methods Required n/a Server
error number Integer that identifies the type of error. Will be a HTTP status code if a standard value has been defined for the error type. Required n/a Server
error code Error string identifying the particular error code. Different Error Codes could occur for the same error number. For example, there may be more than one reason why a user is forbidden to access a resource (Error Number: 403) Required n/a Server
error message Description of the Error. Required n/a Server

Action

Action can take one of four values describing the desired service for the client. There are described in the table below.

Action Description
Subscribe Enables the client to receive a notification containing a JSON data structure with values for one or more vehicle signals. 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.
Get Enables the client to get a value once.
Set Enables the client to set a value once.

Path

This is the desired vehicle signal(s) path, as defined by the Vehicle Signal Specification (VSS) (see here)

Subscription Id

When the client makes a request to the server to create a new subscription, a JSON data object will be returned. This will contain the attributes that were passed to the server to make the subscription and a 'subId' integer handle value which is used to uniquely identify the subscription.

client -> { "action": "subscribe", "interval": 100,
            "path": "body.mirrors.left",
            "reqId":"<some_unique_value>" }

receive on success <- { "subId": 35472, "reqId": "<some_unique_value>" }	

The client can use the 'reqId' value to associate the successful subscription response with the original request.

The 'subId' value is a unique value, created by the server and which may be used internally by the server to manage subscriptions on that WebSocket instance.

The subscription id value can 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 will additionally include the subscription id value that identifies the subscription that triggered that notification.

The server will ensure 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.

Access Control and Authorization

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 user a vehicle or a device). It may be that the client system does not enable users to login, so the request may be from an anonymous user.

Access to signals shall be managed and controlled by the server. The server can elect not to enforce access controls on a particular signal or set of signals and to enforce different access controls on other signals.

Please see the section on Security <TODO add link> for more information.

When a client makes a request for a signal or set of signals that are under access control, the request must contain one or more valid security tokens (e.g. OAuth 2.0 tokens), one for each Security Principal type that the server wishes to authorize.

In addition to the ‘action’ and ‘path’ attributes, a request for a resource that is under access control must also contain a ‘security’ attribute.

The ‘security’ attribute is a JSON data structure that contains one or more name/value pairs, where each name is the name of a valid security token and the value is the token value.

"security": { "Authorization": <some_token_value>, 
              "WWW-Vehicle-Device": "<other_token_value>" }

A subscription request for signals data that is only available to suitably authorised Security Principals is shown below:

vehicle.send('{ "action": "subscribe", "path": "body.mirrors.left", 
	        "security": { "Authorization": <some_token_value>, 
                              "WWW-Vehicle-Device": "<other_token_value>" },
                "reqId": "<some_unique_value>"');

<TODO Clarify whether need to specify that token value is Base64 encoded>

For simplicity, many of the subscription examples included in this document do not include a security data object. This is acceptable if the data that is being accessed is not subject to access control restrictions.

Server Side Filtering

A 'Filter' can 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 dependant allowing a number of potential filtering mechanisms, such as ranges, intervals and minimum changes. This can be implemented using the "filter" option.

The subscription currently defaults to sending values to the client only onchange, however this may cause unnecessary processing demands on the vehicle server.

//client receives data every 100ms
{ "action": "subscribe", "path": "body.mirrors.left", "filters": { <insert custom tags>  } }

Potential tags could include:

  • Interval - provide data every X ms allowing the developer to restrict the number of values received for verbose subscriptions
  • Range - provide a value only when within a given range
  • Minimum change - provide data when a value has changed by a specified amount

<TODO - modify the existing filtering mechanisms>

The 'interval' attribute can be used alongside the 'subscribe' action to allow the client to set a time interval. By specifying an interval, the client is requesting that the server sends a notification to the client containing the requested data each time a full interval has elapsed. This data will be sent regardless of whether the signal value has changed. The server will continue to send notifications containing the requested data until the client unsubscribes.

If the ‘interval’ value is not set or unsupported, and the onchange value is not set or supported, the interval will be determined by the server.

//client receives data every 100ms
{ "action": "subscribe", "path": "body.mirrors.left", "interval": 100 }

On Change

The ‘onchange’ attribute can be used with the 'subscribe' action to enable the client to subscribe to receive a notification when data changes.

The onchange attribute can either be set to the Boolean value of ‘true’ or it can be a JSON object which contains an upper and lower value and a minimum change amount (delta) for the signal.

Setting the onchange attribute to ‘true’ will be valid for any signal type, but is particularly convenient for discrete values. By setting onchange to true, the client is sending a request to the server to send a notification whenever the data changes on the server.

{ "action": "subscribe", "onchange": true, "path": "engine.rpm" }

If the onchange attribute is set to true and the signal is not discrete, but changes continuously, the server is free to determine how often it notifies the client of a change in the value.

If the client wants more control with respect to how often it is notified of a continuously changing signal it can specify an upper and lower value for the signal and the minimum amount (delta) that the signal must change before the server should send a notification to the client. Values outside of the specified upper and lower bounds will not cause notifications and will require separate subscriptions, regardless of the specified minimum amount. The upper and lower bound should be inclusive.

The client should take care to avoid specifying a minimum change amount that is too small in order to prevent unnecessary messages.

In the following example, the client will receive a notification if the engine.rpm signal value changes by more than 100 regardless of any upper or lower attribute value.

N.B. In this example and the others that follow, the server response indicating that the subscription was accepted is not shown.

client -> { "action": "subscribe", 
            "onchange": { "minChange": 100 }, "path": "engine.rpm" } }'

// sometime later
receive <- '{ "id": 35472, "path": "engine.rpm", "value": 600 }'
receive <- '{ "id": 35472, "path": "engine.rpm", "value": 500 }'
receive <- '{ "id": 35472, "path": "engine.rpm", "value": 600 }'
receive <- '{ "id": 35472, "path": "engine.rpm", "value": 700 }'
receive <- '{ "id": 35472, "path": "engine.rpm", "value": 800 }'

The client receives a notification containing engine oil temperature data whenever the temperature is above 150 degrees or below 10 degrees, provided the temperature has changed by at least 5 degrees since the last notification was sent for the subscription.

client -> { "action": "subscribe", 
            "onchange": { "above": 150, "below": 10, "minChange": 5 }, 
            "path": "engine.eot" } }

// sometime later
receive <- '{ "id": 35489, "path": "engine.eot", "value": 150}'
receive <- '{ "id": 35489, "path": "engine.eot", "value": 155}'
receive <- '{ "id": 35489, "path": "engine.eot", "value": 160}'
receive <- '{ "id": 35489, "path": "engine.eot", "value": 165}'
receive <- '{ "id": 35489, "path": "engine.eot", "value": 155}'
receive <- '{ "id": 35489, "path": "engine.eot", "value": 150}'

The client receives power data whenever when the power value is between 10kW and 120kW provided it changes by at least 2kW.

client -> { "action": "subscribe", 
            "onchange": { "above": 10, "below": 120, "minChange": 2 }, 
            "path": "engine.power" } }

// sometime later
receive <- '{"id": 35493, "path": "engine.power", "value": 12}'
receive <- '{"id": 35493, "path": "engine.power", "value": 14}'

The client can subscribe to receive notifications for any of the signals that are specified by a given path value. In this case a notification is sent containing data for all applicable signals provided that the threshold or change criteria for any of the signals has been met.

In the following example the client subscribes to receive engine notifications by setting the “path”: “engine”. The client receives ‘power’ and ‘eot’ updates (for simplicity, other engine signals are not shown, but would be included in the response data packet).

client->{ "action": "subscribe", 
          "onchange": { 
             "power": { "above": 10, "below": 120, "minChange": 2 }, 
             "eot": { "above": 150, "below": 10, "minChange": 5 } ,  
          "path": "engine" } }

// sometime later
receive <- '{ "id": 35502, "path": "engine", 
              "value": { "power": 114, "eot": 151, 
                          <other engine signal name/value pairs> } }'
receive <- '{ "id": 35502, "path": "engine", 
              "value": { "power": 118, "eot": 156, 
                         <other engine signal name/value pairs> } }'

Errors

If there is an error with any of the client’s requests, the server will respond with an error number, code and message.

client -> { "action": "subscribe", "onchange": true, “interval”: 100, "path": "body.mirrors.left" }
receive on error <- { "error": { "number": ERROR_NUM, "code": ERROR_CODE, 
                                 "message": ERROR_MESSAGE }, 
                      "interval": 100, "path": "body.mirrors.left" }

These error messages are defined below.

Error Number Error Code Error Message
304 TODO Correct The subscription is already active, so no changes have been made by the server.
401 user_unauthorised Error message describing particular reason e.g. token expired.
403 user_forbidden Error message describing particular reason user is forbidden from accessing data.
404 invalid path TODO Includes private branch requests if not authorised (XX Can getVSS got public but not private XX - explain this somewhere)
406 TODO look up (auth but can't because of vehicle state) TODO Fix Error message
429 too many requests TODO Fix Error message describing particular reason user is forbidden from accessing data.
502 bad gateway TODO Fix Error message describing particular reason user is forbidden from accessing data.
503 service unavailable TODO Fix Error message describing particular reason user is forbidden from accessing data.
504 timeout TODO Fix Error message (used if server is too busy)
<nn> unrecognised_format The server is unable to fulfil the client’s request due to the format of the data request.
<nn> data_not_supported The server is unable to fulfil the client’s request since the data is not available on the vehicle.
<nn> none_settable_path This will be returned if the specified value does not match the specified path.
<nn> invalid_ID The specified ID used to unsubscribe is not recognised by the server

TODO Add note explaining ReasonCode can be used to describe sub errors e.g. 'invalid_vin'

Web Socket Closure

The WebSocket can 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”);

// WebSocket is used to GET, SET, SUBSCRIBE and UNSUBSCRIBE
…

// Close the WebSocket
vehicle.close();

The WebSocket server can 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 for example request new subscriptions, where these are required.

Dataflow Examples

Subscribe

Subscribe to an attribute at 100ms interval

client -> '{ "action": "subscribe", “interval”: 100, "path": "body.trunk" }'
//confirm subscription
receive <- '{ "id": 13654, "interval": 100, "path": "body.trunk" }
//return data every 100ms
receive <- '{ "id" 13654, "path": "body.trunk", 
              "value": { "isopen": true, "islocked": false } }
receive <- '{ "id" 13654, "path": "body.trunk", 
              "value": { "isopen": true, "islocked": false } }
receive <- '{ "id": 13654, "path": "body.trunk", 
              "value": { "isopen": true, "islocked": false } }

Subscribe to an attribute onchange (discrete values therefore no threshold required). The server returns only the changed attribute.

client -> '{ "action": "subscribe", “onchange”: true, "path": "body.trunk" }'
//confirm subscription
receive <- '{ "id": 13658, "onchange": true, "path": "body.trunk" }
//return data when the trunk is opened
receive <- '{ "id": 13658, "path": "body.trunk", "value": { "isopen": true, "islocked": false } }
//return data when the trunk is closed
receive <- '{ "id": 13658, "path": "body.trunk", "value": { "isopen": false, "islocked": false } }
//return data when the trunk is locked
receive <- '{ "id": 13658, "path": "body.trunk", "value": { "isopen": false, "islocked": true }}

Subscribe to an attribute (tps - throttle position) onchange (continuous value without minChange, so minChange determined by the server).

client -> { "action": "subscribe", "onchange": true, "path": "engine.tps" }}
receive <- '{ "id": 13661, "path": "engine.tps", "value": 54}'
receive <- '{ "id": 13661, "path": "engine.tps", "value": 58}'

Subscribe to an attribute (tps - throttle position) onchange (continuous value with minChange and bounds set).

client -> { "action": "subscribe", "onchange": { "above": 60, "below": 100, "minChange": 20 }, "path": "engine.tps" }}
receive <- '{ "id": 13662, "path": "engine.tps", "value": 60}'
receive <- '{ "id": 13662, "path": "engine.tps", "value": 80}'

Unsubscribe

Unsubscribe from a single subscription with valid ID

client -> '{ "action": "unsubscribe", "id": 35472 }'
receive <- '{ "status": "success", "action": "unsubscribe", "id": 35472}

Unsubscribe from all subscriptions by sending subscription “id” of ‘0’

client -> '{ "action": "unsubscribe", “id”: 0 }'
receive <- '{ "status": "success", "action": "unsubscribe", "id": 0 }

Unsubscribe from a single subscription with an invalid ID

client -> '{ "action": "unsubscribe", “id”: 3542 }'
receive <- '{ "error": { "number":<nn>, "code": INVALID_ID ,"message": "Invalid ID" }, "action": "unsubscribe", “id”: 3542 }

Where <nn> denotes a numeric integer error number (e.g. HTTP status code).

The client should always unsubscribe from receiving notifications when it is no longer interested in receiving them. Over a long vehicle journey, this will significantly reduce the processing required by the server and enable the server to free memory, making it easier for the server to remain responsive to requests from the client.

Get

The server shall return messages to the client using the following format, including the path to the signal and the value held within the signal:

client -> { "action": "get", "path": "engine.rpm" }
receive <- { "path": "engine.rpm", "value": 2372 }

In the case where the client has subscribed to a path which returns a complex type, the value shall be returned as name value pairs in a JSON object, as defined in the vehicle signal specification format. Wildcards can be used in order to specify only a subset of data. This can be specified at any level of the Vehicle Signal Specification (VSS) tree.

Complex type

client -> { "action": "get", "path": "body.trunk" }
receive <- { "path": "body.trunk", "value": { "locked": false, "open": true }}

Complex type with nested array. Path: “body.doors.*.lock” - All doors, lock state.

client -> { "action": "get", "path": " body.doors.*.lock" }
receive <- { "path": "body.doors.*.lock ", "value": { [ {"locked" : true }, {"locked" : true }, 
              {"locked" : false }, {"locked" : true } ] } }

Complex type with nested array. Path: “body.doors.*” - All doors, all door attributes (where door has two attributes, lock and window_pos)

client -> { "action": "get", "path": " body.doors.* " }
receive <- { "path": "body.doors.*.lock ", "value": { [ {“locked” : true, “windows_pos”: 143 }, {“locked” : true, “windows_pos”: 23 },
              {“locked” : false, “windows_pos”: 162 }, {“locked” : true, “windows_pos”: 0 } ] }

Request none-existent data

client -> { "action": "get", "path": "body.flux.capacitor" }
receive <- '{ "error": { "number":<nn>, "code": UNRECOGNISED_FORMAT, "message": "Unrecognised Format" } }

Set

Successfully set a signal.

client -> { "action": "set", "path": " body.doors.*.lock", "value":{ [ {“locked” : true }, {“locked” : true }, 
              {“locked” : false }, {“locked” : true } ] } }
receive <- { "status": "success",  "path": " body.doors.*.lock", "value":{ [ {“locked” : true }, {“locked” : true }, 
              {“locked” : false }, {“locked” : true } ] } }

Unsuccessful set. The value cannot be set.

client -> { "action": "set", "path": "engine.rpm", "value": 2000}
receive <- receive <- '{ "error": { "number": 403, "code": PERMISSION_DENIED , "message": "User is not authorised to set this value" }, "path": "engine.rpm", "value": 2000 }

Unsuccessful set. The value does not exist in the specified path.

client -> { "action": "set", "path": "engine.rpm", "value": { [ {“locked” : true } ]} }
receive <- '{ "error": { "number": <nn>, "code": DATA_NOT_SUPPORTED , "message": "Data Not Supported" }, "path": "engine.rpm", "value": { [ {“locked” : true } ]} }

***************** Vehicle Signal Server Specification Ends Here (within wiki) *****************

JavaScript Library Interface Guidelines

Below are a set of guidelines for developers to follow when creating JavaScript client libraries designed to interface with a VISS server as described above.

Any JS library is responsible for connection lifecycle (open, reconnect, failover, closure), authentication token transmission (not retrieval), request lifecycle, subscription lifecycle, and VSS tree traversal and discovery.

In general, all clients should strive for the following goals:

  1. Clients should provide an even abstraction exposing all functionality of the underlying server.
  2. Clients should provide an API that would be familiar to JavaScript developers including following best-practices when dealing with asynchronous operations.
  3. Clients should exhibit good behavior with regard to socket allocation.
  4. Clients should provide clear feedback on errors, especially those related to security credentials.

Required functions

Working with the Server

Vehicle signal APIs should focus on exposing the primitives exposed by the server's WebSocket API as closely as possible.

  • connect([host, [port]]) - Provides connection establishment and feedback at the WebSocket level. Takes a domain name, IP address, or defaults to a pre-determined value.
  • get(pathString) - Retrieves a single value in an asynchronous manner.
  • set(pathString, value) - Sets a single value in an asynchronous manner.
  • subscribe(pathString [, options]) - Creates a subscription with the given options. This should return an object or reference that can be used to unsubscribe in the future.
  • unsubscribe(subscription/reference) - Unsubscribes from a the specified subscription. This should accept part or all of the returned value fro the subscribe function.
  • disconnect() - Cleanly disconnects from the server, properly releasing WebSocket resources
  • authenticate(token) - Adds a token to the state of the connection.
  • getVSS() - Requests the VSS from the server and returns a VSS object with an interface as described below. This may be cached depending on the application, but should be retrieved anew each time the authentication state is changed (i.e. by passing a new token via the authenticate(token) function above.

Working with the VSS

The VSS object returned by the getVSS() function above should provide APIs sufficient to fully traverse the VSS tree and determine what signals are available at what permission level. Traversing the VSS tree can be accomplished using any of a number of query languages (i.e. CSS path selectors, XPath, etc.); CSS-based querying is shown in the example functions below. References to specific paths are as described above.

  • canGet(path) - Returns a boolean as to whether or not the current socket (given the tokens provided up to this point) has permission to read the given path.
  • canSet(path) - Returns a boolean as to whether or not the current socket (given the tokens provided up to this point) has permission to set the given path.
  • raw(/* path */) - Returns the raw VSS tree object rooted at path (if provided), otherwise returns the entire tree.
  • findPathByCSS(cssSelector) - Returns array of paths that match the given selector.
  • pathExistsByCSS(cssSelector) - Returns a boolean as to whether or not there exist any path that matches the given selector.

Example API

W3CLibrary.connect("wss://wwwivi.local:1234", (conn) => {
  // Connection established

  conn.provideToken("alsdkfja;ldkfj", (error, ttl) => {
    conn.loadVSS((error, vss) => { // see VSS object structure below
      const speedPath = vss.findPathByCSS("vehicle > body speed:first-child data-authorized-for-read:true")[0]

      if (vss.canGet(speedPath)) {
        conn.get(speedPath, (error, speed) => {
          {
            "statusCode": 403,
            "error": "Forbidden",
            "message": "Token provided is not authorized to read vehicle.speed"
          }
        });
      } else {
        // get another token and send it
      }

      conn.set(speedPath, 34, (error) => {});

      conn.subscribe(speedPath, options, (error, path, value) => {}, (error, subscription) => {'
        // use the `subscription` to unsubscribe later:
        conn.unsubscribe(subscription, (error) => {} )

        // TODO: handle subscription loss due to expired token
      });
  });

  conn.reset()
  conn.close()

  // TODO: make the connection lifecycle hooks more idiomatic
  conn.onerror = (error) => {};
  conn.ondisconnect = () => {};
};

/* the VSS object contains the entire tree and function with which to traverse/explore it */
//VSS object functions:

{
  pathExists(cssSelector) { /* returns true/false */ }
  canGet(path) { /* returns true/false */ }
  canSet(path) { /* returns true/false */ }
  findPathByCSS(cssSelector) { /* returns array of paths that match the given selector */ }
  raw(/* path */) { /* returns the raw vss tree object rooted at `path` (if provided), otherwise the entire tree */ }
}

// TODO: VSS raw structure