2
2
also contain some helper functions for packing and unpacking messages. """
3
3
import os
4
4
import json
5
+ import decimal
5
6
from dataclasses import asdict , is_dataclass
6
7
7
8
from jsonschema import validate
@@ -61,11 +62,14 @@ def pack(msg):
61
62
return msg .to_json ()
62
63
63
64
64
- def get_schema (message_type_id , action , ocpp_version ):
65
+ def get_schema (message_type_id , action , ocpp_version , parse_float = float ):
65
66
"""
66
67
Read schema from disk and return in. Reads will be cached for performance
67
68
reasons.
68
69
70
+ The `parse_float` argument can be used to set the conversion method that
71
+ is used to parse floats. It must be a callable taking 1 argument. By
72
+ default it is `float()`, but certain schema's require `decimal.Decimal()`.
69
73
"""
70
74
if ocpp_version not in ["1.6" , "2.0" ]:
71
75
raise ValueError
@@ -95,7 +99,7 @@ def get_schema(message_type_id, action, ocpp_version):
95
99
# Unexpected UTF-8 BOM (decode using utf-8-sig):
96
100
with open (path , 'r' , encoding = 'utf-8-sig' ) as f :
97
101
data = f .read ()
98
- _schemas [relative_path ] = json .loads (data )
102
+ _schemas [relative_path ] = json .loads (data , parse_float = parse_float )
99
103
100
104
return _schemas [relative_path ]
101
105
@@ -108,21 +112,44 @@ def validate_payload(message, ocpp_version):
108
112
"be either 'Call' or 'CallResult'." )
109
113
110
114
try :
111
- schema = get_schema (
112
- message .message_type_id , message .action , ocpp_version
113
- )
115
+ # 3 OCPP 1.6 schedules have fields of type floats. The JSON schema
116
+ # defines a certain precision for these fields of 1 decimal. A value of
117
+ # 21.4 is valid, whereas a value if 4.11 is not.
118
+ #
119
+ # The problem is that Python's internal representation of 21.4 might
120
+ # have more than 1 decimal. It might be 21.399999999999995. This would
121
+ # make the validation fail, although the payload is correct. This is a
122
+ # known issue with jsonschemas, see:
123
+ # https://github.com/Julian/jsonschema/issues/247
124
+ #
125
+ # This issue can be fixed by using a different parser for floats than
126
+ # the default one that is used.
127
+ #
128
+ # Both the schema and the payload must be parsed using the different
129
+ # parser for floats.
130
+ if ocpp_version == '1.6' and (
131
+ (type (message ) == Call and
132
+ message .action in ['SetChargingProfile' , 'RemoteStartTransaction' ]) # noqa
133
+ or
134
+ (type (message ) == CallResult and
135
+ message .action == ['GetCompositeSchedule' ])
136
+ ):
137
+ schema = get_schema (
138
+ message .message_type_id , message .action ,
139
+ ocpp_version , parse_float = decimal .Decimal
140
+ )
141
+
142
+ message .payload = json .loads (
143
+ json .dumps (message .payload ), parse_float = decimal .Decimal
144
+ )
145
+ else :
146
+ schema = get_schema (
147
+ message .message_type_id , message .action , ocpp_version
148
+ )
114
149
except (OSError , json .JSONDecodeError ) as e :
115
150
raise ValidationError ("Failed to load validation schema for action "
116
151
f"'{ message .action } ': { e } " )
117
152
118
- if message .action in [
119
- 'RemoteStartTransaction' ,
120
- 'SetChargingProfile' ,
121
- 'RequestStartTransaction' ,
122
- ]:
123
- # todo: special actions
124
- pass
125
-
126
153
try :
127
154
validate (message .payload , schema )
128
155
except SchemaValidationError as e :
0 commit comments