Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OCPP 1.6 charging profiles remain in the database #978

Open
james-ctc opened this issue Feb 4, 2025 · 0 comments
Open

OCPP 1.6 charging profiles remain in the database #978

james-ctc opened this issue Feb 4, 2025 · 0 comments
Labels

Comments

@james-ctc
Copy link
Contributor

OCPP Version

OCPP1.6

Describe the bug

TxDefault profiles can be applied to a single connector or all connectors.
They are added in smart_charging.cpp in function add_tx_default_profile() which adds the profile to in-memory data structures and also to the database (for long term persistence).

Where connector 0 is specified then it is applied separately to the memory structure for each connector, however it is only placed in the database once (which could be a typo).

In detail:

void SmartChargingHandler::add_tx_default_profile(const ChargingProfile& profile, const int connector_id) {
    std::lock_guard<std::mutex> lk(this->tx_default_profiles_map_mutex);
    if (connector_id == 0) {
        for (size_t id = 1; id <= this->connectors.size() - 1; id++) {
            this->connectors.at(id)->stack_level_tx_default_profiles_map[profile.stackLevel] = profile;
            try {
                this->database_handler->insert_or_update_charging_profile(connector_id, profile);
            } catch (const QueryExecutionException& e) {
                EVLOG_warning << "Could not store TxDefaultProfile in the database: " << e.what();
            }
        }
    } else {
        this->connectors.at(connector_id)->stack_level_tx_default_profiles_map[profile.stackLevel] = profile;
        try {
            this->database_handler->insert_or_update_charging_profile(connector_id, profile);
        } catch (const QueryExecutionException& e) {
            EVLOG_warning << "Could not store TxDefaultProfile in the database: " << e.what();
        }
    }
}

If the intent is for the database to contain the profile with connector ID 0 then the code can be simplified:

void SmartChargingHandler::add_tx_default_profile(const ChargingProfile& profile, const int connector_id) {
    std::lock_guard<std::mutex> lk(this->tx_default_profiles_map_mutex);
    if (connector_id == 0) {
        for (size_t id = 1; id <= this->connectors.size() - 1; id++) {
            this->connectors.at(id)->stack_level_tx_default_profiles_map[profile.stackLevel] = profile;
        }
    } else {
        this->connectors.at(connector_id)->stack_level_tx_default_profiles_map[profile.stackLevel] = profile;
    }
    try {
        this->database_handler->insert_or_update_charging_profile(connector_id, profile);
    } catch (const QueryExecutionException& e) {
        EVLOG_warning << "Could not store TxDefaultProfile in the database: " << e.what();
    }
}

However the code will need updating to ensure that profile updates are correctly removing overridden profiles (see below)
Alternatively the database should contain the same information as the memory-structure and
this->database_handler->insert_or_update_charging_profile(connector_id, profile);
should become
this->database_handler->insert_or_update_charging_profile(id, profile);
so that it uses the loop counter variable.

A new TxDefault profile should replace an existing profile where the purpose and stack level match.
e.g.
Profile A id=123, level=30, purpose=TxDefault, periods ...
Profile B id=124, level=30, purpose=TxDefault, periods ...

If both profiles are added to connector ID 0 then profile B replaces profile A.

In memory the profile is stored connector[n]->stack_level[l]
which ensures that a new profile with the same level overrides the previous one. i.e. profile A is removed.

The database stores profiles in columns: id, connector, JSON profile
which means that the older profile (A) remains in the database. e.g.

id connector JSON
123 0 JSON profile A
124 0 JSON profile B

The CSMS can request that profile B is removed via ID, level, connector ID ...
The in-memory structure is used to find the profiles to remove.
Profile A is not removed because it is no longer in the in-memory structure.

One option would be to redefine the database columns so they match the in-memory representation: level, connector, JSON
and add profiles to the database so that the connector column will never contain 0.
When a new profile is added then it correctly would replace an existing one with the same level

However there is no obvious check that a profile ID is unique. Hence the id column should remain.
id, connector, level, JSON
with the following constraints:

  • (id, connector) pair is unique
  • (connector, level) pair is unique

The database handler would need to ensure that conflicting profiles (the older one) is replaced with the newer one.
One option would be to run two delete SQL commands before the insert:

delete from CHARGING_PROFILES where id = <profile id> and connector_id = <connector id>;
delete from CHARGING_PROFILES where connector_id = <connector id> and level = <profile level>;
insert into CHARGING_PROFILES (id, connector_id, level, profile) values (...); 

To Reproduce

steps:

  1. get composite schedule
  2. clear all profiles
  3. get composite schedule - check clear worked
  4. set profile A (stack level 30, limit 20A)
  5. get composite schedule - check set profile A active
  6. set profile B (stack level 30, limit 10A)
  7. get composite schedule - check set profile B active
  8. clear profile B (by ID, or by level)
  9. get composite schedule - check no profile set
  10. restart
  11. get composite schedule - check no profile set

The bug is at step 12 profile A is active (composite schedule has a 20A limit).

Anything else?

No response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant