ocpp-go: ocpp1.6 - parsing MeterValues from Alfen with invalid TransactionId (9223372036854775807)
an excerpt from the log of the Alfen Eve Double Station, connected via ocpp1.6j shows that the current TransactionId has been lost at the station for some reason. Instead of the valid Transaction Id, 9223372036854775807 is sent that is not according to the specification (Transaction Id can be omitted, if it is set, it must be valid).
2023-04-19T09:52:16.062Z:COM:ocpp_rpc.c:681:-> [2,“42179”,“MeterValues”,{“connectorId”:1,“transactionId”:9223372036854775807 2023-04-19T09:52:16.062Z:COM:ocpp_rpc.c:681:,“meterValue”:[{“timestamp”:“2023-04-15T02:25:11Z”,“sampledValue”:[{“value”:“120 2023-04-19T09:52:16.062Z:COM:ocpp_rpc.c:681:701.000”,“context”:“Sample.Periodic”,“measurand”:“Energy.Active.Import.Register” 2023-04-19T09:52:16.062Z:COM:ocpp_rpc.c:681:,“location”:“Outlet”,“unit”:“Wh”},{“value”:“0.000”,“context”:“Sample.Periodic”," 2023-04-19T09:52:16.062Z:COM:ocpp_rpc.c:681:measurand":“Current.Import”,“location”:“Outlet”,“unit”:“A”,“phase”:“L1”},{“value 2023-04-19T09:52:16.062Z:COM:ocpp_rpc.c:681:”:“0.000”,“context”:“Sample.Periodic”,“measurand”:“Current.Import”,“location”:“O 2023-04-19T09:52:16.062Z:COM:ocpp_rpc.c:681:utlet”,“unit”:“A”,“phase”:“L2”},{“value”:“5.561”,“context”:“Sample.Periodic”,“me 2023-04-19T09:52:16.062Z:COM:ocpp_rpc.c:681:asurand”:“Current.Import”,“location”:“Outlet”,“unit”:“A”,“phase”:“L3”}]}]}] 2023-04-19T09:52:16.167Z:COM:ocpp_rpc.c:1127:<- [4,“42179”,“FormationViolation”,“json: cannot unmarshal number 92233720368547 2023-04-19T09:52:16.167Z:COM:ocpp_rpc.c:1127:76000 into Go struct field MeterValuesRequest.transactionId of type int”,null]
As a consequence, a conversion of the large number (9223372036854775807 -> 9.223372036854776e+18 -> 9223372036854776000) leads to a rounding error and ultimately to a parsing error (json: cannot unmarshal number 9223372036854776000), since the value range for 64Bit Integer is exceeded.
This is done by the double unmarchalling
- in ocppj/server.go::ocppMessageHandler - ParseRawJsonMessage(data)) data ist correct, parsedJson contains transactionId:9.223372036854776e+18 (9223372036854776000)
- ParseMessage(parsedJson, pending) -> FormationViolation - json: cannot unmarshal number 9223372036854776000 into Go struct field MeterValuesRequest.transactionId of type int
Certainly, Alfen has to make sure in a newer firmware (currently 6.2.0) that a valid TransactionId is not lost and as a consequence a non-valid TransactionId is not sent as MaxInt 64Bit but omitted, but basically MaxInt 64Bit (9223372036854775807) would be a valid TransactionId according to ocpp1.6j.
The following code works (if type INT is known):
type MeterValuesRequest struct {
ConnectorId int `json:"connectorId" validate:"gte=0"`
TransactionId *int `json:"transactionId,omitempty"`
}
var m MeterValuesRequest = MeterValuesRequest{}
err = json.Unmarshal(jsonMsg, &m)
if err != nil {
log.Fatal(err)
}
logrus.Infof("msg TransactionId: %d", *m.TransactionId)
The following ultimatively leads to the above-mentioned ‘error’
var arr []interface{}
err := json.Unmarshal(dataJson, &arr)
if err != nil {
return nil, err
}
In principle, it would be desirable that valid values according to the spec do not lead to a parsing error. Furthermore, it is somehow unsightly that the error message with implementation details is ultimately sent back to the client (and ends up in the Alfen log).
Due to the error when sending the MeterValues, the StopTransaction is not sent in the Alfen firmware in the further course, so the station is basically unusable. This transaction with an invalid Id can no longer be cancelled from the outside (via OCPP).
regards
About this issue
- Original URL
- State: open
- Created a year ago
- Comments: 18 (3 by maintainers)
Sorry to hear. Such specific cases are best handled by the service portal, because a bricked bootloader will probably require a controller board replacement.
The ocpp 1.6 spec is actually really unclear about that, since the primitive type is simply defined as
integer. Most languages would assume an integer type to be a 32-bit value and not 64-bit, unless explicitly specified. This is ultimately cleared up in the v2 spec (-> int32, signed), but I’m pretty sure it has caused issues for other implementations before already (see https://github.com/steve-community/steve/issues/280).The library was actually designed with a 32-bit value in mind. The fact that
intin Go has a variable size depending on the underlying architecture is just a “lucky coincidence”. Such a transaction ID wouldn’t work at all if you deployed this on an ARM32 charge point.Regardless, I opened a PR with a potential fix for your immediate issue. I also added the max int case within a test to verify this, but feel free to have a look.