oscura 0.5.0__py3-none-any.whl → 0.6.0__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 (513) hide show
  1. oscura/__init__.py +169 -167
  2. oscura/analyzers/__init__.py +3 -0
  3. oscura/analyzers/classification.py +659 -0
  4. oscura/analyzers/digital/__init__.py +0 -48
  5. oscura/analyzers/digital/edges.py +325 -65
  6. oscura/analyzers/digital/extraction.py +0 -195
  7. oscura/analyzers/digital/quality.py +293 -166
  8. oscura/analyzers/digital/timing.py +260 -115
  9. oscura/analyzers/digital/timing_numba.py +334 -0
  10. oscura/analyzers/entropy.py +605 -0
  11. oscura/analyzers/eye/diagram.py +176 -109
  12. oscura/analyzers/eye/metrics.py +5 -5
  13. oscura/analyzers/jitter/__init__.py +6 -4
  14. oscura/analyzers/jitter/ber.py +52 -52
  15. oscura/analyzers/jitter/classification.py +156 -0
  16. oscura/analyzers/jitter/decomposition.py +163 -113
  17. oscura/analyzers/jitter/spectrum.py +80 -64
  18. oscura/analyzers/ml/__init__.py +39 -0
  19. oscura/analyzers/ml/features.py +600 -0
  20. oscura/analyzers/ml/signal_classifier.py +604 -0
  21. oscura/analyzers/packet/daq.py +246 -158
  22. oscura/analyzers/packet/parser.py +12 -1
  23. oscura/analyzers/packet/payload.py +50 -2110
  24. oscura/analyzers/packet/payload_analysis.py +361 -181
  25. oscura/analyzers/packet/payload_patterns.py +133 -70
  26. oscura/analyzers/packet/stream.py +84 -23
  27. oscura/analyzers/patterns/__init__.py +26 -5
  28. oscura/analyzers/patterns/anomaly_detection.py +908 -0
  29. oscura/analyzers/patterns/clustering.py +169 -108
  30. oscura/analyzers/patterns/clustering_optimized.py +227 -0
  31. oscura/analyzers/patterns/discovery.py +1 -1
  32. oscura/analyzers/patterns/matching.py +581 -197
  33. oscura/analyzers/patterns/pattern_mining.py +778 -0
  34. oscura/analyzers/patterns/periodic.py +121 -38
  35. oscura/analyzers/patterns/sequences.py +175 -78
  36. oscura/analyzers/power/conduction.py +1 -1
  37. oscura/analyzers/power/soa.py +6 -6
  38. oscura/analyzers/power/switching.py +250 -110
  39. oscura/analyzers/protocol/__init__.py +17 -1
  40. oscura/analyzers/protocols/__init__.py +1 -22
  41. oscura/analyzers/protocols/base.py +6 -6
  42. oscura/analyzers/protocols/ble/__init__.py +38 -0
  43. oscura/analyzers/protocols/ble/analyzer.py +809 -0
  44. oscura/analyzers/protocols/ble/uuids.py +288 -0
  45. oscura/analyzers/protocols/can.py +257 -127
  46. oscura/analyzers/protocols/can_fd.py +107 -80
  47. oscura/analyzers/protocols/flexray.py +139 -80
  48. oscura/analyzers/protocols/hdlc.py +93 -58
  49. oscura/analyzers/protocols/i2c.py +247 -106
  50. oscura/analyzers/protocols/i2s.py +138 -86
  51. oscura/analyzers/protocols/industrial/__init__.py +40 -0
  52. oscura/analyzers/protocols/industrial/bacnet/__init__.py +33 -0
  53. oscura/analyzers/protocols/industrial/bacnet/analyzer.py +708 -0
  54. oscura/analyzers/protocols/industrial/bacnet/encoding.py +412 -0
  55. oscura/analyzers/protocols/industrial/bacnet/services.py +622 -0
  56. oscura/analyzers/protocols/industrial/ethercat/__init__.py +30 -0
  57. oscura/analyzers/protocols/industrial/ethercat/analyzer.py +474 -0
  58. oscura/analyzers/protocols/industrial/ethercat/mailbox.py +339 -0
  59. oscura/analyzers/protocols/industrial/ethercat/topology.py +166 -0
  60. oscura/analyzers/protocols/industrial/modbus/__init__.py +31 -0
  61. oscura/analyzers/protocols/industrial/modbus/analyzer.py +525 -0
  62. oscura/analyzers/protocols/industrial/modbus/crc.py +79 -0
  63. oscura/analyzers/protocols/industrial/modbus/functions.py +436 -0
  64. oscura/analyzers/protocols/industrial/opcua/__init__.py +21 -0
  65. oscura/analyzers/protocols/industrial/opcua/analyzer.py +552 -0
  66. oscura/analyzers/protocols/industrial/opcua/datatypes.py +446 -0
  67. oscura/analyzers/protocols/industrial/opcua/services.py +264 -0
  68. oscura/analyzers/protocols/industrial/profinet/__init__.py +23 -0
  69. oscura/analyzers/protocols/industrial/profinet/analyzer.py +441 -0
  70. oscura/analyzers/protocols/industrial/profinet/dcp.py +263 -0
  71. oscura/analyzers/protocols/industrial/profinet/ptcp.py +200 -0
  72. oscura/analyzers/protocols/jtag.py +180 -98
  73. oscura/analyzers/protocols/lin.py +219 -114
  74. oscura/analyzers/protocols/manchester.py +4 -4
  75. oscura/analyzers/protocols/onewire.py +253 -149
  76. oscura/analyzers/protocols/parallel_bus/__init__.py +20 -0
  77. oscura/analyzers/protocols/parallel_bus/centronics.py +92 -0
  78. oscura/analyzers/protocols/parallel_bus/gpib.py +137 -0
  79. oscura/analyzers/protocols/spi.py +192 -95
  80. oscura/analyzers/protocols/swd.py +321 -167
  81. oscura/analyzers/protocols/uart.py +267 -125
  82. oscura/analyzers/protocols/usb.py +235 -131
  83. oscura/analyzers/side_channel/power.py +17 -12
  84. oscura/analyzers/signal/__init__.py +15 -0
  85. oscura/analyzers/signal/timing_analysis.py +1086 -0
  86. oscura/analyzers/signal_integrity/__init__.py +4 -1
  87. oscura/analyzers/signal_integrity/sparams.py +2 -19
  88. oscura/analyzers/spectral/chunked.py +129 -60
  89. oscura/analyzers/spectral/chunked_fft.py +300 -94
  90. oscura/analyzers/spectral/chunked_wavelet.py +100 -80
  91. oscura/analyzers/statistical/checksum.py +376 -217
  92. oscura/analyzers/statistical/classification.py +229 -107
  93. oscura/analyzers/statistical/entropy.py +78 -53
  94. oscura/analyzers/statistics/correlation.py +407 -211
  95. oscura/analyzers/statistics/outliers.py +2 -2
  96. oscura/analyzers/statistics/streaming.py +30 -5
  97. oscura/analyzers/validation.py +216 -101
  98. oscura/analyzers/waveform/measurements.py +9 -0
  99. oscura/analyzers/waveform/measurements_with_uncertainty.py +31 -15
  100. oscura/analyzers/waveform/spectral.py +500 -228
  101. oscura/api/__init__.py +31 -5
  102. oscura/api/dsl/__init__.py +582 -0
  103. oscura/{dsl → api/dsl}/commands.py +43 -76
  104. oscura/{dsl → api/dsl}/interpreter.py +26 -51
  105. oscura/{dsl → api/dsl}/parser.py +107 -77
  106. oscura/{dsl → api/dsl}/repl.py +2 -2
  107. oscura/api/dsl.py +1 -1
  108. oscura/{integrations → api/integrations}/__init__.py +1 -1
  109. oscura/{integrations → api/integrations}/llm.py +201 -102
  110. oscura/api/operators.py +3 -3
  111. oscura/api/optimization.py +144 -30
  112. oscura/api/rest_server.py +921 -0
  113. oscura/api/server/__init__.py +17 -0
  114. oscura/api/server/dashboard.py +850 -0
  115. oscura/api/server/static/README.md +34 -0
  116. oscura/api/server/templates/base.html +181 -0
  117. oscura/api/server/templates/export.html +120 -0
  118. oscura/api/server/templates/home.html +284 -0
  119. oscura/api/server/templates/protocols.html +58 -0
  120. oscura/api/server/templates/reports.html +43 -0
  121. oscura/api/server/templates/session_detail.html +89 -0
  122. oscura/api/server/templates/sessions.html +83 -0
  123. oscura/api/server/templates/waveforms.html +73 -0
  124. oscura/automotive/__init__.py +8 -1
  125. oscura/automotive/can/__init__.py +10 -0
  126. oscura/automotive/can/checksum.py +3 -1
  127. oscura/automotive/can/dbc_generator.py +590 -0
  128. oscura/automotive/can/message_wrapper.py +121 -74
  129. oscura/automotive/can/patterns.py +98 -21
  130. oscura/automotive/can/session.py +292 -56
  131. oscura/automotive/can/state_machine.py +6 -3
  132. oscura/automotive/can/stimulus_response.py +97 -75
  133. oscura/automotive/dbc/__init__.py +10 -2
  134. oscura/automotive/dbc/generator.py +84 -56
  135. oscura/automotive/dbc/parser.py +6 -6
  136. oscura/automotive/dtc/data.json +2763 -0
  137. oscura/automotive/dtc/database.py +2 -2
  138. oscura/automotive/flexray/__init__.py +31 -0
  139. oscura/automotive/flexray/analyzer.py +504 -0
  140. oscura/automotive/flexray/crc.py +185 -0
  141. oscura/automotive/flexray/fibex.py +449 -0
  142. oscura/automotive/j1939/__init__.py +45 -8
  143. oscura/automotive/j1939/analyzer.py +605 -0
  144. oscura/automotive/j1939/spns.py +326 -0
  145. oscura/automotive/j1939/transport.py +306 -0
  146. oscura/automotive/lin/__init__.py +47 -0
  147. oscura/automotive/lin/analyzer.py +612 -0
  148. oscura/automotive/loaders/blf.py +13 -2
  149. oscura/automotive/loaders/csv_can.py +143 -72
  150. oscura/automotive/loaders/dispatcher.py +50 -2
  151. oscura/automotive/loaders/mdf.py +86 -45
  152. oscura/automotive/loaders/pcap.py +111 -61
  153. oscura/automotive/uds/__init__.py +4 -0
  154. oscura/automotive/uds/analyzer.py +725 -0
  155. oscura/automotive/uds/decoder.py +140 -58
  156. oscura/automotive/uds/models.py +7 -1
  157. oscura/automotive/visualization.py +1 -1
  158. oscura/cli/analyze.py +348 -0
  159. oscura/cli/batch.py +142 -122
  160. oscura/cli/benchmark.py +275 -0
  161. oscura/cli/characterize.py +137 -82
  162. oscura/cli/compare.py +224 -131
  163. oscura/cli/completion.py +250 -0
  164. oscura/cli/config_cmd.py +361 -0
  165. oscura/cli/decode.py +164 -87
  166. oscura/cli/export.py +286 -0
  167. oscura/cli/main.py +115 -31
  168. oscura/{onboarding → cli/onboarding}/__init__.py +3 -3
  169. oscura/{onboarding → cli/onboarding}/help.py +80 -58
  170. oscura/{onboarding → cli/onboarding}/tutorials.py +97 -72
  171. oscura/{onboarding → cli/onboarding}/wizard.py +55 -36
  172. oscura/cli/progress.py +147 -0
  173. oscura/cli/shell.py +157 -135
  174. oscura/cli/validate_cmd.py +204 -0
  175. oscura/cli/visualize.py +158 -0
  176. oscura/convenience.py +125 -79
  177. oscura/core/__init__.py +4 -2
  178. oscura/core/backend_selector.py +3 -3
  179. oscura/core/cache.py +126 -15
  180. oscura/core/cancellation.py +1 -1
  181. oscura/{config → core/config}/__init__.py +20 -11
  182. oscura/{config → core/config}/defaults.py +1 -1
  183. oscura/{config → core/config}/loader.py +7 -5
  184. oscura/{config → core/config}/memory.py +5 -5
  185. oscura/{config → core/config}/migration.py +1 -1
  186. oscura/{config → core/config}/pipeline.py +99 -23
  187. oscura/{config → core/config}/preferences.py +1 -1
  188. oscura/{config → core/config}/protocol.py +3 -3
  189. oscura/{config → core/config}/schema.py +426 -272
  190. oscura/{config → core/config}/settings.py +1 -1
  191. oscura/{config → core/config}/thresholds.py +195 -153
  192. oscura/core/correlation.py +5 -6
  193. oscura/core/cross_domain.py +0 -2
  194. oscura/core/debug.py +9 -5
  195. oscura/{extensibility → core/extensibility}/docs.py +158 -70
  196. oscura/{extensibility → core/extensibility}/extensions.py +160 -76
  197. oscura/{extensibility → core/extensibility}/logging.py +1 -1
  198. oscura/{extensibility → core/extensibility}/measurements.py +1 -1
  199. oscura/{extensibility → core/extensibility}/plugins.py +1 -1
  200. oscura/{extensibility → core/extensibility}/templates.py +73 -3
  201. oscura/{extensibility → core/extensibility}/validation.py +1 -1
  202. oscura/core/gpu_backend.py +11 -7
  203. oscura/core/log_query.py +101 -11
  204. oscura/core/logging.py +126 -54
  205. oscura/core/logging_advanced.py +5 -5
  206. oscura/core/memory_limits.py +108 -70
  207. oscura/core/memory_monitor.py +2 -2
  208. oscura/core/memory_progress.py +7 -7
  209. oscura/core/memory_warnings.py +1 -1
  210. oscura/core/numba_backend.py +13 -13
  211. oscura/{plugins → core/plugins}/__init__.py +9 -9
  212. oscura/{plugins → core/plugins}/base.py +7 -7
  213. oscura/{plugins → core/plugins}/cli.py +3 -3
  214. oscura/{plugins → core/plugins}/discovery.py +186 -106
  215. oscura/{plugins → core/plugins}/lifecycle.py +1 -1
  216. oscura/{plugins → core/plugins}/manager.py +7 -7
  217. oscura/{plugins → core/plugins}/registry.py +3 -3
  218. oscura/{plugins → core/plugins}/versioning.py +1 -1
  219. oscura/core/progress.py +16 -1
  220. oscura/core/provenance.py +8 -2
  221. oscura/{schemas → core/schemas}/__init__.py +2 -2
  222. oscura/core/schemas/bus_configuration.json +322 -0
  223. oscura/core/schemas/device_mapping.json +182 -0
  224. oscura/core/schemas/packet_format.json +418 -0
  225. oscura/core/schemas/protocol_definition.json +363 -0
  226. oscura/core/types.py +4 -0
  227. oscura/core/uncertainty.py +3 -3
  228. oscura/correlation/__init__.py +52 -0
  229. oscura/correlation/multi_protocol.py +811 -0
  230. oscura/discovery/auto_decoder.py +117 -35
  231. oscura/discovery/comparison.py +191 -86
  232. oscura/discovery/quality_validator.py +155 -68
  233. oscura/discovery/signal_detector.py +196 -79
  234. oscura/export/__init__.py +18 -20
  235. oscura/export/kaitai_struct.py +513 -0
  236. oscura/export/scapy_layer.py +801 -0
  237. oscura/export/wireshark/README.md +15 -15
  238. oscura/export/wireshark/generator.py +1 -1
  239. oscura/export/wireshark/templates/dissector.lua.j2 +2 -2
  240. oscura/export/wireshark_dissector.py +746 -0
  241. oscura/guidance/wizard.py +207 -111
  242. oscura/hardware/__init__.py +19 -0
  243. oscura/{acquisition → hardware/acquisition}/__init__.py +4 -4
  244. oscura/{acquisition → hardware/acquisition}/file.py +2 -2
  245. oscura/{acquisition → hardware/acquisition}/hardware.py +7 -7
  246. oscura/{acquisition → hardware/acquisition}/saleae.py +15 -12
  247. oscura/{acquisition → hardware/acquisition}/socketcan.py +1 -1
  248. oscura/{acquisition → hardware/acquisition}/streaming.py +2 -2
  249. oscura/{acquisition → hardware/acquisition}/synthetic.py +3 -3
  250. oscura/{acquisition → hardware/acquisition}/visa.py +33 -11
  251. oscura/hardware/firmware/__init__.py +29 -0
  252. oscura/hardware/firmware/pattern_recognition.py +874 -0
  253. oscura/hardware/hal_detector.py +736 -0
  254. oscura/hardware/security/__init__.py +37 -0
  255. oscura/hardware/security/side_channel_detector.py +1126 -0
  256. oscura/inference/__init__.py +4 -0
  257. oscura/inference/active_learning/README.md +7 -7
  258. oscura/inference/active_learning/observation_table.py +4 -1
  259. oscura/inference/alignment.py +216 -123
  260. oscura/inference/bayesian.py +113 -33
  261. oscura/inference/crc_reverse.py +101 -55
  262. oscura/inference/logic.py +6 -2
  263. oscura/inference/message_format.py +342 -183
  264. oscura/inference/protocol.py +95 -44
  265. oscura/inference/protocol_dsl.py +180 -82
  266. oscura/inference/signal_intelligence.py +1439 -706
  267. oscura/inference/spectral.py +99 -57
  268. oscura/inference/state_machine.py +810 -158
  269. oscura/inference/stream.py +270 -110
  270. oscura/iot/__init__.py +34 -0
  271. oscura/iot/coap/__init__.py +32 -0
  272. oscura/iot/coap/analyzer.py +668 -0
  273. oscura/iot/coap/options.py +212 -0
  274. oscura/iot/lorawan/__init__.py +21 -0
  275. oscura/iot/lorawan/crypto.py +206 -0
  276. oscura/iot/lorawan/decoder.py +801 -0
  277. oscura/iot/lorawan/mac_commands.py +341 -0
  278. oscura/iot/mqtt/__init__.py +27 -0
  279. oscura/iot/mqtt/analyzer.py +999 -0
  280. oscura/iot/mqtt/properties.py +315 -0
  281. oscura/iot/zigbee/__init__.py +31 -0
  282. oscura/iot/zigbee/analyzer.py +615 -0
  283. oscura/iot/zigbee/security.py +153 -0
  284. oscura/iot/zigbee/zcl.py +349 -0
  285. oscura/jupyter/display.py +125 -45
  286. oscura/{exploratory → jupyter/exploratory}/__init__.py +8 -8
  287. oscura/{exploratory → jupyter/exploratory}/error_recovery.py +298 -141
  288. oscura/jupyter/exploratory/fuzzy.py +746 -0
  289. oscura/{exploratory → jupyter/exploratory}/fuzzy_advanced.py +258 -100
  290. oscura/{exploratory → jupyter/exploratory}/legacy.py +464 -242
  291. oscura/{exploratory → jupyter/exploratory}/parse.py +167 -145
  292. oscura/{exploratory → jupyter/exploratory}/recovery.py +119 -87
  293. oscura/jupyter/exploratory/sync.py +612 -0
  294. oscura/{exploratory → jupyter/exploratory}/unknown.py +299 -176
  295. oscura/jupyter/magic.py +4 -4
  296. oscura/{ui → jupyter/ui}/__init__.py +2 -2
  297. oscura/{ui → jupyter/ui}/formatters.py +3 -3
  298. oscura/{ui → jupyter/ui}/progressive_display.py +153 -82
  299. oscura/loaders/__init__.py +171 -63
  300. oscura/loaders/binary.py +88 -1
  301. oscura/loaders/chipwhisperer.py +153 -137
  302. oscura/loaders/configurable.py +208 -86
  303. oscura/loaders/csv_loader.py +458 -215
  304. oscura/loaders/hdf5_loader.py +278 -119
  305. oscura/loaders/lazy.py +87 -54
  306. oscura/loaders/mmap_loader.py +1 -1
  307. oscura/loaders/numpy_loader.py +253 -116
  308. oscura/loaders/pcap.py +226 -151
  309. oscura/loaders/rigol.py +110 -49
  310. oscura/loaders/sigrok.py +201 -78
  311. oscura/loaders/tdms.py +81 -58
  312. oscura/loaders/tektronix.py +291 -174
  313. oscura/loaders/touchstone.py +182 -87
  314. oscura/loaders/vcd.py +215 -117
  315. oscura/loaders/wav.py +155 -68
  316. oscura/reporting/__init__.py +9 -7
  317. oscura/reporting/analyze.py +352 -146
  318. oscura/reporting/argument_preparer.py +69 -14
  319. oscura/reporting/auto_report.py +97 -61
  320. oscura/reporting/batch.py +131 -58
  321. oscura/reporting/chart_selection.py +57 -45
  322. oscura/reporting/comparison.py +63 -17
  323. oscura/reporting/content/executive.py +76 -24
  324. oscura/reporting/core_formats/multi_format.py +11 -8
  325. oscura/reporting/engine.py +312 -158
  326. oscura/reporting/enhanced_reports.py +949 -0
  327. oscura/reporting/export.py +86 -43
  328. oscura/reporting/formatting/numbers.py +69 -42
  329. oscura/reporting/html.py +139 -58
  330. oscura/reporting/index.py +137 -65
  331. oscura/reporting/output.py +158 -67
  332. oscura/reporting/pdf.py +67 -102
  333. oscura/reporting/plots.py +191 -112
  334. oscura/reporting/sections.py +88 -47
  335. oscura/reporting/standards.py +104 -61
  336. oscura/reporting/summary_generator.py +75 -55
  337. oscura/reporting/tables.py +138 -54
  338. oscura/reporting/templates/enhanced/protocol_re.html +525 -0
  339. oscura/reporting/templates/index.md +13 -13
  340. oscura/sessions/__init__.py +14 -23
  341. oscura/sessions/base.py +3 -3
  342. oscura/sessions/blackbox.py +106 -10
  343. oscura/sessions/generic.py +2 -2
  344. oscura/sessions/legacy.py +783 -0
  345. oscura/side_channel/__init__.py +63 -0
  346. oscura/side_channel/dpa.py +1025 -0
  347. oscura/utils/__init__.py +15 -1
  348. oscura/utils/autodetect.py +1 -5
  349. oscura/utils/bitwise.py +118 -0
  350. oscura/{builders → utils/builders}/__init__.py +1 -1
  351. oscura/{comparison → utils/comparison}/__init__.py +6 -6
  352. oscura/{comparison → utils/comparison}/compare.py +202 -101
  353. oscura/{comparison → utils/comparison}/golden.py +83 -63
  354. oscura/{comparison → utils/comparison}/limits.py +313 -89
  355. oscura/{comparison → utils/comparison}/mask.py +151 -45
  356. oscura/{comparison → utils/comparison}/trace_diff.py +1 -1
  357. oscura/{comparison → utils/comparison}/visualization.py +147 -89
  358. oscura/{component → utils/component}/__init__.py +3 -3
  359. oscura/{component → utils/component}/impedance.py +122 -58
  360. oscura/{component → utils/component}/reactive.py +165 -168
  361. oscura/{component → utils/component}/transmission_line.py +3 -3
  362. oscura/{filtering → utils/filtering}/__init__.py +6 -6
  363. oscura/{filtering → utils/filtering}/base.py +1 -1
  364. oscura/{filtering → utils/filtering}/convenience.py +2 -2
  365. oscura/{filtering → utils/filtering}/design.py +169 -93
  366. oscura/{filtering → utils/filtering}/filters.py +2 -2
  367. oscura/{filtering → utils/filtering}/introspection.py +2 -2
  368. oscura/utils/geometry.py +31 -0
  369. oscura/utils/imports.py +184 -0
  370. oscura/utils/lazy.py +1 -1
  371. oscura/{math → utils/math}/__init__.py +2 -2
  372. oscura/{math → utils/math}/arithmetic.py +114 -48
  373. oscura/{math → utils/math}/interpolation.py +139 -106
  374. oscura/utils/memory.py +129 -66
  375. oscura/utils/memory_advanced.py +92 -9
  376. oscura/utils/memory_extensions.py +10 -8
  377. oscura/{optimization → utils/optimization}/__init__.py +1 -1
  378. oscura/{optimization → utils/optimization}/search.py +2 -2
  379. oscura/utils/performance/__init__.py +58 -0
  380. oscura/utils/performance/caching.py +889 -0
  381. oscura/utils/performance/lsh_clustering.py +333 -0
  382. oscura/utils/performance/memory_optimizer.py +699 -0
  383. oscura/utils/performance/optimizations.py +675 -0
  384. oscura/utils/performance/parallel.py +654 -0
  385. oscura/utils/performance/profiling.py +661 -0
  386. oscura/{pipeline → utils/pipeline}/base.py +1 -1
  387. oscura/{pipeline → utils/pipeline}/composition.py +11 -3
  388. oscura/{pipeline → utils/pipeline}/parallel.py +3 -2
  389. oscura/{pipeline → utils/pipeline}/pipeline.py +1 -1
  390. oscura/{pipeline → utils/pipeline}/reverse_engineering.py +412 -221
  391. oscura/{search → utils/search}/__init__.py +3 -3
  392. oscura/{search → utils/search}/anomaly.py +188 -58
  393. oscura/utils/search/context.py +294 -0
  394. oscura/{search → utils/search}/pattern.py +138 -10
  395. oscura/utils/serial.py +51 -0
  396. oscura/utils/storage/__init__.py +61 -0
  397. oscura/utils/storage/database.py +1166 -0
  398. oscura/{streaming → utils/streaming}/chunked.py +302 -143
  399. oscura/{streaming → utils/streaming}/progressive.py +1 -1
  400. oscura/{streaming → utils/streaming}/realtime.py +3 -2
  401. oscura/{triggering → utils/triggering}/__init__.py +6 -6
  402. oscura/{triggering → utils/triggering}/base.py +6 -6
  403. oscura/{triggering → utils/triggering}/edge.py +2 -2
  404. oscura/{triggering → utils/triggering}/pattern.py +2 -2
  405. oscura/{triggering → utils/triggering}/pulse.py +115 -74
  406. oscura/{triggering → utils/triggering}/window.py +2 -2
  407. oscura/utils/validation.py +32 -0
  408. oscura/validation/__init__.py +121 -0
  409. oscura/{compliance → validation/compliance}/__init__.py +5 -5
  410. oscura/{compliance → validation/compliance}/advanced.py +5 -5
  411. oscura/{compliance → validation/compliance}/masks.py +1 -1
  412. oscura/{compliance → validation/compliance}/reporting.py +127 -53
  413. oscura/{compliance → validation/compliance}/testing.py +114 -52
  414. oscura/validation/compliance_tests.py +915 -0
  415. oscura/validation/fuzzer.py +990 -0
  416. oscura/validation/grammar_tests.py +596 -0
  417. oscura/validation/grammar_validator.py +904 -0
  418. oscura/validation/hil_testing.py +977 -0
  419. oscura/{quality → validation/quality}/__init__.py +4 -4
  420. oscura/{quality → validation/quality}/ensemble.py +251 -171
  421. oscura/{quality → validation/quality}/explainer.py +3 -3
  422. oscura/{quality → validation/quality}/scoring.py +1 -1
  423. oscura/{quality → validation/quality}/warnings.py +4 -4
  424. oscura/validation/regression_suite.py +808 -0
  425. oscura/validation/replay.py +788 -0
  426. oscura/{testing → validation/testing}/__init__.py +2 -2
  427. oscura/{testing → validation/testing}/synthetic.py +5 -5
  428. oscura/visualization/__init__.py +9 -0
  429. oscura/visualization/accessibility.py +1 -1
  430. oscura/visualization/annotations.py +64 -67
  431. oscura/visualization/colors.py +7 -7
  432. oscura/visualization/digital.py +180 -81
  433. oscura/visualization/eye.py +236 -85
  434. oscura/visualization/interactive.py +320 -143
  435. oscura/visualization/jitter.py +587 -247
  436. oscura/visualization/layout.py +169 -134
  437. oscura/visualization/optimization.py +103 -52
  438. oscura/visualization/palettes.py +1 -1
  439. oscura/visualization/power.py +427 -211
  440. oscura/visualization/power_extended.py +626 -297
  441. oscura/visualization/presets.py +2 -0
  442. oscura/visualization/protocols.py +495 -181
  443. oscura/visualization/render.py +79 -63
  444. oscura/visualization/reverse_engineering.py +171 -124
  445. oscura/visualization/signal_integrity.py +460 -279
  446. oscura/visualization/specialized.py +190 -100
  447. oscura/visualization/spectral.py +670 -255
  448. oscura/visualization/thumbnails.py +166 -137
  449. oscura/visualization/waveform.py +150 -63
  450. oscura/workflows/__init__.py +3 -0
  451. oscura/{batch → workflows/batch}/__init__.py +5 -5
  452. oscura/{batch → workflows/batch}/advanced.py +150 -75
  453. oscura/workflows/batch/aggregate.py +531 -0
  454. oscura/workflows/batch/analyze.py +236 -0
  455. oscura/{batch → workflows/batch}/logging.py +2 -2
  456. oscura/{batch → workflows/batch}/metrics.py +1 -1
  457. oscura/workflows/complete_re.py +1144 -0
  458. oscura/workflows/compliance.py +44 -54
  459. oscura/workflows/digital.py +197 -51
  460. oscura/workflows/legacy/__init__.py +12 -0
  461. oscura/{workflow → workflows/legacy}/dag.py +4 -1
  462. oscura/workflows/multi_trace.py +9 -9
  463. oscura/workflows/power.py +42 -62
  464. oscura/workflows/protocol.py +82 -49
  465. oscura/workflows/reverse_engineering.py +351 -150
  466. oscura/workflows/signal_integrity.py +157 -82
  467. oscura-0.6.0.dist-info/METADATA +643 -0
  468. oscura-0.6.0.dist-info/RECORD +590 -0
  469. oscura/analyzers/digital/ic_database.py +0 -498
  470. oscura/analyzers/digital/timing_paths.py +0 -339
  471. oscura/analyzers/digital/vintage.py +0 -377
  472. oscura/analyzers/digital/vintage_result.py +0 -148
  473. oscura/analyzers/protocols/parallel_bus.py +0 -449
  474. oscura/batch/aggregate.py +0 -300
  475. oscura/batch/analyze.py +0 -139
  476. oscura/dsl/__init__.py +0 -73
  477. oscura/exceptions.py +0 -59
  478. oscura/exploratory/fuzzy.py +0 -513
  479. oscura/exploratory/sync.py +0 -384
  480. oscura/export/wavedrom.py +0 -430
  481. oscura/exporters/__init__.py +0 -94
  482. oscura/exporters/csv.py +0 -303
  483. oscura/exporters/exporters.py +0 -44
  484. oscura/exporters/hdf5.py +0 -217
  485. oscura/exporters/html_export.py +0 -701
  486. oscura/exporters/json_export.py +0 -338
  487. oscura/exporters/markdown_export.py +0 -367
  488. oscura/exporters/matlab_export.py +0 -354
  489. oscura/exporters/npz_export.py +0 -219
  490. oscura/exporters/spice_export.py +0 -210
  491. oscura/exporters/vintage_logic_csv.py +0 -247
  492. oscura/reporting/vintage_logic_report.py +0 -523
  493. oscura/search/context.py +0 -149
  494. oscura/session/__init__.py +0 -34
  495. oscura/session/annotations.py +0 -289
  496. oscura/session/history.py +0 -313
  497. oscura/session/session.py +0 -520
  498. oscura/visualization/digital_advanced.py +0 -718
  499. oscura/visualization/figure_manager.py +0 -156
  500. oscura/workflow/__init__.py +0 -13
  501. oscura-0.5.0.dist-info/METADATA +0 -407
  502. oscura-0.5.0.dist-info/RECORD +0 -486
  503. /oscura/core/{config.py → config/legacy.py} +0 -0
  504. /oscura/{extensibility → core/extensibility}/__init__.py +0 -0
  505. /oscura/{extensibility → core/extensibility}/registry.py +0 -0
  506. /oscura/{plugins → core/plugins}/isolation.py +0 -0
  507. /oscura/{builders → utils/builders}/signal_builder.py +0 -0
  508. /oscura/{optimization → utils/optimization}/parallel.py +0 -0
  509. /oscura/{pipeline → utils/pipeline}/__init__.py +0 -0
  510. /oscura/{streaming → utils/streaming}/__init__.py +0 -0
  511. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/WHEEL +0 -0
  512. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/entry_points.txt +0 -0
  513. {oscura-0.5.0.dist-info → oscura-0.6.0.dist-info}/licenses/LICENSE +0 -0
oscura/cli/decode.py CHANGED
@@ -25,44 +25,44 @@ from oscura.core.types import DigitalTrace, ProtocolPacket, WaveformTrace
25
25
  logger = logging.getLogger("oscura.cli.decode")
26
26
 
27
27
 
28
- @click.command() # type: ignore[misc]
29
- @click.argument("file", type=click.Path(exists=True)) # type: ignore[misc]
30
- @click.option( # type: ignore[misc]
28
+ @click.command()
29
+ @click.argument("file", type=click.Path(exists=True))
30
+ @click.option(
31
31
  "--protocol",
32
32
  type=click.Choice(["uart", "spi", "i2c", "can", "auto"], case_sensitive=False),
33
33
  default="auto",
34
34
  help="Protocol type (default: auto-detect).",
35
35
  )
36
- @click.option( # type: ignore[misc]
36
+ @click.option(
37
37
  "--baud-rate",
38
38
  type=int,
39
39
  default=None,
40
40
  help="Baud rate for UART (auto-detect if not specified).",
41
41
  )
42
- @click.option( # type: ignore[misc]
42
+ @click.option(
43
43
  "--parity",
44
44
  type=click.Choice(["none", "even", "odd"], case_sensitive=False),
45
45
  default="none",
46
46
  help="Parity for UART (default: none).",
47
47
  )
48
- @click.option( # type: ignore[misc]
48
+ @click.option(
49
49
  "--stop-bits",
50
50
  type=click.Choice(["1", "2"]),
51
51
  default="1",
52
52
  help="Stop bits for UART (default: 1).",
53
53
  )
54
- @click.option( # type: ignore[misc]
54
+ @click.option(
55
55
  "--show-errors",
56
56
  is_flag=True,
57
57
  help="Show only errors with context.",
58
58
  )
59
- @click.option( # type: ignore[misc]
59
+ @click.option(
60
60
  "--output",
61
61
  type=click.Choice(["json", "csv", "html", "table"], case_sensitive=False),
62
62
  default="table",
63
63
  help="Output format (default: table).",
64
64
  )
65
- @click.pass_context # type: ignore[misc]
65
+ @click.pass_context
66
66
  def decode(
67
67
  ctx: click.Context,
68
68
  file: str,
@@ -198,80 +198,113 @@ def _perform_decoding(
198
198
  Returns:
199
199
  Dictionary of decoding results.
200
200
  """
201
- # Import protocol decoders
202
- from oscura.inference.protocol import detect_protocol
201
+ # Build base results
202
+ results = _build_base_results(trace)
203
+
204
+ # Auto-detect protocol if needed
205
+ detected_protocol, baud_rate = _detect_protocol_if_auto(trace, protocol, baud_rate, results)
206
+ results["protocol"] = detected_protocol.upper()
207
+
208
+ # Decode with appropriate decoder
209
+ digital_trace = _to_digital(trace)
210
+ packets, errors = _dispatch_protocol_decode(
211
+ digital_trace, detected_protocol, baud_rate, parity, stop_bits, show_errors, results
212
+ )
203
213
 
214
+ # Filter and format results
215
+ if show_errors:
216
+ packets = [p for p in packets if p.errors]
217
+
218
+ _add_packet_summary(results, packets, errors)
219
+ return results
220
+
221
+
222
+ def _build_base_results(trace: WaveformTrace | DigitalTrace) -> dict[str, Any]:
223
+ """Build base results dictionary with trace metadata."""
204
224
  sample_rate = trace.metadata.sample_rate
205
225
  duration_ms = len(trace.data) / sample_rate * 1e3
206
-
207
- results: dict[str, Any] = {
226
+ return {
208
227
  "sample_rate": f"{sample_rate / 1e6:.1f} MHz",
209
228
  "samples": len(trace.data),
210
229
  "duration": f"{duration_ms:.3f} ms",
211
230
  }
212
231
 
213
- # Auto-detect protocol if requested
214
- detected_protocol = protocol
215
- detection_confidence = 1.0
216
232
 
217
- if protocol.lower() == "auto":
218
- try:
219
- detection = detect_protocol(trace, min_confidence=0.5, return_candidates=True) # type: ignore[arg-type]
220
- detected_protocol = detection["protocol"].lower()
221
- detection_confidence = detection["confidence"]
222
- results["auto_detection"] = {
223
- "protocol": detection["protocol"],
224
- "confidence": f"{detection_confidence:.1%}",
225
- "candidates": [
226
- {"protocol": c["protocol"], "confidence": f"{c['confidence']:.1%}"}
227
- for c in detection.get("candidates", [])[:3]
228
- ],
229
- }
230
- # Extract config suggestions
231
- if "config" in detection:
232
- if detected_protocol == "uart" and baud_rate is None:
233
- baud_rate = detection["config"].get("baud_rate")
234
- except Exception as e:
235
- logger.warning(f"Auto-detection failed: {e}, defaulting to UART")
236
- detected_protocol = "uart"
237
- detection_confidence = 0.0
233
+ def _detect_protocol_if_auto(
234
+ trace: WaveformTrace | DigitalTrace,
235
+ protocol: str,
236
+ baud_rate: int | None,
237
+ results: dict[str, Any],
238
+ ) -> tuple[str, int | None]:
239
+ """Auto-detect protocol if requested, otherwise return protocol as-is."""
240
+ if protocol.lower() != "auto":
241
+ return protocol, baud_rate
238
242
 
239
- results["protocol"] = detected_protocol.upper()
243
+ from oscura.inference.protocol import detect_protocol
240
244
 
241
- # Convert to digital trace for decoding
242
- digital_trace = _to_digital(trace)
245
+ try:
246
+ detection = detect_protocol(trace, min_confidence=0.5, return_candidates=True) # type: ignore[arg-type]
247
+ detected_protocol = detection["protocol"].lower()
248
+ results["auto_detection"] = _format_detection_results(detection)
249
+
250
+ # Extract config suggestions
251
+ if "config" in detection and detected_protocol == "uart" and baud_rate is None:
252
+ baud_rate = detection["config"].get("baud_rate")
253
+
254
+ return detected_protocol, baud_rate
255
+ except Exception as e:
256
+ logger.warning(f"Auto-detection failed: {e}, defaulting to UART")
257
+ return "uart", baud_rate
258
+
259
+
260
+ def _format_detection_results(detection: dict[str, Any]) -> dict[str, Any]:
261
+ """Format auto-detection results for display."""
262
+ return {
263
+ "protocol": detection["protocol"],
264
+ "confidence": f"{detection['confidence']:.1%}",
265
+ "candidates": [
266
+ {"protocol": c["protocol"], "confidence": f"{c['confidence']:.1%}"}
267
+ for c in detection.get("candidates", [])[:3]
268
+ ],
269
+ }
243
270
 
244
- # Decode based on protocol
245
- packets: list[ProtocolPacket] = []
246
- errors: list[dict[str, Any]] = []
247
271
 
248
- if detected_protocol == "uart":
272
+ def _dispatch_protocol_decode(
273
+ digital_trace: DigitalTrace,
274
+ protocol: str,
275
+ baud_rate: int | None,
276
+ parity: str,
277
+ stop_bits: int,
278
+ show_errors: bool,
279
+ results: dict[str, Any],
280
+ ) -> tuple[list[ProtocolPacket], list[dict[str, Any]]]:
281
+ """Dispatch to appropriate protocol decoder."""
282
+ if protocol == "uart":
249
283
  packets, errors, protocol_info = _decode_uart(
250
284
  digital_trace, baud_rate, parity, stop_bits, show_errors
251
285
  )
252
- results.update(protocol_info)
253
-
254
- elif detected_protocol == "spi":
286
+ elif protocol == "spi":
255
287
  packets, errors, protocol_info = _decode_spi(digital_trace, show_errors)
256
- results.update(protocol_info)
257
-
258
- elif detected_protocol == "i2c":
288
+ elif protocol == "i2c":
259
289
  packets, errors, protocol_info = _decode_i2c(digital_trace, show_errors)
260
- results.update(protocol_info)
261
-
262
- elif detected_protocol == "can":
290
+ elif protocol == "can":
263
291
  packets, errors, protocol_info = _decode_can(digital_trace, baud_rate, show_errors)
264
- results.update(protocol_info)
292
+ else:
293
+ packets, errors, protocol_info = [], [], {}
265
294
 
266
- # Filter to errors only if requested
267
- if show_errors:
268
- packets = [p for p in packets if p.errors]
295
+ results.update(protocol_info)
296
+ return packets, errors
269
297
 
270
- # Summarize results
298
+
299
+ def _add_packet_summary(
300
+ results: dict[str, Any],
301
+ packets: list[ProtocolPacket],
302
+ errors: list[dict[str, Any]],
303
+ ) -> None:
304
+ """Add packet summary and details to results."""
271
305
  results["packets_decoded"] = len(packets)
272
306
  results["errors_found"] = len(errors)
273
307
 
274
- # Add packet details
275
308
  results["packets"] = [
276
309
  {
277
310
  "index": i,
@@ -280,17 +313,14 @@ def _perform_decoding(
280
313
  "errors": p.errors,
281
314
  **{k: v for k, v in (p.annotations or {}).items() if k != "data_bits"},
282
315
  }
283
- for i, p in enumerate(packets[:100]) # Limit to first 100 packets
316
+ for i, p in enumerate(packets[:100])
284
317
  ]
285
318
 
286
319
  if len(packets) > 100:
287
320
  results["note"] = f"Showing first 100 of {len(packets)} packets"
288
321
 
289
- # Add error details if any
290
322
  if errors:
291
- results["error_details"] = errors[:20] # Limit to first 20 errors
292
-
293
- return results
323
+ results["error_details"] = errors[:20]
294
324
 
295
325
 
296
326
  def _decode_uart(
@@ -498,29 +528,60 @@ def _decode_can(
498
528
  """
499
529
  from oscura.analyzers.protocols.can import CANDecoder
500
530
 
501
- # Try common CAN baud rates if not specified
502
531
  if baud_rate is None:
503
- common_rates = [500000, 250000, 125000, 1000000]
504
- best_rate = 500000
505
- max_packets = 0
506
-
507
- for rate in common_rates:
508
- try:
509
- decoder = CANDecoder(bitrate=rate)
510
- test_packets = list(decoder.decode(trace))
511
- if len(test_packets) > max_packets:
512
- max_packets = len(test_packets)
513
- best_rate = rate
514
- except Exception:
515
- continue
516
-
517
- baud_rate = best_rate
532
+ baud_rate = _detect_can_baud_rate(trace)
518
533
 
519
534
  decoder = CANDecoder(bitrate=baud_rate)
520
535
  packets = list(decoder.decode(trace))
521
- errors = []
522
536
 
537
+ errors, arbitration_ids = _extract_can_errors(packets)
538
+ protocol_info = _build_can_protocol_info(baud_rate, packets, arbitration_ids)
539
+
540
+ return packets, errors, protocol_info
541
+
542
+
543
+ def _detect_can_baud_rate(trace: DigitalTrace) -> int:
544
+ """Detect CAN baud rate by trying common rates.
545
+
546
+ Args:
547
+ trace: Digital trace to analyze.
548
+
549
+ Returns:
550
+ Best matching baud rate.
551
+ """
552
+ from oscura.analyzers.protocols.can import CANDecoder
553
+
554
+ common_rates = [500000, 250000, 125000, 1000000]
555
+ best_rate = 500000
556
+ max_packets = 0
557
+
558
+ for rate in common_rates:
559
+ try:
560
+ decoder = CANDecoder(bitrate=rate)
561
+ test_packets = list(decoder.decode(trace))
562
+ if len(test_packets) > max_packets:
563
+ max_packets = len(test_packets)
564
+ best_rate = rate
565
+ except Exception:
566
+ continue
567
+
568
+ return best_rate
569
+
570
+
571
+ def _extract_can_errors(
572
+ packets: list[ProtocolPacket],
573
+ ) -> tuple[list[dict[str, Any]], set[int]]:
574
+ """Extract errors and arbitration IDs from CAN packets.
575
+
576
+ Args:
577
+ packets: Decoded packets.
578
+
579
+ Returns:
580
+ Tuple of (errors list, arbitration ID set).
581
+ """
582
+ errors = []
523
583
  arbitration_ids: set[int] = set()
584
+
524
585
  for i, pkt in enumerate(packets):
525
586
  arb_id = pkt.annotations.get("arbitration_id", 0) if pkt.annotations else 0
526
587
  arbitration_ids.add(arb_id)
@@ -536,16 +597,32 @@ def _decode_can(
536
597
  }
537
598
  )
538
599
 
539
- protocol_info = {
600
+ return errors, arbitration_ids
601
+
602
+
603
+ def _build_can_protocol_info(
604
+ baud_rate: int, packets: list[ProtocolPacket], arbitration_ids: set[int]
605
+ ) -> dict[str, Any]:
606
+ """Build CAN protocol info dictionary.
607
+
608
+ Args:
609
+ baud_rate: Detected baud rate.
610
+ packets: Decoded packets.
611
+ arbitration_ids: Set of arbitration IDs.
612
+
613
+ Returns:
614
+ Protocol info dictionary.
615
+ """
616
+ extended_count = sum(1 for p in packets if p.annotations and p.annotations.get("is_extended"))
617
+
618
+ info = {
540
619
  "bit_rate": f"{baud_rate / 1000:.0f} kbps",
541
620
  "messages": len(packets),
542
621
  "arbitration_ids": [f"0x{a:03X}" for a in sorted(arbitration_ids)[:10]],
543
- "extended_frames": sum(
544
- 1 for p in packets if p.annotations and p.annotations.get("is_extended")
545
- ),
622
+ "extended_frames": extended_count,
546
623
  }
547
624
 
548
625
  if len(arbitration_ids) > 10:
549
- protocol_info["note"] = f"Showing first 10 of {len(arbitration_ids)} arbitration IDs"
626
+ info["note"] = f"Showing first 10 of {len(arbitration_ids)} arbitration IDs"
550
627
 
551
- return packets, errors, protocol_info
628
+ return info
oscura/cli/export.py ADDED
@@ -0,0 +1,286 @@
1
+ """Oscura Export Command - Export Analysis Results.
2
+
3
+ Provides CLI for exporting analysis sessions and results to various formats.
4
+
5
+
6
+ Example:
7
+ $ oscura export json session.tks --output results.json
8
+ $ oscura export html session.tks --output report.html
9
+ $ oscura export wireshark session.tks --output dissector.lua
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import logging
15
+ from pathlib import Path
16
+ from typing import Any
17
+
18
+ import click
19
+
20
+ logger = logging.getLogger("oscura.cli.export")
21
+
22
+
23
+ @click.command()
24
+ @click.argument(
25
+ "format",
26
+ type=click.Choice(
27
+ ["json", "html", "csv", "matlab", "wireshark", "scapy", "kaitai"], case_sensitive=False
28
+ ),
29
+ )
30
+ @click.argument("session", type=click.Path(exists=True))
31
+ @click.option(
32
+ "--output",
33
+ "-o",
34
+ type=click.Path(),
35
+ required=True,
36
+ help="Output file path.",
37
+ )
38
+ @click.option(
39
+ "--include-traces",
40
+ is_flag=True,
41
+ default=True,
42
+ help="Include trace data in export (default: True).",
43
+ )
44
+ @click.pass_context
45
+ def export(
46
+ ctx: click.Context,
47
+ format: str,
48
+ session: str,
49
+ output: str,
50
+ include_traces: bool,
51
+ ) -> None:
52
+ """Export analysis session to various formats.
53
+
54
+ Supports exporting to JSON, HTML, CSV, MATLAB, Wireshark dissector,
55
+ Scapy layer, and Kaitai struct formats.
56
+
57
+ Args:
58
+ ctx: Click context object.
59
+ format: Export format.
60
+ session: Path to session file (.tks).
61
+ output: Output file path.
62
+ include_traces: Include trace data in export.
63
+
64
+ Examples:
65
+
66
+ \b
67
+ # Export to JSON
68
+ $ oscura export json session.tks --output results.json
69
+
70
+ \b
71
+ # Generate HTML report
72
+ $ oscura export html session.tks --output report.html
73
+
74
+ \b
75
+ # Generate Wireshark dissector
76
+ $ oscura export wireshark session.tks --output protocol.lua
77
+ """
78
+ verbose = ctx.obj.get("verbose", 0)
79
+
80
+ if verbose:
81
+ logger.info(f"Exporting session {session} to {format}")
82
+
83
+ try:
84
+ # Note: Session loading functionality has been redesigned
85
+ # For now, this is a placeholder - session export needs to be reimplemented
86
+ # using the new AnalysisSession architecture
87
+ raise NotImplementedError(
88
+ "Session export has been redesigned. Use the new AnalysisSession API instead."
89
+ )
90
+
91
+ except Exception as e:
92
+ logger.error(f"Export failed: {e}")
93
+ if verbose > 1:
94
+ raise
95
+ click.echo(f"Error: {e}", err=True)
96
+ ctx.exit(1)
97
+
98
+
99
+ def _export_json(session: Any, output_path: Path, include_traces: bool) -> None:
100
+ """Export session to JSON.
101
+
102
+ Args:
103
+ session: Session object.
104
+ output_path: Output file path.
105
+ include_traces: Include trace data.
106
+ """
107
+ import json
108
+
109
+ data = session._to_dict(include_traces=include_traces)
110
+
111
+ with open(output_path, "w") as f:
112
+ json.dump(data, f, indent=2, default=str)
113
+
114
+
115
+ def _export_html(session: Any, output_path: Path) -> None:
116
+ """Export session to HTML report.
117
+
118
+ Args:
119
+ session: Session object.
120
+ output_path: Output file path.
121
+ """
122
+ html_content = f"""<!DOCTYPE html>
123
+ <html>
124
+ <head>
125
+ <meta charset='utf-8'>
126
+ <title>Oscura Analysis Report - {session.name}</title>
127
+ <style>
128
+ body {{ font-family: Arial, sans-serif; margin: 20px; }}
129
+ h1 {{ color: #4CAF50; }}
130
+ table {{ border-collapse: collapse; width: 100%; margin: 20px 0; }}
131
+ th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
132
+ th {{ background-color: #4CAF50; color: white; }}
133
+ tr:nth-child(even) {{ background-color: #f2f2f2; }}
134
+ .metadata {{ background: #f9f9f9; padding: 10px; margin: 10px 0; }}
135
+ </style>
136
+ </head>
137
+ <body>
138
+ <h1>Oscura Analysis Report</h1>
139
+ <div class="metadata">
140
+ <h2>Session: {session.name}</h2>
141
+ <p>Created: {session.created_at}</p>
142
+ <p>Modified: {session.modified_at}</p>
143
+ </div>
144
+
145
+ <h2>Traces ({len(session.traces)})</h2>
146
+ <ul>
147
+ """
148
+
149
+ for trace_name in session.list_traces():
150
+ html_content += f" <li>{trace_name}</li>\n"
151
+
152
+ html_content += """ </ul>
153
+
154
+ <h2>Measurements</h2>
155
+ <table>
156
+ <tr><th>Measurement</th><th>Value</th><th>Unit</th></tr>
157
+ """
158
+
159
+ for name, meas in session.get_measurements().items():
160
+ value = meas.get("value", "N/A")
161
+ unit = meas.get("unit", "")
162
+ html_content += f" <tr><td>{name}</td><td>{value}</td><td>{unit}</td></tr>\n"
163
+
164
+ html_content += """ </table>
165
+ </body>
166
+ </html>
167
+ """
168
+
169
+ with open(output_path, "w") as f:
170
+ f.write(html_content)
171
+
172
+
173
+ def _export_csv(session: Any, output_path: Path) -> None:
174
+ """Export measurements to CSV.
175
+
176
+ Args:
177
+ session: Session object.
178
+ output_path: Output file path.
179
+ """
180
+ import csv
181
+
182
+ with open(output_path, "w", newline="") as f:
183
+ writer = csv.writer(f)
184
+ writer.writerow(["Measurement", "Value", "Unit", "Trace"])
185
+
186
+ for name, meas in session.get_measurements().items():
187
+ writer.writerow(
188
+ [
189
+ name,
190
+ meas.get("value", ""),
191
+ meas.get("unit", ""),
192
+ meas.get("trace", ""),
193
+ ]
194
+ )
195
+
196
+
197
+ def _export_matlab(session: Any, output_path: Path) -> None:
198
+ """Export to MATLAB format.
199
+
200
+ Args:
201
+ session: Session object.
202
+ output_path: Output file path.
203
+ """
204
+ # MATLAB export has been redesigned - use new AnalysisSession API
205
+ raise NotImplementedError("MATLAB export needs reimplementation with new API")
206
+
207
+
208
+ def _export_wireshark(session: Any, output_path: Path) -> None:
209
+ """Export to Wireshark dissector.
210
+
211
+ Args:
212
+ session: Session object.
213
+ output_path: Output file path.
214
+ """
215
+ # Placeholder - requires protocol specification
216
+ lua_template = """-- Wireshark Dissector for Unknown Protocol
217
+ -- Generated by Oscura
218
+
219
+ local proto = Proto("unknown", "Unknown Protocol")
220
+
221
+ function proto.dissector(buffer, pinfo, tree)
222
+ pinfo.cols.protocol = "UNKNOWN"
223
+ local subtree = tree:add(proto, buffer(), "Unknown Protocol Data")
224
+ subtree:add(buffer(0), "Raw data: " .. buffer():bytes():tohex())
225
+ end
226
+
227
+ -- Register dissector
228
+ local udp_table = DissectorTable.get("udp.port")
229
+ -- udp_table:add(YOUR_PORT, proto)
230
+ """
231
+
232
+ with open(output_path, "w") as f:
233
+ f.write(lua_template)
234
+
235
+
236
+ def _export_scapy(session: Any, output_path: Path) -> None:
237
+ """Export to Scapy layer.
238
+
239
+ Args:
240
+ session: Session object.
241
+ output_path: Output file path.
242
+ """
243
+ # Placeholder - requires protocol specification
244
+ scapy_template = """# Scapy Layer for Unknown Protocol
245
+ # Generated by Oscura
246
+
247
+ from scapy.all import *
248
+
249
+ class UnknownProtocol(Packet):
250
+ name = "UnknownProtocol"
251
+ fields_desc = [
252
+ # Define fields here
253
+ XByteField("data", 0x00),
254
+ ]
255
+
256
+ # Bind to lower layer if needed
257
+ # bind_layers(UDP, UnknownProtocol, dport=YOUR_PORT)
258
+ """
259
+
260
+ with open(output_path, "w") as f:
261
+ f.write(scapy_template)
262
+
263
+
264
+ def _export_kaitai(session: Any, output_path: Path) -> None:
265
+ """Export to Kaitai struct.
266
+
267
+ Args:
268
+ session: Session object.
269
+ output_path: Output file path.
270
+ """
271
+ # Placeholder - requires protocol specification
272
+ kaitai_template = """meta:
273
+ id: unknown_protocol
274
+ title: Unknown Protocol
275
+ file-extension: dat
276
+ endian: le
277
+
278
+ seq:
279
+ - id: header
280
+ type: u1
281
+ - id: data
282
+ size-eos: true
283
+ """
284
+
285
+ with open(output_path, "w") as f:
286
+ f.write(kaitai_template)