1 /*
2  * Copyright (c) 2025 Linumiz GmbH
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "ocpp_i.h"
8 
9 #define OCPP_PDU_TIMEOUT 2
10 #define OCPP_USER_REQ_PDU_BUF 350
11 #define FILL_METER_TABLE(_mes, _smes, _unit)[_mes] = {.mes = _mes,	\
12 						 .smes = _smes, .unit = _unit}
13 
14 static struct {
15 	enum ocpp_meter_measurand	mes;
16 	const char * const smes;
17 	const char * const unit;
18 } mtr_ref_table[] = {
19 	FILL_METER_TABLE(OCPP_OMM_CURRENT_FROM_EV,
20 			 "Current.Export", "A"),
21 	FILL_METER_TABLE(OCPP_OMM_CURRENT_TO_EV,
22 			 "Current.Import", "A"),
23 	FILL_METER_TABLE(OCPP_OMM_CURRENT_MAX_OFFERED_TO_EV,
24 			 "Current.OfferedMaximum", "A"),
25 	FILL_METER_TABLE(OCPP_OMM_ACTIVE_ENERGY_FROM_EV,
26 			 "Energy.Active.Export.Register", "Wh"),
27 	FILL_METER_TABLE(OCPP_OMM_ACTIVE_ENERGY_TO_EV,
28 			 "Energy.Active.Import.Register", "Wh"),
29 	FILL_METER_TABLE(OCPP_OMM_REACTIVE_ENERGY_FROM_EV,
30 			 "Energy.Reactive.Export.Register", "varh"),
31 	FILL_METER_TABLE(OCPP_OMM_REACTIVE_ENERGY_TO_EV,
32 			 "Energy.Reactive.Import.Register", "varh"),
33 	FILL_METER_TABLE(OCPP_OMM_ACTIVE_POWER_FROM_EV,
34 			 "Power.Active.Export", "W"),
35 	FILL_METER_TABLE(OCPP_OMM_ACTIVE_POWER_TO_EV,
36 			 "Power.Active.Import", "W"),
37 	FILL_METER_TABLE(OCPP_OMM_REACTIVE_POWER_FROM_EV,
38 			 "Power.Reactive.Export", "var"),
39 	FILL_METER_TABLE(OCPP_OMM_REACTIVE_POWER_TO_EV,
40 			 "Power.Reactive.Import", "var"),
41 	FILL_METER_TABLE(OCPP_OMM_POWERLINE_FREQ,
42 			 "Frequency", NULL),
43 	FILL_METER_TABLE(OCPP_OMM_POWER_FACTOR,
44 			 "Power.Factor", NULL),
45 	FILL_METER_TABLE(OCPP_OMM_POWER_MAX_OFFERED_TO_EV,
46 			 "Power.Offered", NULL),
47 	FILL_METER_TABLE(OCPP_OMM_FAN_SPEED,
48 			 "RPM", "rpm"),
49 	FILL_METER_TABLE(OCPP_OMM_CHARGING_PERCENT,
50 			 "SoCState", "Percent"),
51 	FILL_METER_TABLE(OCPP_OMM_TEMPERATURE,
52 			 "Temperature", "Celsius"),
53 	FILL_METER_TABLE(OCPP_OMM_VOLTAGE_AC_RMS,
54 			 "Voltage", "V")
55 };
56 
ocpp_boot_notification(ocpp_session_handle_t hndl,struct ocpp_cp_info * cpi)57 int ocpp_boot_notification(ocpp_session_handle_t hndl,
58 			   struct ocpp_cp_info *cpi)
59 {
60 	int ret;
61 	struct ocpp_session *sh = (struct ocpp_session *)hndl;
62 	struct ocpp_info *ctx = sh->ctx;
63 	struct ocpp_upstream_info *ui = &ctx->ui;
64 	char *buf = ctx->pdu_buf;
65 	ocpp_msg_fp_t fn;
66 	struct ocpp_wamp_rpc_msg rmsg = {0};
67 
68 	fn = ctx->cfn[PDU_BOOTNOTIFICATION];
69 	sh->uid = fn(buf, sizeof(ctx->pdu_buf), sh, cpi);
70 
71 	rmsg.ctx = ctx;
72 	rmsg.msg = buf;
73 	rmsg.msg_len = strlen(buf);
74 	rmsg.sndlock = &ui->ws_sndlock;
75 	rmsg.rspsig = &ui->ws_rspsig;
76 
77 	ret = ocpp_send_to_server(&rmsg, K_SECONDS(OCPP_PDU_TIMEOUT));
78 	if (ret == 0 && sh->resp_status != BOOT_ACCEPTED) {
79 		ret = -EAGAIN;
80 	}
81 
82 	return ret;
83 }
84 
ocpp_get_configuration(enum ocpp_key key,struct ocpp_info * ctx,char * uid)85 int ocpp_get_configuration(enum ocpp_key key, struct ocpp_info *ctx, char *uid)
86 {
87 	int ret;
88 	char tmp[32];
89 	char *sval = NULL;
90 	char *buf = ctx->pdu_buf;
91 	struct ocpp_wamp_rpc_msg rmsg = {0};
92 	union ocpp_keyval *kval;
93 	ocpp_msg_fp_t fn;
94 	bool is_rw;
95 	enum ocpp_key_type ktype;
96 
97 	if (key >= OCPP_CFG_END) {
98 		return -EINVAL;
99 	}
100 
101 	ktype = ocpp_get_keyval_type(key);
102 	is_rw = ocpp_is_key_rw(key);
103 	kval = ocpp_get_key_val(key);
104 
105 	if (ktype < KEY_TYPE_STR) {
106 		sval = tmp;
107 		snprintk(tmp, sizeof(tmp), "%d", kval->ival);
108 	} else {
109 		sval = kval->str;
110 	}
111 
112 	fn = ctx->cfn[PDU_GET_CONFIGURATION];
113 	fn(buf, sizeof(ctx->pdu_buf), ocpp_get_key_literal(key), sval, is_rw, uid);
114 
115 	rmsg.ctx = ctx;
116 	rmsg.msg = buf;
117 	rmsg.msg_len = strlen(buf);
118 
119 	ret = ocpp_send_to_server(&rmsg, K_SECONDS(OCPP_PDU_TIMEOUT));
120 	return ret;
121 }
122 
ocpp_change_configuration(char * skey,struct ocpp_info * ctx,char * sval,char * uid)123 int ocpp_change_configuration(char *skey, struct ocpp_info *ctx,
124 			      char *sval, char *uid)
125 {
126 	int ret = -EINVAL;
127 	char *buf = ctx->pdu_buf;
128 	struct ocpp_wamp_rpc_msg rmsg = {0};
129 	union ocpp_keyval kval;
130 	ocpp_msg_fp_t fn;
131 	enum ocpp_key key;
132 	const char *res = "Accepted";
133 
134 	key = ocpp_key_to_cfg(skey);
135 	if (key < OCPP_CFG_END) {
136 		enum ocpp_key_type ktype;
137 
138 		ktype = ocpp_get_keyval_type(key);
139 
140 		if (ktype < KEY_TYPE_STR) {
141 			kval.ival = atoi(sval);
142 		} else {
143 			kval.str = sval;
144 		}
145 
146 		ret = ocpp_update_cfg_val(key, &kval);
147 		if (ret < 0) {
148 			res = "Rejected";
149 		}
150 	} else {
151 		res = "NotSupported";
152 	}
153 
154 	if (ret == 0) {
155 		switch (key) {
156 		case CFG_MTR_VAL_SAMPLE_INTERVAL:
157 			if (atomic_get(&ctx->mtr_timer_ref_cnt) <= 0) {
158 				break;
159 			}
160 
161 			k_timer_start(&ctx->mtr_timer,
162 				      K_SECONDS(kval.ival),
163 				      K_SECONDS(kval.ival));
164 			break;
165 
166 		default:
167 			break;
168 		}
169 	}
170 
171 	fn = ctx->cfn[PDU_CHANGE_CONFIGURATION];
172 	fn(buf, sizeof(ctx->pdu_buf), res, uid);
173 
174 	rmsg.ctx = ctx;
175 	rmsg.msg = buf;
176 	rmsg.msg_len = strlen(buf);
177 
178 	ret = ocpp_send_to_server(&rmsg, K_SECONDS(OCPP_PDU_TIMEOUT));
179 
180 	return ret;
181 }
182 
ocpp_authorize(ocpp_session_handle_t hndl,char * idtag,enum ocpp_auth_status * status,uint32_t timeout_ms)183 int ocpp_authorize(ocpp_session_handle_t hndl, char *idtag,
184 		   enum ocpp_auth_status *status,
185 		   uint32_t timeout_ms)
186 {
187 	int ret;
188 	struct ocpp_session *sh = (struct ocpp_session *)hndl;
189 	struct ocpp_info *ctx;
190 	struct ocpp_upstream_info *ui;
191 	char buf[OCPP_USER_REQ_PDU_BUF];
192 	union ocpp_keyval *val;
193 	struct ocpp_wamp_rpc_msg rmsg = {0};
194 	ocpp_msg_fp_t fn;
195 
196 	if (idtag == NULL || status == NULL || !ocpp_session_is_valid(sh)) {
197 		return -EINVAL;
198 	}
199 
200 	ctx = sh->ctx;
201 	if (ctx->state < CP_STATE_READY) {
202 		return -EAGAIN;
203 	}
204 
205 	if (ctx->is_cs_offline) {
206 		val = ocpp_get_key_val(CFG_LOCAL_AUTH_OFFLINE);
207 		if (val != NULL && val->ival == 0) {
208 			return -EAGAIN;
209 		}
210 	}
211 
212 	strncpy(sh->idtag, idtag, sizeof(sh->idtag));
213 	ui = &ctx->ui;
214 	fn = ctx->cfn[PDU_AUTHORIZE];
215 	sh->uid = fn(buf, sizeof(buf), sh);
216 
217 	rmsg.ctx = ctx;
218 	rmsg.msg = buf;
219 	rmsg.msg_len = strlen(buf);
220 	rmsg.sndlock = &ui->ws_sndlock;
221 	rmsg.rspsig = &ui->ws_rspsig;
222 	ret = ocpp_send_to_server(&rmsg, K_MSEC(timeout_ms));
223 	if (ret < 0) {
224 		return ret;
225 	}
226 
227 	*status = sh->resp_status;
228 	if (sh->resp_status == OCPP_AUTH_ACCEPTED) {
229 		sh->is_active = true;
230 	}
231 
232 	return 0;
233 }
234 
ocpp_heartbeat(ocpp_session_handle_t hndl)235 int ocpp_heartbeat(ocpp_session_handle_t hndl)
236 {
237 	int ret;
238 	struct ocpp_session *sh = (struct ocpp_session *)hndl;
239 	struct ocpp_info *ctx = sh->ctx;
240 	struct ocpp_upstream_info *ui = &ctx->ui;
241 	ocpp_msg_fp_t fn;
242 	struct ocpp_wamp_rpc_msg rmsg = {0};
243 	char *buf = ctx->pdu_buf;
244 
245 	fn = ctx->cfn[PDU_HEARTBEAT];
246 	sh->uid = fn(buf, sizeof(ctx->pdu_buf), sh);
247 
248 	rmsg.ctx = ctx;
249 	rmsg.msg = buf;
250 	rmsg.msg_len = strlen(buf);
251 	rmsg.sndlock = &ui->ws_sndlock;
252 	rmsg.rspsig = &ui->ws_rspsig;
253 
254 	ret = ocpp_send_to_server(&rmsg, K_SECONDS(OCPP_PDU_TIMEOUT));
255 	if (ret == 0 && sh->resp_status != BOOT_ACCEPTED) {
256 		ret = -EAGAIN;
257 	}
258 
259 	return ret;
260 }
261 
ocpp_start_transaction(ocpp_session_handle_t hndl,int meter_val,uint8_t conn_id,uint32_t timeout_ms)262 int ocpp_start_transaction(ocpp_session_handle_t hndl,
263 			   int meter_val,
264 			   uint8_t conn_id,
265 			   uint32_t timeout_ms)
266 {
267 	struct ocpp_session *sh = (struct ocpp_session *)hndl;
268 	struct ocpp_info *ctx;
269 	struct ocpp_upstream_info *ui;
270 	char buf[OCPP_USER_REQ_PDU_BUF];
271 	struct ocpp_wamp_rpc_msg rmsg = {0};
272 	ocpp_msg_fp_t fn;
273 	char utc[CISTR25] = {0};
274 	atomic_val_t ref;
275 	int ret;
276 
277 	if (!ocpp_session_is_valid(sh) ||
278 	    conn_id <= 0) {
279 		return -EINVAL;
280 	}
281 	ctx = sh->ctx;
282 	sh->idcon = conn_id;
283 	if (ctx->state < CP_STATE_READY) {
284 		return -EAGAIN;
285 	}
286 
287 	if (ctx->is_cs_offline) {
288 		/* todo: fill meter reading
289 		 * server offline, accept start txn and save it in Q.
290 		 */
291 		return 0;
292 	}
293 
294 	fn = ctx->cfn[PDU_START_TRANSACTION];
295 	ocpp_get_utc_now(utc);
296 	sh->uid = fn(buf, sizeof(buf), sh, meter_val, -1, utc);
297 
298 	ui = &ctx->ui;
299 	rmsg.ctx = ctx;
300 	rmsg.msg = buf;
301 	rmsg.msg_len = strlen(buf);
302 	rmsg.sndlock = &ui->ws_sndlock;
303 	rmsg.rspsig = &ui->ws_rspsig;
304 	ret = ocpp_send_to_server(&rmsg, K_MSEC(timeout_ms));
305 	if (ret < 0) {
306 		return ret;
307 	}
308 
309 	if (sh->resp_status != OCPP_AUTH_ACCEPTED) {
310 		sh->is_active = false;
311 		ret = -EACCES;
312 	} else {
313 		union ocpp_keyval *keyval;
314 
315 		keyval = ocpp_get_key_val(CFG_MTR_VAL_SAMPLE_INTERVAL);
316 		do {
317 			ref = atomic_get(&ctx->mtr_timer_ref_cnt);
318 			if (ref == 0) {
319 				k_timer_start(&ctx->mtr_timer,
320 					      K_SECONDS(keyval->ival),
321 					      K_SECONDS(keyval->ival));
322 			}
323 		} while (!atomic_cas(&ctx->mtr_timer_ref_cnt, ref, ref + 1));
324 	}
325 
326 	return ret;
327 }
328 
ocpp_stop_transaction(ocpp_session_handle_t hndl,int meter_val,uint32_t timeout_ms)329 int ocpp_stop_transaction(ocpp_session_handle_t hndl,
330 			  int meter_val,
331 			  uint32_t timeout_ms)
332 {
333 	struct ocpp_session *sh = (struct ocpp_session *)hndl;
334 	struct ocpp_info *ctx;
335 	struct ocpp_upstream_info *ui;
336 	char buf[OCPP_USER_REQ_PDU_BUF];
337 	char utc[CISTR25] = {0};
338 	struct ocpp_wamp_rpc_msg rmsg = {0};
339 	ocpp_msg_fp_t fn;
340 	atomic_val_t ref;
341 	int ret;
342 
343 	if (!ocpp_session_is_valid(sh)) {
344 		return -EINVAL;
345 	}
346 
347 	ctx = sh->ctx;
348 	if (ctx->state < CP_STATE_READY) {
349 		return -EAGAIN;
350 	}
351 
352 	sh->is_active = false;
353 	do {
354 		ref = atomic_get(&ctx->mtr_timer_ref_cnt);
355 		if (ref == 0) {
356 			k_timer_stop(&ctx->mtr_timer);
357 			break;
358 		}
359 	} while (!atomic_cas(&ctx->mtr_timer_ref_cnt, ref, ref - 1));
360 
361 	if (ctx->is_cs_offline) {
362 		/* todo: fill meter reading
363 		 * server offline, accept start txn and save it in Q.
364 		 */
365 		return 0;
366 	}
367 
368 	fn = ctx->cfn[PDU_STOP_TRANSACTION];
369 	ocpp_get_utc_now(utc);
370 	sh->uid = fn(buf, sizeof(buf), sh, meter_val, NULL, utc);
371 
372 	ui = &ctx->ui;
373 	rmsg.ctx = ctx;
374 	rmsg.msg = buf;
375 	rmsg.msg_len = strlen(buf);
376 	rmsg.sndlock = &ui->ws_sndlock;
377 	rmsg.rspsig = &ui->ws_rspsig;
378 	ret = ocpp_send_to_server(&rmsg, K_MSEC(timeout_ms));
379 
380 	return ret;
381 }
382 
ocpp_remote_start_transaction(struct ocpp_info * ctx,struct internal_msg * msg,char * uid)383 int ocpp_remote_start_transaction(struct ocpp_info *ctx,
384 				  struct internal_msg *msg,
385 				  char *uid)
386 {
387 	char *buf = ctx->pdu_buf;
388 	const char *resp = "Rejected";
389 	struct ocpp_wamp_rpc_msg rmsg = {0};
390 	ocpp_msg_fp_t fn;
391 	int ret;
392 
393 	ret = k_msgq_put(ctx->msgq, msg, K_MSEC(100));
394 	if (ret == 0) {
395 		resp = "Accepted";
396 	}
397 
398 	fn = ctx->cfn[PDU_REMOTE_START_TRANSACTION];
399 	fn(buf, sizeof(ctx->pdu_buf), resp, uid);
400 
401 	rmsg.ctx = ctx;
402 	rmsg.msg = buf;
403 	rmsg.msg_len = strlen(buf);
404 	ret = ocpp_send_to_server(&rmsg, K_SECONDS(OCPP_PDU_TIMEOUT));
405 
406 	return ret;
407 }
408 
ocpp_remote_stop_transaction(struct ocpp_info * ctx,struct internal_msg * msg,int idtxn,char * uid)409 int ocpp_remote_stop_transaction(struct ocpp_info *ctx,
410 				 struct internal_msg *msg,
411 				 int idtxn, char *uid)
412 {
413 	char *buf = ctx->pdu_buf;
414 	const char *resp = "Rejected";
415 	struct ocpp_wamp_rpc_msg rmsg = {0};
416 	ocpp_msg_fp_t fn;
417 	sys_snode_t *node;
418 	struct ocpp_session *sh;
419 	bool is_found = false;
420 	int ret;
421 
422 	k_mutex_lock(&ctx->ilock, K_FOREVER);
423 	SYS_SLIST_FOR_EACH_NODE(&ctx->slist, node) {
424 		sh = to_session(node);
425 		if (sh->is_active &&
426 		    sh->idtxn == idtxn) {
427 			is_found = true;
428 			break;
429 		}
430 	}
431 	k_mutex_unlock(&ctx->ilock);
432 
433 	if (is_found) {
434 		msg->usr.stop_charge.id_con = sh->idcon;
435 		ret = k_msgq_put(ctx->msgq, msg, K_MSEC(100));
436 		if (ret == 0) {
437 			resp = "Accepted";
438 		}
439 	}
440 
441 	fn = ctx->cfn[PDU_REMOTE_STOP_TRANSACTION];
442 	fn(buf, sizeof(ctx->pdu_buf), resp, uid);
443 
444 	rmsg.ctx = ctx;
445 	rmsg.msg = buf;
446 	rmsg.msg_len = strlen(buf);
447 	ret = ocpp_send_to_server(&rmsg, K_SECONDS(OCPP_PDU_TIMEOUT));
448 
449 	return ret;
450 }
451 
ocpp_unlock_connector(struct ocpp_info * ctx,struct internal_msg * msg,char * uid)452 int ocpp_unlock_connector(struct ocpp_info *ctx,
453 			  struct internal_msg *msg,
454 			  char *uid)
455 {
456 	char *buf = ctx->pdu_buf;
457 	char *resp = "UnlockFailed";
458 	struct ocpp_wamp_rpc_msg rmsg = {0};
459 	ocpp_msg_fp_t fn;
460 	int ret;
461 
462 	ret = k_msgq_put(ctx->msgq, msg, K_MSEC(100));
463 	if (ret == 0) {
464 		resp = "Unlocked";
465 	}
466 
467 	fn = ctx->cfn[PDU_UNLOCK_CONNECTOR];
468 	fn(buf, sizeof(ctx->pdu_buf), resp, uid);
469 
470 	rmsg.ctx = ctx;
471 	rmsg.msg = buf;
472 	rmsg.msg_len = strlen(buf);
473 	ret = ocpp_send_to_server(&rmsg, K_SECONDS(OCPP_PDU_TIMEOUT));
474 
475 	return ret;
476 }
477 
ocpp_meter_values(ocpp_session_handle_t hndl,enum ocpp_meter_measurand mes,char * sval)478 int ocpp_meter_values(ocpp_session_handle_t hndl,
479 		      enum ocpp_meter_measurand mes,
480 		      char *sval)
481 {
482 	struct ocpp_session *sh = (struct ocpp_session *)hndl;
483 	struct ocpp_info *ctx = sh->ctx;
484 	struct ocpp_upstream_info *ui = &ctx->ui;
485 	char *buf = ctx->pdu_buf;
486 	char utc[CISTR25] = {0};
487 	struct ocpp_wamp_rpc_msg rmsg = {0};
488 	ocpp_msg_fp_t fn;
489 	int ret;
490 
491 	if (ctx->is_cs_offline) {
492 		return -EAGAIN;
493 	}
494 
495 	fn = ctx->cfn[PDU_METER_VALUES];
496 	ocpp_get_utc_now(utc);
497 	sh->uid = fn(buf, sizeof(ctx->pdu_buf), sh, utc, sval,
498 			mtr_ref_table[mes].smes,
499 			mtr_ref_table[mes].unit);
500 
501 	rmsg.ctx = ctx;
502 	rmsg.msg = buf;
503 	rmsg.msg_len = strlen(buf);
504 	rmsg.sndlock = &ui->ws_sndlock;
505 	rmsg.rspsig = &ui->ws_rspsig;
506 	ret = ocpp_send_to_server(&rmsg, K_SECONDS(OCPP_PDU_TIMEOUT));
507 
508 	return ret;
509 }
510