w3c / automotive

W3C Automotive Working Group Specifications
Other
146 stars 68 forks source link

Use a service-based API instead of WebIDL #81

Closed djensen47 closed 7 years ago

djensen47 commented 8 years ago

I'd like to get the conversation started on using a service-based approach.

The idea is to use WebSockets) or an API that resembles WebSockets and, if needed, something that resembles REST. (A REST-ish approach might be required for one time "get" requests or "set" commands).

For the WebSocket approach the idea would be to use a constructor to start listening to a signal using a URI. I'm not sure what the protocol and host should be so I will leave them off for the time being. The example below does not diverge from the WebSocket spec.

Update (2016-03-03): This approach would require many sockets for many different signals, which might be a problem.

var doorSocket = new Vehicle.Socket('/door/front/left');
doorSocket.onopen = function() {
  console.log('Front left door socket is open');
};
doorSocket.onmessage = function(message) {
  // message.data is the data requested and would be a JSON or JSON-LD
  console.log(message.data);
  console.log(message.origin);
  console.log(message.source);
  console.log(message.lastEventId);
  console.log(message.ports);
};
doorSocket.onclose = function(event) {
  console.log(event.code);
};
doorSocket.close();

There is also a send() method but I think we would use that approach for setting values.

If we want to grab data just once or set data, then the WebSocket approach may not be ideal. The old XMLHTTPRequest approach is also antiquated. I am not aware of any other spec for APIs make REST calls in the browser but if there is one we should follow it. In the meantime, here's a thought on what we can do.

var doorClient = new Vehicle.Client();
doorClient.get('/door/front/left')
  .then(function(result) {})
  .catch(function(error) {});
doorClient.post('/door/front/left', {'state': 'lock'})
  .then(function(result) {})
  .catch(function(error) {});

We can either use promises or callbacks. In the above example, I used promises.

The details still need to be worked out but I think this is a good starting point for a discussion.

djensen47 commented 8 years ago

Ted suggested using either localhost or a .local domain for the hostname.

Song Li mentioned that ws:// or wss:// should be adequate.

djensen47 commented 8 years ago

Questions

djensen47 commented 8 years ago

Powell [sic] mentioned in the meeting that we might need something that is more of a pub/sub or channel based approach (e.g., MQTT or Socket.io) so that only one socket will be needed. Powell to provide more details. Urata-san also described his prototype that uses a single websocket to perform the communication.

pkinney commented 8 years ago

For a more channel-like approach, a single-socket with a subscription layer that would allow a single WebSocket connection for one or more data element/source:

var socket = new Vehicle.Socket();
socket.onopen = function() {
  //subscribe to front-left door events
  socket.send('{ "action": "subscribe", path: "/door/front/left" }');

  //subscribe to sunroof events
  socket.send('{ "action": "subscribe", path: "/sunroof" }');
};

socket.onmessage = function(message) {
  const payload = JSON.parse(message);

  if (payload.path === '/sunroof') {
    console.log(`Sunroof is ${payload.data.status}`);
  }

  if (payload.path === '/door/front/left') {
    console.log(`Front-left door is ${payload.data.status}`);
  }
};

// at some point in time, one could unsubscribe from a particular path

socket.send('{ "action": "unsubscribe", path: "/sunroof" }');

This could also be extended to allow for a more rest-full approach using the same socket, but would instead get most recent value:

socket.get('{ "action": "get", path: "/sunroof" }');
djensen47 commented 8 years ago

@pkinney :+1: This looks good.

aShinjiroUrata commented 8 years ago

Hi, below example is basic idea of our polyfill. Basically similar to @pkinney san's example and added simple pub/sub.

/*
 In this example, assuming
 - Databroker just keep sending vehicle data which is one way communication.
 - set() is not considered here.
 - get(), subscribe() is handled at JavaScript layer. 
   Databroker side doesn't know how many get(), subscribe() are registered.

 We suppose incomming json like:
 {"Vehicle":
   {"RunningStatus": 
     {"VehicleSpeed":  {"speed": "40000","timeStamp": "1457079732000"},
       "EngineSpeed":   {"speed": "40000","timeStamp": "1457079732000"},
       "SteeringWheel": {"angle": "60"   , timeStamp": "1457079732000"} }
   }
 }
*/

var subHandlers = new Array();
var getHandlers = new Array();
var dataobj;

var socket = new WebSocket(url);

socket.onmessage = function(event) {
  dataobj = JSON.parse(event.data);

  subHandlers.forEach(function(handler) {
    setTimeout(function(){
      handler(dataobj);
    });
  });

  getHandlers.forEach(function(handler, idx) {
    setTimeout(function(){
      handler(dataobj, idx);
    });
  });
}

vehicleSpeed could be like

var vehicleImpl;

vehicleImpl.vehicleSpeeed = (function() {
  var _speed;
  var _timeStamp;
  return {
    get : function() {
      var p = new Promise(function(resolve, reject) {
        getHandlers.push(function(obj, idx) {
          if (obj.Vehicle.RunningStatus.VehicleSpeed){
            _speed = parseInt(obj.Vehicle.RunningStatus.VehicleSpeed);
            _timeStamp = parseInt(obj.Vehicle.RunningStatus.VehicleSpeed.timeStamp);
            callback();
            delete getHandlers[idx];
          }
        });
      });
      return p;
    },
    subscribe : function(callback) {
      subHandlers.push(function(obj) {
        if (obj.Vehicle.RunningStatus.VehicleSpeed) {
          _speed = parseInt(obj.Vehicle.RunningStatus.VehicleSpeed);
          _timeStamp = parseInt(obj.Vehicle.RunningStatus.VehicleSpeed.timeStamp);
          callback(this);
        }
      });
    }
  }
})();

navigator.vehicle = vehicleImpl;
wonsuk73 commented 8 years ago

Websocket-ish and RESTful style is different. RESTful style directly use HTTP methods(e.g. GET, POST, PUT, DELETE, UPDATE) for request messages to the server. In addition it is on the connectionless protocol. By the way websocket-ish style looks like JSON-RPC. AS proposal from @pkinney it need to describe requested action and target resource with JSON format. Refer to JSON-RPC 2.0 specification. For protocols underneath it would be better to define the specs regardless of the protocols even currently I am not sure it is possible.

wonsuk73 commented 8 years ago

It's from Paul's email. There is the activity which specifies a set of vehicle signals that can be used in automotive applications to communicate the state of various vehicle systems. This is very similar with the work we would like to do. For detail info, refer to VEHICLE SIGNAL SPECIFICATION

In the standard aspect, main standardization work looks below.

What do you think? @djensen47 @pkinney @aShinjiroUrata @QingAn @paulboyes ? I think we need to use RAML and JSON schema for defining RESTFul APIs for our specs What do you think?

peterMelco commented 8 years ago

One question - I am assuming a web socket solution here - should the Vehicle API consider to support optional JSON payload signing using a private/public key scheme.

(For reference http://hdknr.bitbucket.org/accounts/jws.html#json-web-signature-jws).

The background for this would be to keep the data integrity and prevent MITM attacks on the WSS connection to manipulate the data. (Note that this does not prevent the middle man to eavesdrop) . I know from previous OEM projects that requests were made to use payload signing for web service communication. Would this be in scope ? or would this be something that we should consider an implementation specific issue ?

A more general question is: What level of security this spec change(refactor) should or is able to support ? I think that by specifying the API using a web socket-ish approach we open up for, or at least facilitate a number of use cases, known and uknown. I think that some of them were presented by JLR during the Stuttgart meeting. What other security considerations do we need to do ? Encrypting the payload ?

So using @pkinney payload as an example in some wierd pseudo code:

payload = { "action": "subscribe", path: "/door/front/left" }
...
H = Hash(payload)
SH = sign(H,priv-key) 
...

Which would then be sent with the payload + the public key to verify the data as a new payload:

...
payloadwithsignature = {"payload":H,"signature":SH,"public-key":"pubkey"}
...
SH-pub = sign(H,pubkey)
if verify(SH,SH-pub)
   unhash(H)
...
wonsuk73 commented 8 years ago

@peterMelco It would be good if we could provide security guideline via security consideration as a part of the spec. I am not a security expert, but I think that basically for using Vehicle API it should be use HTTPS between web platform and web server. In addition as you said we could strongly recommend to use end to end security. I think W3C already almost developed Web Cryptography API spec. Do you think this spec could meet thing you want to do?

aShinjiroUrata commented 8 years ago

Hi, I checked VEHICLE SIGNAL SPECIFICATION. In my understanding, this specification can create replacement of WG's Vehicle Data spec.

chassis.doors.right_front.window_pos
chassis.doors.left_front.window_pos

body.mirrors.left.heated
body.mirrors.right.heated

Less rule style and it can be regarded as simple and flexible.

I think this spec is created by JLR people and is going to be used by Genivi/AGL, so to collaborate with Genivi/AGL, it should be better to use this as replacement of Vehicle Data spec.

This may be not a small change but it is not good to create similar but different specs at two places.

peterMelco commented 8 years ago

I agree with @aShinjiroUrata , if there is a vecihle data spec under development under AGL/Genivi we should try to coordinate. This would be something to adress at F2F ? Could we invite someone from this group during the f2f ? At least it should be on the agenda.

peterMelco commented 8 years ago

Yes, @wonsuk73 we should definitely try to point to a security pattern "attached" to the spec. Question is if we in the actual spec could add some - perhaps optional security enhancing functionality. WSS/HTTPS is basically taking care of confidentialty but it does not in any way prevent MITM attacks. These type of attacks are important to prevent for a number of use cases - and the implementor can take a number of steps to do this. One layer of security would be to support payload data signing as I so poorly tried to show above. I believe that this could be added to the API spec but it could also be left to the implementation. Anyway I will do some more thinking and discuss this with our security and network experts. Agenda topic for F2F ?

wonsuk73 commented 8 years ago

@aShinjiroUrata I think your point is also reasonable. We need to make clear the scope of the work between W3C Automotive WG and AGL/Genivi work. Do you think any proposal for this(role alignment btw two group)? In my point of view for standardization work, below is only required to be defined for RESTful API. I don't think it is required to define API when it is using websocket or XMLHTTPRequest like @pkinney 's proposal in the upper. @djensen47 @QingAn @peterMelco Am I right?

@paulboyes What do you think? It's quite big change I think :)

wonsuk73 commented 8 years ago

@peterMelco Yap! Absolutely we need to align the work with AGL/Genivi as soon as possible so that Inviting AGL/Genivi guys to upcoming F2F and having a their plan and aligning the work are good idea. By the way I think it would be nice if we have a discussion within the group for role alignment btw two groups before having a meeting with AGL guys. @paulboyes What do you think?

wonsuk73 commented 8 years ago

@aShinjiroUrata @peterMelco @paulboyes I have a question in general. If AGL/Genivi made a spec for RESTful API spec based on our spec, would it be published as a Genivi standards only? or is there some place (e.g. ISO, IEEE) to publish that as a international standards?

peterMelco commented 8 years ago

@wonsuk73 Genivi is not a world standard organization. It is a "closed" consortium of member companies. AGL is also a consortium of member companies under the Linux foundation. Basically it is not addressing ISO,IEEE. This is a problem, especially if we want to cover use-cases like vehicle-to-vehicle communication - not only do we need an international standard for the API itself, we also need to have standard way of making the API secure. How else can OEM1 vehicles trust information sent from OEM2. This also includes infrastructure or traffic control use-case, i.e green wave, traffic control etc...I think this is why our work is very important to get right. W3C is a much better forum to publish from. However, if we can at least use the same data spec as AGL/Genivi as you suggested we are on a much better path of setting this as a world standard. Now, this my personal interpretation - I don't have any great insight on how AGL is viewing this.

aShinjiroUrata commented 8 years ago

@aShinjiroUrata I think your point is also reasonable. We need to make clear the scope of the work >between W3C Automotive WG and AGL/Genivi work. Do you think any proposal for this(role >alignment btw two group)?

Actually I have not much idea but AGL/Genivi's VEHICLE SIGNAL SPECIFICATION is corresponds to our Vehicle Data spec and so we should synchronize with AGL/Genivi on this point. Regarding Vehicle Information API (Websocket-ish/REST-ish api are proposed on this part) AGL/Genivi seems not defining, so W3C WG should take care of this part. But this is my instant idea and I think better to talk with AGL/Genivi liaison.

wonsuk73 commented 8 years ago

Regarding Vehicle Information API (Websocket-ish/REST-ish api are proposed on this part) >AGL/Genivi seems not defining, so W3C WG should take care of this part. But this is my instant idea and I think better to talk with AGL/Genivi liaison.

I am not sure but I guess "Vehicle Information API" might not be needed when we go to the RESTful Model. Refer to previous proposal from @pkinney. Might be we need to revisit this. But anyway his proposal only use primitive websocket API for the Request with JSON description which has "ACTION" and "PATH". For the response it might use the same things with request case. @aShinjiroUrata Am I right?

wonsuk73 commented 8 years ago

@peterMelco Thanks for the information. I guess it would be good if we could make a RESTful API Model for the specs we have with collaboration with AGL/Genivi. Then our group makes the specs based on the collaboration result with W3C style and shape. In AGL/Genivi side, they might implement the specification on top of their reference implementation. What do you think?

peterMelco commented 8 years ago

@wonsuk73, @aShinjiroUrata We need to setup an interface for this collaboration with Genivi and AGL. I guess we already have regular meetings with Genivi. One question is: Is Genivi and AGL synchronized around the vehicle spec ? : https://github.com/PDXostc/vehicle_signal_specification. It seems to JLR that is driving this definition, but I am not sure if it is done within AGL or Genivi or both.

peterMelco commented 8 years ago

@wonsuk73 I agree with your suggestion. What is the opinion of the WG ?

wonsuk73 commented 8 years ago

@peterMelco I couldn't see any consensus of the group yet so that I think we need to have more opinions from members of the group including chairs. @paulboyes @QingAn @djensen47 @acrofts84 What do you guys think? What is your view on it?

QingAn commented 8 years ago

@wonsuk73 @peterMelco Considering AGL/Genivi's https://github.com/PDXostc/vehicle_signal_specification is related to our Vehicle Data spec, it is needed to discuss with AGL and Genivi on the VEHICLE SIGNAL SPECIFICATION's current status and make sure whether the Websocket-ish/REST-ish API is planned or not. This could be one agenda topic in upcoming F2F.

paulboyes commented 8 years ago

@wonsuk73 @peterMelco @aShinjiroUrata @QingAn @pkinney @djensen47 @drkevg @acrofts84 I think this is a great discussion. I feel that we should align with GENIVI/AGL Vehicle Signal Spec. JLR Portland is definitely leading their efforts and has said they will step up their participation in this group (@mfeuer @rstreif). The goal here is to make a spec that is useful, implementable and implemented by OEMs. We will be coordinating with GENIVI and JLR tomorrow. I think we should devote our April 5 meeting to this topic as well as a big part of the F2F. As for mechanism details for access, I do not have strong opinions at this point. However, I would say that performance and ease of data use should be considered. For example consideration of the following: vehicle data can flow quickly and in large amounts. Opening sockets can be expensive. I really would like to see a strawman architecture diagram for our proposal as well. Will post more tomorrow after our discussions with GENIVI and JLR, but all in all it would seem that on this thread, we are all essentially on the same page. It is a big change, but if it is useful, implementable and implemented, it will be a good thing.

peterMelco commented 8 years ago

@QingAn I think that aligning vehicle data spec(Vehicle signaling) is one seperate thing - or at least should/could be independant from the underlying impmentation that sends data signals back and forth. In our case we have a suggestion to step away from the browser dependency (Web IDL) and use a WebSocket-ish approach which could be something that Genivi/AGL is also considering. Yet again it could be something that they are not considering at this point. Anyway it should be interesting to hear Genivi/AGL thoughts on how they view an full implementation and have their take on the WebSocket-ish approach. @paulboyes This is something we should put on the table during the Genivi/AGL discussions . Also, is AGL actually on board here ? or is it only Genivi ?

rstreif commented 8 years ago

@paulboyes The idea of the vehicle signal specification that JLR OSTC is pursuing is to establish a catalog of signals with structuring and naming conventions that can be shared across OEMs to enable 3rd party developers to write applications that can run on across OEM platforms. However, it of course does not stop here. We need to have a reference implementation of an API that developers actually can use to start developing with. These are some top-level requirements (off the top of my head without any claim to be complete):

pkinney commented 8 years ago

Addressing the subscription-based and poll-based access to a vehicle data model (whatever it may be), I wanted to at least float a more formal version than what I had above. I'm using the model from https://github.com/PDXostc/vehicle_signal_specification as a stand-in, and the JSON formats are just potential ones--the overall behavior is not dependent on them as written.

The below is designed to allow for easy adaptation into whatever higher-level abstraction we want.

Subscription

A client can subscribe to a single parameter or branch of the vehicle model. For example, subscribing to a the open/close state of the front-right door would subscribe to the path body.door.0.right.open, or subscribing to all values within the second row of doors would subscribe to the path body.door.1 (vs. body.door.1.* ?). This would expose the client to updates to values at or within the path subscribed to:

{ "action": "subscribe", "path": "body.door.0.right.open" }

Additionally, a client can define whether a subscription will return on any new value or just different values, i.e. if the door is closed, does the client get a messages at a regular interval saying that the value is closed or just a message when the value changes. {..., "type" : "change"} vs. {..., "type": "value"}. [This might just default to change?]

client -> { "action": "subscribe", "path": "body.door.0.right.open" }
receive <- { "path": "body.door.0.right.open", "value": true}
receive <- { "path": "body.door.0.right.open", "value": false}
receive <- { "path": "body.door.0.right.open", "value": true}
client -> { "action": "subscribe", "path": "body.door" }
receive <- { "path": "body.door.1.left.open", "value": true }
receive <- { "path": "body.door.0.right.locked", "value": false }
receive <- { "path": "body.door.1.right.window_position", "value": 34 }
receive <- { "path": "body.door.1.right.window_position", "value": 46 }
client -> { "action": "subscribe", "type": "value", "path": "engine.rpm" }
receive <- { "path": "engine.rpm", "value": 2941 }
receive <- { "path": "engine.rpm", "value": 2425 }
receive <- { "path": "engine.rpm", "value": 2242 }
receive <- { "path": "engine.rpm", "value": 2242 }
receive <- { "path": "engine.rpm", "value": 2242 }
receive <- { "path": "engine.rpm", "value": 3623 }

Polling

A client can poll for the most recent value of a parameter. This is done with an action of type get and a path and triggers a value response for the given path.

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

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

Response

When a subscription is made, each value from the named sensor or each change should generate a response. When a poll request is sent, the client should receive as soon as possible based on some stored value.

Example

const socket = new Vehicle.Socket();

socket.onopen = function() {
  //subscribe to changes to new front-right door open state
  socket.send('{ "action": "subscribe", "type": "change", "path": "body.door.0.right.open" }');
  //subscribe to changes to state change on any child of the 2nd-row doors
  socket.send('{ "action": "subscribe", "type": "change", "path": "body.door.1" }');
  //subscribe to RPM values
  socket.send('{ "action": "subscribe", "type": "value", "path": "engine.rpm" }');

  startPollingMAF();
};

const startPollingMAF = function() {
  setInterval(function() {
    socket.send('{ "action": "get", "path": "engine.maf" }')
  }, 1000)
}

socket.onmessage = function(message) {
  const payload = JSON.parse(message);

  //  See "responses" above
};
rstreif commented 8 years ago

@pkinney

I think your suggestion essentially using long-polling is reasonable. The client opens the socket and keeps it open. That assumes that the server can never close the socket on its end, unless the handle has become stale because of the client going away without actually closing the socket on its end. Stale handles could happen for many reasons but the most common ones, I think, would be either the network connection is broken or a programming error on the client side. Potentially, this could lead to resource contention on the server side.

An actual implementation, should probably contain a server-side ping/heartbeat to which the client has to respond, so that the server can close the socket when the client disappears.

pkinney commented 8 years ago

@rstreif: You are correct that the client will have to implement disconnect/reconnect handling, but fortunately this is pretty ubiquitous and straight-forward with most clients:

// ugly reconnect example
const connect = () => {
    const ws = new WebSocket(host);
    ...
    ws.onclose = () => {
        setTimeout(connect, 1000);
    };
}

The Websocket protocol specifies a ping/pong heartbeat (https://tools.ietf.org/html/rfc6455#section-5.5.2) that is used to determine disconnection on both ends. This should already be fully implemented in any client or server library invisible to the end-developer.

The most difficult implementation detail for this type of reconnect is that all of the state will be maintained by the client and reasserted on reconnect (i.e. the subscriptions have to be reregistered), but this is not as bad as I'm making it sound.