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

Add dataclasses for ChargingProfile and its constituents #172

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

grutts
Copy link

@grutts grutts commented Jan 26, 2021

Some questions:

  • These objects don't quite fit into calls.py, because they're not call payloads. On the other hand, they don't really fit in enums.py either. I considered adding a new file like types.py, but figured it best to see what your preference is?

  • I also wanted to add a test to ensure a SetChargingProfilePayload is serialised to the correct representation when using these objects. I noticed the codebase doesn't have such tests for other payloads, so I left it. Would you like to keep it this way?

Thanks!

@grutts grutts changed the title Add dataclasses for ChargingProfile and it's constituents Add dataclasses for ChargingProfile and its constituents Jan 26, 2021
Copy link
Collaborator

@OrangeTux OrangeTux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your PR!

I'm unsure what to do this PR. I think your code is an improvement and should be included at some point.. However, the changes are backwards incompatible with the current code.

There are more backwards incompatible changes that we want to integrate on our way to a v1.0.0 release. I'll discuss with others how and what to do.

In the mean time there are some things that you can improve to your code.

Nested dataclasses can't be deserialized. Consider the following example:

from dataclasses import asdict
from ocpp.v16.call import ChargingSchedule, ChargingProfile, ChargingSchedulePeriod
from ocpp.v16.enums import ChargingProfilePurposeType, ChargingProfileKindType, ChargingRateUnitType

charging_schedule_period = { 'start_period': 0, 'limit': 10 }

charging_schedule = {
        'charging_rate_unit': ChargingRateUnitType.amps,
        'charging_schedule_period': [charging_schedule_period],
    }

charging_profile = {
    'charging_profile_id': 1,
    'stack_level': 0,
    'charging_profile_purpose': ChargingProfilePurposeType.tx_profile,
    'charging_profile_kind': ChargingProfileKindType.absolute,
    'charging_schedule': charging_schedule,
}


profile = ChargingProfile(**charging_profile)

# This fails as `profile.charging_schedule` is of type `dict`.
assert type(profile.charging_schedule) == ChargingSchedule

Can you you modify the code and make sure that nested dataclasses are deserialized correctly? I think you can use __post_init__() for that.

Regarding the location, I suggest to put the code in it's own module ocpp.v16.charging_profile. It doesn't really fit in ocpp.v16.call as that module contains only payloads to construct OCPP Calls. It also doesn't fit in ocpp.v16.enums because it isn't an enum.

ocpp/v16/call.py Outdated Show resolved Hide resolved
@OrangeTux OrangeTux mentioned this pull request Jan 26, 2021
26 tasks
@grutts
Copy link
Author

grutts commented Jan 27, 2021

No problem!

Good point regarding the de-serialisation, I had missed that. I'll look to make changes to support this.

Apologies, I don't quite understand the backwards incompatibility. My understanding when the payload is passed to Call all dataclasses are converted to dictionaries (similarly before any validation), and there is no enforcing of the types on the dataclass, and thus any code which is passing dictionaries to cs_charging_profiles would continue to work. Similarly, my understanding is we don't de-serialise back to the payload class automatically anywhere (nor validate against the dataclass). I do see a backwards incompatibility in static type checking (i.e. mypy), and if this is your concern I would propose we modify the type to still accept dicts (cs_charging_profiles: Untion[ChargingProfile, Dict]) ? Or am I missing something?

@OrangeTux
Copy link
Collaborator

Changing type of SetChargingProfilePayload.cs_charging_profiles from Dict to ChargingProfile is a backwards incompatible change.

However, you're right when you write "my understanding is we don't de-serialise back to the payload class automatically anywhere ". In practice it means users implementing a @on() handler for SetChargingProfile still get cs_charging_profiles as a Dict.

Changing the type hints, as you suggested, is required to express that cs_charging_profiles can be both Dict or ChargingProfile. And with this modification this PR is backwards compatible again.

I think for v1.0.0 we should drop Dict as possible type for cs_charging_profiles and only allow ChargingProfile. That means this library should automatically deserialize incoming SetChargingProfile requests into ChargingProfile and the other nested dataclasses. But these change are in the scope of a different issue.

@grutts
Copy link
Author

grutts commented Jan 28, 2021

Ok, I follow you now. I'll go with Union for now then to avoid making a backwards incompatible change.

I agree with the changes on (de)serialisation for v1.0.0 - we're doing this internally on top of the library at the minute at it would be handy to have it upstream!

grutts and others added 4 commits February 28, 2021 20:35
In order for remove_nones to work with the charging profile
data structures it needs to be able to remove nones of nested
dictionaries, as well as dictionaries made of lists of dictionaries
(for example, a list of charging schedules represented as dicts).

This commit modifies the logic, and adds tests
For now, dicts are acceptable to maintain backwards compatibility.
Perhaps in v1.0 only the dataclass will be accepted.
@grutts
Copy link
Author

grutts commented Feb 28, 2021

remove_nones needed to change to remove keys where the value is None for nested dicts, and dicts which contains lists of dicts. These structures appear in the charging profile code. I've added that in this commit 19bfe11

If we agree that deserialisation of dicts to dataclasses is out of scope and something for v1.0 then I think this PR is ready to go.

@proelke
Copy link
Collaborator

proelke commented Feb 28, 2021

There is a PR I'd like to submit and was working on a variation of your change to remove_nones. +1 on that change from me.

* Change Decimal to float
* Move dataclasses to separate charging_profile module
* Add ChargingProfile type to RemoteStartTransaction payload
@grutts grutts requested review from OrangeTux and removed request for tropxy March 24, 2021 09:28
@grutts
Copy link
Author

grutts commented May 3, 2021

@OrangeTux @tropxy I think this is ready to go. Are there any other changes you need?

@jerome-benoit
Copy link

jerome-benoit commented Jun 12, 2024

The PR is quite interesting in the context of smart charging algos conception and validation, which has become a requirement for charging infrastructure.

What is the rationale for not next merging it since 3 years?

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

Successfully merging this pull request may close these issues.

6 participants