oscura 0.5.1__py3-none-any.whl → 0.7.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 (497) 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/edges.py +325 -65
  5. oscura/analyzers/digital/quality.py +293 -166
  6. oscura/analyzers/digital/timing.py +260 -115
  7. oscura/analyzers/digital/timing_numba.py +334 -0
  8. oscura/analyzers/entropy.py +605 -0
  9. oscura/analyzers/eye/diagram.py +176 -109
  10. oscura/analyzers/eye/metrics.py +5 -5
  11. oscura/analyzers/jitter/__init__.py +6 -4
  12. oscura/analyzers/jitter/ber.py +52 -52
  13. oscura/analyzers/jitter/classification.py +156 -0
  14. oscura/analyzers/jitter/decomposition.py +163 -113
  15. oscura/analyzers/jitter/spectrum.py +80 -64
  16. oscura/analyzers/ml/__init__.py +39 -0
  17. oscura/analyzers/ml/features.py +600 -0
  18. oscura/analyzers/ml/signal_classifier.py +604 -0
  19. oscura/analyzers/packet/daq.py +246 -158
  20. oscura/analyzers/packet/parser.py +12 -1
  21. oscura/analyzers/packet/payload.py +50 -2110
  22. oscura/analyzers/packet/payload_analysis.py +361 -181
  23. oscura/analyzers/packet/payload_patterns.py +133 -70
  24. oscura/analyzers/packet/stream.py +84 -23
  25. oscura/analyzers/patterns/__init__.py +26 -5
  26. oscura/analyzers/patterns/anomaly_detection.py +908 -0
  27. oscura/analyzers/patterns/clustering.py +169 -108
  28. oscura/analyzers/patterns/clustering_optimized.py +227 -0
  29. oscura/analyzers/patterns/discovery.py +1 -1
  30. oscura/analyzers/patterns/matching.py +581 -197
  31. oscura/analyzers/patterns/pattern_mining.py +778 -0
  32. oscura/analyzers/patterns/periodic.py +121 -38
  33. oscura/analyzers/patterns/sequences.py +175 -78
  34. oscura/analyzers/power/conduction.py +1 -1
  35. oscura/analyzers/power/soa.py +6 -6
  36. oscura/analyzers/power/switching.py +250 -110
  37. oscura/analyzers/protocol/__init__.py +17 -1
  38. oscura/analyzers/protocols/base.py +6 -6
  39. oscura/analyzers/protocols/ble/__init__.py +38 -0
  40. oscura/analyzers/protocols/ble/analyzer.py +809 -0
  41. oscura/analyzers/protocols/ble/uuids.py +288 -0
  42. oscura/analyzers/protocols/can.py +257 -127
  43. oscura/analyzers/protocols/can_fd.py +107 -80
  44. oscura/analyzers/protocols/flexray.py +139 -80
  45. oscura/analyzers/protocols/hdlc.py +93 -58
  46. oscura/analyzers/protocols/i2c.py +247 -106
  47. oscura/analyzers/protocols/i2s.py +138 -86
  48. oscura/analyzers/protocols/industrial/__init__.py +40 -0
  49. oscura/analyzers/protocols/industrial/bacnet/__init__.py +33 -0
  50. oscura/analyzers/protocols/industrial/bacnet/analyzer.py +708 -0
  51. oscura/analyzers/protocols/industrial/bacnet/encoding.py +412 -0
  52. oscura/analyzers/protocols/industrial/bacnet/services.py +622 -0
  53. oscura/analyzers/protocols/industrial/ethercat/__init__.py +30 -0
  54. oscura/analyzers/protocols/industrial/ethercat/analyzer.py +474 -0
  55. oscura/analyzers/protocols/industrial/ethercat/mailbox.py +339 -0
  56. oscura/analyzers/protocols/industrial/ethercat/topology.py +166 -0
  57. oscura/analyzers/protocols/industrial/modbus/__init__.py +31 -0
  58. oscura/analyzers/protocols/industrial/modbus/analyzer.py +525 -0
  59. oscura/analyzers/protocols/industrial/modbus/crc.py +79 -0
  60. oscura/analyzers/protocols/industrial/modbus/functions.py +436 -0
  61. oscura/analyzers/protocols/industrial/opcua/__init__.py +21 -0
  62. oscura/analyzers/protocols/industrial/opcua/analyzer.py +552 -0
  63. oscura/analyzers/protocols/industrial/opcua/datatypes.py +446 -0
  64. oscura/analyzers/protocols/industrial/opcua/services.py +264 -0
  65. oscura/analyzers/protocols/industrial/profinet/__init__.py +23 -0
  66. oscura/analyzers/protocols/industrial/profinet/analyzer.py +441 -0
  67. oscura/analyzers/protocols/industrial/profinet/dcp.py +263 -0
  68. oscura/analyzers/protocols/industrial/profinet/ptcp.py +200 -0
  69. oscura/analyzers/protocols/jtag.py +180 -98
  70. oscura/analyzers/protocols/lin.py +219 -114
  71. oscura/analyzers/protocols/manchester.py +4 -4
  72. oscura/analyzers/protocols/onewire.py +253 -149
  73. oscura/analyzers/protocols/parallel_bus/__init__.py +20 -0
  74. oscura/analyzers/protocols/parallel_bus/centronics.py +92 -0
  75. oscura/analyzers/protocols/parallel_bus/gpib.py +137 -0
  76. oscura/analyzers/protocols/spi.py +192 -95
  77. oscura/analyzers/protocols/swd.py +321 -167
  78. oscura/analyzers/protocols/uart.py +267 -125
  79. oscura/analyzers/protocols/usb.py +235 -131
  80. oscura/analyzers/side_channel/power.py +17 -12
  81. oscura/analyzers/signal/__init__.py +15 -0
  82. oscura/analyzers/signal/timing_analysis.py +1086 -0
  83. oscura/analyzers/signal_integrity/__init__.py +4 -1
  84. oscura/analyzers/signal_integrity/sparams.py +2 -19
  85. oscura/analyzers/spectral/chunked.py +129 -60
  86. oscura/analyzers/spectral/chunked_fft.py +300 -94
  87. oscura/analyzers/spectral/chunked_wavelet.py +100 -80
  88. oscura/analyzers/statistical/checksum.py +376 -217
  89. oscura/analyzers/statistical/classification.py +229 -107
  90. oscura/analyzers/statistical/entropy.py +78 -53
  91. oscura/analyzers/statistics/correlation.py +407 -211
  92. oscura/analyzers/statistics/outliers.py +2 -2
  93. oscura/analyzers/statistics/streaming.py +30 -5
  94. oscura/analyzers/validation.py +216 -101
  95. oscura/analyzers/waveform/measurements.py +9 -0
  96. oscura/analyzers/waveform/measurements_with_uncertainty.py +31 -15
  97. oscura/analyzers/waveform/spectral.py +500 -228
  98. oscura/api/__init__.py +31 -5
  99. oscura/api/dsl/__init__.py +582 -0
  100. oscura/{dsl → api/dsl}/commands.py +43 -76
  101. oscura/{dsl → api/dsl}/interpreter.py +26 -51
  102. oscura/{dsl → api/dsl}/parser.py +107 -77
  103. oscura/{dsl → api/dsl}/repl.py +2 -2
  104. oscura/api/dsl.py +1 -1
  105. oscura/{integrations → api/integrations}/__init__.py +1 -1
  106. oscura/{integrations → api/integrations}/llm.py +201 -102
  107. oscura/api/operators.py +3 -3
  108. oscura/api/optimization.py +144 -30
  109. oscura/api/rest_server.py +921 -0
  110. oscura/api/server/__init__.py +17 -0
  111. oscura/api/server/dashboard.py +850 -0
  112. oscura/api/server/static/README.md +34 -0
  113. oscura/api/server/templates/base.html +181 -0
  114. oscura/api/server/templates/export.html +120 -0
  115. oscura/api/server/templates/home.html +284 -0
  116. oscura/api/server/templates/protocols.html +58 -0
  117. oscura/api/server/templates/reports.html +43 -0
  118. oscura/api/server/templates/session_detail.html +89 -0
  119. oscura/api/server/templates/sessions.html +83 -0
  120. oscura/api/server/templates/waveforms.html +73 -0
  121. oscura/automotive/__init__.py +8 -1
  122. oscura/automotive/can/__init__.py +10 -0
  123. oscura/automotive/can/checksum.py +3 -1
  124. oscura/automotive/can/dbc_generator.py +590 -0
  125. oscura/automotive/can/message_wrapper.py +121 -74
  126. oscura/automotive/can/patterns.py +98 -21
  127. oscura/automotive/can/session.py +292 -56
  128. oscura/automotive/can/state_machine.py +6 -3
  129. oscura/automotive/can/stimulus_response.py +97 -75
  130. oscura/automotive/dbc/__init__.py +10 -2
  131. oscura/automotive/dbc/generator.py +84 -56
  132. oscura/automotive/dbc/parser.py +6 -6
  133. oscura/automotive/dtc/data.json +17 -102
  134. oscura/automotive/dtc/database.py +2 -2
  135. oscura/automotive/flexray/__init__.py +31 -0
  136. oscura/automotive/flexray/analyzer.py +504 -0
  137. oscura/automotive/flexray/crc.py +185 -0
  138. oscura/automotive/flexray/fibex.py +449 -0
  139. oscura/automotive/j1939/__init__.py +45 -8
  140. oscura/automotive/j1939/analyzer.py +605 -0
  141. oscura/automotive/j1939/spns.py +326 -0
  142. oscura/automotive/j1939/transport.py +306 -0
  143. oscura/automotive/lin/__init__.py +47 -0
  144. oscura/automotive/lin/analyzer.py +612 -0
  145. oscura/automotive/loaders/blf.py +13 -2
  146. oscura/automotive/loaders/csv_can.py +143 -72
  147. oscura/automotive/loaders/dispatcher.py +50 -2
  148. oscura/automotive/loaders/mdf.py +86 -45
  149. oscura/automotive/loaders/pcap.py +111 -61
  150. oscura/automotive/uds/__init__.py +4 -0
  151. oscura/automotive/uds/analyzer.py +725 -0
  152. oscura/automotive/uds/decoder.py +140 -58
  153. oscura/automotive/uds/models.py +7 -1
  154. oscura/automotive/visualization.py +1 -1
  155. oscura/cli/analyze.py +348 -0
  156. oscura/cli/batch.py +142 -122
  157. oscura/cli/benchmark.py +275 -0
  158. oscura/cli/characterize.py +137 -82
  159. oscura/cli/compare.py +224 -131
  160. oscura/cli/completion.py +250 -0
  161. oscura/cli/config_cmd.py +361 -0
  162. oscura/cli/decode.py +164 -87
  163. oscura/cli/export.py +286 -0
  164. oscura/cli/main.py +115 -31
  165. oscura/{onboarding → cli/onboarding}/__init__.py +3 -3
  166. oscura/{onboarding → cli/onboarding}/help.py +80 -58
  167. oscura/{onboarding → cli/onboarding}/tutorials.py +97 -72
  168. oscura/{onboarding → cli/onboarding}/wizard.py +55 -36
  169. oscura/cli/progress.py +147 -0
  170. oscura/cli/shell.py +157 -135
  171. oscura/cli/validate_cmd.py +204 -0
  172. oscura/cli/visualize.py +158 -0
  173. oscura/convenience.py +125 -79
  174. oscura/core/__init__.py +4 -2
  175. oscura/core/backend_selector.py +3 -3
  176. oscura/core/cache.py +126 -15
  177. oscura/core/cancellation.py +1 -1
  178. oscura/{config → core/config}/__init__.py +20 -11
  179. oscura/{config → core/config}/defaults.py +1 -1
  180. oscura/{config → core/config}/loader.py +7 -5
  181. oscura/{config → core/config}/memory.py +5 -5
  182. oscura/{config → core/config}/migration.py +1 -1
  183. oscura/{config → core/config}/pipeline.py +99 -23
  184. oscura/{config → core/config}/preferences.py +1 -1
  185. oscura/{config → core/config}/protocol.py +3 -3
  186. oscura/{config → core/config}/schema.py +426 -272
  187. oscura/{config → core/config}/settings.py +1 -1
  188. oscura/{config → core/config}/thresholds.py +195 -153
  189. oscura/core/correlation.py +5 -6
  190. oscura/core/cross_domain.py +0 -2
  191. oscura/core/debug.py +9 -5
  192. oscura/{extensibility → core/extensibility}/docs.py +158 -70
  193. oscura/{extensibility → core/extensibility}/extensions.py +160 -76
  194. oscura/{extensibility → core/extensibility}/logging.py +1 -1
  195. oscura/{extensibility → core/extensibility}/measurements.py +1 -1
  196. oscura/{extensibility → core/extensibility}/plugins.py +1 -1
  197. oscura/{extensibility → core/extensibility}/templates.py +73 -3
  198. oscura/{extensibility → core/extensibility}/validation.py +1 -1
  199. oscura/core/gpu_backend.py +11 -7
  200. oscura/core/log_query.py +101 -11
  201. oscura/core/logging.py +126 -54
  202. oscura/core/logging_advanced.py +5 -5
  203. oscura/core/memory_limits.py +108 -70
  204. oscura/core/memory_monitor.py +2 -2
  205. oscura/core/memory_progress.py +7 -7
  206. oscura/core/memory_warnings.py +1 -1
  207. oscura/core/numba_backend.py +13 -13
  208. oscura/{plugins → core/plugins}/__init__.py +9 -9
  209. oscura/{plugins → core/plugins}/base.py +7 -7
  210. oscura/{plugins → core/plugins}/cli.py +3 -3
  211. oscura/{plugins → core/plugins}/discovery.py +186 -106
  212. oscura/{plugins → core/plugins}/lifecycle.py +1 -1
  213. oscura/{plugins → core/plugins}/manager.py +7 -7
  214. oscura/{plugins → core/plugins}/registry.py +3 -3
  215. oscura/{plugins → core/plugins}/versioning.py +1 -1
  216. oscura/core/progress.py +16 -1
  217. oscura/core/provenance.py +8 -2
  218. oscura/{schemas → core/schemas}/__init__.py +2 -2
  219. oscura/{schemas → core/schemas}/device_mapping.json +2 -8
  220. oscura/{schemas → core/schemas}/packet_format.json +4 -24
  221. oscura/{schemas → core/schemas}/protocol_definition.json +2 -12
  222. oscura/core/types.py +4 -0
  223. oscura/core/uncertainty.py +3 -3
  224. oscura/correlation/__init__.py +52 -0
  225. oscura/correlation/multi_protocol.py +811 -0
  226. oscura/discovery/auto_decoder.py +117 -35
  227. oscura/discovery/comparison.py +191 -86
  228. oscura/discovery/quality_validator.py +155 -68
  229. oscura/discovery/signal_detector.py +196 -79
  230. oscura/export/__init__.py +18 -8
  231. oscura/export/kaitai_struct.py +513 -0
  232. oscura/export/scapy_layer.py +801 -0
  233. oscura/export/wireshark/generator.py +1 -1
  234. oscura/export/wireshark/templates/dissector.lua.j2 +2 -2
  235. oscura/export/wireshark_dissector.py +746 -0
  236. oscura/guidance/wizard.py +207 -111
  237. oscura/hardware/__init__.py +19 -0
  238. oscura/{acquisition → hardware/acquisition}/__init__.py +4 -4
  239. oscura/{acquisition → hardware/acquisition}/file.py +2 -2
  240. oscura/{acquisition → hardware/acquisition}/hardware.py +7 -7
  241. oscura/{acquisition → hardware/acquisition}/saleae.py +15 -12
  242. oscura/{acquisition → hardware/acquisition}/socketcan.py +1 -1
  243. oscura/{acquisition → hardware/acquisition}/streaming.py +2 -2
  244. oscura/{acquisition → hardware/acquisition}/synthetic.py +3 -3
  245. oscura/{acquisition → hardware/acquisition}/visa.py +33 -11
  246. oscura/hardware/firmware/__init__.py +29 -0
  247. oscura/hardware/firmware/pattern_recognition.py +874 -0
  248. oscura/hardware/hal_detector.py +736 -0
  249. oscura/hardware/security/__init__.py +37 -0
  250. oscura/hardware/security/side_channel_detector.py +1126 -0
  251. oscura/inference/__init__.py +4 -0
  252. oscura/inference/active_learning/observation_table.py +4 -1
  253. oscura/inference/alignment.py +216 -123
  254. oscura/inference/bayesian.py +113 -33
  255. oscura/inference/crc_reverse.py +101 -55
  256. oscura/inference/logic.py +6 -2
  257. oscura/inference/message_format.py +342 -183
  258. oscura/inference/protocol.py +95 -44
  259. oscura/inference/protocol_dsl.py +180 -82
  260. oscura/inference/signal_intelligence.py +1439 -706
  261. oscura/inference/spectral.py +99 -57
  262. oscura/inference/state_machine.py +810 -158
  263. oscura/inference/stream.py +270 -110
  264. oscura/iot/__init__.py +34 -0
  265. oscura/iot/coap/__init__.py +32 -0
  266. oscura/iot/coap/analyzer.py +668 -0
  267. oscura/iot/coap/options.py +212 -0
  268. oscura/iot/lorawan/__init__.py +21 -0
  269. oscura/iot/lorawan/crypto.py +206 -0
  270. oscura/iot/lorawan/decoder.py +801 -0
  271. oscura/iot/lorawan/mac_commands.py +341 -0
  272. oscura/iot/mqtt/__init__.py +27 -0
  273. oscura/iot/mqtt/analyzer.py +999 -0
  274. oscura/iot/mqtt/properties.py +315 -0
  275. oscura/iot/zigbee/__init__.py +31 -0
  276. oscura/iot/zigbee/analyzer.py +615 -0
  277. oscura/iot/zigbee/security.py +153 -0
  278. oscura/iot/zigbee/zcl.py +349 -0
  279. oscura/jupyter/display.py +125 -45
  280. oscura/{exploratory → jupyter/exploratory}/__init__.py +8 -8
  281. oscura/{exploratory → jupyter/exploratory}/error_recovery.py +298 -141
  282. oscura/jupyter/exploratory/fuzzy.py +746 -0
  283. oscura/{exploratory → jupyter/exploratory}/fuzzy_advanced.py +258 -100
  284. oscura/{exploratory → jupyter/exploratory}/legacy.py +464 -242
  285. oscura/{exploratory → jupyter/exploratory}/parse.py +167 -145
  286. oscura/{exploratory → jupyter/exploratory}/recovery.py +119 -87
  287. oscura/jupyter/exploratory/sync.py +612 -0
  288. oscura/{exploratory → jupyter/exploratory}/unknown.py +299 -176
  289. oscura/jupyter/magic.py +4 -4
  290. oscura/{ui → jupyter/ui}/__init__.py +2 -2
  291. oscura/{ui → jupyter/ui}/formatters.py +3 -3
  292. oscura/{ui → jupyter/ui}/progressive_display.py +153 -82
  293. oscura/loaders/__init__.py +183 -67
  294. oscura/loaders/binary.py +88 -1
  295. oscura/loaders/chipwhisperer.py +153 -137
  296. oscura/loaders/configurable.py +208 -86
  297. oscura/loaders/csv_loader.py +458 -215
  298. oscura/loaders/hdf5_loader.py +278 -119
  299. oscura/loaders/lazy.py +87 -54
  300. oscura/loaders/mmap_loader.py +1 -1
  301. oscura/loaders/numpy_loader.py +253 -116
  302. oscura/loaders/pcap.py +226 -151
  303. oscura/loaders/rigol.py +110 -49
  304. oscura/loaders/sigrok.py +201 -78
  305. oscura/loaders/tdms.py +81 -58
  306. oscura/loaders/tektronix.py +291 -174
  307. oscura/loaders/touchstone.py +182 -87
  308. oscura/loaders/tss.py +456 -0
  309. oscura/loaders/vcd.py +215 -117
  310. oscura/loaders/wav.py +155 -68
  311. oscura/reporting/__init__.py +9 -0
  312. oscura/reporting/analyze.py +352 -146
  313. oscura/reporting/argument_preparer.py +69 -14
  314. oscura/reporting/auto_report.py +97 -61
  315. oscura/reporting/batch.py +131 -58
  316. oscura/reporting/chart_selection.py +57 -45
  317. oscura/reporting/comparison.py +63 -17
  318. oscura/reporting/content/executive.py +76 -24
  319. oscura/reporting/core_formats/multi_format.py +11 -8
  320. oscura/reporting/engine.py +312 -158
  321. oscura/reporting/enhanced_reports.py +949 -0
  322. oscura/reporting/export.py +86 -43
  323. oscura/reporting/formatting/numbers.py +69 -42
  324. oscura/reporting/html.py +139 -58
  325. oscura/reporting/index.py +137 -65
  326. oscura/reporting/output.py +158 -67
  327. oscura/reporting/pdf.py +67 -102
  328. oscura/reporting/plots.py +191 -112
  329. oscura/reporting/sections.py +88 -47
  330. oscura/reporting/standards.py +104 -61
  331. oscura/reporting/summary_generator.py +75 -55
  332. oscura/reporting/tables.py +138 -54
  333. oscura/reporting/templates/enhanced/protocol_re.html +525 -0
  334. oscura/sessions/__init__.py +14 -23
  335. oscura/sessions/base.py +3 -3
  336. oscura/sessions/blackbox.py +106 -10
  337. oscura/sessions/generic.py +2 -2
  338. oscura/sessions/legacy.py +783 -0
  339. oscura/side_channel/__init__.py +63 -0
  340. oscura/side_channel/dpa.py +1025 -0
  341. oscura/utils/__init__.py +15 -1
  342. oscura/utils/bitwise.py +118 -0
  343. oscura/{builders → utils/builders}/__init__.py +1 -1
  344. oscura/{comparison → utils/comparison}/__init__.py +6 -6
  345. oscura/{comparison → utils/comparison}/compare.py +202 -101
  346. oscura/{comparison → utils/comparison}/golden.py +83 -63
  347. oscura/{comparison → utils/comparison}/limits.py +313 -89
  348. oscura/{comparison → utils/comparison}/mask.py +151 -45
  349. oscura/{comparison → utils/comparison}/trace_diff.py +1 -1
  350. oscura/{comparison → utils/comparison}/visualization.py +147 -89
  351. oscura/{component → utils/component}/__init__.py +3 -3
  352. oscura/{component → utils/component}/impedance.py +122 -58
  353. oscura/{component → utils/component}/reactive.py +165 -168
  354. oscura/{component → utils/component}/transmission_line.py +3 -3
  355. oscura/{filtering → utils/filtering}/__init__.py +6 -6
  356. oscura/{filtering → utils/filtering}/base.py +1 -1
  357. oscura/{filtering → utils/filtering}/convenience.py +2 -2
  358. oscura/{filtering → utils/filtering}/design.py +169 -93
  359. oscura/{filtering → utils/filtering}/filters.py +2 -2
  360. oscura/{filtering → utils/filtering}/introspection.py +2 -2
  361. oscura/utils/geometry.py +31 -0
  362. oscura/utils/imports.py +184 -0
  363. oscura/utils/lazy.py +1 -1
  364. oscura/{math → utils/math}/__init__.py +2 -2
  365. oscura/{math → utils/math}/arithmetic.py +114 -48
  366. oscura/{math → utils/math}/interpolation.py +139 -106
  367. oscura/utils/memory.py +129 -66
  368. oscura/utils/memory_advanced.py +92 -9
  369. oscura/utils/memory_extensions.py +10 -8
  370. oscura/{optimization → utils/optimization}/__init__.py +1 -1
  371. oscura/{optimization → utils/optimization}/search.py +2 -2
  372. oscura/utils/performance/__init__.py +58 -0
  373. oscura/utils/performance/caching.py +889 -0
  374. oscura/utils/performance/lsh_clustering.py +333 -0
  375. oscura/utils/performance/memory_optimizer.py +699 -0
  376. oscura/utils/performance/optimizations.py +675 -0
  377. oscura/utils/performance/parallel.py +654 -0
  378. oscura/utils/performance/profiling.py +661 -0
  379. oscura/{pipeline → utils/pipeline}/base.py +1 -1
  380. oscura/{pipeline → utils/pipeline}/composition.py +1 -1
  381. oscura/{pipeline → utils/pipeline}/parallel.py +3 -2
  382. oscura/{pipeline → utils/pipeline}/pipeline.py +1 -1
  383. oscura/{pipeline → utils/pipeline}/reverse_engineering.py +412 -221
  384. oscura/{search → utils/search}/__init__.py +3 -3
  385. oscura/{search → utils/search}/anomaly.py +188 -58
  386. oscura/utils/search/context.py +294 -0
  387. oscura/{search → utils/search}/pattern.py +138 -10
  388. oscura/utils/serial.py +51 -0
  389. oscura/utils/storage/__init__.py +61 -0
  390. oscura/utils/storage/database.py +1166 -0
  391. oscura/{streaming → utils/streaming}/chunked.py +302 -143
  392. oscura/{streaming → utils/streaming}/progressive.py +1 -1
  393. oscura/{streaming → utils/streaming}/realtime.py +3 -2
  394. oscura/{triggering → utils/triggering}/__init__.py +6 -6
  395. oscura/{triggering → utils/triggering}/base.py +6 -6
  396. oscura/{triggering → utils/triggering}/edge.py +2 -2
  397. oscura/{triggering → utils/triggering}/pattern.py +2 -2
  398. oscura/{triggering → utils/triggering}/pulse.py +115 -74
  399. oscura/{triggering → utils/triggering}/window.py +2 -2
  400. oscura/utils/validation.py +32 -0
  401. oscura/validation/__init__.py +121 -0
  402. oscura/{compliance → validation/compliance}/__init__.py +5 -5
  403. oscura/{compliance → validation/compliance}/advanced.py +5 -5
  404. oscura/{compliance → validation/compliance}/masks.py +1 -1
  405. oscura/{compliance → validation/compliance}/reporting.py +127 -53
  406. oscura/{compliance → validation/compliance}/testing.py +114 -52
  407. oscura/validation/compliance_tests.py +915 -0
  408. oscura/validation/fuzzer.py +990 -0
  409. oscura/validation/grammar_tests.py +596 -0
  410. oscura/validation/grammar_validator.py +904 -0
  411. oscura/validation/hil_testing.py +977 -0
  412. oscura/{quality → validation/quality}/__init__.py +4 -4
  413. oscura/{quality → validation/quality}/ensemble.py +251 -171
  414. oscura/{quality → validation/quality}/explainer.py +3 -3
  415. oscura/{quality → validation/quality}/scoring.py +1 -1
  416. oscura/{quality → validation/quality}/warnings.py +4 -4
  417. oscura/validation/regression_suite.py +808 -0
  418. oscura/validation/replay.py +788 -0
  419. oscura/{testing → validation/testing}/__init__.py +2 -2
  420. oscura/{testing → validation/testing}/synthetic.py +5 -5
  421. oscura/visualization/__init__.py +9 -0
  422. oscura/visualization/accessibility.py +1 -1
  423. oscura/visualization/annotations.py +64 -67
  424. oscura/visualization/colors.py +7 -7
  425. oscura/visualization/digital.py +180 -81
  426. oscura/visualization/eye.py +236 -85
  427. oscura/visualization/interactive.py +320 -143
  428. oscura/visualization/jitter.py +587 -247
  429. oscura/visualization/layout.py +169 -134
  430. oscura/visualization/optimization.py +103 -52
  431. oscura/visualization/palettes.py +1 -1
  432. oscura/visualization/power.py +427 -211
  433. oscura/visualization/power_extended.py +626 -297
  434. oscura/visualization/presets.py +2 -0
  435. oscura/visualization/protocols.py +495 -181
  436. oscura/visualization/render.py +79 -63
  437. oscura/visualization/reverse_engineering.py +171 -124
  438. oscura/visualization/signal_integrity.py +460 -279
  439. oscura/visualization/specialized.py +190 -100
  440. oscura/visualization/spectral.py +670 -255
  441. oscura/visualization/thumbnails.py +166 -137
  442. oscura/visualization/waveform.py +150 -63
  443. oscura/workflows/__init__.py +3 -0
  444. oscura/{batch → workflows/batch}/__init__.py +5 -5
  445. oscura/{batch → workflows/batch}/advanced.py +150 -75
  446. oscura/workflows/batch/aggregate.py +531 -0
  447. oscura/workflows/batch/analyze.py +236 -0
  448. oscura/{batch → workflows/batch}/logging.py +2 -2
  449. oscura/{batch → workflows/batch}/metrics.py +1 -1
  450. oscura/workflows/complete_re.py +1144 -0
  451. oscura/workflows/compliance.py +44 -54
  452. oscura/workflows/digital.py +197 -51
  453. oscura/workflows/legacy/__init__.py +12 -0
  454. oscura/{workflow → workflows/legacy}/dag.py +4 -1
  455. oscura/workflows/multi_trace.py +9 -9
  456. oscura/workflows/power.py +42 -62
  457. oscura/workflows/protocol.py +82 -49
  458. oscura/workflows/reverse_engineering.py +351 -150
  459. oscura/workflows/signal_integrity.py +157 -82
  460. oscura-0.7.0.dist-info/METADATA +661 -0
  461. oscura-0.7.0.dist-info/RECORD +591 -0
  462. oscura/batch/aggregate.py +0 -300
  463. oscura/batch/analyze.py +0 -139
  464. oscura/dsl/__init__.py +0 -73
  465. oscura/exceptions.py +0 -59
  466. oscura/exploratory/fuzzy.py +0 -513
  467. oscura/exploratory/sync.py +0 -384
  468. oscura/exporters/__init__.py +0 -94
  469. oscura/exporters/csv.py +0 -303
  470. oscura/exporters/exporters.py +0 -44
  471. oscura/exporters/hdf5.py +0 -217
  472. oscura/exporters/html_export.py +0 -701
  473. oscura/exporters/json_export.py +0 -291
  474. oscura/exporters/markdown_export.py +0 -367
  475. oscura/exporters/matlab_export.py +0 -354
  476. oscura/exporters/npz_export.py +0 -219
  477. oscura/exporters/spice_export.py +0 -210
  478. oscura/search/context.py +0 -149
  479. oscura/session/__init__.py +0 -34
  480. oscura/session/annotations.py +0 -289
  481. oscura/session/history.py +0 -313
  482. oscura/session/session.py +0 -520
  483. oscura/workflow/__init__.py +0 -13
  484. oscura-0.5.1.dist-info/METADATA +0 -583
  485. oscura-0.5.1.dist-info/RECORD +0 -481
  486. /oscura/core/{config.py → config/legacy.py} +0 -0
  487. /oscura/{extensibility → core/extensibility}/__init__.py +0 -0
  488. /oscura/{extensibility → core/extensibility}/registry.py +0 -0
  489. /oscura/{plugins → core/plugins}/isolation.py +0 -0
  490. /oscura/{schemas → core/schemas}/bus_configuration.json +0 -0
  491. /oscura/{builders → utils/builders}/signal_builder.py +0 -0
  492. /oscura/{optimization → utils/optimization}/parallel.py +0 -0
  493. /oscura/{pipeline → utils/pipeline}/__init__.py +0 -0
  494. /oscura/{streaming → utils/streaming}/__init__.py +0 -0
  495. {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/WHEEL +0 -0
  496. {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/entry_points.txt +0 -0
  497. {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,436 @@
1
+ """Modbus function code parsers.
2
+
3
+ This module provides parsers for all standard Modbus function codes,
4
+ supporting both request and response parsing.
5
+
6
+ References:
7
+ Modbus Application Protocol V1.1b3
8
+ https://modbus.org/docs/Modbus_Application_Protocol_V1_1b3.pdf
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from typing import Any
14
+
15
+
16
+ def parse_read_coils_request(data: bytes) -> dict[str, Any]:
17
+ """Parse Read Coils (FC 01) request.
18
+
19
+ Request Format:
20
+ - Starting Address (2 bytes, big-endian)
21
+ - Quantity of Coils (2 bytes, big-endian, 1-2000)
22
+
23
+ Args:
24
+ data: Function data (excludes address and function code).
25
+
26
+ Returns:
27
+ Parsed request data.
28
+
29
+ Raises:
30
+ ValueError: If data is invalid.
31
+ """
32
+ if len(data) < 4:
33
+ raise ValueError("Insufficient data for Read Coils request")
34
+
35
+ starting_address = int.from_bytes(data[0:2], "big")
36
+ quantity = int.from_bytes(data[2:4], "big")
37
+
38
+ if quantity < 1 or quantity > 2000:
39
+ raise ValueError(f"Invalid quantity: {quantity} (must be 1-2000)")
40
+
41
+ return {
42
+ "starting_address": starting_address,
43
+ "quantity": quantity,
44
+ }
45
+
46
+
47
+ def parse_read_coils_response(data: bytes) -> dict[str, Any]:
48
+ """Parse Read Coils (FC 01) response.
49
+
50
+ Response Format:
51
+ - Byte Count (1 byte)
52
+ - Coil Status (N bytes, LSB first)
53
+
54
+ Args:
55
+ data: Function data.
56
+
57
+ Returns:
58
+ Parsed response data with coil values.
59
+
60
+ Raises:
61
+ ValueError: If data is invalid.
62
+ """
63
+ if len(data) < 1:
64
+ raise ValueError("Insufficient data for Read Coils response")
65
+
66
+ byte_count = data[0]
67
+ if len(data) < 1 + byte_count:
68
+ raise ValueError("Insufficient coil data")
69
+
70
+ # Parse coil values (each byte contains 8 coils, LSB first)
71
+ coils = []
72
+ for i in range(byte_count):
73
+ byte_val = data[1 + i]
74
+ for bit in range(8):
75
+ coils.append(bool(byte_val & (1 << bit)))
76
+
77
+ return {
78
+ "byte_count": byte_count,
79
+ "coils": coils,
80
+ }
81
+
82
+
83
+ def parse_read_discrete_inputs_request(data: bytes) -> dict[str, Any]:
84
+ """Parse Read Discrete Inputs (FC 02) request.
85
+
86
+ Same format as Read Coils request.
87
+
88
+ Args:
89
+ data: Function data.
90
+
91
+ Returns:
92
+ Parsed request data.
93
+ """
94
+ return parse_read_coils_request(data)
95
+
96
+
97
+ def parse_read_discrete_inputs_response(data: bytes) -> dict[str, Any]:
98
+ """Parse Read Discrete Inputs (FC 02) response.
99
+
100
+ Same format as Read Coils response.
101
+
102
+ Args:
103
+ data: Function data.
104
+
105
+ Returns:
106
+ Parsed response data.
107
+ """
108
+ return parse_read_coils_response(data)
109
+
110
+
111
+ def parse_read_holding_registers_request(data: bytes) -> dict[str, Any]:
112
+ """Parse Read Holding Registers (FC 03) request.
113
+
114
+ Request Format:
115
+ - Starting Address (2 bytes, big-endian)
116
+ - Quantity of Registers (2 bytes, big-endian, 1-125)
117
+
118
+ Args:
119
+ data: Function data.
120
+
121
+ Returns:
122
+ Parsed request data.
123
+
124
+ Raises:
125
+ ValueError: If data is invalid.
126
+ """
127
+ if len(data) < 4:
128
+ raise ValueError("Insufficient data for Read Holding Registers request")
129
+
130
+ starting_address = int.from_bytes(data[0:2], "big")
131
+ quantity = int.from_bytes(data[2:4], "big")
132
+
133
+ if quantity < 1 or quantity > 125:
134
+ raise ValueError(f"Invalid quantity: {quantity} (must be 1-125)")
135
+
136
+ return {
137
+ "starting_address": starting_address,
138
+ "quantity": quantity,
139
+ }
140
+
141
+
142
+ def parse_read_holding_registers_response(data: bytes) -> dict[str, Any]:
143
+ """Parse Read Holding Registers (FC 03) response.
144
+
145
+ Response Format:
146
+ - Byte Count (1 byte)
147
+ - Register Values (N bytes, 2 bytes per register, big-endian)
148
+
149
+ Args:
150
+ data: Function data.
151
+
152
+ Returns:
153
+ Parsed response data with register values.
154
+
155
+ Raises:
156
+ ValueError: If data is invalid.
157
+ """
158
+ if len(data) < 1:
159
+ raise ValueError("Insufficient data for Read Holding Registers response")
160
+
161
+ byte_count = data[0]
162
+ if len(data) < 1 + byte_count:
163
+ raise ValueError("Insufficient register data")
164
+
165
+ if byte_count % 2 != 0:
166
+ raise ValueError(f"Byte count must be even, got {byte_count}")
167
+
168
+ # Parse registers (2 bytes each, big-endian)
169
+ registers = []
170
+ for i in range(byte_count // 2):
171
+ offset = 1 + i * 2
172
+ reg_value = int.from_bytes(data[offset : offset + 2], "big")
173
+ registers.append(reg_value)
174
+
175
+ return {
176
+ "byte_count": byte_count,
177
+ "registers": registers,
178
+ }
179
+
180
+
181
+ def parse_read_input_registers_request(data: bytes) -> dict[str, Any]:
182
+ """Parse Read Input Registers (FC 04) request.
183
+
184
+ Same format as Read Holding Registers request.
185
+
186
+ Args:
187
+ data: Function data.
188
+
189
+ Returns:
190
+ Parsed request data.
191
+ """
192
+ return parse_read_holding_registers_request(data)
193
+
194
+
195
+ def parse_read_input_registers_response(data: bytes) -> dict[str, Any]:
196
+ """Parse Read Input Registers (FC 04) response.
197
+
198
+ Same format as Read Holding Registers response.
199
+
200
+ Args:
201
+ data: Function data.
202
+
203
+ Returns:
204
+ Parsed response data.
205
+ """
206
+ return parse_read_holding_registers_response(data)
207
+
208
+
209
+ def parse_write_single_coil(data: bytes) -> dict[str, Any]:
210
+ """Parse Write Single Coil (FC 05) request/response.
211
+
212
+ Format:
213
+ - Output Address (2 bytes, big-endian)
214
+ - Output Value (2 bytes, 0x0000 or 0xFF00)
215
+
216
+ Args:
217
+ data: Function data.
218
+
219
+ Returns:
220
+ Parsed data.
221
+
222
+ Raises:
223
+ ValueError: If data is invalid.
224
+ """
225
+ if len(data) < 4:
226
+ raise ValueError("Insufficient data for Write Single Coil")
227
+
228
+ output_address = int.from_bytes(data[0:2], "big")
229
+ output_value = int.from_bytes(data[2:4], "big")
230
+
231
+ if output_value not in (0x0000, 0xFF00):
232
+ raise ValueError(f"Invalid coil value: 0x{output_value:04X} (must be 0x0000 or 0xFF00)")
233
+
234
+ return {
235
+ "output_address": output_address,
236
+ "output_value": output_value,
237
+ "coil_state": output_value == 0xFF00,
238
+ }
239
+
240
+
241
+ def parse_write_single_register(data: bytes) -> dict[str, Any]:
242
+ """Parse Write Single Register (FC 06) request/response.
243
+
244
+ Format:
245
+ - Register Address (2 bytes, big-endian)
246
+ - Register Value (2 bytes, big-endian)
247
+
248
+ Args:
249
+ data: Function data.
250
+
251
+ Returns:
252
+ Parsed data.
253
+
254
+ Raises:
255
+ ValueError: If data is invalid.
256
+ """
257
+ if len(data) < 4:
258
+ raise ValueError("Insufficient data for Write Single Register")
259
+
260
+ register_address = int.from_bytes(data[0:2], "big")
261
+ register_value = int.from_bytes(data[2:4], "big")
262
+
263
+ return {
264
+ "register_address": register_address,
265
+ "register_value": register_value,
266
+ }
267
+
268
+
269
+ def parse_write_multiple_coils_request(data: bytes) -> dict[str, Any]:
270
+ """Parse Write Multiple Coils (FC 15) request.
271
+
272
+ Request Format:
273
+ - Starting Address (2 bytes, big-endian)
274
+ - Quantity of Outputs (2 bytes, big-endian, 1-1968)
275
+ - Byte Count (1 byte)
276
+ - Output Values (N bytes)
277
+
278
+ Args:
279
+ data: Function data.
280
+
281
+ Returns:
282
+ Parsed request data.
283
+
284
+ Raises:
285
+ ValueError: If data is invalid.
286
+ """
287
+ if len(data) < 5:
288
+ raise ValueError("Insufficient data for Write Multiple Coils request")
289
+
290
+ starting_address = int.from_bytes(data[0:2], "big")
291
+ quantity = int.from_bytes(data[2:4], "big")
292
+ byte_count = data[4]
293
+
294
+ if quantity < 1 or quantity > 1968:
295
+ raise ValueError(f"Invalid quantity: {quantity} (must be 1-1968)")
296
+
297
+ if len(data) < 5 + byte_count:
298
+ raise ValueError("Insufficient coil data")
299
+
300
+ # Parse coil values
301
+ coils: list[bool] = []
302
+ for i in range(byte_count):
303
+ byte_val = data[5 + i]
304
+ for bit in range(8):
305
+ if len(coils) < quantity:
306
+ coils.append(bool(byte_val & (1 << bit)))
307
+
308
+ return {
309
+ "starting_address": starting_address,
310
+ "quantity": quantity,
311
+ "byte_count": byte_count,
312
+ "coils": coils,
313
+ }
314
+
315
+
316
+ def parse_write_multiple_coils_response(data: bytes) -> dict[str, Any]:
317
+ """Parse Write Multiple Coils (FC 15) response.
318
+
319
+ Response Format:
320
+ - Starting Address (2 bytes, big-endian)
321
+ - Quantity of Outputs (2 bytes, big-endian)
322
+
323
+ Args:
324
+ data: Function data.
325
+
326
+ Returns:
327
+ Parsed response data.
328
+
329
+ Raises:
330
+ ValueError: If data is invalid.
331
+ """
332
+ if len(data) < 4:
333
+ raise ValueError("Insufficient data for Write Multiple Coils response")
334
+
335
+ starting_address = int.from_bytes(data[0:2], "big")
336
+ quantity = int.from_bytes(data[2:4], "big")
337
+
338
+ return {
339
+ "starting_address": starting_address,
340
+ "quantity": quantity,
341
+ }
342
+
343
+
344
+ def parse_write_multiple_registers_request(data: bytes) -> dict[str, Any]:
345
+ """Parse Write Multiple Registers (FC 16) request.
346
+
347
+ Request Format:
348
+ - Starting Address (2 bytes, big-endian)
349
+ - Quantity of Registers (2 bytes, big-endian, 1-123)
350
+ - Byte Count (1 byte)
351
+ - Register Values (N bytes, 2 bytes per register)
352
+
353
+ Args:
354
+ data: Function data.
355
+
356
+ Returns:
357
+ Parsed request data.
358
+
359
+ Raises:
360
+ ValueError: If data is invalid.
361
+ """
362
+ if len(data) < 5:
363
+ raise ValueError("Insufficient data for Write Multiple Registers request")
364
+
365
+ starting_address = int.from_bytes(data[0:2], "big")
366
+ quantity = int.from_bytes(data[2:4], "big")
367
+ byte_count = data[4]
368
+
369
+ if quantity < 1 or quantity > 123:
370
+ raise ValueError(f"Invalid quantity: {quantity} (must be 1-123)")
371
+
372
+ if byte_count != quantity * 2:
373
+ raise ValueError(f"Byte count mismatch: {byte_count} != {quantity * 2}")
374
+
375
+ if len(data) < 5 + byte_count:
376
+ raise ValueError("Insufficient register data")
377
+
378
+ # Parse registers
379
+ registers = []
380
+ for i in range(quantity):
381
+ offset = 5 + i * 2
382
+ reg_value = int.from_bytes(data[offset : offset + 2], "big")
383
+ registers.append(reg_value)
384
+
385
+ return {
386
+ "starting_address": starting_address,
387
+ "quantity": quantity,
388
+ "byte_count": byte_count,
389
+ "registers": registers,
390
+ }
391
+
392
+
393
+ def parse_write_multiple_registers_response(data: bytes) -> dict[str, Any]:
394
+ """Parse Write Multiple Registers (FC 16) response.
395
+
396
+ Response Format:
397
+ - Starting Address (2 bytes, big-endian)
398
+ - Quantity of Registers (2 bytes, big-endian)
399
+
400
+ Args:
401
+ data: Function data.
402
+
403
+ Returns:
404
+ Parsed response data.
405
+
406
+ Raises:
407
+ ValueError: If data is invalid.
408
+ """
409
+ if len(data) < 4:
410
+ raise ValueError("Insufficient data for Write Multiple Registers response")
411
+
412
+ starting_address = int.from_bytes(data[0:2], "big")
413
+ quantity = int.from_bytes(data[2:4], "big")
414
+
415
+ return {
416
+ "starting_address": starting_address,
417
+ "quantity": quantity,
418
+ }
419
+
420
+
421
+ __all__ = [
422
+ "parse_read_coils_request",
423
+ "parse_read_coils_response",
424
+ "parse_read_discrete_inputs_request",
425
+ "parse_read_discrete_inputs_response",
426
+ "parse_read_holding_registers_request",
427
+ "parse_read_holding_registers_response",
428
+ "parse_read_input_registers_request",
429
+ "parse_read_input_registers_response",
430
+ "parse_write_multiple_coils_request",
431
+ "parse_write_multiple_coils_response",
432
+ "parse_write_multiple_registers_request",
433
+ "parse_write_multiple_registers_response",
434
+ "parse_write_single_coil",
435
+ "parse_write_single_register",
436
+ ]
@@ -0,0 +1,21 @@
1
+ """OPC UA (Unified Architecture) protocol analyzer.
2
+
3
+ This package provides comprehensive OPC UA binary protocol analysis for
4
+ industrial automation and SCADA systems.
5
+
6
+ Example:
7
+ >>> from oscura.analyzers.protocols.industrial.opcua import OPCUAAnalyzer
8
+ >>> analyzer = OPCUAAnalyzer()
9
+ >>> # Parse OPC UA Hello message
10
+ >>> hello = bytes([0x48, 0x45, 0x4C, 0x46, ...]) # "HEL" + "F"
11
+ >>> msg = analyzer.parse_message(hello, timestamp=0.0)
12
+ >>> print(f"{msg.message_type}: {msg.decoded_service}")
13
+ """
14
+
15
+ from oscura.analyzers.protocols.industrial.opcua.analyzer import (
16
+ OPCUAAnalyzer,
17
+ OPCUAMessage,
18
+ OPCUANode,
19
+ )
20
+
21
+ __all__ = ["OPCUAAnalyzer", "OPCUAMessage", "OPCUANode"]