Skip to content

Commit

Permalink
Changes to the database and distinguish between external and ocpp val…
Browse files Browse the repository at this point in the history
…ues. Currently only the 'OCPP' source is accepted.

Signed-off-by: Maaike Zijderveld, iolar <[email protected]>
  • Loading branch information
maaikez committed Sep 4, 2024
1 parent d588926 commit f108bf3
Show file tree
Hide file tree
Showing 15 changed files with 132 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"properties": {
"AlignedDataCtrlrEnabled": {
"variable_name": "Enabled",
"source": "OCPP",
"characteristics": {
"supportsMonitoring": true,
"dataType": "boolean"
Expand All @@ -22,6 +23,7 @@
},
"AlignedDataCtrlrAvailable": {
"variable_name": "Available",
"source": "OCPP",
"characteristics": {
"supportsMonitoring": true,
"dataType": "boolean"
Expand All @@ -38,6 +40,7 @@
},
"AlignedDataInterval": {
"variable_name": "Interval",
"source": "OCPP",
"characteristics": {
"unit": "s",
"supportsMonitoring": true,
Expand Down
1 change: 1 addition & 0 deletions config/v201/component_config/standardized/ClockCtrlr.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"properties": {
"ClockCtrlrEnabled": {
"variable_name": "Enabled",
"source": "OCPP",
"characteristics": {
"supportsMonitoring": true,
"dataType": "boolean"
Expand Down
8 changes: 8 additions & 0 deletions config/v201/component_config/standardized/SecurityCtrlr.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
},
"AdditionalRootCertificateCheck": {
"variable_name": "AdditionalRootCertificateCheck",
"source": "OCPP",
"characteristics": {
"supportsMonitoring": true,
"dataType": "boolean"
Expand All @@ -38,6 +39,7 @@
},
"BasicAuthPassword": {
"variable_name": "BasicAuthPassword",
"source": "OCPP",
"characteristics": {
"minLimit": 16,
"maxLimit": 40,
Expand Down Expand Up @@ -75,6 +77,7 @@
},
"CertSigningRepeatTimes": {
"variable_name": "CertSigningRepeatTimes",
"source": "OCPP",
"characteristics": {
"supportsMonitoring": true,
"dataType": "integer"
Expand All @@ -92,6 +95,7 @@
},
"CertSigningWaitMinimum": {
"variable_name": "CertSigningWaitMinimum",
"source": "OCPP",
"characteristics": {
"unit": "s",
"supportsMonitoring": true,
Expand All @@ -110,6 +114,7 @@
},
"SecurityCtrlrIdentity": {
"variable_name": "Identity",
"source": "OCPP",
"characteristics": {
"supportsMonitoring": true,
"dataType": "string"
Expand All @@ -127,6 +132,7 @@
},
"MaxCertificateChainSize": {
"variable_name": "MaxCertificateChainSize",
"source": "OCPP",
"characteristics": {
"supportsMonitoring": true,
"dataType": "integer"
Expand All @@ -142,6 +148,7 @@
},
"OrganizationName": {
"variable_name": "OrganizationName",
"source": "OCPP",
"characteristics": {
"supportsMonitoring": true,
"dataType": "string"
Expand All @@ -159,6 +166,7 @@
},
"SecurityProfile": {
"variable_name": "SecurityProfile",
"source": "OCPP",
"characteristics": {
"minLimit": 1,
"maxLimit": 3,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE VARIABLE

Check failure on line 1 in config/v201/device_model_migrations/2_down-variable_source.sql

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/v201/device_model_migrations/2_down-variable_source.sql#L1

Expected SET ANSI_NULLS ON near top of file

Check failure on line 1 in config/v201/device_model_migrations/2_down-variable_source.sql

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/v201/device_model_migrations/2_down-variable_source.sql#L1

Expected SET QUOTED_IDENTIFIER ON near top of file

Check failure on line 1 in config/v201/device_model_migrations/2_down-variable_source.sql

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/v201/device_model_migrations/2_down-variable_source.sql#L1

Expected SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED near top of file
DROP COLUMN SOURCE;
2 changes: 2 additions & 0 deletions config/v201/device_model_migrations/2_up-variable_source.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE VARIABLE

Check failure on line 1 in config/v201/device_model_migrations/2_up-variable_source.sql

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/v201/device_model_migrations/2_up-variable_source.sql#L1

Expected SET ANSI_NULLS ON near top of file

Check failure on line 1 in config/v201/device_model_migrations/2_up-variable_source.sql

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/v201/device_model_migrations/2_up-variable_source.sql#L1

Expected SET NOCOUNT ON near top of file

Check failure on line 1 in config/v201/device_model_migrations/2_up-variable_source.sql

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/v201/device_model_migrations/2_up-variable_source.sql#L1

Expected SET QUOTED_IDENTIFIER ON near top of file

Check failure on line 1 in config/v201/device_model_migrations/2_up-variable_source.sql

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

config/v201/device_model_migrations/2_up-variable_source.sql#L1

Expected SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED near top of file
ADD COLUMN SOURCE TEXT;
1 change: 1 addition & 0 deletions include/ocpp/v201/device_model_interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ struct VariableMonitoringPeriodic {
struct VariableMetaData {
VariableCharacteristics characteristics;
std::unordered_map<int64_t, VariableMonitoringMeta> monitors;
std::optional<VariableSource> source;
};

using VariableMap = std::map<Variable, VariableMetaData>;
Expand Down
26 changes: 26 additions & 0 deletions include/ocpp/v201/enums.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#ifndef OCPP_V201_ENUMS_HPP
#define OCPP_V201_ENUMS_HPP

#include <string>

namespace ocpp {
namespace v201 {

Expand Down Expand Up @@ -36,6 +38,30 @@ constexpr int32_t MIN = Danger;
constexpr int32_t MAX = Debug;
} // namespace MonitoringLevelSeverity

///
/// \brief The VariableSource enum used for the device model
///
enum class VariableSource {
OCPP,
EVEREST_CORE
};

namespace conversions {
///
/// \brief Converts the given VariableSource to a string.
/// \param s The source to convert.
/// \return The string representation of VariableSource
///
std::string variable_source_enum_to_string(const VariableSource s);

///
/// \brief Converts the given string representation of VariableSource to VariableSource enum
/// \param s The string to convert
/// \return The VariableSource
///
VariableSource string_to_variable_source_enum(const std::string& s);
} // namespace conversions

} // namespace v201
} // namespace ocpp

Expand Down
2 changes: 2 additions & 0 deletions include/ocpp/v201/init_device_model_db.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ struct DeviceModelVariable {
std::optional<std::string> default_actual_value;
/// \brief Config monitors, if any
std::vector<VariableMonitoringMeta> monitors;
/// \brief Source of the variable.
std::optional<VariableSource> source;
};

/// \brief Convert from json to a ComponentKey struct.
Expand Down
11 changes: 7 additions & 4 deletions lib/ocpp/v201/device_model_storage_sqlite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@ DeviceModelStorageSqlite::DeviceModelStorageSqlite(const fs::path& db_path, cons
const fs::path& config_path, const bool init_db) {
if (init_db) {
if (db_path.empty() || migration_files_path.empty() || config_path.empty()) {
EVLOG_AND_THROW(
DeviceModelError("Can not initialize device model storage: one of the paths is empty."));
EVLOG_AND_THROW(DeviceModelError("Can not initialize device model storage: one of the paths is empty."));
}
InitDeviceModelDb init_device_model_db(db_path, migration_files_path);
init_device_model_db.initialize_database(config_path, false);
Expand Down Expand Up @@ -97,7 +96,7 @@ DeviceModelMap DeviceModelStorageSqlite::get_device_model() {

std::string select_query =
"SELECT c.NAME, c.EVSE_ID, c.CONNECTOR_ID, c.INSTANCE, v.NAME, v.INSTANCE, vc.DATATYPE_ID, "
"vc.SUPPORTS_MONITORING, vc.UNIT, vc.MIN_LIMIT, vc.MAX_LIMIT, vc.VALUES_LIST "
"vc.SUPPORTS_MONITORING, vc.UNIT, vc.MIN_LIMIT, vc.MAX_LIMIT, vc.VALUES_LIST, v.SOURCE "
"FROM COMPONENT c "
"JOIN VARIABLE v ON c.ID = v.COMPONENT_ID "
"JOIN VARIABLE_CHARACTERISTICS vc ON vc.VARIABLE_ID = v.ID";
Expand Down Expand Up @@ -130,6 +129,7 @@ DeviceModelMap DeviceModelStorageSqlite::get_device_model() {
}

VariableCharacteristics characteristics;
VariableMetaData meta_data;
characteristics.dataType = static_cast<DataEnum>(select_stmt->column_int(6));
characteristics.supportsMonitoring = select_stmt->column_int(7) != 0;

Expand All @@ -149,7 +149,10 @@ DeviceModelMap DeviceModelStorageSqlite::get_device_model() {
characteristics.valuesList = select_stmt->column_text(11);
}

VariableMetaData meta_data;
if (select_stmt->column_type(12) != SQLITE_NULL) {
meta_data.source = conversions::string_to_variable_source_enum(select_stmt->column_text(12));
}

meta_data.characteristics = characteristics;

// Query all monitors for this variable
Expand Down
26 changes: 25 additions & 1 deletion lib/ocpp/v201/enums.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,30 @@ VariableMonitorType string_to_variable_monitor_type(const std::string& s) {

throw std::out_of_range("Provided string " + s + " could not be converted to enum of type VariableMonitorType");
}

std::string variable_source_enum_to_string(const VariableSource s) {
switch (s) {
case VariableSource::OCPP:
return "OCPP";
case VariableSource::EVEREST_CORE:
return "EVEREST_CORE";
}

throw std::out_of_range("VariableSource enum is out of range.");
}

VariableSource string_to_variable_source_enum(const std::string& s) {
if (s == "OCPP") {
return VariableSource::OCPP;
}

if (s == "EVEREST_CORE") {
return VariableSource::EVEREST_CORE;
}

throw std::out_of_range("Provided string " + s + " could not be converted to enum of type VariableSource");
}

} // namespace conversions

} // namespace ocpp::v201
} // namespace ocpp::v201
46 changes: 39 additions & 7 deletions lib/ocpp/v201/init_device_model_db.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,14 +362,14 @@ void InitDeviceModelDb::update_variable_characteristics(const VariableCharacteri

void InitDeviceModelDb::insert_variable(const DeviceModelVariable& variable, const uint64_t& component_id) {
static const std::string statement =
"INSERT OR REPLACE INTO VARIABLE (NAME, INSTANCE, COMPONENT_ID, REQUIRED) VALUES "
"(@name, @instance, @component_id, @required)";
"INSERT OR REPLACE INTO VARIABLE (NAME, INSTANCE, COMPONENT_ID, REQUIRED, SOURCE) VALUES "
"(@name, @instance, @component_id, @required, @source)";

std::unique_ptr<common::SQLiteStatementInterface> insert_variable_statement;
try {
insert_variable_statement = this->database->new_statement(statement);
} catch (const common::QueryExecutionException&) {
throw InitDeviceModelDbError("Could not create statement " + statement);
} catch (const common::QueryExecutionException& e) {
throw InitDeviceModelDbError("Could not create statement " + statement + ": " + e.what());
}

insert_variable_statement->bind_text("@name", variable.name, ocpp::common::SQLiteString::Transient);
Expand All @@ -385,6 +385,13 @@ void InitDeviceModelDb::insert_variable(const DeviceModelVariable& variable, con
const uint8_t required_int = (variable.required ? 1 : 0);
insert_variable_statement->bind_int("@required", required_int);

if (variable.source.has_value()) {
insert_variable_statement->bind_text("@source",
conversions::variable_source_enum_to_string(variable.source.value()));
} else {
insert_variable_statement->bind_null("@source");
}

if (insert_variable_statement->step() != SQLITE_DONE) {
throw InitDeviceModelDbError("Variable " + variable.name +
" could not be inserted: " + std::string(this->database->get_error_message()));
Expand All @@ -405,8 +412,8 @@ void InitDeviceModelDb::update_variable(const DeviceModelVariable& variable, con
}

static const std::string update_variable_statement =
"UPDATE VARIABLE SET NAME=@name, INSTANCE=@instance, COMPONENT_ID=@component_id, REQUIRED=@required WHERE "
"ID=@variable_id";
"UPDATE VARIABLE SET NAME=@name, INSTANCE=@instance, COMPONENT_ID=@component_id, REQUIRED=@required, "
"SOURCE=@source WHERE ID=@variable_id";

std::unique_ptr<common::SQLiteStatementInterface> update_statement;
try {
Expand All @@ -428,6 +435,13 @@ void InitDeviceModelDb::update_variable(const DeviceModelVariable& variable, con
const uint8_t required_int = (variable.required ? 1 : 0);
update_statement->bind_int("@required", required_int);

if (variable.source.has_value()) {
update_statement->bind_text("@source", conversions::variable_source_enum_to_string(variable.source.value()),
ocpp::common::SQLiteString::Transient);
} else {
update_statement->bind_null("@source");
}

if (update_statement->step() != SQLITE_DONE) {
throw InitDeviceModelDbError("Could not update variable " + variable.name + ": " +
std::string(this->database->get_error_message()));
Expand Down Expand Up @@ -804,7 +818,8 @@ std::map<ComponentKey, std::vector<DeviceModelVariable>> InitDeviceModelDb::get_
"c.ID, c.NAME, c.INSTANCE, c.EVSE_ID, c.CONNECTOR_ID, "
"v.ID, v.NAME, v.INSTANCE, v.REQUIRED, "
"vc.ID, vc.DATATYPE_ID, vc.MAX_LIMIT, vc.MIN_LIMIT, vc.SUPPORTS_MONITORING, vc.UNIT, vc.VALUES_LIST, "
"va.ID, va.MUTABILITY_ID, va.PERSISTENT, va.CONSTANT, va.TYPE_ID, va.VALUE, va.VALUE_SOURCE "
"va.ID, va.MUTABILITY_ID, va.PERSISTENT, va.CONSTANT, va.TYPE_ID, va.VALUE, va.VALUE_SOURCE,"
"v.SOURCE "
"FROM "
"COMPONENT c "
"JOIN VARIABLE v ON v.COMPONENT_ID = c.ID "
Expand Down Expand Up @@ -895,6 +910,14 @@ std::map<ComponentKey, std::vector<DeviceModelVariable>> InitDeviceModelDb::get_
attribute.variable_attribute.value = select_statement->column_text_nullable(21);
attribute.value_source = select_statement->column_text_nullable(22);

if (select_statement->column_type(23) != SQLITE_NULL) {
try {
variable->source = conversions::string_to_variable_source_enum(select_statement->column_text(23));
} catch (const std::out_of_range& e) {
EVLOG_error << e.what() << ": Variable Source will not be set (so default will be used)";
}
}

variable->attributes.push_back(attribute);

// Query all monitors
Expand Down Expand Up @@ -1209,6 +1232,10 @@ void from_json(const json& j, DeviceModelVariable& c) {
c.default_actual_value = get_string_value_from_json(default_value);
}

if (j.contains("source")) {
c.source = conversions::string_to_variable_source_enum(j.at("source"));
}

if (j.contains("monitors")) {
if (!c.characteristics.supportsMonitoring) {
const std::string error =
Expand Down Expand Up @@ -1284,6 +1311,11 @@ static void check_integrity(const std::map<ComponentKey, std::vector<DeviceModel
/// \return std::nullopt if the required variable has a value or default value. Error message if it is not.
///
static std::optional<std::string> check_integrity_required_value(const DeviceModelVariable& variable) {
// Required value has a different source so it is correct that the value is not set here.
if (variable.source.has_value() && variable.source != VariableSource::OCPP) {
return std::nullopt;
}

// For now, we assume that if a variable is required, it should have an 'Actual' value. But the spec is not clear
// about this. There are some implicit signs in favor of having always at least an 'Actual' value, but it is not
// explicitly stated. Robert asked OCA about this.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"properties": {
"UnitTestPropertyA": {
"variable_name": "UnitTestPropertyAName",
"source": "OCPP",
"characteristics": {
"supportsMonitoring": true,
"dataType": "boolean"
Expand Down
19 changes: 15 additions & 4 deletions tests/lib/ocpp/v201/test_init_device_model_db.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ class InitDeviceModelDbTest : public DatabaseTestingUtils {
///
bool variable_exists(const std::string& component_name, const std::optional<std::string>& component_instance,
const std::optional<int>& component_evse_id, const std::optional<int>& component_connector_id,
const std::string& variable_name, const std::optional<std::string>& variable_instance);
const std::string& variable_name, const std::optional<std::string>& variable_instance,
const std::optional<VariableSource> source = std::nullopt);

///
/// \brief Check if variable characteristics exists in the database.
Expand Down Expand Up @@ -243,7 +244,8 @@ TEST_F(InitDeviceModelDbTest, init_db) {
EXPECT_TRUE(characteristics_exists("UnitTestCtrlr", std::nullopt, 2, 3, "UnitTestPropertyAName", std::nullopt,
DataEnum::boolean, std::nullopt, std::nullopt, true, std::nullopt,
std::nullopt));
EXPECT_TRUE(variable_exists("UnitTestCtrlr", std::nullopt, 2, 3, "UnitTestPropertyAName", std::nullopt));
EXPECT_TRUE(variable_exists("UnitTestCtrlr", std::nullopt, 2, 3, "UnitTestPropertyAName", std::nullopt,
VariableSource::OCPP));
EXPECT_TRUE(variable_exists("UnitTestCtrlr", std::nullopt, 2, 3, "UnitTestPropertyBName", std::nullopt));
EXPECT_TRUE(variable_exists("UnitTestCtrlr", std::nullopt, 2, 3, "UnitTestPropertyCName", std::nullopt));

Expand Down Expand Up @@ -589,7 +591,8 @@ bool InitDeviceModelDbTest::variable_exists(const std::string& component_name,
const std::optional<int>& component_evse_id,
const std::optional<int>& component_connector_id,
const std::string& variable_name,
const std::optional<std::string>& variable_instance) {
const std::optional<std::string>& variable_instance,
const std::optional<VariableSource> source) {
static const std::string select_variable_statement = "SELECT ID "
"FROM VARIABLE v "
"WHERE v.COMPONENT_ID=("
Expand All @@ -600,7 +603,8 @@ bool InitDeviceModelDbTest::variable_exists(const std::string& component_name,
"AND c.EVSE_ID IS @evse_id "
"AND c.CONNECTOR_ID IS @connector_id) "
"AND v.NAME=@variable_name "
"AND v.INSTANCE IS @variable_instance";
"AND v.INSTANCE IS @variable_instance "
"AND v.source IS @variable_source";

std::unique_ptr<common::SQLiteStatementInterface> statement =
this->database->new_statement(select_variable_statement);
Expand Down Expand Up @@ -632,6 +636,13 @@ bool InitDeviceModelDbTest::variable_exists(const std::string& component_name,
statement->bind_null("@variable_instance");
}

if (source.has_value()) {
statement->bind_text("@variable_source", conversions::variable_source_enum_to_string(source.value()),
ocpp::common::SQLiteString::Transient);
} else {
statement->bind_null("@variable_source");
}

if (statement->step() == SQLITE_ERROR) {
return false;
}
Expand Down
Binary file modified tests/resources/unittest_device_model.db
Binary file not shown.
Binary file modified tests/resources/unittest_device_model_missing_required.db
Binary file not shown.

0 comments on commit f108bf3

Please sign in to comment.