OCPP-J Codec

Wire Format

The OCPP-J protocol (JSON over WebSocket) uses JSON arrays with a message type ID as the first element:

TypeIDFormatPurpose
Call2[2, unique_id, action, payload]Request from client or server
CallResult3[3, unique_id, payload]Successful response
CallError4[4, unique_id, error_code, description, details]Error response

The unique_id correlates a CallResult or CallError back to its originating Call.

Message Types

using OCPPData

The three concrete subtypes of OCPPMessage:

using InteractiveUtils: subtypes
subtypes(OCPPMessage)
3-element Vector{Any}:
 Call
 CallError
 CallResult

Constructing Messages

Convenience constructors auto-fill message_type_id:

call = Call("id-1", "Heartbeat", Dict{String,Any}())
Call(2, "id-1", "Heartbeat", Dict{String, Any}())
result = CallResult("id-1", Dict{String,Any}("status" => "Accepted"))
CallResult(3, "id-1", Dict{String, Any}("status" => "Accepted"))
error = CallError("id-1", "NotImplemented", "Not supported", Dict{String,Any}())
CallError(4, "id-1", "NotImplemented", "Not supported", Dict{String, Any}())

Encoding

encode converts an OCPPMessage to a JSON string ready for WebSocket transmission:

encode(call)
"[2,\"id-1\",\"Heartbeat\",{}]"
encode(result)
"[3,\"id-1\",{\"status\":\"Accepted\"}]"
encode(error)
"[4,\"id-1\",\"NotImplemented\",\"Not supported\",{}]"

Decoding

decode parses a raw JSON string back into the appropriate OCPPMessage subtype:

decoded = decode("[2,\"id-1\",\"Heartbeat\",{}]")
Call(2, "id-1", "Heartbeat", Dict{String, Any}())
decoded isa Call
true
decoded.action
"Heartbeat"
decoded.payload
Dict{String, Any}()

Decoding a CallResult:

decode("[3,\"id-1\",{\"status\":\"Accepted\"}]")
CallResult(3, "id-1", Dict{String, Any}("status" => "Accepted"))

Decoding a CallError:

decode("[4,\"id-1\",\"NotImplemented\",\"Not supported\",{}]")
CallError(4, "id-1", "NotImplemented", "Not supported", Dict{String, Any}())

Round-Trip

msg = Call("abc-123", "BootNotification", Dict{String,Any}(
    "chargePointVendor" => "V",
    "chargePointModel" => "M",
))
wire = encode(msg)
"[2,\"abc-123\",\"BootNotification\",{\"chargePointVendor\":\"V\",\"chargePointModel\":\"M\"}]"
decoded = decode(wire)
decoded.action, decoded.payload
("BootNotification", Dict{String, Any}("chargePointVendor" => "V", "chargePointModel" => "M"))

Generating Unique IDs

id = generate_unique_id()
println(id)  # UUID v4 string
length(id)   # 36 characters (8-4-4-4-12 format)
36