-
Notifications
You must be signed in to change notification settings - Fork 57
/
Copy pathdevice_model.hpp
316 lines (275 loc) · 16.1 KB
/
device_model.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest
#ifndef DEVICE_MODEL_HPP
#define DEVICE_MODEL_HPP
#include <type_traits>
#include <everest/logging.hpp>
#include <ocpp/v2/device_model_storage_interface.hpp>
namespace ocpp {
namespace v2 {
/// \brief Response to requesting a value from the device model
/// \tparam T
template <typename T> struct RequestDeviceModelResponse {
GetVariableStatusEnum status;
std::optional<T> value;
};
/// \brief Converts the given \p value to the specific type based on the template parameter
/// \tparam T
/// \param value
/// \return
template <typename T> T to_specific_type(const std::string& value) {
static_assert(std::is_same<T, std::string>::value || std::is_same<T, int>::value ||
std::is_same<T, double>::value || std::is_same<T, size_t>::value ||
std::is_same<T, DateTime>::value || std::is_same<T, bool>::value ||
std::is_same<T, uint64_t>::value,
"Requested unknown datatype");
if constexpr (std::is_same<T, std::string>::value) {
return value;
} else if constexpr (std::is_same<T, int>::value) {
return std::stoi(value);
} else if constexpr (std::is_same<T, double>::value) {
return std::stod(value);
} else if constexpr (std::is_same<T, size_t>::value) {
size_t res = std::stoul(value);
return res;
} else if constexpr (std::is_same<T, DateTime>::value) {
return DateTime(value);
} else if constexpr (std::is_same<T, bool>::value) {
return ocpp::conversions::string_to_bool(value);
} else if constexpr (std::is_same<T, uint64_t>::value) {
return std::stoull(value);
}
}
template <DataEnum T> auto to_specific_type_auto(const std::string& value) {
static_assert(T == DataEnum::string || T == DataEnum::integer || T == DataEnum::decimal ||
T == DataEnum::dateTime || T == DataEnum::boolean,
"Requested unknown datatype");
if constexpr (T == DataEnum::string) {
return to_specific_type<std::string>(value);
} else if constexpr (T == DataEnum::integer) {
return to_specific_type<int>(value);
} else if constexpr (T == DataEnum::decimal) {
return to_specific_type<double>(value);
} else if constexpr (T == DataEnum::dateTime) {
return to_specific_type<DateTime>(value);
} else if constexpr (T == DataEnum::boolean) {
return to_specific_type<bool>(value);
}
}
template <DataEnum T> bool is_type_numeric() {
static_assert(T == DataEnum::string || T == DataEnum::integer || T == DataEnum::decimal ||
T == DataEnum::dateTime || T == DataEnum::boolean || T == DataEnum::OptionList ||
T == DataEnum::SequenceList || T == DataEnum::MemberList,
"Requested unknown datatype");
if constexpr (T == DataEnum::integer || T == DataEnum::decimal) {
return true;
} else {
return false;
}
}
typedef std::function<void(const std::unordered_map<int64_t, VariableMonitoringMeta>& monitors,
const Component& component, const Variable& variable,
const VariableCharacteristics& characteristics, const VariableAttribute& attribute,
const std::string& value_previous, const std::string& value_current)>
on_variable_changed;
typedef std::function<void(const VariableMonitoringMeta& updated_monitor, const Component& component,
const Variable& variable, const VariableCharacteristics& characteristics,
const VariableAttribute& attribute, const std::string& current_value)>
on_monitor_updated;
/// \brief This class manages access to the device model representation and to the device model interface and provides
/// functionality to support the use cases defined in the functional block Provisioning
class DeviceModel {
private:
DeviceModelMap device_model_map;
std::unique_ptr<DeviceModelStorageInterface> device_model;
/// \brief Listener for the internal change of a variable
on_variable_changed variable_listener;
/// \brief Listener for the internal update of a monitor
on_monitor_updated monitor_update_listener;
/// \brief Private helper method that does some checks with the device model representation in memory to evaluate if
/// a value for the given parameters can be requested. If it can be requested it will be retrieved from the device
/// model interface and the given \p value will be set to the value that was retrieved
/// \param component_id
/// \param variable_id
/// \param attribute_enum
/// \param value string reference to value: will be set to requested value if value is present
/// \param allow_write_only true to allow a writeOnly value to be read.
/// \return GetVariableStatusEnum that indicates the result of the request
GetVariableStatusEnum request_value_internal(const Component& component_id, const Variable& variable_id,
const AttributeEnum& attribute_enum, std::string& value,
bool allow_write_only) const;
/// \brief Iterates over the given \p component_criteria and converts this to the variable names
/// (Active,Available,Enabled,Problem). If any of the variables can not be found as part of a component this
/// function returns false. If any of those variable's value is true, this function returns true (except for
/// criteria problem). If all variable's value are false, this function returns false
/// \param component_id
/// \param /// component_criteria
/// \return
bool component_criteria_match(const Component& component_id,
const std::vector<ComponentCriterionEnum>& component_criteria);
/// @brief Iterates over the given \p component_variables and filters them according to the requirement conditions.
/// @param component_variables
/// @param component_ current component
/// @param variable_ current variable
/// @return true if the component is found according to any of the requirement conditions.
bool component_variables_match(const std::vector<ComponentVariable>& component_variables,
const ocpp::v2::Component& component_, const struct ocpp::v2::Variable& variable_);
public:
/// \brief Constructor for the device model
/// \param device_model_storage_interface pointer to a device model interface class
explicit DeviceModel(std::unique_ptr<DeviceModelStorageInterface> device_model_storage_interface);
/// \brief Direct access to value of a VariableAttribute for the given component, variable and attribute_enum. This
/// should only be called for variables that have a role standardized in the OCPP2.0.1 specification.
/// \tparam T datatype of the value that is requested
/// \param component_variable Combination of Component and Variable that identifies the Variable
/// \param attribute_enum defaults to AttributeEnum::Actual
/// \return the requested value from the device model interface
template <typename T>
T get_value(const RequiredComponentVariable& component_variable,
const AttributeEnum& attribute_enum = AttributeEnum::Actual) const {
std::string value;
auto response = GetVariableStatusEnum::UnknownVariable;
if (component_variable.variable.has_value()) {
response = this->request_value_internal(component_variable.component, component_variable.variable.value(),
attribute_enum, value, true);
}
if (response == GetVariableStatusEnum::Accepted) {
return to_specific_type<T>(value);
} else {
EVLOG_critical << "Directly requested value for ComponentVariable that doesn't exist in the device model: "
<< component_variable;
EVLOG_AND_THROW(std::runtime_error(
"Directly requested value for ComponentVariable that doesn't exist in the device model."));
}
}
/// \brief Access to std::optional of a VariableAttribute for the given component, variable and attribute_enum.
/// \tparam T Type of the value that is requested
/// \param component_variable Combination of Component and Variable that identifies the Variable
/// \param attribute_enum
/// \return std::optional<T> if the combination of \p component_variable and \p attribute_enum could successfully
/// requested from the storage and a value is present for this combination, else std::nullopt .
template <typename T>
std::optional<T> get_optional_value(const ComponentVariable& component_variable,
const AttributeEnum& attribute_enum = AttributeEnum::Actual) const {
std::string value;
auto response = GetVariableStatusEnum::UnknownVariable;
if (component_variable.variable.has_value()) {
response = this->request_value_internal(component_variable.component, component_variable.variable.value(),
attribute_enum, value, true);
}
if (response == GetVariableStatusEnum::Accepted) {
return to_specific_type<T>(value);
} else {
return std::nullopt;
}
}
/// \brief Requests a value of a VariableAttribute specified by combination of \p component_id and \p variable_id
/// from the device model
/// \tparam T datatype of the value that is requested
/// \param component_id
/// \param variable_id
/// \param attribute_enum
/// \return Response to request that contains status of the request and the requested value as std::optional<T> .
/// The value is present if the status is GetVariableStatusEnum::Accepted
template <typename T>
RequestDeviceModelResponse<T> request_value(const Component& component_id, const Variable& variable_id,
const AttributeEnum& attribute_enum) {
std::string value;
const auto req_status = this->request_value_internal(component_id, variable_id, attribute_enum, value, false);
if (req_status == GetVariableStatusEnum::Accepted) {
return {GetVariableStatusEnum::Accepted, to_specific_type<T>(value)};
} else {
return {req_status};
}
}
/// \brief Get the mutability for the given component, variable and attribute_enum
/// \param component_id
/// \param variable_id
/// \param attribute_enum
/// \return The mutability of the given component variable, else std::nullopt
std::optional<MutabilityEnum> get_mutability(const Component& component_id, const Variable& variable,
const AttributeEnum& attribute_enum);
/// \brief Sets the variable_id attribute \p value specified by \p component_id , \p variable_id and \p
/// attribute_enum
/// \param component_id
/// \param variable_id
/// \param attribute_enum
/// \param value
/// \param source The source of the value (for example 'csms' or 'default').
/// \param allow_read_only If this is true, read-only variables can be changed,
/// otherwise only non read-only variables can be changed. Defaults to false
/// \return Result of the requested operation
SetVariableStatusEnum set_value(const Component& component_id, const Variable& variable_id,
const AttributeEnum& attribute_enum, const std::string& value,
const std::string& source, const bool allow_read_only = false);
/// \brief Sets the variable_id attribute \p value specified by \p component_id , \p variable_id and \p
/// attribute_enum for read only variables only. Only works on certain allowed components.
/// \param component_id
/// \param variable_id
/// \param attribute_enum
/// \param value
/// \param source The source of the value (for example 'csms' or 'default').
/// \return Result of the requested operation
SetVariableStatusEnum set_read_only_value(const Component& component_id, const Variable& variable_id,
const AttributeEnum& attribute_enum, const std::string& value,
const std::string& source);
/// \brief Gets the VariableMetaData for the given \p component_id and \p variable_id
/// \param component_id
/// \param variable_id
/// \return VariableMetaData or std::nullopt if \p component_id or \p variable_id not present
std::optional<VariableMetaData> get_variable_meta_data(const Component& component_id, const Variable& variable_id);
/// \brief Gets the ReportData for the specifed filter \p report_base \p component_variables and \p
/// component_criteria
/// \param report_base
/// \param component_variables
/// \param component_criteria
/// \return
std::vector<ReportData> get_base_report_data(const ReportBaseEnum& report_base);
/// \brief Gets the ReportData for the specifed filter \p component_variables and \p
/// component_criteria
/// \param report_base
/// \param component_variables
/// \param component_criteria
/// \return
std::vector<ReportData>
get_custom_report_data(const std::optional<std::vector<ComponentVariable>>& component_variables = std::nullopt,
const std::optional<std::vector<ComponentCriterionEnum>>& component_criteria = std::nullopt);
void register_variable_listener(on_variable_changed&& listener) {
variable_listener = std::move(listener);
}
void register_monitor_listener(on_monitor_updated&& listener) {
monitor_update_listener = std::move(listener);
}
/// \brief Sets the given monitor \p requests in the device model
/// \param request
/// \param type The type of the set monitors. HardWiredMonitor - used for OEM specific monitors,
/// PreconfiguredMonitor - monitors that were manually defined in the component config,
/// CustomMonitor - used for monitors that are set by the CSMS,
/// \return List of results of the requested operation
std::vector<SetMonitoringResult> set_monitors(const std::vector<SetMonitoringData>& requests,
const VariableMonitorType type = VariableMonitorType::CustomMonitor);
bool update_monitor_reference(int32_t monitor_id, const std::string& reference_value);
std::vector<VariableMonitoringPeriodic> get_periodic_monitors();
/// \brief Gets the Monitoring data for the request \p criteria and \p component_variables
/// \param criteria
/// \param component_variables
/// \return List of results of the requested monitors
std::vector<MonitoringData> get_monitors(const std::vector<MonitoringCriterionEnum>& criteria,
const std::vector<ComponentVariable>& component_variables);
/// \brief Clears the given \p request_ids from the registered monitors if request_id is present
/// \param request_ids
/// \param allow_protected if we should delete the non-custom monitors, defaults to false when
/// this operation is requested by the CSMS
/// \return List of results of the requested operation
std::vector<ClearMonitoringResult> clear_monitors(const std::vector<int>& request_ids,
bool allow_protected = false);
/// \brief Clears all the custom monitors (set by the CSMS) present in the database
/// \return count of monitors deleted, can be 0 if no custom monitors were present in the db
int32_t clear_custom_monitors();
/// \brief Check data integrity of the device model provided by the device model data storage:
/// For "required" variables, assert values exist. Checks might be extended in the future.
void check_integrity(const std::map<int32_t, int32_t>& evse_connector_structure);
};
} // namespace v2
} // namespace ocpp
#endif // DEVICE_MODEL_HPP