OrangeTux / rauts

A Rust project exploring a routing mechanis for OCPP (Open Charge Point Protocol).
4 stars 0 forks source link

Change `Call` and `CallResult` to allow for cusomization #2

Open OrangeTux opened 1 year ago

OrangeTux commented 1 year ago

I've worked with over a dozen of different charge points vendors. And I've learned that an OCPP framework must allow for customization as not all chargers follow the the OCPP specification exactly.

For example, chargers might add extra fields to a message. Or chargers add extra messages. Or chargers might support a mix of OCPP 1.6 and OCPP 2.0.1 messages.

The current implementation of rauts doesn't allow for customization. The problems are:

  1. Call.action is of type Action. Action is an enum like:
pub enum Action {
   Authorize,
   Heartbeat,
   ...
}

This choice makes it not possible to support a custom action.

  1. Call.payload is of type Payload. Again, Payload is an enum type. Adding a new payload or modify an existing payload requires modification to Payload.

CallResult suffers from the same problem.

The API of both types should be changed to allow users of rauts support for custom messages.

konrad-grochowski-codetain commented 1 year ago

For accepting extra fields in the message, serde's flatten can be used: https://github.com/serde-rs/serde/issues/941#issuecomment-375486935

As for extending the variants of Action, my opinion is this requires big architecture changes. enum is supposed to represent a closed set of variants, the requirements you are describing make it cross this bound.

One proposition to fulfill this is to move all of enum into trait. So instead of Payload enum it would now become Payload trait, with arbitrary number of implementations (by default it would be implemented for all payload types). Going this way requires dynamic dispatch (type of payload fields in ocpp::Message would then be Box<dyn Payload>). Using this effectively in Router would probably mean going back to using TypeId as HashMap key, and then downcasting the message payload from Box<dyn ...> to concrete payload type in handler.

If it is feasible to know the set of messages beforehand for a type of charger, it is also possible to just make whole system generic across Payload type and create different Payload enums for different chargers. Sounds like not an easy solution to maintain though.

The last point, mix of OCPP 1.6 and OCPP 2.0.1 messages, can also be somehow solved like the previous problem, but it sounds like a nighmare to accept random messages one after another in different protocols. I could see this somehow working if we had two Payload traits, say PayloadOcpp16 and PayloadOcpp201, and additional Payload trait on top of them, which would combine them with impl Payload for T: PayloadOcpp16 {..} and impl Payload for T: PayloadOcpp201 {..}. Again, doesn't feel too clean to me.