Skip to content

Commit 5304dfd

Browse files
authored
Merge pull request #19 from c-jimenez/release/v0.3.0
Release/v0.3.0
2 parents f806637 + c1c0d91 commit 5304dfd

File tree

148 files changed

+6526
-1073
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

148 files changed

+6526
-1073
lines changed

.github/release.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# .github/release.yml
2+
3+
changelog:
4+
exclude:
5+
labels:
6+
- duplicate
7+
categories:
8+
- title: New Features 🎉
9+
labels:
10+
- enhancement
11+
- title: Bug fixes 🛠
12+
labels:
13+
- bug
14+
- title: Other Changes
15+
labels:
16+
- "*"

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
build
12
build_*
23
bin/*
34
.vscode/settings.json

README.md

Lines changed: 190 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Open OCPP
22

3-
**Open OCPP** is an Open Source C++ implementation of the OCPP 1.6 protocol written by the [Open Charge Alliance](https://www.openchargealliance.org/).
3+
**Open OCPP** is an Open Source C++ implementation of the OCPP 1.6 protocol ([Open Charge Alliance](https://www.openchargealliance.org/)).
44
This implementation targets only the Websocket/JSON version of this protocol.
55

66
This implementation is based on the following libraries :
@@ -10,13 +10,15 @@ This implementation is based on the following libraries :
1010
* [rapidjson](https://rapidjson.org/) : JSON serialization/deserialization
1111
* [doctest](https://github.com/doctest/doctest) : Unit tests
1212

13-
**Table of contents**
13+
## Table of contents
1414

15-
1. [Features](#features)
16-
2. [Build](#build)
17-
3. [Quick start](#quick-start)
18-
4. [Contributing](#contributing)
19-
5. [Examples](./examples/README.md)
15+
* [Features](#features)
16+
* [Build](#build)
17+
* [Quick start](#quick-start)
18+
+ [Charge Point role](#charge-point-role)
19+
+ [Central System role](#central-system-role)
20+
* [Contributing](#contributing)
21+
* [Examples](./examples/README.md)
2022

2123
## Features
2224

@@ -30,21 +32,26 @@ This implementation is based on the following libraries :
3032

3133
As of this version :
3234

33-
* Only websocket client has been implemented
34-
* Only Charge Point role has been implemented
35-
* No Charge Point behavior related to the OCPP 1.6 security whitepaper edition 2 has been implemented (work in progress)
36-
* Nearly all the messages defined in the OCPP 1.6 edition 2 protocol have been implemented
37-
* Nearly all the configuration keys defined in the OCPP 1.6 edition 2 protocol have been implemented for the Charge Point role
35+
* No Charge Point nor Central System behavior related to the OCPP 1.6 security whitepaper edition 2 has been implemented (work in progress)
36+
* All the messages defined in the OCPP 1.6 edition 2 protocol have been implemented except GetCompositeSchedule for Charge Point role
37+
* All the configuration keys defined in the OCPP 1.6 edition 2 protocol have been implemented for the Charge Point role
3838

3939
The user application will have to implement some callbacks to provide the data needed by **Open OCPP** or to handle OCPP events (boot notification, remote start/stop notifications, meter values...).
4040

4141
The persistent data handled by **Open OCPP** is stored into a single file which is an [SQLite](https://www.sqlite.org/) database. It contains :
4242

43-
* Internal configuration
44-
* Persistent data : Central System's registration status, connector state, OCPP transaction related messages when offline, StopTx meter values
45-
* Badge cache and local list
46-
* Smart charging profile
47-
* Logs
43+
* For Charge Point role :
44+
45+
+ Internal configuration
46+
+ Persistent data : Central System's registration status, connector state, OCPP transaction related messages when offline, StopTx meter values
47+
+ Badge cache and local list
48+
+ Smart charging profile
49+
+ Logs
50+
51+
* For Central System role :
52+
53+
+ Internal configuration
54+
+ Logs
4855

4956
The standard OCPP configuration persistency has to be handled by the user application.
5057

@@ -56,10 +63,10 @@ The standard OCPP configuration persistency has to be handled by the user applic
5663
| Firmware Management | Support for firmware update management and diagnostic log file download | Actual file download/upload as well as firmware installation must be handled by the user application in the callbacks provided by **Open OCPP** |
5764
| Local Auth List Management | Features to manage the local authorization list in Charge Points | None |
5865
| Reservation | Support for reservation of a Charge Point. | None |
59-
| Smart Charging | Support for basic Smart Charging, for instance using control pilot | GetCompositeSchedule is not supported for now |
66+
| Smart Charging | Support for basic Smart Charging, for instance using control pilot | GetCompositeSchedule is not supported for now in Chare Point role |
6067
| Remote Trigger | Support for remote triggering of Charge Point initiated messages | None |
6168

62-
### Supported OCPP configuration keys
69+
### Supported OCPP configuration keys (Charge Point role)
6370

6471
In the "Owner" column, "S" means that the configuration key behavior is handled by the stack, "U" means that it must handled by the user application.
6572

@@ -160,14 +167,16 @@ And to run the unit tests :
160167

161168
## Quick start
162169

163-
The best way to start is to take a look at the [examples](./examples/README.md) and more specifically at the [quick start example](./examples/quick_start_chargepoint/README.md).
170+
The best way to start is to take a look at the [examples](./examples/README.md) and more specifically at the [quick start Charge Point example](./examples/quick_start_chargepoint/README.md) and the [quick start Central System example](./examples/quick_start_centralsystem/README.md).
164171

165-
The implementation of a program using **Open OCPP** is done in 3 steps :
172+
### Charge Point role
173+
174+
The implementation of a program using **Open OCPP** in Charge Point role is done in 3 steps :
166175
* Implementation of the configuration interfaces [IOcppConfig](./src/config/IOcppConfig.h) and [IChargePointConfig](./src/chargepoint/interface/IChargePointConfig.h)
167176
* Implementation of the event handler interface [IChargePointEventsHandler](./src/chargepoint/interface/IChargePointEventsHandler.h)
168177
* Instanciation and use of the Charge Point object [IChargePoint](./src/chargepoint/interface/IChargePoint.h)
169178

170-
### Configuration interface
179+
#### Configuration interface
171180

172181
The configuration interface allow **Open OCPP** to access to the values of the standard OCPP configuration keys and to the user application specific configuration keys.
173182

@@ -183,7 +192,7 @@ The configuration interface is split in 2 parts :
183192

184193
In the examples, the interfaces have been implemented to retrieve the values from a file stored into an INI format. This is a simple implementaton which is good to show how to implement these interfaces but which is not the most optimized one since every access to a configuration value implies a conversion from a string. Try to have a better implementation to boost the performances of the software ;)
185194

186-
### Event handler interface
195+
#### Event handler interface
187196

188197
Most of the OCPP behavior is handled by **Open OCPP** but to complete the implementation of the OCPP standard, some information may be needed by **Open OCPP** (Meter values, change availability permissions...) or some operations may be done by the user application (File upload/download, firmware install...).
189198

@@ -193,7 +202,7 @@ Most of the notifications/operations can be left empty if the corresponding OCPP
193202

194203
Please keep in mind that all the calls to the event handler interface are made from different threads managed by **Open OCPP** depending the kind of notification/operation and that the treatment of theses calls must not be blocking (except for file upload/download) since it will have an impact to the global scheduling of **Open OCPP**.
195204

196-
### Charge Point object
205+
#### Charge Point object
197206

198207
This is the easiest part :)
199208

@@ -222,15 +231,15 @@ Once the Charge Point object has been started, **Open OCPP** will continuously t
222231

223232
Connectivity status, registration status and Central System initiated operations will be notified through the event handler interface.
224233

225-
OCPP Charge Point operation are triggered by the Charge Point object interface.
234+
OCPP Charge Point operations are triggered by the Charge Point object interface.
226235

227236
Extract of a quick start main() :
228237

229238
```
230239
int main()
231240
{
232241
// Configuration
233-
ChargePointConfig config("config.ini");
242+
ChargePointDemoConfig config("config.ini");
234243
235244
// Event handler
236245
DefaultChargePointEventsHandler event_handler(config);
@@ -301,6 +310,161 @@ int main()
301310
}
302311
```
303312

313+
### Central System role
314+
315+
The implementation of a program using **Open OCPP** in Central System role is done in 3 steps :
316+
* Implementation of the configuration interface [ICentralSystemConfig](./src/centralsystem/interface/ICentralSystemConfig.h)
317+
* Implementation of the event handler interfaces [ICentralSystemEventsHandler](./src/centralsystem/interface/ICentralSystemEventsHandler.h) and [IChargePointRequestHandler](./src/centralsystem/interface/IChargePointRequestHandler.h)
318+
* Instanciation and use of the Central System object [ICentralSystem](./src/centralsystem/interface/ICentralSystem.h)
319+
320+
#### Configuration interface
321+
322+
The configuration interface allow **Open OCPP** to access to its configuration values.
323+
The persistency of the configuration is not handled by **Open OCPP** for 2 main reasons :
324+
325+
* The user application will surely already have a configuration management component
326+
* The user application may have to access the **Open OCPP** configuration
327+
328+
In the examples, the interface has been implemented to retrieve the values from a file stored into an INI format. This is a simple implementaton which is good to show how to implement this interface but which is not the most optimized one since every access to a configuration value implies a conversion from a string. Try to have a better implementation to boost the performances of the software ;)
329+
330+
#### Event handler interfaces
331+
332+
In Central System role, the OCPP behavior must be implemented by the user application. **Open OCPP** handles all the other layers of the stack (websocket, RPC, JSON serialization/deserialization).
333+
334+
The **Open OCPP** stack will interact with the user application through 2 interfaces :
335+
336+
* [ICentralSystemEventsHandler](./src/centralsystem/interface/ICentralSystemEventsHandler.h) : used for all connection related events (credentials check, connection notification...)
337+
* [IChargePointRequestHandler](./src/centralsystem/interface/IChargePointRequestHandler.h) : used to handle incoming requests from a Charge Point (boot notification, status notification, start transaction...)
338+
339+
The **ICentralSystemEventsHandler** must be instanciated only once for a Central System implementation.
340+
341+
The **IChargePointRequestHandler** must be instanciated and registered to each connected Charge Point in the **ICentralSystemEventsHandler::chargePointConnected** method implementation.
342+
343+
Example of Charge Point connection handling :
344+
345+
```
346+
/** @copydoc bool ICentralSystemEventsHandler::chargePointConnected(std::shared_ptr<ICentralSystem::IChargePoint>) */
347+
void MyCentralSystemEventsHandler::chargePointConnected(std::shared_ptr<ocpp::centralsystem::ICentralSystem::IChargePoint> chargepoint)
348+
{
349+
cout << "Charge point [" << chargepoint->identifier() << "] connected" << endl;
350+
auto iter_chargepoint = m_chargepoints.find(chargepoint->identifier());
351+
if (iter_chargepoint == m_chargepoints.end())
352+
{
353+
MyChargePointRequestHandler* my_handler = new MyChargePointRequestHandler(*this, chargepoint);
354+
chargepoint->registerHandler(*my_handler);
355+
m_chargepoints[chargepoint->identifier()] =
356+
std::shared_ptr<ChargePointRequestHandler>(my_handler);
357+
}
358+
else
359+
{
360+
cout << "Charge point [" << chargepoint->identifier() << "] already connected" << endl;
361+
chargepoint.reset();
362+
}
363+
}
364+
```
365+
366+
Please keep in mind that all the calls to the event handler interface are made from different threads managed by **Open OCPP** depending the kind of notification/operation and that the treatment of theses calls must not be blocking since it will have an impact to the global scheduling of **Open OCPP**.
367+
368+
#### Central System object
369+
370+
This is the easiest part :)
371+
372+
The Central System object is instanciated through a factory interface :
373+
374+
```
375+
/**
376+
* @brief Instanciate a central system
377+
* @param stack_config Stack configuration
378+
* @param event_handler Stack event handler
379+
*/
380+
static std::unique_ptr<ICentralSystem> create(const ocpp::config::ICentralSystemConfig& stack_config,
381+
ICentralSystemEventsHandler& events_handler);
382+
```
383+
384+
The 2 parameters are the instances of the interfaces you have implemented in the previous steps.
385+
386+
Before starting the Central System object and thus the OCPP stack with the ```start()``` method, you can clear existing persistent data using the following method:
387+
388+
```resetData()``` : clear all the persistent data
389+
390+
Once the Central System object has been started, **Open OCPP** will continuously listen to incoming connections from the Charge Points until a call to the ```stop()``` method which will disconnect all the Charge Points and release the connection.
391+
392+
Connectivity status and Charge Point initiated operations will be notified through the event handler interfaces.
393+
394+
OCPP Central System operations are triggered by the Charge Point proxy interface [ICentralSystem::IChargePoint](./src/centralsystem/interface/ICentralSystem.h) which is instanciated by **Open OCPP** for each connected Charge Point.
395+
396+
Extract of a quick start main() :
397+
398+
```
399+
int main()
400+
{
401+
// Configuration
402+
CentralSystemDemoConfig config("config.ini");
403+
404+
// Event handler
405+
CentralSystemDemoConfig event_handler(config);
406+
407+
// Instanciate central system
408+
std::unique_ptr<ICentralSystem> central_system = ICentralSystem::create(config.stackConfig(), event_handler);
409+
central_system->start();
410+
411+
// From now on the stack is alive :)
412+
413+
// App loop
414+
while (true)
415+
{
416+
// Wait for at least 1 connected charge point
417+
while (event_handler.chargePoints().size() == 0)
418+
{
419+
std::this_thread::sleep_for(std::chrono::milliseconds(250));
420+
}
421+
std::this_thread::sleep_for(std::chrono::seconds(1));
422+
423+
// For each connected charge point
424+
for (auto& iter_chargepoint : event_handler.chargePoints())
425+
{
426+
{
427+
auto chargepoint = iter_chargepoint.second->proxy();
428+
429+
std::cout << "---------------------------------------------" << std::endl;
430+
std::cout << "Charge point : " << chargepoint->identifier() << std::endl;
431+
std::cout << "---------------------------------------------" << std::endl;
432+
433+
std::cout << "Read whole charge point configuration..." << std::endl;
434+
std::vector<std::string> keys;
435+
std::vector<KeyValue> config_keys;
436+
std::vector<std::string> unknown_keys;
437+
if (chargepoint->getConfiguration(keys, config_keys, unknown_keys))
438+
{
439+
std::cout << "Configuration keys :" << std::endl;
440+
for (const KeyValue& key_value : config_keys)
441+
{
442+
std::cout << " - " << key_value.key.str() << " = " << (key_value.value.isSet() ? key_value.value.value().str() : "")
443+
<< " " << (key_value.readonly ? "(read-only)" : "") << std::endl;
444+
}
445+
}
446+
else
447+
{
448+
std::cout << "Failed!" << std::endl;
449+
}
450+
451+
std::cout << "Configure heartbeat interval..." << std::endl;
452+
ConfigurationStatus config_status = chargepoint->changeConfiguration("HeartbeatInterval", "10");
453+
std::cout << ConfigurationStatusHelper.toString(config_status) << std::endl;
454+
455+
std::cout << "Trigger status notification..." << std::endl;
456+
TriggerMessageStatus trigger_status =
457+
chargepoint->triggerMessage(MessageTrigger::StatusNotification, Optional<unsigned int>());
458+
std::cout << TriggerMessageStatusHelper.toString(trigger_status) << std::endl;
459+
}
460+
461+
std::this_thread::sleep_for(std::chrono::seconds(10));
462+
}
463+
}
464+
465+
return 0;
466+
}
467+
```
304468

305469
## Contributing
306470

examples/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11

22
# Subdirectories
33
add_subdirectory(common)
4+
add_subdirectory(quick_start_centralsystem)
45
add_subdirectory(quick_start_chargepoint)
56
add_subdirectory(remote_chargepoint)

examples/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
All the examples are licensed under the MIT licence so that code can be used and modified at will without having to contribute back.
44

55
The following examples are available :
6+
* [Quick start Central System example](./quick_start_centralsystem/README.md)
67
* [Quick start Charge Point example](./quick_start_chargepoint/README.md)
78
* [Remote Charge Point example](./remote_chargepoint/README.md)
89

910
How to run the examples:
1011
* Customize the *config.ini* file of the selected example with the URL of the Central System and the other connection parameters has well has the OCPP configuration keys
11-
* Run the exmaple using the **-w** option to specify the path of the configuration file
12+
* Run the example using the **-w** option to specify the path of the configuration file

examples/certificates/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*.param
2+
*.srl
3+
*.csr

examples/certificates/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Certificate generation for Open OCPP examples
2+
3+
The **generate_certificates.sh** script allow to generate new certificates.
4+
5+
The following certificates are generated:
6+
7+
* open-ocpp_ca.crt : CA organization certificate
8+
* open-ocpp_central-system.crt : Central System's certificate
9+
* open-ocpp_charge-point.crt : Charge Point's certificate
10+
11+
The associated private keys are generated in the .key files.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/bin/bash
2+
3+
echo "Generating private key for CA..."
4+
openssl ecparam -name prime256v1 -out open-ocpp_ca.param
5+
openssl ecparam -in open-ocpp_ca.param -genkey -noout -out open-ocpp_ca.key
6+
echo "Generating certificate for CA..."
7+
openssl req -new -sha256 -key open-ocpp_ca.key -extensions v3_ca -config open-ocpp_ca.cnf -out open-ocpp_ca.csr
8+
echo "Self-signing CA certificate..."
9+
openssl x509 -req -sha256 -days 3650 -in open-ocpp_ca.csr -extensions v3_ca -extfile open-ocpp_ca.cnf -signkey open-ocpp_ca.key -out open-ocpp_ca.crt
10+
echo ""
11+
12+
echo "Generating private key for Central System..."
13+
openssl ecparam -name prime256v1 -out open-ocpp_central-system.param
14+
openssl ecparam -in open-ocpp_central-system.param -genkey -noout -out open-ocpp_central-system.key
15+
echo "Generating certificate request for Central System..."
16+
openssl req -new -sha256 -key open-ocpp_central-system.key -extensions v3_ca -config open-ocpp_central-system.cnf -out open-ocpp_central-system.csr
17+
echo "Signing Central System certificate with CA certificate..."
18+
openssl x509 -req -sha256 -days 3650 -in open-ocpp_central-system.csr -extensions v3_ca -extfile open-ocpp_central-system.cnf -CA open-ocpp_ca.crt -CAkey open-ocpp_ca.key -CAcreateserial -out open-ocpp_central-system.crt
19+
echo "Verify certificate chain..."
20+
openssl verify -verbose -CAfile open-ocpp_ca.crt open-ocpp_central-system.crt
21+
echo ""
22+
23+
echo "Generating private key for Charge Point..."
24+
openssl ecparam -name prime256v1 -out open-ocpp_charge-point.param
25+
openssl ecparam -in open-ocpp_charge-point.param -genkey -noout -out open-ocpp_charge-point.key
26+
echo "Generating certificate request for Charge Point..."
27+
openssl req -new -sha256 -key open-ocpp_charge-point.key -extensions v3_ca -config open-ocpp_charge-point.cnf -out open-ocpp_charge-point.csr
28+
echo "Signing Charge Point certificate with CA certificate..."
29+
openssl x509 -req -sha256 -days 3650 -in open-ocpp_charge-point.csr -extensions v3_ca -extfile open-ocpp_charge-point.cnf -CA open-ocpp_ca.crt -CAkey open-ocpp_ca.key -CAcreateserial -out open-ocpp_charge-point.crt
30+
echo "Verify certificate chain..."
31+
openssl verify -verbose -CAfile open-ocpp_ca.crt open-ocpp_charge-point.crt
32+
echo ""

0 commit comments

Comments
 (0)