Vulcan
Aerospace Engineering Utilities Built on Janus
Loading...
Searching...
No Matches
TelemetrySchema.hpp
Go to the documentation of this file.
1
9
10#pragma once
11
12#include <cstddef>
13#include <cstdint>
14#include <stdexcept>
15#include <string>
16#include <unordered_map>
17#include <vector>
18
20#include <vulcan/io/Signal.hpp>
21
22namespace vulcan::io {
23
42 public:
43 TelemetrySchema() = default;
44
45 // =========================================================================
46 // Core signal registration (scalar only)
47 // =========================================================================
48
57 add_double(const std::string &name,
59 const std::string &unit = "") {
60 add_signal({name, SignalType::Double, lifecycle, unit, "", 0});
61 return *this;
62 }
63
72 add_int32(const std::string &name,
74 const std::string &semantic = "") {
75 add_signal({name, SignalType::Int32, lifecycle, "", semantic, 0});
76 return *this;
77 }
78
86 add_int64(const std::string &name,
88 add_signal({name, SignalType::Int64, lifecycle, "", "", 0});
89 return *this;
90 }
91
92 // =========================================================================
93 // Convenience: vector expansion
94 // =========================================================================
95
104 add_vec3(const std::string &name,
106 const std::string &unit = "") {
107 add_double(name + ".x", lifecycle, unit);
108 add_double(name + ".y", lifecycle, unit);
109 add_double(name + ".z", lifecycle, unit);
110 return *this;
111 }
112
120 add_quat(const std::string &name,
122 add_double(name + ".w", lifecycle);
123 add_double(name + ".x", lifecycle);
124 add_double(name + ".y", lifecycle);
125 add_double(name + ".z", lifecycle);
126 return *this;
127 }
128
129 // =========================================================================
130 // Access
131 // =========================================================================
132
134 const std::vector<SignalDescriptor> &signals() const { return signals_; }
135
137 std::vector<SignalDescriptor> dynamic_signals() const {
138 std::vector<SignalDescriptor> result;
139 for (const auto &s : signals_) {
140 if (s.lifecycle == SignalLifecycle::Dynamic) {
141 result.push_back(s);
142 }
143 }
144 return result;
145 }
146
148 std::vector<SignalDescriptor> static_signals() const {
149 std::vector<SignalDescriptor> result;
150 for (const auto &s : signals_) {
151 if (s.lifecycle == SignalLifecycle::Static) {
152 result.push_back(s);
153 }
154 }
155 return result;
156 }
157
159 const SignalDescriptor &signal(const std::string &name) const {
160 auto it = index_.find(name);
161 if (it == index_.end()) {
162 throw vulcan::SignalError("Signal not found: " + name);
163 }
164 return signals_[it->second];
165 }
166
168 bool has_signal(const std::string &name) const {
169 return index_.find(name) != index_.end();
170 }
171
173 size_t offset(const std::string &name) const { return signal(name).offset; }
174
176 size_t index(const std::string &name) const {
177 auto it = index_.find(name);
178 if (it == index_.end()) {
179 throw vulcan::SignalError("Signal not found: " + name);
180 }
181 return it->second;
182 }
183
185 size_t frame_size_bytes() const { return next_offset_; }
186
189 size_t size = 0;
190 for (const auto &s : signals_) {
191 if (s.lifecycle == SignalLifecycle::Dynamic) {
192 size += s.size_bytes();
193 }
194 }
195 return size;
196 }
197
199 size_t static_frame_size_bytes() const {
200 size_t size = 0;
201 for (const auto &s : signals_) {
202 if (s.lifecycle == SignalLifecycle::Static) {
203 size += s.size_bytes();
204 }
205 }
206 return size;
207 }
208
210 size_t signal_count() const { return signals_.size(); }
211
213 std::vector<std::string> signal_names() const {
214 std::vector<std::string> names;
215 names.reserve(signals_.size());
216 for (const auto &s : signals_) {
217 names.push_back(s.name);
218 }
219 return names;
220 }
221
222 // =========================================================================
223 // Validation
224 // =========================================================================
225
227 void validate() const {
228 // Check for empty schema
229 if (signals_.empty()) {
230 throw vulcan::SignalError("Schema has no signals");
231 }
232 // Duplicate names are already caught during add_signal
233 }
234
235 // =========================================================================
236 // JSON serialization
237 // =========================================================================
238
240 std::string to_json() const {
241 std::string json = "{\n \"signals\": [\n";
242 for (size_t i = 0; i < signals_.size(); ++i) {
243 const auto &s = signals_[i];
244 json += " {\n";
245 json += " \"name\": \"" + s.name + "\",\n";
246 json += " \"type\": \"" + type_to_string(s.type) + "\",\n";
247 json += " \"lifecycle\": \"" +
248 lifecycle_to_string(s.lifecycle) + "\",\n";
249 json += " \"offset\": " + std::to_string(s.offset);
250 if (!s.unit.empty()) {
251 json += ",\n \"unit\": \"" + s.unit + "\"";
252 }
253 if (!s.semantic.empty()) {
254 json += ",\n \"semantic\": \"" + s.semantic + "\"";
255 }
256 json += "\n }";
257 if (i + 1 < signals_.size()) {
258 json += ",";
259 }
260 json += "\n";
261 }
262 json += " ],\n";
263 json +=
264 " \"frame_size_bytes\": " + std::to_string(next_offset_) + "\n";
265 json += "}";
266 return json;
267 }
268
270 static TelemetrySchema from_json(const std::string &json);
271
272 private:
273 std::vector<SignalDescriptor> signals_;
274 std::unordered_map<std::string, size_t> index_;
275 size_t next_offset_ = 0;
276
277 void add_signal(SignalDescriptor desc) {
278 if (index_.find(desc.name) != index_.end()) {
279 throw vulcan::SignalError("Duplicate signal name: " + desc.name);
280 }
281 desc.offset = next_offset_;
282 next_offset_ += 8; // All signals are 8-byte aligned
283 index_[desc.name] = signals_.size();
284 signals_.push_back(std::move(desc));
285 }
286
287 static std::string type_to_string(SignalType type) {
288 switch (type) {
290 return "double";
292 return "int32";
294 return "int64";
295 }
296 return "unknown";
297 }
298
299 static std::string lifecycle_to_string(SignalLifecycle lc) {
300 switch (lc) {
302 return "static";
304 return "dynamic";
305 }
306 return "unknown";
307 }
308};
309
310} // namespace vulcan::io
Core signal types for telemetry and streaming.
Exception hierarchy for Vulcan aerospace library.
Signal schema and frame errors.
Definition VulcanError.hpp:35
Telemetry schema builder and container.
Definition TelemetrySchema.hpp:41
size_t offset(const std::string &name) const
Get offset for signal.
Definition TelemetrySchema.hpp:173
std::vector< std::string > signal_names() const
Get all signal names.
Definition TelemetrySchema.hpp:213
bool has_signal(const std::string &name) const
Check if signal exists.
Definition TelemetrySchema.hpp:168
static TelemetrySchema from_json(const std::string &json)
Deserialize from JSON string (basic parser).
TelemetrySchema & add_int32(const std::string &name, SignalLifecycle lifecycle=SignalLifecycle::Dynamic, const std::string &semantic="")
Add an int32 signal.
Definition TelemetrySchema.hpp:72
size_t dynamic_frame_size_bytes() const
Dynamic signals frame size in bytes (for streaming).
Definition TelemetrySchema.hpp:188
TelemetrySchema & add_int64(const std::string &name, SignalLifecycle lifecycle=SignalLifecycle::Dynamic)
Add an int64 signal.
Definition TelemetrySchema.hpp:86
size_t signal_count() const
Number of signals.
Definition TelemetrySchema.hpp:210
std::vector< SignalDescriptor > static_signals() const
Get only static signals.
Definition TelemetrySchema.hpp:148
size_t index(const std::string &name) const
Get index for signal.
Definition TelemetrySchema.hpp:176
const std::vector< SignalDescriptor > & signals() const
Get all signals.
Definition TelemetrySchema.hpp:134
size_t static_frame_size_bytes() const
Static signals frame size in bytes.
Definition TelemetrySchema.hpp:199
TelemetrySchema & add_double(const std::string &name, SignalLifecycle lifecycle=SignalLifecycle::Dynamic, const std::string &unit="")
Add a double signal.
Definition TelemetrySchema.hpp:57
void validate() const
Validate schema (throws on errors).
Definition TelemetrySchema.hpp:227
std::string to_json() const
Serialize to JSON string.
Definition TelemetrySchema.hpp:240
TelemetrySchema & add_vec3(const std::string &name, SignalLifecycle lifecycle=SignalLifecycle::Dynamic, const std::string &unit="")
Add a 3-component vector (expands to name.x, name.y, name.z).
Definition TelemetrySchema.hpp:104
const SignalDescriptor & signal(const std::string &name) const
Get signal by name.
Definition TelemetrySchema.hpp:159
TelemetrySchema & add_quat(const std::string &name, SignalLifecycle lifecycle=SignalLifecycle::Dynamic)
Add a quaternion (expands to name.w, name.x, name.y, name.z).
Definition TelemetrySchema.hpp:120
std::vector< SignalDescriptor > dynamic_signals() const
Get only dynamic signals.
Definition TelemetrySchema.hpp:137
size_t frame_size_bytes() const
Total frame size in bytes (all signals).
Definition TelemetrySchema.hpp:185
Definition CSVExport.hpp:20
SignalType
Signal data type (all scalar, 8-byte aligned slots).
Definition Signal.hpp:18
@ Int32
4 bytes + 4 padding - modes, phases, counters
Definition Signal.hpp:20
@ Double
8 bytes - physical quantities
Definition Signal.hpp:19
@ Int64
8 bytes - timestamps, large counters
Definition Signal.hpp:21
SignalLifecycle
Signal lifecycle for telemetry optimization.
Definition Signal.hpp:25
@ Static
Constant after init, sent in schema handshake only.
Definition Signal.hpp:26
@ Dynamic
Updated each step, included in streaming telemetry.
Definition Signal.hpp:27
Individual signal descriptor.
Definition Signal.hpp:31
std::string name
Signal name (e.g., "position.x").
Definition Signal.hpp:32
size_t offset
Byte offset in frame buffer.
Definition Signal.hpp:37