oscura 0.0.1__py3-none-any.whl → 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (465) hide show
  1. oscura/__init__.py +813 -8
  2. oscura/__main__.py +392 -0
  3. oscura/analyzers/__init__.py +37 -0
  4. oscura/analyzers/digital/__init__.py +177 -0
  5. oscura/analyzers/digital/bus.py +691 -0
  6. oscura/analyzers/digital/clock.py +805 -0
  7. oscura/analyzers/digital/correlation.py +720 -0
  8. oscura/analyzers/digital/edges.py +632 -0
  9. oscura/analyzers/digital/extraction.py +413 -0
  10. oscura/analyzers/digital/quality.py +878 -0
  11. oscura/analyzers/digital/signal_quality.py +877 -0
  12. oscura/analyzers/digital/thresholds.py +708 -0
  13. oscura/analyzers/digital/timing.py +1104 -0
  14. oscura/analyzers/eye/__init__.py +46 -0
  15. oscura/analyzers/eye/diagram.py +434 -0
  16. oscura/analyzers/eye/metrics.py +555 -0
  17. oscura/analyzers/jitter/__init__.py +83 -0
  18. oscura/analyzers/jitter/ber.py +333 -0
  19. oscura/analyzers/jitter/decomposition.py +759 -0
  20. oscura/analyzers/jitter/measurements.py +413 -0
  21. oscura/analyzers/jitter/spectrum.py +220 -0
  22. oscura/analyzers/measurements.py +40 -0
  23. oscura/analyzers/packet/__init__.py +171 -0
  24. oscura/analyzers/packet/daq.py +1077 -0
  25. oscura/analyzers/packet/metrics.py +437 -0
  26. oscura/analyzers/packet/parser.py +327 -0
  27. oscura/analyzers/packet/payload.py +2156 -0
  28. oscura/analyzers/packet/payload_analysis.py +1312 -0
  29. oscura/analyzers/packet/payload_extraction.py +236 -0
  30. oscura/analyzers/packet/payload_patterns.py +670 -0
  31. oscura/analyzers/packet/stream.py +359 -0
  32. oscura/analyzers/patterns/__init__.py +266 -0
  33. oscura/analyzers/patterns/clustering.py +1036 -0
  34. oscura/analyzers/patterns/discovery.py +539 -0
  35. oscura/analyzers/patterns/learning.py +797 -0
  36. oscura/analyzers/patterns/matching.py +1091 -0
  37. oscura/analyzers/patterns/periodic.py +650 -0
  38. oscura/analyzers/patterns/sequences.py +767 -0
  39. oscura/analyzers/power/__init__.py +116 -0
  40. oscura/analyzers/power/ac_power.py +391 -0
  41. oscura/analyzers/power/basic.py +383 -0
  42. oscura/analyzers/power/conduction.py +314 -0
  43. oscura/analyzers/power/efficiency.py +297 -0
  44. oscura/analyzers/power/ripple.py +356 -0
  45. oscura/analyzers/power/soa.py +372 -0
  46. oscura/analyzers/power/switching.py +479 -0
  47. oscura/analyzers/protocol/__init__.py +150 -0
  48. oscura/analyzers/protocols/__init__.py +150 -0
  49. oscura/analyzers/protocols/base.py +500 -0
  50. oscura/analyzers/protocols/can.py +620 -0
  51. oscura/analyzers/protocols/can_fd.py +448 -0
  52. oscura/analyzers/protocols/flexray.py +405 -0
  53. oscura/analyzers/protocols/hdlc.py +399 -0
  54. oscura/analyzers/protocols/i2c.py +368 -0
  55. oscura/analyzers/protocols/i2s.py +296 -0
  56. oscura/analyzers/protocols/jtag.py +393 -0
  57. oscura/analyzers/protocols/lin.py +445 -0
  58. oscura/analyzers/protocols/manchester.py +333 -0
  59. oscura/analyzers/protocols/onewire.py +501 -0
  60. oscura/analyzers/protocols/spi.py +334 -0
  61. oscura/analyzers/protocols/swd.py +325 -0
  62. oscura/analyzers/protocols/uart.py +393 -0
  63. oscura/analyzers/protocols/usb.py +495 -0
  64. oscura/analyzers/signal_integrity/__init__.py +63 -0
  65. oscura/analyzers/signal_integrity/embedding.py +294 -0
  66. oscura/analyzers/signal_integrity/equalization.py +370 -0
  67. oscura/analyzers/signal_integrity/sparams.py +484 -0
  68. oscura/analyzers/spectral/__init__.py +53 -0
  69. oscura/analyzers/spectral/chunked.py +273 -0
  70. oscura/analyzers/spectral/chunked_fft.py +571 -0
  71. oscura/analyzers/spectral/chunked_wavelet.py +391 -0
  72. oscura/analyzers/spectral/fft.py +92 -0
  73. oscura/analyzers/statistical/__init__.py +250 -0
  74. oscura/analyzers/statistical/checksum.py +923 -0
  75. oscura/analyzers/statistical/chunked_corr.py +228 -0
  76. oscura/analyzers/statistical/classification.py +778 -0
  77. oscura/analyzers/statistical/entropy.py +1113 -0
  78. oscura/analyzers/statistical/ngrams.py +614 -0
  79. oscura/analyzers/statistics/__init__.py +119 -0
  80. oscura/analyzers/statistics/advanced.py +885 -0
  81. oscura/analyzers/statistics/basic.py +263 -0
  82. oscura/analyzers/statistics/correlation.py +630 -0
  83. oscura/analyzers/statistics/distribution.py +298 -0
  84. oscura/analyzers/statistics/outliers.py +463 -0
  85. oscura/analyzers/statistics/streaming.py +93 -0
  86. oscura/analyzers/statistics/trend.py +520 -0
  87. oscura/analyzers/validation.py +598 -0
  88. oscura/analyzers/waveform/__init__.py +36 -0
  89. oscura/analyzers/waveform/measurements.py +943 -0
  90. oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
  91. oscura/analyzers/waveform/spectral.py +1689 -0
  92. oscura/analyzers/waveform/wavelets.py +298 -0
  93. oscura/api/__init__.py +62 -0
  94. oscura/api/dsl.py +538 -0
  95. oscura/api/fluent.py +571 -0
  96. oscura/api/operators.py +498 -0
  97. oscura/api/optimization.py +392 -0
  98. oscura/api/profiling.py +396 -0
  99. oscura/automotive/__init__.py +73 -0
  100. oscura/automotive/can/__init__.py +52 -0
  101. oscura/automotive/can/analysis.py +356 -0
  102. oscura/automotive/can/checksum.py +250 -0
  103. oscura/automotive/can/correlation.py +212 -0
  104. oscura/automotive/can/discovery.py +355 -0
  105. oscura/automotive/can/message_wrapper.py +375 -0
  106. oscura/automotive/can/models.py +385 -0
  107. oscura/automotive/can/patterns.py +381 -0
  108. oscura/automotive/can/session.py +452 -0
  109. oscura/automotive/can/state_machine.py +300 -0
  110. oscura/automotive/can/stimulus_response.py +461 -0
  111. oscura/automotive/dbc/__init__.py +15 -0
  112. oscura/automotive/dbc/generator.py +156 -0
  113. oscura/automotive/dbc/parser.py +146 -0
  114. oscura/automotive/dtc/__init__.py +30 -0
  115. oscura/automotive/dtc/database.py +3036 -0
  116. oscura/automotive/j1939/__init__.py +14 -0
  117. oscura/automotive/j1939/decoder.py +745 -0
  118. oscura/automotive/loaders/__init__.py +35 -0
  119. oscura/automotive/loaders/asc.py +98 -0
  120. oscura/automotive/loaders/blf.py +77 -0
  121. oscura/automotive/loaders/csv_can.py +136 -0
  122. oscura/automotive/loaders/dispatcher.py +136 -0
  123. oscura/automotive/loaders/mdf.py +331 -0
  124. oscura/automotive/loaders/pcap.py +132 -0
  125. oscura/automotive/obd/__init__.py +14 -0
  126. oscura/automotive/obd/decoder.py +707 -0
  127. oscura/automotive/uds/__init__.py +48 -0
  128. oscura/automotive/uds/decoder.py +265 -0
  129. oscura/automotive/uds/models.py +64 -0
  130. oscura/automotive/visualization.py +369 -0
  131. oscura/batch/__init__.py +55 -0
  132. oscura/batch/advanced.py +627 -0
  133. oscura/batch/aggregate.py +300 -0
  134. oscura/batch/analyze.py +139 -0
  135. oscura/batch/logging.py +487 -0
  136. oscura/batch/metrics.py +556 -0
  137. oscura/builders/__init__.py +41 -0
  138. oscura/builders/signal_builder.py +1131 -0
  139. oscura/cli/__init__.py +14 -0
  140. oscura/cli/batch.py +339 -0
  141. oscura/cli/characterize.py +273 -0
  142. oscura/cli/compare.py +775 -0
  143. oscura/cli/decode.py +551 -0
  144. oscura/cli/main.py +247 -0
  145. oscura/cli/shell.py +350 -0
  146. oscura/comparison/__init__.py +66 -0
  147. oscura/comparison/compare.py +397 -0
  148. oscura/comparison/golden.py +487 -0
  149. oscura/comparison/limits.py +391 -0
  150. oscura/comparison/mask.py +434 -0
  151. oscura/comparison/trace_diff.py +30 -0
  152. oscura/comparison/visualization.py +481 -0
  153. oscura/compliance/__init__.py +70 -0
  154. oscura/compliance/advanced.py +756 -0
  155. oscura/compliance/masks.py +363 -0
  156. oscura/compliance/reporting.py +483 -0
  157. oscura/compliance/testing.py +298 -0
  158. oscura/component/__init__.py +38 -0
  159. oscura/component/impedance.py +365 -0
  160. oscura/component/reactive.py +598 -0
  161. oscura/component/transmission_line.py +312 -0
  162. oscura/config/__init__.py +191 -0
  163. oscura/config/defaults.py +254 -0
  164. oscura/config/loader.py +348 -0
  165. oscura/config/memory.py +271 -0
  166. oscura/config/migration.py +458 -0
  167. oscura/config/pipeline.py +1077 -0
  168. oscura/config/preferences.py +530 -0
  169. oscura/config/protocol.py +875 -0
  170. oscura/config/schema.py +713 -0
  171. oscura/config/settings.py +420 -0
  172. oscura/config/thresholds.py +599 -0
  173. oscura/convenience.py +457 -0
  174. oscura/core/__init__.py +299 -0
  175. oscura/core/audit.py +457 -0
  176. oscura/core/backend_selector.py +405 -0
  177. oscura/core/cache.py +590 -0
  178. oscura/core/cancellation.py +439 -0
  179. oscura/core/confidence.py +225 -0
  180. oscura/core/config.py +506 -0
  181. oscura/core/correlation.py +216 -0
  182. oscura/core/cross_domain.py +422 -0
  183. oscura/core/debug.py +301 -0
  184. oscura/core/edge_cases.py +541 -0
  185. oscura/core/exceptions.py +535 -0
  186. oscura/core/gpu_backend.py +523 -0
  187. oscura/core/lazy.py +832 -0
  188. oscura/core/log_query.py +540 -0
  189. oscura/core/logging.py +931 -0
  190. oscura/core/logging_advanced.py +952 -0
  191. oscura/core/memoize.py +171 -0
  192. oscura/core/memory_check.py +274 -0
  193. oscura/core/memory_guard.py +290 -0
  194. oscura/core/memory_limits.py +336 -0
  195. oscura/core/memory_monitor.py +453 -0
  196. oscura/core/memory_progress.py +465 -0
  197. oscura/core/memory_warnings.py +315 -0
  198. oscura/core/numba_backend.py +362 -0
  199. oscura/core/performance.py +352 -0
  200. oscura/core/progress.py +524 -0
  201. oscura/core/provenance.py +358 -0
  202. oscura/core/results.py +331 -0
  203. oscura/core/types.py +504 -0
  204. oscura/core/uncertainty.py +383 -0
  205. oscura/discovery/__init__.py +52 -0
  206. oscura/discovery/anomaly_detector.py +672 -0
  207. oscura/discovery/auto_decoder.py +415 -0
  208. oscura/discovery/comparison.py +497 -0
  209. oscura/discovery/quality_validator.py +528 -0
  210. oscura/discovery/signal_detector.py +769 -0
  211. oscura/dsl/__init__.py +73 -0
  212. oscura/dsl/commands.py +246 -0
  213. oscura/dsl/interpreter.py +455 -0
  214. oscura/dsl/parser.py +689 -0
  215. oscura/dsl/repl.py +172 -0
  216. oscura/exceptions.py +59 -0
  217. oscura/exploratory/__init__.py +111 -0
  218. oscura/exploratory/error_recovery.py +642 -0
  219. oscura/exploratory/fuzzy.py +513 -0
  220. oscura/exploratory/fuzzy_advanced.py +786 -0
  221. oscura/exploratory/legacy.py +831 -0
  222. oscura/exploratory/parse.py +358 -0
  223. oscura/exploratory/recovery.py +275 -0
  224. oscura/exploratory/sync.py +382 -0
  225. oscura/exploratory/unknown.py +707 -0
  226. oscura/export/__init__.py +25 -0
  227. oscura/export/wireshark/README.md +265 -0
  228. oscura/export/wireshark/__init__.py +47 -0
  229. oscura/export/wireshark/generator.py +312 -0
  230. oscura/export/wireshark/lua_builder.py +159 -0
  231. oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
  232. oscura/export/wireshark/type_mapping.py +165 -0
  233. oscura/export/wireshark/validator.py +105 -0
  234. oscura/exporters/__init__.py +94 -0
  235. oscura/exporters/csv.py +303 -0
  236. oscura/exporters/exporters.py +44 -0
  237. oscura/exporters/hdf5.py +219 -0
  238. oscura/exporters/html_export.py +701 -0
  239. oscura/exporters/json_export.py +291 -0
  240. oscura/exporters/markdown_export.py +367 -0
  241. oscura/exporters/matlab_export.py +354 -0
  242. oscura/exporters/npz_export.py +219 -0
  243. oscura/exporters/spice_export.py +210 -0
  244. oscura/extensibility/__init__.py +131 -0
  245. oscura/extensibility/docs.py +752 -0
  246. oscura/extensibility/extensions.py +1125 -0
  247. oscura/extensibility/logging.py +259 -0
  248. oscura/extensibility/measurements.py +485 -0
  249. oscura/extensibility/plugins.py +414 -0
  250. oscura/extensibility/registry.py +346 -0
  251. oscura/extensibility/templates.py +913 -0
  252. oscura/extensibility/validation.py +651 -0
  253. oscura/filtering/__init__.py +89 -0
  254. oscura/filtering/base.py +563 -0
  255. oscura/filtering/convenience.py +564 -0
  256. oscura/filtering/design.py +725 -0
  257. oscura/filtering/filters.py +32 -0
  258. oscura/filtering/introspection.py +605 -0
  259. oscura/guidance/__init__.py +24 -0
  260. oscura/guidance/recommender.py +429 -0
  261. oscura/guidance/wizard.py +518 -0
  262. oscura/inference/__init__.py +251 -0
  263. oscura/inference/active_learning/README.md +153 -0
  264. oscura/inference/active_learning/__init__.py +38 -0
  265. oscura/inference/active_learning/lstar.py +257 -0
  266. oscura/inference/active_learning/observation_table.py +230 -0
  267. oscura/inference/active_learning/oracle.py +78 -0
  268. oscura/inference/active_learning/teachers/__init__.py +15 -0
  269. oscura/inference/active_learning/teachers/simulator.py +192 -0
  270. oscura/inference/adaptive_tuning.py +453 -0
  271. oscura/inference/alignment.py +653 -0
  272. oscura/inference/bayesian.py +943 -0
  273. oscura/inference/binary.py +1016 -0
  274. oscura/inference/crc_reverse.py +711 -0
  275. oscura/inference/logic.py +288 -0
  276. oscura/inference/message_format.py +1305 -0
  277. oscura/inference/protocol.py +417 -0
  278. oscura/inference/protocol_dsl.py +1084 -0
  279. oscura/inference/protocol_library.py +1230 -0
  280. oscura/inference/sequences.py +809 -0
  281. oscura/inference/signal_intelligence.py +1509 -0
  282. oscura/inference/spectral.py +215 -0
  283. oscura/inference/state_machine.py +634 -0
  284. oscura/inference/stream.py +918 -0
  285. oscura/integrations/__init__.py +59 -0
  286. oscura/integrations/llm.py +1827 -0
  287. oscura/jupyter/__init__.py +32 -0
  288. oscura/jupyter/display.py +268 -0
  289. oscura/jupyter/magic.py +334 -0
  290. oscura/loaders/__init__.py +526 -0
  291. oscura/loaders/binary.py +69 -0
  292. oscura/loaders/configurable.py +1255 -0
  293. oscura/loaders/csv.py +26 -0
  294. oscura/loaders/csv_loader.py +473 -0
  295. oscura/loaders/hdf5.py +9 -0
  296. oscura/loaders/hdf5_loader.py +510 -0
  297. oscura/loaders/lazy.py +370 -0
  298. oscura/loaders/mmap_loader.py +583 -0
  299. oscura/loaders/numpy_loader.py +436 -0
  300. oscura/loaders/pcap.py +432 -0
  301. oscura/loaders/preprocessing.py +368 -0
  302. oscura/loaders/rigol.py +287 -0
  303. oscura/loaders/sigrok.py +321 -0
  304. oscura/loaders/tdms.py +367 -0
  305. oscura/loaders/tektronix.py +711 -0
  306. oscura/loaders/validation.py +584 -0
  307. oscura/loaders/vcd.py +464 -0
  308. oscura/loaders/wav.py +233 -0
  309. oscura/math/__init__.py +45 -0
  310. oscura/math/arithmetic.py +824 -0
  311. oscura/math/interpolation.py +413 -0
  312. oscura/onboarding/__init__.py +39 -0
  313. oscura/onboarding/help.py +498 -0
  314. oscura/onboarding/tutorials.py +405 -0
  315. oscura/onboarding/wizard.py +466 -0
  316. oscura/optimization/__init__.py +19 -0
  317. oscura/optimization/parallel.py +440 -0
  318. oscura/optimization/search.py +532 -0
  319. oscura/pipeline/__init__.py +43 -0
  320. oscura/pipeline/base.py +338 -0
  321. oscura/pipeline/composition.py +242 -0
  322. oscura/pipeline/parallel.py +448 -0
  323. oscura/pipeline/pipeline.py +375 -0
  324. oscura/pipeline/reverse_engineering.py +1119 -0
  325. oscura/plugins/__init__.py +122 -0
  326. oscura/plugins/base.py +272 -0
  327. oscura/plugins/cli.py +497 -0
  328. oscura/plugins/discovery.py +411 -0
  329. oscura/plugins/isolation.py +418 -0
  330. oscura/plugins/lifecycle.py +959 -0
  331. oscura/plugins/manager.py +493 -0
  332. oscura/plugins/registry.py +421 -0
  333. oscura/plugins/versioning.py +372 -0
  334. oscura/py.typed +0 -0
  335. oscura/quality/__init__.py +65 -0
  336. oscura/quality/ensemble.py +740 -0
  337. oscura/quality/explainer.py +338 -0
  338. oscura/quality/scoring.py +616 -0
  339. oscura/quality/warnings.py +456 -0
  340. oscura/reporting/__init__.py +248 -0
  341. oscura/reporting/advanced.py +1234 -0
  342. oscura/reporting/analyze.py +448 -0
  343. oscura/reporting/argument_preparer.py +596 -0
  344. oscura/reporting/auto_report.py +507 -0
  345. oscura/reporting/batch.py +615 -0
  346. oscura/reporting/chart_selection.py +223 -0
  347. oscura/reporting/comparison.py +330 -0
  348. oscura/reporting/config.py +615 -0
  349. oscura/reporting/content/__init__.py +39 -0
  350. oscura/reporting/content/executive.py +127 -0
  351. oscura/reporting/content/filtering.py +191 -0
  352. oscura/reporting/content/minimal.py +257 -0
  353. oscura/reporting/content/verbosity.py +162 -0
  354. oscura/reporting/core.py +508 -0
  355. oscura/reporting/core_formats/__init__.py +17 -0
  356. oscura/reporting/core_formats/multi_format.py +210 -0
  357. oscura/reporting/engine.py +836 -0
  358. oscura/reporting/export.py +366 -0
  359. oscura/reporting/formatting/__init__.py +129 -0
  360. oscura/reporting/formatting/emphasis.py +81 -0
  361. oscura/reporting/formatting/numbers.py +403 -0
  362. oscura/reporting/formatting/standards.py +55 -0
  363. oscura/reporting/formatting.py +466 -0
  364. oscura/reporting/html.py +578 -0
  365. oscura/reporting/index.py +590 -0
  366. oscura/reporting/multichannel.py +296 -0
  367. oscura/reporting/output.py +379 -0
  368. oscura/reporting/pdf.py +373 -0
  369. oscura/reporting/plots.py +731 -0
  370. oscura/reporting/pptx_export.py +360 -0
  371. oscura/reporting/renderers/__init__.py +11 -0
  372. oscura/reporting/renderers/pdf.py +94 -0
  373. oscura/reporting/sections.py +471 -0
  374. oscura/reporting/standards.py +680 -0
  375. oscura/reporting/summary_generator.py +368 -0
  376. oscura/reporting/tables.py +397 -0
  377. oscura/reporting/template_system.py +724 -0
  378. oscura/reporting/templates/__init__.py +15 -0
  379. oscura/reporting/templates/definition.py +205 -0
  380. oscura/reporting/templates/index.html +649 -0
  381. oscura/reporting/templates/index.md +173 -0
  382. oscura/schemas/__init__.py +158 -0
  383. oscura/schemas/bus_configuration.json +322 -0
  384. oscura/schemas/device_mapping.json +182 -0
  385. oscura/schemas/packet_format.json +418 -0
  386. oscura/schemas/protocol_definition.json +363 -0
  387. oscura/search/__init__.py +16 -0
  388. oscura/search/anomaly.py +292 -0
  389. oscura/search/context.py +149 -0
  390. oscura/search/pattern.py +160 -0
  391. oscura/session/__init__.py +34 -0
  392. oscura/session/annotations.py +289 -0
  393. oscura/session/history.py +313 -0
  394. oscura/session/session.py +445 -0
  395. oscura/streaming/__init__.py +43 -0
  396. oscura/streaming/chunked.py +611 -0
  397. oscura/streaming/progressive.py +393 -0
  398. oscura/streaming/realtime.py +622 -0
  399. oscura/testing/__init__.py +54 -0
  400. oscura/testing/synthetic.py +808 -0
  401. oscura/triggering/__init__.py +68 -0
  402. oscura/triggering/base.py +229 -0
  403. oscura/triggering/edge.py +353 -0
  404. oscura/triggering/pattern.py +344 -0
  405. oscura/triggering/pulse.py +581 -0
  406. oscura/triggering/window.py +453 -0
  407. oscura/ui/__init__.py +48 -0
  408. oscura/ui/formatters.py +526 -0
  409. oscura/ui/progressive_display.py +340 -0
  410. oscura/utils/__init__.py +99 -0
  411. oscura/utils/autodetect.py +338 -0
  412. oscura/utils/buffer.py +389 -0
  413. oscura/utils/lazy.py +407 -0
  414. oscura/utils/lazy_imports.py +147 -0
  415. oscura/utils/memory.py +836 -0
  416. oscura/utils/memory_advanced.py +1326 -0
  417. oscura/utils/memory_extensions.py +465 -0
  418. oscura/utils/progressive.py +352 -0
  419. oscura/utils/windowing.py +362 -0
  420. oscura/visualization/__init__.py +321 -0
  421. oscura/visualization/accessibility.py +526 -0
  422. oscura/visualization/annotations.py +374 -0
  423. oscura/visualization/axis_scaling.py +305 -0
  424. oscura/visualization/colors.py +453 -0
  425. oscura/visualization/digital.py +337 -0
  426. oscura/visualization/eye.py +420 -0
  427. oscura/visualization/histogram.py +281 -0
  428. oscura/visualization/interactive.py +858 -0
  429. oscura/visualization/jitter.py +702 -0
  430. oscura/visualization/keyboard.py +394 -0
  431. oscura/visualization/layout.py +365 -0
  432. oscura/visualization/optimization.py +1028 -0
  433. oscura/visualization/palettes.py +446 -0
  434. oscura/visualization/plot.py +92 -0
  435. oscura/visualization/power.py +290 -0
  436. oscura/visualization/power_extended.py +626 -0
  437. oscura/visualization/presets.py +467 -0
  438. oscura/visualization/protocols.py +932 -0
  439. oscura/visualization/render.py +207 -0
  440. oscura/visualization/rendering.py +444 -0
  441. oscura/visualization/reverse_engineering.py +791 -0
  442. oscura/visualization/signal_integrity.py +808 -0
  443. oscura/visualization/specialized.py +553 -0
  444. oscura/visualization/spectral.py +811 -0
  445. oscura/visualization/styles.py +381 -0
  446. oscura/visualization/thumbnails.py +311 -0
  447. oscura/visualization/time_axis.py +351 -0
  448. oscura/visualization/waveform.py +367 -0
  449. oscura/workflow/__init__.py +13 -0
  450. oscura/workflow/dag.py +377 -0
  451. oscura/workflows/__init__.py +58 -0
  452. oscura/workflows/compliance.py +280 -0
  453. oscura/workflows/digital.py +272 -0
  454. oscura/workflows/multi_trace.py +502 -0
  455. oscura/workflows/power.py +178 -0
  456. oscura/workflows/protocol.py +492 -0
  457. oscura/workflows/reverse_engineering.py +639 -0
  458. oscura/workflows/signal_integrity.py +227 -0
  459. oscura-0.1.1.dist-info/METADATA +300 -0
  460. oscura-0.1.1.dist-info/RECORD +463 -0
  461. oscura-0.1.1.dist-info/entry_points.txt +2 -0
  462. {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/licenses/LICENSE +1 -1
  463. oscura-0.0.1.dist-info/METADATA +0 -63
  464. oscura-0.0.1.dist-info/RECORD +0 -5
  465. {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/WHEEL +0 -0
@@ -0,0 +1,25 @@
1
+ """Export module for Oscura protocol definitions.
2
+
3
+ This module provides functionality to export Oscura protocol definitions
4
+ to various formats for integration with other tools and workflows.
5
+
6
+ Supported export formats:
7
+ - Wireshark Lua dissectors
8
+ - (Future) Scapy packet definitions
9
+ - (Future) Kaitai Struct definitions
10
+ - (Future) C/C++ parser code
11
+
12
+ Example:
13
+ >>> from oscura.export.wireshark import WiresharkDissectorGenerator
14
+ >>> from oscura.inference.protocol_dsl import ProtocolDefinition
15
+ >>> protocol = ProtocolDefinition(name="myproto", description="My Protocol")
16
+ >>> generator = WiresharkDissectorGenerator()
17
+ >>> generator.generate(protocol, Path("myproto.lua"))
18
+ """
19
+
20
+ # Import main exports
21
+ from . import wireshark
22
+
23
+ __all__ = [
24
+ "wireshark",
25
+ ]
@@ -0,0 +1,265 @@
1
+ # Wireshark Lua Dissector Export
2
+
3
+ This module generates Wireshark Lua dissectors from Oscura protocol definitions, enabling seamless integration with Wireshark for interactive protocol analysis.
4
+
5
+ ## Features
6
+
7
+ - **Complete Protocol Support**: Handles all Oscura field types (uint8, uint16, uint32, int8, etc.)
8
+ - **Variable-Length Fields**: Supports fields with dynamic lengths based on other fields
9
+ - **Endianness Support**: Generates correct big-endian and little-endian field readers
10
+ - **Enum Values**: Creates value_string tables for human-readable enum display
11
+ - **Transport Registration**: Automatic TCP/UDP port registration
12
+ - **Syntax Validation**: Optional Lua syntax validation using `luac`
13
+ - **Error Handling**: Generates robust dissectors with malformed packet detection
14
+
15
+ ## Quick Start
16
+
17
+ ```python
18
+ from pathlib import Path
19
+ from oscura.export.wireshark import WiresharkDissectorGenerator
20
+ from oscura.inference.protocol_dsl import ProtocolDefinition, FieldDefinition
21
+
22
+ # Define your protocol
23
+ protocol = ProtocolDefinition(
24
+ name="myproto",
25
+ description="My Custom Protocol",
26
+ settings={"transport": "tcp", "port": 8000},
27
+ fields=[
28
+ FieldDefinition(name="type", field_type="uint8", size=1),
29
+ FieldDefinition(name="length", field_type="uint16", size=2),
30
+ FieldDefinition(name="data", field_type="bytes", size="length"),
31
+ ],
32
+ )
33
+
34
+ # Generate dissector
35
+ generator = WiresharkDissectorGenerator()
36
+ generator.generate(protocol, Path("myproto.lua"))
37
+ ```
38
+
39
+ ## Installation in Wireshark
40
+
41
+ Copy the generated `.lua` file to your Wireshark plugins directory:
42
+
43
+ **Linux:**
44
+
45
+ ```bash
46
+ mkdir -p ~/.local/lib/wireshark/plugins/
47
+ cp myproto.lua ~/.local/lib/wireshark/plugins/
48
+ ```
49
+
50
+ **macOS:**
51
+
52
+ ```bash
53
+ mkdir -p ~/.config/wireshark/plugins/
54
+ cp myproto.lua ~/.config/wireshark/plugins/
55
+ ```
56
+
57
+ **Windows:**
58
+
59
+ ```powershell
60
+ Copy-Item myproto.lua $env:APPDATA\Wireshark\plugins\
61
+ ```
62
+
63
+ Then reload Lua plugins in Wireshark: **Analyze > Reload Lua Plugins** (Ctrl+Shift+L)
64
+
65
+ ## Supported Field Types
66
+
67
+ |Oscura Type|Wireshark Type|Size|Notes|
68
+ |---|---|---|---|
69
+ |`uint8`|`ProtoField.uint8`|1 byte|Unsigned 8-bit integer|
70
+ |`uint16`|`ProtoField.uint16`|2 bytes|Unsigned 16-bit integer|
71
+ |`uint32`|`ProtoField.uint32`|4 bytes|Unsigned 32-bit integer|
72
+ |`uint64`|`ProtoField.uint64`|8 bytes|Unsigned 64-bit integer|
73
+ |`int8`|`ProtoField.int8`|1 byte|Signed 8-bit integer|
74
+ |`int16`|`ProtoField.int16`|2 bytes|Signed 16-bit integer|
75
+ |`int32`|`ProtoField.int32`|4 bytes|Signed 32-bit integer|
76
+ |`int64`|`ProtoField.int64`|8 bytes|Signed 64-bit integer|
77
+ |`float32`|`ProtoField.float`|4 bytes|IEEE 754 single precision|
78
+ |`float64`|`ProtoField.double`|8 bytes|IEEE 754 double precision|
79
+ |`bool`|`ProtoField.bool`|1 byte|Boolean value|
80
+ |`bytes`|`ProtoField.bytes`|Variable|Raw byte array|
81
+ |`string`|`ProtoField.string`|Variable|Text string|
82
+
83
+ ## Display Bases
84
+
85
+ The generator automatically selects appropriate display bases:
86
+
87
+ - **Unsigned integers** (uint\*): Displayed in hexadecimal (`base.HEX`)
88
+ - **Signed integers** (int\*): Displayed in decimal (`base.DEC`)
89
+ - **Floating point**: No base (`base.NONE`)
90
+ - **Strings/bytes**: No base (`base.NONE`)
91
+
92
+ You can override the display base using the `display_base` parameter.
93
+
94
+ ## Variable-Length Fields
95
+
96
+ For fields with variable lengths, reference another field's value:
97
+
98
+ ```python
99
+ fields=[
100
+ FieldDefinition(name="length", field_type="uint16", size=2),
101
+ FieldDefinition(name="payload", field_type="bytes", size="length"),
102
+ ]
103
+ ```
104
+
105
+ The generator automatically:
106
+
107
+ 1. Reads the `length` field value
108
+ 2. Uses it to determine `payload` size
109
+ 3. Validates that enough data is available
110
+
111
+ ## Enum Fields
112
+
113
+ Create human-readable enum displays:
114
+
115
+ ```python
116
+ FieldDefinition(
117
+ name="msg_type",
118
+ field_type="uint8",
119
+ size=1,
120
+ enum={
121
+ 0x01: "REQUEST",
122
+ 0x02: "RESPONSE",
123
+ 0x03: "ERROR",
124
+ }
125
+ )
126
+ ```
127
+
128
+ Wireshark will display "REQUEST" instead of "0x01" in the packet tree.
129
+
130
+ ## Transport Registration
131
+
132
+ ### TCP Registration
133
+
134
+ ```python
135
+ settings={"transport": "tcp", "port": 8000}
136
+ ```
137
+
138
+ ### UDP Registration
139
+
140
+ ```python
141
+ settings={"transport": "udp", "port": 5000}
142
+ ```
143
+
144
+ The dissector automatically registers on the specified port. Packets on that port will be decoded automatically.
145
+
146
+ ## Endianness
147
+
148
+ ### Protocol-Level
149
+
150
+ ```python
151
+ protocol = ProtocolDefinition(
152
+ name="myproto",
153
+ endian="little", # All fields default to little-endian
154
+ ...
155
+ )
156
+ ```
157
+
158
+ ### Field-Level
159
+
160
+ ```python
161
+ FieldDefinition(
162
+ name="value",
163
+ field_type="uint32",
164
+ size=4,
165
+ endian="little", # Override protocol endianness
166
+ )
167
+ ```
168
+
169
+ ## Conditional Fields
170
+
171
+ Fields with conditions are documented in comments (evaluation not yet implemented):
172
+
173
+ ```python
174
+ FieldDefinition(
175
+ name="optional_field",
176
+ field_type="uint32",
177
+ size=4,
178
+ condition="flags == 1", # Only present when flags field equals 1
179
+ )
180
+ ```
181
+
182
+ ## Syntax Validation
183
+
184
+ Enable automatic Lua syntax validation:
185
+
186
+ ```python
187
+ generator = WiresharkDissectorGenerator(validate=True)
188
+ generator.generate(protocol, Path("myproto.lua"))
189
+ ```
190
+
191
+ Requires `luac` (Lua compiler) to be installed:
192
+
193
+ - **Linux**: `sudo apt install lua5.3` or `sudo yum install lua`
194
+ - **macOS**: `brew install lua`
195
+ - **Windows**: Download from [lua.org](https://www.lua.org/download.html)
196
+
197
+ ## Examples
198
+
199
+ See `examples/05_export/wireshark_dissector_example.py` for complete examples:
200
+
201
+ - Simple protocol with enum fields
202
+ - Modbus-like protocol
203
+ - Custom protocol with multiple field types
204
+
205
+ Run the example:
206
+
207
+ ```bash
208
+ python examples/05_export/wireshark_dissector_example.py
209
+ ```
210
+
211
+ ## API Reference
212
+
213
+ ### WiresharkDissectorGenerator
214
+
215
+ Main class for generating dissectors.
216
+
217
+ **Methods:**
218
+
219
+ - `generate(protocol, output_path)`: Generate dissector to file
220
+ - `generate_to_string(protocol)`: Generate dissector as string
221
+
222
+ ### Helper Functions
223
+
224
+ - `get_protofield_type(field_type, display_base)`: Map field type to ProtoField
225
+ - `get_field_size(field_type)`: Get fixed size for field type
226
+ - `is_variable_length(field_type)`: Check if field is variable length
227
+ - `get_lua_reader_function(field_type, endian)`: Get Lua buffer reader function
228
+ - `validate_lua_syntax(lua_code)`: Validate Lua code syntax
229
+ - `check_luac_available()`: Check if luac is available
230
+
231
+ ## Limitations
232
+
233
+ - **Conditional fields**: Comment generated but evaluation not implemented
234
+ - **Nested structures**: Not yet supported
235
+ - **Arrays**: Not yet supported
236
+ - **Heuristic dissectors**: Pattern-based registration not implemented
237
+ - **TCP reassembly**: Not yet supported for stream protocols
238
+
239
+ ## Troubleshooting
240
+
241
+ ### Dissector not loading in Wireshark
242
+
243
+ 1. Check Wireshark's Lua console: **Tools > Lua > Evaluate**
244
+ 2. Verify file is in correct plugins directory
245
+ 3. Check for syntax errors: `luac -p myproto.lua`
246
+ 4. Ensure Lua support is enabled in Wireshark build
247
+
248
+ ### Protocol not decoding automatically
249
+
250
+ 1. Verify port registration matches capture
251
+ 2. Try manual decode: Right-click packet > Decode As > select protocol
252
+ 3. Check minimum length requirements
253
+
254
+ ### Malformed packet errors
255
+
256
+ 1. Verify field sizes match actual protocol
257
+ 2. Check variable-length field references are correct
258
+ 3. Ensure endianness is set correctly
259
+
260
+ ## References
261
+
262
+ - [Wireshark Lua API](https://wiki.wireshark.org/LuaAPI)
263
+ - [Writing Lua Dissectors](https://wiki.wireshark.org/lua/dissectors)
264
+ - [ProtoField Reference](https://wiki.wireshark.org/LuaAPI/Proto)
265
+ - [DissectorTable Reference](https://wiki.wireshark.org/LuaAPI/DissectorTable)
@@ -0,0 +1,47 @@
1
+ """Wireshark dissector export module.
2
+
3
+ This module provides functionality to export Oscura protocol definitions
4
+ as Wireshark Lua dissectors for integration with Wireshark's protocol
5
+ analysis tools.
6
+
7
+ Example:
8
+ >>> from oscura.inference.protocol_dsl import ProtocolDefinition
9
+ >>> from oscura.export.wireshark import WiresharkDissectorGenerator
10
+ >>> protocol = ProtocolDefinition(name="myproto", description="My Protocol")
11
+ >>> generator = WiresharkDissectorGenerator()
12
+ >>> generator.generate(protocol, Path("myproto.lua"))
13
+
14
+ Installation:
15
+ Copy the generated .lua file to your Wireshark plugins directory:
16
+ - Linux: ~/.local/lib/wireshark/plugins/
17
+ - macOS: ~/.config/wireshark/plugins/
18
+ - Windows: %APPDATA%\\Wireshark\\plugins\\
19
+ """
20
+
21
+ from .generator import WiresharkDissectorGenerator
22
+ from .lua_builder import LuaCodeBuilder
23
+ from .type_mapping import (
24
+ BASE_MAPPING,
25
+ DEFAULT_BASE,
26
+ FIELD_TYPE_MAPPING,
27
+ get_field_size,
28
+ get_lua_reader_function,
29
+ get_protofield_type,
30
+ is_variable_length,
31
+ )
32
+ from .validator import check_luac_available, validate_lua_file, validate_lua_syntax
33
+
34
+ __all__ = [
35
+ "BASE_MAPPING",
36
+ "DEFAULT_BASE",
37
+ "FIELD_TYPE_MAPPING",
38
+ "LuaCodeBuilder",
39
+ "WiresharkDissectorGenerator",
40
+ "check_luac_available",
41
+ "get_field_size",
42
+ "get_lua_reader_function",
43
+ "get_protofield_type",
44
+ "is_variable_length",
45
+ "validate_lua_file",
46
+ "validate_lua_syntax",
47
+ ]
@@ -0,0 +1,312 @@
1
+ """Wireshark Lua dissector generator.
2
+
3
+ This module generates Wireshark Lua dissectors from Oscura protocol definitions.
4
+ The generated dissectors can be loaded into Wireshark for interactive protocol analysis.
5
+
6
+ Features:
7
+ - Generate ProtoField declarations for all field types
8
+ - Handle fixed and variable-length fields
9
+ - Support TCP/UDP port registration
10
+ - Checksum validation support
11
+ - Proper error handling for malformed packets
12
+
13
+ References:
14
+ https://wiki.wireshark.org/lua/dissectors
15
+ """
16
+
17
+ from datetime import UTC, datetime
18
+ from pathlib import Path
19
+ from typing import Any
20
+
21
+ from jinja2 import Environment, PackageLoader, select_autoescape
22
+
23
+ from oscura.inference.protocol_dsl import FieldDefinition, ProtocolDefinition
24
+
25
+ from .type_mapping import get_field_size, get_lua_reader_function, get_protofield_type
26
+ from .validator import validate_lua_syntax
27
+
28
+
29
+ class WiresharkDissectorGenerator:
30
+ """Generate Wireshark Lua dissectors from protocol definitions.
31
+
32
+ This class converts Oscura protocol definitions into Wireshark Lua
33
+ dissectors that can be loaded into Wireshark for protocol analysis.
34
+
35
+ Features:
36
+ - Generate ProtoField declarations
37
+ - Handle fixed and variable-length fields
38
+ - Support nested protocols (dissector stacking)
39
+ - TCP/UDP registration
40
+ - Checksum validation
41
+ - Registration on port/pattern
42
+
43
+ Example:
44
+ >>> from oscura.inference.protocol_dsl import ProtocolDefinition
45
+ >>> protocol = ProtocolDefinition(name="simple", description="Simple Protocol")
46
+ >>> generator = WiresharkDissectorGenerator()
47
+ >>> generator.generate(protocol, Path("simple.lua"))
48
+
49
+ References:
50
+ https://wiki.wireshark.org/lua/dissectors
51
+ https://wiki.wireshark.org/LuaAPI/Proto
52
+ """
53
+
54
+ def __init__(self, validate: bool = True) -> None:
55
+ """Initialize the generator.
56
+
57
+ Args:
58
+ validate: Run luac syntax validation if available
59
+ """
60
+ self.validate = validate
61
+ self.env = Environment(
62
+ loader=PackageLoader("oscura.export.wireshark"),
63
+ autoescape=select_autoescape(),
64
+ )
65
+
66
+ def generate(self, protocol: ProtocolDefinition, output_path: Path) -> None:
67
+ """Generate Lua dissector file.
68
+
69
+ Args:
70
+ protocol: Protocol definition to export
71
+ output_path: Where to write .lua file
72
+
73
+ Raises:
74
+ RuntimeError: If Lua syntax validation fails
75
+ """
76
+ lua_code = self.generate_to_string(protocol)
77
+
78
+ # Validate if requested
79
+ if self.validate:
80
+ is_valid, error_msg = validate_lua_syntax(lua_code)
81
+ if not is_valid:
82
+ raise RuntimeError(f"Generated Lua code has syntax errors:\n{error_msg}")
83
+
84
+ # Write to file
85
+ output_path.parent.mkdir(parents=True, exist_ok=True)
86
+ output_path.write_text(lua_code)
87
+
88
+ def generate_to_string(self, protocol: ProtocolDefinition) -> str:
89
+ """Generate Lua dissector as string.
90
+
91
+ Args:
92
+ protocol: Protocol definition to export
93
+
94
+ Returns:
95
+ Generated Lua code
96
+
97
+ Raises:
98
+ ValueError: If protocol definition is invalid
99
+ """
100
+ # Validate protocol
101
+ if not protocol.name:
102
+ raise ValueError("Protocol name is required")
103
+
104
+ # Prepare template context
105
+ context = self._build_template_context(protocol)
106
+
107
+ # Render template
108
+ template = self.env.get_template("dissector.lua.j2")
109
+ return template.render(**context)
110
+
111
+ def _build_template_context(self, protocol: ProtocolDefinition) -> dict[str, Any]:
112
+ """Build context dictionary for template rendering.
113
+
114
+ Args:
115
+ protocol: Protocol definition
116
+
117
+ Returns:
118
+ Template context dictionary
119
+ """
120
+ # Generate proto variable name (lowercase, no spaces)
121
+ proto_var = protocol.name.lower().replace(" ", "_").replace("-", "_") + "_proto"
122
+
123
+ # Process fields and calculate offsets
124
+ field_data = []
125
+ field_offsets = {} # Track offset of each field by name
126
+ current_offset = 0
127
+ min_length = 0
128
+
129
+ for field_def in protocol.fields:
130
+ # Store offset for this field
131
+ field_offsets[field_def.name] = current_offset
132
+
133
+ field_info = self._process_field(field_def, protocol, field_offsets)
134
+ field_data.append(field_info)
135
+
136
+ # Update offset for next field (if fixed size)
137
+ if field_info["size"] is not None and not field_info["is_variable_length"]:
138
+ current_offset += field_info["size"]
139
+
140
+ # Calculate minimum length (sum of fixed-size fields)
141
+ if not field_info["is_variable_length"]:
142
+ min_length += field_info["size"]
143
+
144
+ # Extract transport settings
145
+ transport = None
146
+ port = None
147
+
148
+ if "transport" in protocol.settings:
149
+ transport = protocol.settings["transport"]
150
+ if "port" in protocol.settings:
151
+ port = protocol.settings["port"]
152
+
153
+ # Check framing for transport/port
154
+ if transport is None and "transport" in protocol.framing:
155
+ transport = protocol.framing["transport"]
156
+ if port is None and "port" in protocol.framing:
157
+ port = protocol.framing["port"]
158
+
159
+ context = {
160
+ "protocol": protocol,
161
+ "proto_var": proto_var,
162
+ "fields": field_data,
163
+ "min_length": max(min_length, 1), # At least 1 byte
164
+ "timestamp": datetime.now(UTC).isoformat(),
165
+ "transport": transport,
166
+ "port": port,
167
+ "pattern": protocol.framing.get("sync_pattern"),
168
+ }
169
+
170
+ return context
171
+
172
+ def _process_field(
173
+ self,
174
+ field_def: FieldDefinition,
175
+ protocol: ProtocolDefinition,
176
+ field_offsets: dict[str, int],
177
+ ) -> dict[str, Any]:
178
+ """Process a field definition for template rendering.
179
+
180
+ Args:
181
+ field_def: Field definition
182
+ protocol: Parent protocol definition
183
+ field_offsets: Dictionary mapping field names to byte offsets
184
+
185
+ Returns:
186
+ Dictionary with field information for template
187
+ """
188
+ # Get ProtoField type and display base
189
+ protofield_type, display_base = get_protofield_type(field_def.field_type)
190
+
191
+ # Get field size
192
+ size = get_field_size(field_def.field_type)
193
+ is_variable = size is None
194
+
195
+ # Handle explicit size specification
196
+ if field_def.size is not None:
197
+ if isinstance(field_def.size, int):
198
+ size = field_def.size
199
+ is_variable = False
200
+ elif isinstance(field_def.size, str):
201
+ # Size is a reference to another field
202
+ is_variable = True
203
+
204
+ # Generate display name
205
+ display_name = field_def.description or field_def.name.replace("_", " ").title()
206
+
207
+ # Get Lua reader function
208
+ reader_func = get_lua_reader_function(field_def.field_type, field_def.endian)
209
+
210
+ field_info = {
211
+ "name": field_def.name,
212
+ "display_name": display_name,
213
+ "protofield_type": protofield_type,
214
+ "display_base": display_base,
215
+ "size": size,
216
+ "is_variable_length": is_variable,
217
+ "condition": field_def.condition,
218
+ "reader_function": reader_func,
219
+ "endian": field_def.endian,
220
+ "value_string": self._generate_value_string(field_def),
221
+ }
222
+
223
+ # Handle variable-length fields with size reference
224
+ if is_variable and isinstance(field_def.size, str):
225
+ # Find the size field
226
+ size_field_def = self._find_field(protocol, field_def.size)
227
+ if size_field_def:
228
+ size_field_size = get_field_size(size_field_def.field_type)
229
+ size_reader = get_lua_reader_function(
230
+ size_field_def.field_type, size_field_def.endian
231
+ )
232
+
233
+ # Get the offset of the size field
234
+ size_field_offset = field_offsets.get(field_def.size, 0)
235
+
236
+ field_info.update(
237
+ {
238
+ "size_field": field_def.size,
239
+ "size_offset": size_field_offset,
240
+ "size_field_size": size_field_size,
241
+ "size_reader": size_reader,
242
+ }
243
+ )
244
+
245
+ return field_info
246
+
247
+ def _find_field(self, protocol: ProtocolDefinition, field_name: str) -> FieldDefinition | None:
248
+ """Find a field definition by name.
249
+
250
+ Args:
251
+ protocol: Protocol definition
252
+ field_name: Field name to find
253
+
254
+ Returns:
255
+ Field definition or None if not found
256
+ """
257
+ for field_def in protocol.fields:
258
+ if field_def.name == field_name:
259
+ return field_def
260
+ return None
261
+
262
+ def _generate_value_string(self, field_def: FieldDefinition) -> str | None:
263
+ """Generate Wireshark value_string table for enum fields.
264
+
265
+ Args:
266
+ field_def: Field definition
267
+
268
+ Returns:
269
+ Lua table definition or None if field has no enum
270
+ """
271
+ if not field_def.enum:
272
+ return None
273
+
274
+ # Generate Lua table for enum
275
+ entries = []
276
+ for key, value in field_def.enum.items():
277
+ if isinstance(key, int):
278
+ entries.append(f'[{key}] = "{value}"')
279
+ else:
280
+ # String key - try to get value
281
+ entries.append(f'["{key}"] = "{value}"')
282
+
283
+ return "{" + ", ".join(entries) + "}"
284
+
285
+ def _calculate_min_length(self, protocol: ProtocolDefinition) -> int:
286
+ """Calculate the minimum packet length for the protocol.
287
+
288
+ Args:
289
+ protocol: Protocol definition
290
+
291
+ Returns:
292
+ Minimum length in bytes
293
+ """
294
+ min_length = 0
295
+
296
+ for field_def in protocol.fields:
297
+ # Skip conditional fields (they might not be present)
298
+ if field_def.condition:
299
+ continue
300
+
301
+ # Get field size
302
+ size = get_field_size(field_def.field_type)
303
+
304
+ # Handle explicit size
305
+ if field_def.size is not None and isinstance(field_def.size, int):
306
+ size = field_def.size
307
+
308
+ # Add to minimum length if fixed size
309
+ if size is not None:
310
+ min_length += size
311
+
312
+ return max(min_length, 1) # At least 1 byte