Skip to content

Commit d36ccd5

Browse files
committed
add generic MeterValue support
1 parent 569240f commit d36ccd5

14 files changed

Lines changed: 327 additions & 121 deletions

src/ArduinoOcpp.cpp

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,16 @@ void setPowerActiveImportSampler(std::function<float()> power) {
168168
model.setMeteringSerivce(std::unique_ptr<MeteringService>(
169169
new MeteringService(*ocppEngine, OCPP_NUMCONNECTORS)));
170170
}
171-
model.getMeteringService()->setPowerSampler(OCPP_ID_OF_CONNECTOR, power); //connectorId=1
171+
SampledValueProperties meterProperties;
172+
meterProperties.setMeasurand("Power.Active.Import");
173+
meterProperties.setUnit("W");
174+
auto mvs = std::unique_ptr<SampledValueSamplerConcrete<int32_t, SampledValueDeSerializer<int32_t>>>(
175+
new SampledValueSamplerConcrete<int32_t, SampledValueDeSerializer<int32_t>>(
176+
meterProperties,
177+
power
178+
));
179+
model.getMeteringService()->addMeterValueSampler(OCPP_ID_OF_CONNECTOR, std::move(mvs)); //connectorId=1
180+
model.getMeteringService()->setPowerSampler(OCPP_ID_OF_CONNECTOR, power);
172181
}
173182

174183
void setEnergyActiveImportSampler(std::function<float()> energy) {
@@ -181,7 +190,27 @@ void setEnergyActiveImportSampler(std::function<float()> energy) {
181190
model.setMeteringSerivce(std::unique_ptr<MeteringService>(
182191
new MeteringService(*ocppEngine, OCPP_NUMCONNECTORS)));
183192
}
184-
model.getMeteringService()->setEnergySampler(OCPP_ID_OF_CONNECTOR, energy); //connectorId=1
193+
SampledValueProperties meterProperties;
194+
meterProperties.setMeasurand("Energy.Active.Import.Register");
195+
meterProperties.setUnit("Wh");
196+
auto mvs = std::unique_ptr<SampledValueSamplerConcrete<int32_t, SampledValueDeSerializer<int32_t>>>(
197+
new SampledValueSamplerConcrete<int32_t, SampledValueDeSerializer<int32_t>>(
198+
meterProperties, energy));
199+
model.getMeteringService()->addMeterValueSampler(OCPP_ID_OF_CONNECTOR, std::move(mvs)); //connectorId=1
200+
model.getMeteringService()->setEnergySampler(OCPP_ID_OF_CONNECTOR, energy);
201+
}
202+
203+
void addMeterValueSampler(std::unique_ptr<SampledValueSampler> meterValueSampler) {
204+
if (!ocppEngine) {
205+
AO_DBG_ERR("Please call OCPP_initialize before");
206+
return;
207+
}
208+
auto& model = ocppEngine->getOcppModel();
209+
if (!model.getMeteringService()) {
210+
model.setMeteringSerivce(std::unique_ptr<MeteringService>(
211+
new MeteringService(*ocppEngine, OCPP_NUMCONNECTORS)));
212+
}
213+
model.getMeteringService()->addMeterValueSampler(OCPP_ID_OF_CONNECTOR, std::move(meterValueSampler)); //connectorId=1
185214
}
186215

187216
void setEvRequestsEnergySampler(std::function<bool()> evRequestsEnergy) {

src/ArduinoOcpp.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <ArduinoOcpp/Core/OcppOperationCallbacks.h>
1414
#include <ArduinoOcpp/Core/OcppOperationTimeout.h>
1515
#include <ArduinoOcpp/Core/OcppSocket.h>
16+
#include <ArduinoOcpp/Tasks/Metering/SampledValue.h>
1617

1718
using ArduinoOcpp::OnReceiveConfListener;
1819
using ArduinoOcpp::OnReceiveReqListener;
@@ -50,6 +51,8 @@ void setPowerActiveImportSampler(std::function<float()> power);
5051

5152
void setEnergyActiveImportSampler(std::function<float()> energy);
5253

54+
void addMeterValueSampler(std::unique_ptr<ArduinoOcpp::SampledValueSampler> meterValueSampler);
55+
5356
void setEvRequestsEnergySampler(std::function<bool()> evRequestsEnergy);
5457

5558
void setConnectorEnergizedSampler(std::function<bool()> connectorEnergized);

src/ArduinoOcpp/MessagesV16/MeterValues.cpp

Lines changed: 24 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <ArduinoOcpp/MessagesV16/MeterValues.h>
66
#include <ArduinoOcpp/Core/OcppModel.h>
77
#include <ArduinoOcpp/Tasks/ChargePointStatus/ChargePointStatusService.h>
8+
#include <ArduinoOcpp/Tasks/Metering/MeterValue.h>
89
#include <ArduinoOcpp/Debug.h>
910

1011
using ArduinoOcpp::Ocpp16::MeterValues;
@@ -14,14 +15,12 @@ MeterValues::MeterValues() {
1415

1516
}
1617

17-
MeterValues::MeterValues(const std::vector<OcppTimestamp> *sampleTime, const std::vector<float> *energy, const std::vector<float> *power, int connectorId, int transactionId)
18+
MeterValues::MeterValues(const std::vector<std::unique_ptr<MeterValue>>& meterValue, int connectorId, int transactionId)
1819
: connectorId{connectorId}, transactionId{transactionId} {
19-
if (sampleTime)
20-
this->sampleTime = std::vector<OcppTimestamp>(*sampleTime);
21-
if (energy)
22-
this->energy = std::vector<float>(*energy);
23-
if (power)
24-
this->power = std::vector<float>(*power);
20+
21+
for (auto value = meterValue.begin(); value != meterValue.end(); value++) {
22+
this->meterValue.push_back(std::unique_ptr<MeterValue>(new MeterValue(**value)));
23+
}
2524
}
2625

2726
MeterValues::~MeterValues(){
@@ -34,56 +33,34 @@ const char* MeterValues::getOcppOperationType(){
3433

3534
std::unique_ptr<DynamicJsonDocument> MeterValues::createReq() {
3635

37-
int numEntries = sampleTime.size();
38-
39-
const size_t VALUE_MAXPRECISION = 10;
40-
const size_t VALUE_MAXSIZE = VALUE_MAXPRECISION + 7;
41-
char value_str [VALUE_MAXSIZE] = {'\0'};
42-
43-
auto doc = std::unique_ptr<DynamicJsonDocument>(new DynamicJsonDocument(
44-
JSON_OBJECT_SIZE(3) //connectorID, transactionId, meterValue entry
45-
+ JSON_ARRAY_SIZE(numEntries) //metervalue array
46-
+ numEntries * JSON_OBJECT_SIZE(1) //sampledValue entry
47-
+ numEntries * (JSON_OBJECT_SIZE(1) + (JSONDATE_LENGTH + 1)) //timestamp
48-
+ numEntries * JSON_ARRAY_SIZE(2) //sampledValue
49-
+ 2 * numEntries * (JSON_OBJECT_SIZE(1) + VALUE_MAXSIZE) //value
50-
+ 2 * numEntries * JSON_OBJECT_SIZE(1) //measurand
51-
+ 2 * numEntries * JSON_OBJECT_SIZE(1) //unit
52-
+ 230)); //"safety space"
53-
JsonObject payload = doc->to<JsonObject>();
36+
size_t capacity = 0;
5437

55-
payload["connectorId"] = connectorId;
56-
JsonArray meterValues = payload.createNestedArray("meterValue");
57-
for (size_t i = 0; i < sampleTime.size(); i++) {
58-
JsonObject meterValue = meterValues.createNestedObject();
59-
char timestamp[JSONDATE_LENGTH + 1] = {'\0'};
60-
OcppTimestamp otimestamp = sampleTime.at(i);
61-
otimestamp.toJsonString(timestamp, JSONDATE_LENGTH + 1);
62-
meterValue["timestamp"] = timestamp;
63-
JsonArray sampledValue = meterValue.createNestedArray("sampledValue");
64-
if (energy.size() >= i + 1) {
65-
JsonObject sampledValue_1 = sampledValue.createNestedObject();
66-
snprintf(value_str, VALUE_MAXSIZE, "%.*g", VALUE_MAXPRECISION, energy.at(i));
67-
sampledValue_1["value"] = value_str;
68-
sampledValue_1["measurand"] = "Energy.Active.Import.Register";
69-
sampledValue_1["unit"] = "Wh";
70-
}
71-
if (power.size() >= i + 1) {
72-
JsonObject sampledValue_2 = sampledValue.createNestedObject();
73-
snprintf(value_str, VALUE_MAXSIZE, "%.*g", VALUE_MAXPRECISION, power.at(i));
74-
sampledValue_2["value"] = value_str;
75-
sampledValue_2["measurand"] = "Power.Active.Import";
76-
sampledValue_2["unit"] = "W";
77-
}
38+
std::vector<std::unique_ptr<DynamicJsonDocument>> entries;
39+
for (auto value = meterValue.begin(); value != meterValue.end(); value++) {
40+
auto entry = (*value)->toJson();
41+
capacity += entry->capacity();
42+
entries.push_back(std::move(entry));
7843
}
7944

45+
capacity += JSON_OBJECT_SIZE(3);
46+
capacity += JSON_ARRAY_SIZE(entries.size());
47+
48+
auto doc = std::unique_ptr<DynamicJsonDocument>(new DynamicJsonDocument(capacity + 100)); //TODO remove safety space
49+
auto payload = doc->to<JsonObject>();
50+
payload["connectorId"] = connectorId;
51+
8052
if (ocppModel && ocppModel->getConnectorStatus(connectorId)) {
8153
auto connector = ocppModel->getConnectorStatus(connectorId);
8254
if (connector->getTransactionIdSync() >= 0) {
8355
payload["transactionId"] = connector->getTransactionIdSync();
8456
}
8557
}
8658

59+
auto meterValueJson = payload.createNestedArray("meterValue");
60+
for (auto entry = entries.begin(); entry != entries.end(); entry++) {
61+
meterValueJson.add(**entry);
62+
}
63+
8764
return doc;
8865
}
8966

src/ArduinoOcpp/MessagesV16/MeterValues.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,20 @@
77

88
#include <ArduinoOcpp/Core/OcppMessage.h>
99
#include <ArduinoOcpp/Core/OcppTime.h>
10+
#include <ArduinoOcpp/Tasks/Metering/MeterValue.h>
1011

1112
namespace ArduinoOcpp {
1213
namespace Ocpp16 {
1314

1415
class MeterValues : public OcppMessage {
1516
private:
16-
17-
std::vector<OcppTimestamp> sampleTime;
18-
std::vector<float> power;
19-
std::vector<float> energy;
17+
std::vector<std::unique_ptr<MeterValue>> meterValue;
2018

2119
int connectorId = 0;
2220
int transactionId = -1;
2321

2422
public:
25-
MeterValues(const std::vector<OcppTimestamp> *sampleTime, const std::vector<float> *energy, const std::vector<float> *power, int connectorId, int transactionId);
23+
MeterValues(const std::vector<std::unique_ptr<MeterValue>>& meterValue, int connectorId, int transactionId);
2624

2725
MeterValues(); //for debugging only. Make this for the server pendant
2826

src/ArduinoOcpp/MessagesV16/StartTransaction.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const char* StartTransaction::getOcppOperationType(){
2828
void StartTransaction::initiate() {
2929
if (ocppModel && ocppModel->getMeteringService()) {
3030
auto meteringService = ocppModel->getMeteringService();
31-
meterStart = (int) meteringService->readEnergyActiveImportRegister(connectorId);
31+
meterStart = meteringService->readEnergyActiveImportRegister(connectorId);
3232
}
3333

3434
if (ocppModel) {

src/ArduinoOcpp/MessagesV16/StartTransaction.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace Ocpp16 {
1515
class StartTransaction : public OcppMessage {
1616
private:
1717
int connectorId = 1;
18-
int meterStart = -1;
18+
int32_t meterStart = -1;
1919
OcppTimestamp otimestamp;
2020
char idTag [IDTAG_LEN_MAX + 1] = {'\0'};
2121
uint16_t transactionRev = 0;

src/ArduinoOcpp/MessagesV16/StopTransaction.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ void StopTransaction::initiate() {
2222

2323
if (ocppModel && ocppModel->getMeteringService()) {
2424
auto meteringService = ocppModel->getMeteringService();
25-
meterStop = (int) meteringService->readEnergyActiveImportRegister(connectorId);
25+
meterStop = meteringService->readEnergyActiveImportRegister(connectorId);
2626
}
2727

2828
if (ocppModel) {

src/ArduinoOcpp/MessagesV16/StopTransaction.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace Ocpp16 {
1414
class StopTransaction : public OcppMessage {
1515
private:
1616
int connectorId = 1;
17-
int meterStop = -1;
17+
int32_t meterStop = -1;
1818
OcppTimestamp otimestamp;
1919
public:
2020

src/ArduinoOcpp/Tasks/Metering/ConnectorMeterValuesRecorder.cpp

Lines changed: 34 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,27 @@ using namespace ArduinoOcpp::Ocpp16;
1515

1616
ConnectorMeterValuesRecorder::ConnectorMeterValuesRecorder(OcppModel& context, int connectorId)
1717
: context(context), connectorId{connectorId} {
18-
sampleTimestamp = std::vector<OcppTimestamp>();
19-
energy = std::vector<float>();
20-
power = std::vector<float>();
2118

2219
MeterValueSampleInterval = declareConfiguration("MeterValueSampleInterval", 60);
2320
MeterValuesSampledDataMaxLength = declareConfiguration("MeterValuesSampledDataMaxLength", 4, CONFIGURATION_VOLATILE, false, true, false, false);
2421
}
2522

2623
void ConnectorMeterValuesRecorder::takeSample() {
27-
if (energySampler != nullptr || powerSampler != nullptr) {
28-
if (!context.getOcppTime().isValid()) return;
29-
sampleTimestamp.push_back(context.getOcppTime().getOcppTimestampNow());
30-
}
24+
if (meterValueSamplers.empty()) return;
3125

32-
if (energySampler != nullptr) {
33-
energy.push_back(energySampler());
26+
std::unique_ptr<MeterValue> sample;
27+
if (context.getOcppTime().isValid()) {
28+
sample.reset(new MeterValue(context.getOcppTime().getOcppTimestampNow()));
29+
}
30+
if (!sample) {
31+
return;
3432
}
3533

36-
if (powerSampler != nullptr) {
37-
power.push_back(powerSampler());
34+
for (auto mvs = meterValueSamplers.begin(); mvs != meterValueSamplers.end(); mvs++) {
35+
sample->addSampledValue((*mvs)->takeValue());
3836
}
37+
38+
meterValue.push_back(std::move(sample));
3939
}
4040

4141
OcppMessage *ConnectorMeterValuesRecorder::loop() {
@@ -73,7 +73,7 @@ OcppMessage *ConnectorMeterValuesRecorder::loop() {
7373
/*
7474
* Is the value buffer already full? If yes, return MeterValues message
7575
*/
76-
if (((int) sampleTimestamp.size()) >= (int) *MeterValuesSampledDataMaxLength) {
76+
if (((int) meterValue.size()) >= (int) *MeterValuesSampledDataMaxLength) {
7777
auto result = toMeterValues();
7878
return result;
7979
}
@@ -82,59 +82,35 @@ OcppMessage *ConnectorMeterValuesRecorder::loop() {
8282
}
8383

8484
OcppMessage *ConnectorMeterValuesRecorder::toMeterValues() {
85-
if (sampleTimestamp.size() == 0) {
85+
if (meterValue.empty()) {
8686
AO_DBG_DEBUG("Checking if to send MeterValues ... No");
8787
clear();
8888
return nullptr;
89-
}
90-
91-
//decide which measurands to send. If a measurand is missing at at least one point in time, omit that measurand completely
92-
93-
if (energy.size() == sampleTimestamp.size() && power.size() == sampleTimestamp.size()) {
94-
auto result = new MeterValues(&sampleTimestamp, &energy, &power, connectorId, lastTransactionId);
95-
clear();
96-
return result;
97-
}
98-
99-
if (energy.size() == sampleTimestamp.size() && power.size() != sampleTimestamp.size()) {
100-
auto result = new MeterValues(&sampleTimestamp, &energy, nullptr, connectorId, lastTransactionId);
101-
clear();
102-
return result;
103-
}
104-
105-
if (energy.size() != sampleTimestamp.size() && power.size() == sampleTimestamp.size()) {
106-
auto result = new MeterValues(&sampleTimestamp, nullptr, &power, connectorId, lastTransactionId);
89+
} else {
90+
auto result = new MeterValues(meterValue, connectorId, lastTransactionId);
10791
clear();
10892
return result;
10993
}
110-
111-
//Maybe the energy sampler or power sampler was set during recording. Discard recorded data.
112-
AO_DBG_WARN("Invalid data set. Discard data set and restart recording");
113-
clear();
114-
115-
return nullptr;
11694
}
11795

11896
OcppMessage *ConnectorMeterValuesRecorder::takeMeterValuesNow() {
11997

120-
if (!energySampler && !powerSampler) {
98+
if (meterValueSamplers.empty()) {
12199
return nullptr;
122100
}
123101

124-
decltype(sampleTimestamp) t_now;
125-
decltype(energy) e_now;
126-
decltype(power) p_now;
102+
std::unique_ptr<MeterValue> value;
127103

128104
if (context.getOcppTime().isValid()) {
129-
t_now.push_back(context.getOcppTime().getOcppTimestampNow());
105+
value.reset(new MeterValue(context.getOcppTime().getOcppTimestampNow()));
130106
}
131107

132-
if (energySampler) {
133-
e_now.push_back(energySampler());
108+
if (!value) {
109+
return nullptr;
134110
}
135111

136-
if (powerSampler) {
137-
p_now.push_back(powerSampler());
112+
for (auto mvs = meterValueSamplers.begin(); mvs != meterValueSamplers.end(); mvs++) {
113+
value->addSampledValue((*mvs)->takeValue());
138114
}
139115

140116
int txId_now = -1;
@@ -143,24 +119,29 @@ OcppMessage *ConnectorMeterValuesRecorder::takeMeterValuesNow() {
143119
txId_now = connector->getTransactionId();
144120
}
145121

146-
return new MeterValues(&t_now, &e_now, &p_now, connectorId, txId_now);
122+
decltype(meterValue) mv_now;
123+
mv_now.push_back(std::move(value));
124+
125+
return new MeterValues(mv_now, connectorId, txId_now);
147126
}
148127

149128
void ConnectorMeterValuesRecorder::clear() {
150-
sampleTimestamp.clear();
151-
energy.clear();
152-
power.clear();
129+
meterValue.clear();
153130
}
154131

155132
void ConnectorMeterValuesRecorder::setPowerSampler(PowerSampler ps){
156-
this->powerSampler = ps;
133+
this->powerSampler = ps;
157134
}
158135

159136
void ConnectorMeterValuesRecorder::setEnergySampler(EnergySampler es){
160-
this->energySampler = es;
137+
this->energySampler = es;
138+
}
139+
140+
void ConnectorMeterValuesRecorder::addMeterValueSampler(std::unique_ptr<SampledValueSampler> meterValueSampler) {
141+
meterValueSamplers.push_back(std::move(meterValueSampler));
161142
}
162143

163-
float ConnectorMeterValuesRecorder::readEnergyActiveImportRegister() {
144+
int32_t ConnectorMeterValuesRecorder::readEnergyActiveImportRegister() {
164145
if (energySampler != nullptr) {
165146
return energySampler();
166147
} else {

0 commit comments

Comments
 (0)