ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
openadr.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file openadr.cpp
6 * @brief OpenADR 3.0 JSON codec (see openadr.h).
7 */
8
10#include "shared_primitives/strbuf.h" // pc_sb frame builder
11
12#if PC_ENABLE_OPENADR
13
14#include <string.h>
15
16namespace
17{
18void put_json_str(pc_sb *b, const char *s)
19{
20 pc_sb_put(b, "\"");
21 for (const char *p = s ? s : ""; *p; p++)
22 {
23 if (*p == '"' || *p == '\\')
24 {
25 char esc[3] = {'\\', *p, '\0'};
26 pc_sb_put(b, esc);
27 }
28 else if (*p == '\n')
29 {
30 pc_sb_put(b, "\\n");
31 }
32 else
33 {
34 if (b->len + 1 >= b->cap)
35 {
36 b->ok = false;
37 return;
38 }
39 b->p[b->len++] = *p;
40 }
41 }
42 pc_sb_put(b, "\"");
43}
44
45void put_u64(pc_sb *b, uint64_t v)
46{
47 char tmp[21];
48 int n = 0;
49 do
50 {
51 tmp[n++] = (char)('0' + (int)(v % 10));
52 v /= 10;
53 } while (v);
54 char out[22];
55 for (int i = 0; i < n; i++)
56 {
57 out[i] = tmp[n - 1 - i];
58 }
59 out[n] = '\0';
60 pc_sb_put(b, out);
61}
62
63// Format a double with 3 decimal places (no stdlib). Rounds to milli-units; handles the sign.
64void put_double(pc_sb *b, double v)
65{
66 if (v < 0)
67 {
68 pc_sb_put(b, "-");
69 v = -v;
70 }
71 // scale by 1000 and round.
72 uint64_t scaled = (uint64_t)(v * 1000.0 + 0.5);
73 uint64_t whole = scaled / 1000;
74 uint32_t frac = (uint32_t)(scaled % 1000);
75 put_u64(b, whole);
76 pc_sb_put(b, ".");
77 // three digits, zero-padded.
78 char f[4] = {(char)('0' + (frac / 100) % 10), (char)('0' + (frac / 10) % 10), (char)('0' + frac % 10), '\0'};
79 pc_sb_put(b, f);
80}
81} // namespace
82
83size_t pc_openadr_event(const char *program_id, const char *event_name, const OpenAdrInterval *intervals, size_t count,
84 char *out, size_t cap)
85{
86 if (!out || (count && !intervals))
87 {
88 return 0;
89 }
90 pc_sb b = {out, cap, 0, cap > 0};
91 pc_sb_put(&b, "{\"objectType\":\"EVENT\",\"programID\":");
92 put_json_str(&b, program_id);
93 pc_sb_put(&b, ",\"eventName\":");
94 put_json_str(&b, event_name);
95 pc_sb_put(&b, ",\"intervals\":[");
96 for (size_t i = 0; i < count; i++)
97 {
98 if (i)
99 {
100 pc_sb_put(&b, ",");
101 }
102 pc_sb_put(&b, "{\"id\":");
103 put_u64(&b, i);
104 pc_sb_put(&b, ",\"interval\":{\"start\":");
105 put_u64(&b, intervals[i].start);
106 pc_sb_put(&b, ",\"duration\":");
107 put_u64(&b, intervals[i].duration);
108 pc_sb_put(&b, "},\"payloads\":[{\"type\":");
109 put_json_str(&b, intervals[i].type);
110 pc_sb_put(&b, ",\"values\":[");
111 put_double(&b, intervals[i].value);
112 pc_sb_put(&b, "]}]}");
113 }
114 pc_sb_put(&b, "]}");
115 return pc_sb_finish(&b);
116}
117
118size_t pc_openadr_report(const char *program_id, const char *event_id, const char *resource_name, double value,
119 uint32_t timestamp, char *out, size_t cap)
120{
121 if (!out)
122 {
123 return 0;
124 }
125 pc_sb b2 = {out, cap, 0, cap > 0};
126 pc_sb_put(&b2, "{\"objectType\":\"REPORT\",\"programID\":");
127 put_json_str(&b2, program_id);
128 pc_sb_put(&b2, ",\"eventID\":");
129 put_json_str(&b2, event_id);
130 pc_sb_put(&b2, ",\"resources\":[{\"resourceName\":");
131 put_json_str(&b2, resource_name);
132 pc_sb_put(&b2, ",\"intervals\":[{\"interval\":{\"start\":");
133 put_u64(&b2, timestamp);
134 pc_sb_put(&b2, "},\"payloads\":[{\"type\":\"READING\",\"values\":[");
135 put_double(&b2, value);
136 pc_sb_put(&b2, "]}]}]}]}");
137 return pc_sb_finish(&b2);
138}
139
140#endif // PC_ENABLE_OPENADR
OpenADR 3.0 (Open Automated Demand Response) JSON codec (PC_ENABLE_OPENADR).
Bounded no-heap string builder that fails closed on overflow (one shared copy).
size_t pc_sb_finish(pc_sb *b)
NUL-terminate and return the built length, or 0 if the build overflowed.
Definition strbuf.h:648
void pc_sb_put(pc_sb *b, const char *s)
Append NUL-terminated s; leaves the buffer untouched and clears ok if it would not fit.
Definition strbuf.h:60
Bump-append target; ok latches false once an append would overflow cap.
Definition strbuf.h:30
char * p
Definition strbuf.h:31
size_t cap
Definition strbuf.h:32
size_t len
Definition strbuf.h:33
bool ok
Definition strbuf.h:34