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

Refactored libocpp documentation #847

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ find_package(Doxygen)
if(DOXYGEN_FOUND)
set(DOXYGEN_OUTPUT_DIRECTORY dist/docs)
set(DOXYGEN_USE_MDFILE_AS_MAINPAGE README.md)
doxygen_add_docs(doxygen-${PROJECT_NAME} README.md include lib src)
doxygen_add_docs(doxygen-${PROJECT_NAME} README.md include lib src doc)
else()
message("Doxygen is needed to generate documentation")
endif()
614 changes: 88 additions & 526 deletions README.md

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions doc/common/build-with-fetchcontent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Building with FetchContent instead of EDM

In this directory you can find a [CMakeLists.txt](CMakeLists.txt) that serves as an example how to build libocpp with FetchContent instead of EDM.
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,31 @@

OCPP has several whitepapers, which can be found here: https://openchargealliance.org/whitepapers/

One of them is OCPP & California Pricing Requirements. This can be optionally enabled in libocpp, for OCPP 1.6 as well
as OCPP 2.0.1.
One of them is OCPP & California Pricing Requirements. This can be optionally enabled in libocpp, for OCPP 1.6 as well as OCPP 2.0.1.

Check notice on line 5 in doc/common/california_pricing_requirements.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/common/california_pricing_requirements.md#L5

Expected: 80; Actual: 133

## Callbacks in libocpp

To be kind of compatible with eachother, the callbacks for OCPP 1.6 and 2.0.1 use the same structs with the pricing
To be kind of compatible with eachother, the callbacks for OCPP 1.6 and 2.0.1 use the same structs with the pricing
information.

### User-specific price / SetUserPrice

The User-specific price is used for display purposes only and can be sent as soon as the user identifies itself with an
id token. It should not be used to calculate prices.
Internally, the messages in the DataTransfer json (for 1.6) is converted to a `DisplayMessage`, defined in
id token. It should not be used to calculate prices.
Internally, the messages in the DataTransfer json (for 1.6) is converted to a `DisplayMessage`, defined in
`common/types.hpp`. In case of multi language messages, they are all added to the DisplayMessage vector.
If the message is sent when a transaction has already started, the session id will be included in the display message
and the `IdentifierType` will be set to `SessionId`. If it has not started yet, the id token is sent with
If the message is sent when a transaction has already started, the session id will be included in the display message and the `IdentifierType` will be set to `SessionId`. If it has not started yet, the id token is sent with
`IdentifierType` set to `IdToken`.


### Running cost and Final / total cost

The running cost and final cost messages are converted to a `RunningCost` struct, also defined in `common/types.hpp`.
The triggers in the message (running cost) are handled in libocpp itself.
The running cost and final cost messages are converted to a `RunningCost` struct, also defined in `common/types.hpp`.
The triggers in the message (running cost) are handled in libocpp itself.
The prices are converted to integers, because floating point numbers are not precise enough for pricing calculations.
To set the number of decimals to calculate with, you should set NumberOfDecimalsForCostValues (1.6, in CostAndPrice /
To set the number of decimals to calculate with, you should set NumberOfDecimalsForCostValues (1.6, in CostAndPrice /
2.0.1, TariffCostCtrlr). Default is 3. There might be messages in multiple languages, they are all added to the messages
vector.


## OCPP 1.6

OCPP 1.6 mostly uses DataTransfer to send the pricing messages, and also has some extra configuration items. In libocpp,
Expand All @@ -52,19 +48,16 @@
| `NextTimeOffsetTransitionDateTime` | When to change to summer or winter time, to the offset `TimeOffsetNextTransition` |
| `TimeOffsetNextTransition` | What the new offset should be at the given `NextTimeOffsetTransationDateTime` (readwrite) |


### Callbacks

For California Pricing to work, the following callbacks must be enabled:

- `session_cost_callback`, used for running cost and final cost
- `set_display_message_callback`, used to show a user specific price


## OCPP 2.0.1

OCPP 2.0.1 uses different mechanisms to send pricing information. The messages are converted to internally used structs
as descripbed above. For California Pricing Requirements to work, DisplayMessage and TariffAndCost must be implemented
as well.
OCPP 2.0.1 uses different mechanisms to send pricing information. The messages are converted to internally used structs as descripbed above. For California Pricing Requirements to work, DisplayMessage and TariffAndCost must be implemented as well.

### Device Model Variables

Expand All @@ -87,31 +80,24 @@
| `TotalCostFallbackMessage` | `<language code>` | `TariffCostCtrlr` | Multi language TotalCostFallbackMessage. There must be a variable with the language as instance for every supported language. |
| `Language` | | `DisplayMessageCtrlr` | Default language code (RFC 5646). The `valuesList` holds the supported languages of the charging station. The value must be one of `valuesList`. |


> **_NOTE:_** Tariff and cost can be enabled separately. To be able to use all functionality, it is recommended to
enable both. If cost is enabled and tariff is not enabled, the total cost message will not contain the personal message
(`set_running_cost_callback`).
If tariff is enabled and cost is not enabled, the total cost message will only be a DisplayMessage
> **_NOTE:_** Tariff and cost can be enabled separately. To be able to use all functionality, it is recommended to
enable both. If cost is enabled and tariff is not enabled, the total cost message will not contain the personal message (`set_running_cost_callback`).
If tariff is enabled and cost is not enabled, the total cost message will only be a DisplayMessage
(`set_display_message_callback`) containing the personal message(s).


### Callbacks

For California Pricing to work, the following callbacks must be enabled:

- `set_running_cost_callback`
- `set_display_message_callback`

For the tariff information (the personal messages), the `set_display_message_callback` is used. The same callback is
also used for the SetDisplayMessageRequest in OCPP. The latter does require an id, the former will not have an id. So
when `GetDisplayMessageRequest` is called from the CSMS, the Tariff display messages (that do not have an id) should not
be returned. They should also be removed as soon as the transaction has ended.
For the tariff information (the personal messages), the `set_display_message_callback` is used. The same callback is also used for the SetDisplayMessageRequest in OCPP. The latter does require an id, the former will not have an id. So when `GetDisplayMessageRequest` is called from the CSMS, the Tariff display messages (that do not have an id) should not be returned. They should also be removed as soon as the transaction has ended.

Driver specific tariffs / pricing information can be returned by the CSMS in the `AuthorizeResponse` message. In
libocpp, the whole message is just forwared (pricing information is not extracted from it), because the pricing
information is coupled to the authorize response. So when Tariff and Cost are enabled, the `idTokenInfo` field must be
read for pricing information.
Driver specific tariffs / pricing information can be returned by the CSMS in the `AuthorizeResponse` message. In

Check notice on line 97 in doc/common/california_pricing_requirements.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/common/california_pricing_requirements.md#L97

Expected: 80; Actual: 112
libocpp, the whole message is just forwared (pricing information is not extracted from it), because the pricing

Check notice on line 98 in doc/common/california_pricing_requirements.md

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

doc/common/california_pricing_requirements.md#L98

Expected: 80; Actual: 111
information is coupled to the authorize response. So when Tariff and Cost are enabled, the `idTokenInfo` field must be read for pricing information.

Cost information is also sent by the CSMS in the TransactionEventResponse. In that case, the pricing / cost information
is extracted from the message and a RunningCost message is sent containing the current cost and extra messages
(optional). If only Tariff is enabled and there is a personal message in the TransationEventResponse, a DisplayMessage
is sent.
is extracted from the message and a RunningCost message is sent containing the current cost and extra messages
(optional). If only Tariff is enabled and there is a personal message in the TransationEventResponse, a DisplayMessage is sent.
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@ Using the database migrations is fairly straightforward. Most of the details are
- Old databases need to be removed so a new database can be created using the migrations. This is to make sure that there is exact control over the schema of the database and no remains are present.

**Requirements:**
- Minimal SQLite version 3.35.0 for `ALTER TABLE DROP COLUMN` support

- Minimal SQLite version 3.35.0 for `ALTER TABLE DROP COLUMN` support

## How to contribute changes to the database schema

If changes need to be made to the database schema this can be done by adding new migration files in the relevant `config/vxx/core_migrations` folder.

### File format

The filenames must have the following format:
`x_up[-description].sql` and `x_down[-description].sql`

Expand All @@ -32,17 +33,17 @@ The files always exist in pairs except for the initial "up" file used to start t
CMake will validate the completeness of the migration pairs and the filenames. If this is not correct CMake will fail to initialize.

### Schema changes

The schema changes should be done in such a way that the data present in the databases will persist unless it is really necessary to remove stuff.

### Unit testing

There are already unit tests present that will automatically perform a full up and down migration with all the intermediate steps in the `core_migrations` folder. The files will be auto detected for this.

Additionally it is recommended to write specific unit tests for your migration that validate that the changes you have made have the expected outcome.


## Design consideration


- We start out with an SQL init file that is used to start the database with. This file should not be changed anymore once this functionality is implemented.
- Every change we want to make to the database is done through migration files. These files are simply more SQL files that can be applied to the database in the right order.
- Every change should consist of an up and a down file.
Expand Down
63 changes: 63 additions & 0 deletions doc/common/getting_started.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Getting Started

## Requirements

For Debian GNU/Linux 11 you will need the following dependencies:

```bash
sudo apt install build-essential cmake python3-pip libboost-all-dev libsqlite3-dev libssl-dev
```

OpenSSL version 3.0 or above is required.

Clone this repository.

```bash
git clone https://github.com/EVerest/libocpp
```

In the libocpp folder create a folder named build and cd into it.
Execute cmake and then make:

```bash
mkdir build && cd build
cmake ..
make -j$(nproc)
```

## Unit testing

GTest is required for building the test cases target.
To build the target and run the tests you can reference the script `.ci/build-kit/install_and_test.sh`.
The script allows the GitHub Actions runner to execute.

Local testing:

```bash
mkdir build
cmake -B build -DBUILD_TESTING=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="./dist"
cd build
make -j$(nproc) install
```

Run any required tests from build/tests.

## Get Started with OCPP1.6

Please see the [Getting Started documentation for OCPP1.6](../v16/getting_started.md).

## Get Started with OCPP2.0.1

Please see the [Getting Started documentation for OCPP1.6](../v201/getting_started.md).

## Building the doxygen documentation

```bash
cmake -S . -B build
cmake --build build --target doxygen-ocpp
```

You will find the generated doxygen documentation at:
`build/dist/docs/html/index.html`

The main reference for the integration of libocpp for OCPP1.6 is the ocpp::v16::ChargePoint class defined in libocpp/include/ocpp/v16/charge_point.hpp .
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Notes on Smart Charging Profiles and approach

There are some complexities calculating composite schedules and this note explains the approach.

The new approach is in `profile.cpp` and `profile.hpp` and then integrated into `smart_charging.cpp` maintaining the same API.

```
```cpp
std::vector<ChargingProfile> get_valid_profiles(
const DateTime& start_time, const DateTime& end_time,
int connector_id);
Expand All @@ -17,7 +18,9 @@ The new approach is in `profile.cpp` and `profile.hpp` and then integrated into
```

## get_valid_profiles()

Retrieves all profiles that should be considered for calculating the composite schedule.

- checks that profiles are associated with the correct connector
- checks that profiles match the transaction ID (when there is an active transaction)
- start_time and end_time are not used in the new implementation
Expand All @@ -28,9 +31,11 @@ within that period. However only the `validFrom` and `validTo` settings should b
`calculate_enhanced_composite_schedule` checks the `start_time` and `end_time` so it is not essential to remove profiles at this point.

## calculate_enhanced_composite_schedule()

Assumes that profiles for other connectors and transactions have been removed.

Processes profiles:

1. obtain session/transaction start time
2. split the profiles into ChargePointMax/TxDefault/Tx sets
3. for each set calculate the composite schedule using the preferred units (Amps/Watts)
Expand All @@ -40,6 +45,7 @@ Processes profiles:
When combining; TxDefault is the starting limit, overridden by Tx and potentially lowered by ChargePointMax. The result will never be higher than a ChargePointMax limit (where there is one).

## Time handling

The approach removes all calls for obtaining the current date and time.

- where there is a transaction then the start time of the transaction is obtained
Expand All @@ -50,6 +56,7 @@ For generating a composite schedule relative schedules are included based on the
The removal of any relationship to the current time simplifies writing test cases and debugging test failures.

## Default limit

The OCPP 1.6 specification doesn't support gaps in charging schedules. This presents a problem while creating a composite schedule when there is a period of time when no profile is active.

- profile 1: stack level 10, Monday 10:00 for 2 hours
Expand All @@ -65,7 +72,7 @@ Where the limit is not known then a default limit of `48.0 Amps` is used when ca

A different default can be specified by installing a lower stack level TxDefault profile e.g.

```
```json
{
"chargingProfileId": 1,
"chargingProfileKind": "Relative",
Expand All @@ -84,11 +91,12 @@ A different default can be specified by installing a lower stack level TxDefault
```

## No valid profiles

Since a default limit is applied a composite schedule will always start at the `start_time` and have a fixed duration even when there are no valid profiles for that time period.

e.g. for 2024-01-01 starting at 08:00 for 10 minutes

```
```json
{
"status": "Accepted",
"scheduleStart": "2024-01-01T08:00:00Z",
Expand All @@ -108,7 +116,7 @@ When building the OCPP response "startSchedule" could be excluded however the co

According to the OCPP specification section `7.13 ChargingSchedule` chargingSchedulePeriod is a required field and must have at least one entry. Hence the following is not a valid response:

```
```json
{
"status": "Accepted",
"scheduleStart": "2024-01-01T08:00:00Z",
Expand All @@ -124,6 +132,7 @@ According to the OCPP specification section `7.13 ChargingSchedule` chargingSche
## Profile validity

The following items need to be considered when looking at a schedule:

- validFrom
- validTo
- transaction start time
Expand All @@ -133,11 +142,11 @@ The following items need to be considered when looking at a schedule:

The following sections explore some interesting edge cases.


### validFrom & validTo and transaction start time

For the following schedule
```

```json
{
"chargingProfileId": 1,
"chargingProfileKind": "Relative",
Expand All @@ -156,7 +165,7 @@ For the following schedule
}
],
},
"validFrom": "2024-01-01T12:00:00Z"
"validFrom": "2024-01-01T12:00:00Z",
"validTo": "2024-01-01T20:00:00Z"
}
```
Expand All @@ -168,7 +177,8 @@ The PEV plugs in at "2024-01-01T19:50:00Z" the result is charging for 10 minutes
### startSchedule and daily recurring

For the following schedule
```

```json
{
"chargingProfileId": 1,
"chargingProfileKind": "Recurring",
Expand Down Expand Up @@ -199,7 +209,8 @@ The PEV plugs in at "2024-01-10T11:50:00Z" the result is charging at 6.0A for 10
### startSchedule and daily recurring with duration

For the following schedule
```

```json
{
"chargingProfileId": 1,
"chargingProfileKind": "Recurring",
Expand All @@ -221,7 +232,7 @@ For the following schedule
}
],
},
"validFrom": "2024-02-01T12:00:00Z"
"validFrom": "2024-02-01T12:00:00Z",
"validTo": "2024-03-01T09:00:00Z"
}
```
Expand All @@ -230,4 +241,4 @@ The PEV plugs in at "2024-02-10T11:50:00Z" the result is based on the default li

The PEV plugs in at "2024-02-01T11:50:00Z" the result is based on the default limit since the profile isn't valid yet. i.e. no charging for 10 minutes and then at 32.0A from 12:00 when the schedule starts.

The PEV plugs in at "2024-03-01T08:00:00Z" the result is charging at 6.0A (based on the previous day) and then no charging at 09:00 when the profile is no longer valid.
The PEV plugs in at "2024-03-01T08:00:00Z" the result is charging at 6.0A (based on the previous day) and then no charging at 09:00 when the profile is no longer valid.
Loading
Loading