oscura 0.0.1__py3-none-any.whl → 0.1.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 (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.0.dist-info/METADATA +300 -0
  460. oscura-0.1.0.dist-info/RECORD +463 -0
  461. oscura-0.1.0.dist-info/entry_points.txt +2 -0
  462. {oscura-0.0.1.dist-info → oscura-0.1.0.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.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,809 @@
1
+ """Sequence pattern detection and request-response correlation.
2
+
3
+ - RE-SEQ-002: Sequence Pattern Detection
4
+ - RE-SEQ-003: Request-Response Correlation
5
+
6
+ This module provides tools for detecting sequential patterns in message
7
+ streams, identifying request-response pairs, and analyzing communication
8
+ flows.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from collections import defaultdict
14
+ from collections.abc import Callable, Sequence
15
+ from dataclasses import dataclass, field
16
+ from typing import Any
17
+
18
+ import numpy as np
19
+
20
+
21
+ @dataclass
22
+ class SequencePattern:
23
+ """A detected sequence pattern.
24
+
25
+ Implements RE-SEQ-002: Sequence pattern representation.
26
+
27
+ Attributes:
28
+ pattern: List of message identifiers in sequence.
29
+ frequency: Number of occurrences.
30
+ positions: Starting positions in stream.
31
+ confidence: Detection confidence (0-1).
32
+ avg_gap: Average gap between elements.
33
+ gap_variance: Variance in inter-element gaps.
34
+ """
35
+
36
+ pattern: list[Any]
37
+ frequency: int
38
+ positions: list[int] = field(default_factory=list)
39
+ confidence: float = 0.0
40
+ avg_gap: float = 0.0
41
+ gap_variance: float = 0.0
42
+
43
+
44
+ @dataclass
45
+ class RequestResponsePair:
46
+ """A correlated request-response pair.
47
+
48
+ Implements RE-SEQ-003: Request-response pair.
49
+
50
+ Attributes:
51
+ request_index: Index of request message.
52
+ response_index: Index of response message.
53
+ request: Request message data.
54
+ response: Response message data.
55
+ latency: Time between request and response.
56
+ correlation_id: Detected correlation identifier.
57
+ confidence: Correlation confidence (0-1).
58
+ """
59
+
60
+ request_index: int
61
+ response_index: int
62
+ request: Any
63
+ response: Any
64
+ latency: float
65
+ correlation_id: bytes | int | None = None
66
+ confidence: float = 0.0
67
+
68
+
69
+ @dataclass
70
+ class CommunicationFlow:
71
+ """A complete communication flow.
72
+
73
+ Implements RE-SEQ-003: Communication flow.
74
+
75
+ Attributes:
76
+ flow_id: Unique flow identifier.
77
+ messages: List of messages in flow.
78
+ pairs: Request-response pairs.
79
+ direction: Primary direction ('request_first' or 'response_first').
80
+ participants: Identified participants.
81
+ duration: Total flow duration.
82
+ """
83
+
84
+ flow_id: int
85
+ messages: list[Any]
86
+ pairs: list[RequestResponsePair]
87
+ direction: str
88
+ participants: list[str]
89
+ duration: float
90
+
91
+
92
+ class SequencePatternDetector:
93
+ """Detect sequential patterns in message streams.
94
+
95
+ Implements RE-SEQ-002: Sequence Pattern Detection.
96
+
97
+ Identifies recurring patterns of message types or values in
98
+ communication streams.
99
+
100
+ Example:
101
+ >>> detector = SequencePatternDetector()
102
+ >>> patterns = detector.detect_patterns(messages, key=lambda m: m.type)
103
+ """
104
+
105
+ def __init__(
106
+ self,
107
+ min_pattern_length: int = 2,
108
+ max_pattern_length: int = 10,
109
+ min_frequency: int = 2,
110
+ max_gap: float | None = None,
111
+ ) -> None:
112
+ """Initialize detector.
113
+
114
+ Args:
115
+ min_pattern_length: Minimum pattern length.
116
+ max_pattern_length: Maximum pattern length.
117
+ min_frequency: Minimum occurrences to consider.
118
+ max_gap: Maximum allowed gap between pattern elements.
119
+ """
120
+ self.min_pattern_length = min_pattern_length
121
+ self.max_pattern_length = max_pattern_length
122
+ self.min_frequency = min_frequency
123
+ self.max_gap = max_gap
124
+
125
+ def detect_patterns(
126
+ self,
127
+ messages: Sequence[Any],
128
+ key: Callable[[Any], Any] | None = None,
129
+ timestamp_key: Callable[[Any], float] | None = None,
130
+ ) -> list[SequencePattern]:
131
+ """Detect sequential patterns in message stream.
132
+
133
+ Implements RE-SEQ-002: Pattern detection workflow.
134
+
135
+ Args:
136
+ messages: Sequence of messages.
137
+ key: Function to extract message identifier.
138
+ timestamp_key: Function to extract timestamp.
139
+
140
+ Returns:
141
+ List of detected patterns.
142
+
143
+ Example:
144
+ >>> patterns = detector.detect_patterns(
145
+ ... messages,
146
+ ... key=lambda m: m.get('type'),
147
+ ... timestamp_key=lambda m: m.get('timestamp')
148
+ ... )
149
+ """
150
+ if not messages:
151
+ return []
152
+
153
+ # Extract identifiers
154
+ if key is not None:
155
+ identifiers = [key(m) for m in messages]
156
+ else:
157
+ identifiers = list(messages)
158
+
159
+ # Extract timestamps if provided
160
+ timestamps = None
161
+ if timestamp_key is not None:
162
+ timestamps = [timestamp_key(m) for m in messages]
163
+
164
+ # Find all n-grams
165
+ candidates = self._find_ngram_patterns(identifiers, timestamps)
166
+
167
+ # Filter and score
168
+ patterns = self._score_patterns(candidates, identifiers, timestamps)
169
+
170
+ # Sort by confidence
171
+ patterns.sort(key=lambda p: (-p.confidence, -p.frequency))
172
+
173
+ return patterns
174
+
175
+ def find_repeating_sequences(
176
+ self,
177
+ messages: Sequence[Any],
178
+ key: Callable[[Any], Any] | None = None,
179
+ ) -> list[tuple[list[Any], int, list[int]]]:
180
+ """Find exactly repeating message sequences.
181
+
182
+ Implements RE-SEQ-002: Exact sequence detection.
183
+
184
+ Args:
185
+ messages: Sequence of messages.
186
+ key: Function to extract message identifier.
187
+
188
+ Returns:
189
+ List of (sequence, count, positions) tuples.
190
+ """
191
+ if not messages:
192
+ return []
193
+
194
+ # Extract identifiers
195
+ if key is not None:
196
+ identifiers = tuple(key(m) for m in messages)
197
+ else:
198
+ identifiers = tuple(messages)
199
+
200
+ results = []
201
+
202
+ for length in range(self.min_pattern_length, self.max_pattern_length + 1):
203
+ # Count n-grams
204
+ ngram_positions = defaultdict(list)
205
+
206
+ for i in range(len(identifiers) - length + 1):
207
+ ngram = identifiers[i : i + length]
208
+ ngram_positions[ngram].append(i)
209
+
210
+ # Filter by frequency
211
+ for ngram, positions in ngram_positions.items():
212
+ if len(positions) >= self.min_frequency:
213
+ results.append((list(ngram), len(positions), positions))
214
+
215
+ # Sort by frequency
216
+ results.sort(key=lambda x: -x[1])
217
+
218
+ return results
219
+
220
+ def detect_periodic_patterns(
221
+ self,
222
+ messages: Sequence[Any],
223
+ key: Callable[[Any], Any] | None = None,
224
+ timestamp_key: Callable[[Any], float] | None = None,
225
+ ) -> list[SequencePattern]:
226
+ """Detect patterns that occur at regular intervals.
227
+
228
+ Implements RE-SEQ-002: Periodic pattern detection.
229
+
230
+ Args:
231
+ messages: Sequence of messages.
232
+ key: Function to extract message identifier.
233
+ timestamp_key: Function to extract timestamp.
234
+
235
+ Returns:
236
+ List of periodic patterns.
237
+ """
238
+ patterns = self.detect_patterns(messages, key, timestamp_key)
239
+
240
+ # Filter for low gap variance (periodic)
241
+ periodic = []
242
+ for pattern in patterns:
243
+ if pattern.frequency >= 3 and pattern.avg_gap > 0:
244
+ # Calculate coefficient of variation
245
+ if pattern.gap_variance > 0:
246
+ cv = (pattern.gap_variance**0.5) / pattern.avg_gap
247
+ else:
248
+ cv = 0
249
+ # Low CV indicates periodicity
250
+ if cv < 0.2: # Less than 20% variation
251
+ periodic.append(pattern)
252
+
253
+ return periodic
254
+
255
+ def _find_ngram_patterns(
256
+ self,
257
+ identifiers: list[Any],
258
+ timestamps: list[float] | None,
259
+ ) -> dict[tuple[Any, ...], list[int]]:
260
+ """Find all n-gram patterns.
261
+
262
+ Args:
263
+ identifiers: Message identifiers.
264
+ timestamps: Message timestamps.
265
+
266
+ Returns:
267
+ Dictionary mapping patterns to positions.
268
+ """
269
+ candidates = defaultdict(list)
270
+
271
+ for length in range(self.min_pattern_length, self.max_pattern_length + 1):
272
+ for i in range(len(identifiers) - length + 1):
273
+ ngram = tuple(identifiers[i : i + length])
274
+
275
+ # Check gap constraint
276
+ if self.max_gap is not None and timestamps is not None:
277
+ gaps = [timestamps[i + j + 1] - timestamps[i + j] for j in range(length - 1)]
278
+ if any(g > self.max_gap for g in gaps):
279
+ continue
280
+
281
+ candidates[ngram].append(i)
282
+
283
+ return candidates
284
+
285
+ def _score_patterns(
286
+ self,
287
+ candidates: dict[tuple[Any, ...], list[int]],
288
+ identifiers: list[Any],
289
+ timestamps: list[float] | None,
290
+ ) -> list[SequencePattern]:
291
+ """Score candidate patterns.
292
+
293
+ Args:
294
+ candidates: Pattern -> positions mapping.
295
+ identifiers: Original identifiers.
296
+ timestamps: Message timestamps.
297
+
298
+ Returns:
299
+ List of scored patterns.
300
+ """
301
+ patterns = []
302
+
303
+ for pattern_tuple, positions in candidates.items():
304
+ if len(positions) < self.min_frequency:
305
+ continue
306
+
307
+ pattern_list = list(pattern_tuple)
308
+ length = len(pattern_list)
309
+
310
+ # Calculate gap statistics if timestamps available
311
+ avg_gap = 0.0
312
+ gap_variance = 0.0
313
+
314
+ if timestamps is not None and len(positions) > 1:
315
+ # Calculate gaps between pattern occurrences
316
+ gaps = []
317
+ for i in range(len(positions) - 1):
318
+ start_time = timestamps[positions[i]]
319
+ end_time = timestamps[positions[i + 1]]
320
+ gaps.append(end_time - start_time)
321
+
322
+ if gaps:
323
+ avg_gap = sum(gaps) / len(gaps)
324
+ gap_variance = sum((g - avg_gap) ** 2 for g in gaps) / len(gaps)
325
+
326
+ # Calculate confidence
327
+ # Higher confidence for: frequent, consistent gaps, longer patterns
328
+ frequency_score = min(1.0, len(positions) / 10)
329
+ length_score = min(1.0, length / 5)
330
+
331
+ if gap_variance > 0 and avg_gap > 0:
332
+ consistency_score = 1.0 / (1.0 + (gap_variance**0.5 / avg_gap))
333
+ else:
334
+ consistency_score = 0.5
335
+
336
+ confidence = 0.4 * frequency_score + 0.3 * consistency_score + 0.3 * length_score
337
+
338
+ patterns.append(
339
+ SequencePattern(
340
+ pattern=pattern_list,
341
+ frequency=len(positions),
342
+ positions=positions,
343
+ confidence=confidence,
344
+ avg_gap=avg_gap,
345
+ gap_variance=gap_variance,
346
+ )
347
+ )
348
+
349
+ return patterns
350
+
351
+
352
+ class RequestResponseCorrelator:
353
+ """Correlate request and response messages.
354
+
355
+ Implements RE-SEQ-003: Request-Response Correlation.
356
+
357
+ Identifies matching request-response pairs in bidirectional
358
+ communication streams.
359
+
360
+ Example:
361
+ >>> correlator = RequestResponseCorrelator()
362
+ >>> pairs = correlator.correlate(
363
+ ... messages,
364
+ ... request_filter=lambda m: m.type == 'REQ',
365
+ ... response_filter=lambda m: m.type == 'RSP'
366
+ ... )
367
+ """
368
+
369
+ def __init__(
370
+ self,
371
+ max_latency: float = 10.0,
372
+ correlation_key: Callable[[Any], Any] | None = None,
373
+ ) -> None:
374
+ """Initialize correlator.
375
+
376
+ Args:
377
+ max_latency: Maximum time between request and response.
378
+ correlation_key: Function to extract correlation ID.
379
+ """
380
+ self.max_latency = max_latency
381
+ self.correlation_key = correlation_key
382
+
383
+ def correlate(
384
+ self,
385
+ messages: Sequence[Any],
386
+ request_filter: Callable[[Any], bool] | None = None,
387
+ response_filter: Callable[[Any], bool] | None = None,
388
+ timestamp_key: Callable[[Any], float] | None = None,
389
+ ) -> list[RequestResponsePair]:
390
+ """Correlate requests with responses.
391
+
392
+ Implements RE-SEQ-003: Request-response correlation workflow.
393
+
394
+ Args:
395
+ messages: All messages in stream.
396
+ request_filter: Function to identify requests.
397
+ response_filter: Function to identify responses.
398
+ timestamp_key: Function to extract timestamp.
399
+
400
+ Returns:
401
+ List of correlated pairs.
402
+
403
+ Example:
404
+ >>> pairs = correlator.correlate(
405
+ ... messages,
406
+ ... request_filter=lambda m: m['direction'] == 'out',
407
+ ... response_filter=lambda m: m['direction'] == 'in',
408
+ ... timestamp_key=lambda m: m['time']
409
+ ... )
410
+ """
411
+ # Separate requests and responses
412
+ requests = []
413
+ responses = []
414
+
415
+ for i, msg in enumerate(messages):
416
+ ts = timestamp_key(msg) if timestamp_key else float(i)
417
+
418
+ if request_filter is None or request_filter(msg):
419
+ correlation_id = None
420
+ if self.correlation_key is not None:
421
+ try:
422
+ correlation_id = self.correlation_key(msg)
423
+ except (KeyError, TypeError):
424
+ pass
425
+ requests.append((i, msg, ts, correlation_id))
426
+
427
+ if response_filter is None or response_filter(msg):
428
+ correlation_id = None
429
+ if self.correlation_key is not None:
430
+ try:
431
+ correlation_id = self.correlation_key(msg)
432
+ except (KeyError, TypeError):
433
+ pass
434
+ responses.append((i, msg, ts, correlation_id))
435
+
436
+ # Match pairs
437
+ return self._match_pairs(requests, responses)
438
+
439
+ def correlate_by_content(
440
+ self,
441
+ messages: Sequence[Any],
442
+ content_key: Callable[[Any], bytes],
443
+ timestamp_key: Callable[[Any], float] | None = None,
444
+ ) -> list[RequestResponsePair]:
445
+ """Correlate by analyzing message content similarity.
446
+
447
+ Implements RE-SEQ-003: Content-based correlation.
448
+
449
+ Args:
450
+ messages: All messages.
451
+ content_key: Function to extract message content.
452
+ timestamp_key: Function to extract timestamp.
453
+
454
+ Returns:
455
+ List of correlated pairs.
456
+ """
457
+ pairs = []
458
+ used_responses = set()
459
+
460
+ for i, msg in enumerate(messages):
461
+ req_content = content_key(msg)
462
+ req_ts = timestamp_key(msg) if timestamp_key else float(i)
463
+
464
+ best_match = None
465
+ best_score = 0.0
466
+
467
+ for j in range(i + 1, len(messages)):
468
+ if j in used_responses:
469
+ continue
470
+
471
+ resp = messages[j]
472
+ resp_ts = timestamp_key(resp) if timestamp_key else float(j)
473
+
474
+ latency = resp_ts - req_ts
475
+ if latency < 0 or latency > self.max_latency:
476
+ continue
477
+
478
+ resp_content = content_key(resp)
479
+ score = self._content_similarity(req_content, resp_content)
480
+
481
+ if score > best_score:
482
+ best_score = score
483
+ best_match = (j, resp, latency)
484
+
485
+ if best_match is not None and best_score > 0.3:
486
+ j, resp, latency = best_match
487
+ used_responses.add(j)
488
+ pairs.append(
489
+ RequestResponsePair(
490
+ request_index=i,
491
+ response_index=j,
492
+ request=msg,
493
+ response=resp,
494
+ latency=latency,
495
+ confidence=best_score,
496
+ )
497
+ )
498
+
499
+ return pairs
500
+
501
+ def extract_flows(
502
+ self,
503
+ pairs: Sequence[RequestResponsePair],
504
+ messages: Sequence[Any],
505
+ flow_key: Callable[[Any], str] | None = None,
506
+ ) -> list[CommunicationFlow]:
507
+ """Extract communication flows from pairs.
508
+
509
+ Implements RE-SEQ-003: Flow extraction.
510
+
511
+ Args:
512
+ pairs: Correlated request-response pairs.
513
+ messages: All messages.
514
+ flow_key: Function to extract flow identifier.
515
+
516
+ Returns:
517
+ List of communication flows.
518
+ """
519
+ if flow_key is None:
520
+ # Group all pairs into one flow
521
+ return [
522
+ CommunicationFlow(
523
+ flow_id=0,
524
+ messages=list(messages),
525
+ pairs=list(pairs),
526
+ direction="request_first",
527
+ participants=[],
528
+ duration=0.0,
529
+ )
530
+ ]
531
+
532
+ # Group by flow key
533
+ flow_groups = defaultdict(list)
534
+ for pair in pairs:
535
+ key = flow_key(pair.request)
536
+ flow_groups[key].append(pair)
537
+
538
+ flows = []
539
+ for i, (key, group_pairs) in enumerate(flow_groups.items()):
540
+ # Get all messages in this flow
541
+ indices = set()
542
+ for pair in group_pairs:
543
+ indices.add(pair.request_index)
544
+ indices.add(pair.response_index)
545
+
546
+ flow_messages = [messages[j] for j in sorted(indices)]
547
+
548
+ # Calculate duration
549
+ if group_pairs:
550
+ _start = min(p.latency for p in group_pairs)
551
+ duration = max(p.latency for p in group_pairs)
552
+ else:
553
+ duration = 0.0
554
+
555
+ flows.append(
556
+ CommunicationFlow(
557
+ flow_id=i,
558
+ messages=flow_messages,
559
+ pairs=group_pairs,
560
+ direction="request_first",
561
+ participants=[str(key)],
562
+ duration=duration,
563
+ )
564
+ )
565
+
566
+ return flows
567
+
568
+ def _match_pairs(
569
+ self,
570
+ requests: list[tuple[int, Any, float, Any]],
571
+ responses: list[tuple[int, Any, float, Any]],
572
+ ) -> list[RequestResponsePair]:
573
+ """Match request and response messages.
574
+
575
+ Args:
576
+ requests: List of (index, message, timestamp, correlation_id).
577
+ responses: List of (index, message, timestamp, correlation_id).
578
+
579
+ Returns:
580
+ List of matched pairs.
581
+ """
582
+ pairs = []
583
+ used_responses = set()
584
+
585
+ for req_idx, req_msg, req_ts, req_id in requests:
586
+ best_match = None
587
+ best_score = 0.0
588
+
589
+ for resp_idx, resp_msg, resp_ts, resp_id in responses:
590
+ if resp_idx in used_responses:
591
+ continue
592
+
593
+ # Check timing
594
+ latency = resp_ts - req_ts
595
+ if latency < 0 or latency > self.max_latency:
596
+ continue
597
+
598
+ # Check correlation ID
599
+ if req_id is not None and resp_id is not None:
600
+ if req_id == resp_id:
601
+ score = 1.0
602
+ else:
603
+ score = 0.0
604
+ else:
605
+ # Use timing proximity
606
+ score = 1.0 - (latency / self.max_latency)
607
+
608
+ if score > best_score:
609
+ best_score = score
610
+ best_match = (resp_idx, resp_msg, latency, resp_id)
611
+
612
+ if best_match is not None:
613
+ resp_idx, resp_msg, latency, resp_id = best_match
614
+ used_responses.add(resp_idx)
615
+
616
+ pairs.append(
617
+ RequestResponsePair(
618
+ request_index=req_idx,
619
+ response_index=resp_idx,
620
+ request=req_msg,
621
+ response=resp_msg,
622
+ latency=latency,
623
+ correlation_id=req_id if req_id is not None else resp_id,
624
+ confidence=best_score,
625
+ )
626
+ )
627
+
628
+ return pairs
629
+
630
+ def _content_similarity(self, content_a: bytes, content_b: bytes) -> float:
631
+ """Calculate content similarity.
632
+
633
+ Args:
634
+ content_a: First content.
635
+ content_b: Second content.
636
+
637
+ Returns:
638
+ Similarity score (0-1).
639
+ """
640
+ if not content_a or not content_b:
641
+ return 0.0
642
+
643
+ # Use byte set similarity (Jaccard-like)
644
+ set_a = set(content_a)
645
+ set_b = set(content_b)
646
+
647
+ intersection = len(set_a & set_b)
648
+ union = len(set_a | set_b)
649
+
650
+ if union == 0:
651
+ return 0.0
652
+
653
+ return intersection / union
654
+
655
+
656
+ # =============================================================================
657
+ # Convenience functions
658
+ # =============================================================================
659
+
660
+
661
+ def detect_sequence_patterns(
662
+ messages: Sequence[Any],
663
+ key: Callable[[Any], Any] | None = None,
664
+ min_length: int = 2,
665
+ max_length: int = 10,
666
+ min_frequency: int = 2,
667
+ ) -> list[SequencePattern]:
668
+ """Detect sequential patterns in messages.
669
+
670
+ Implements RE-SEQ-002: Sequence Pattern Detection.
671
+
672
+ Args:
673
+ messages: Sequence of messages.
674
+ key: Function to extract message identifier.
675
+ min_length: Minimum pattern length.
676
+ max_length: Maximum pattern length.
677
+ min_frequency: Minimum occurrences.
678
+
679
+ Returns:
680
+ List of detected patterns.
681
+
682
+ Example:
683
+ >>> patterns = detect_sequence_patterns(
684
+ ... messages,
685
+ ... key=lambda m: m['type']
686
+ ... )
687
+ """
688
+ detector = SequencePatternDetector(
689
+ min_pattern_length=min_length,
690
+ max_pattern_length=max_length,
691
+ min_frequency=min_frequency,
692
+ )
693
+ return detector.detect_patterns(messages, key)
694
+
695
+
696
+ def correlate_requests(
697
+ messages: Sequence[Any],
698
+ request_filter: Callable[[Any], bool],
699
+ response_filter: Callable[[Any], bool],
700
+ timestamp_key: Callable[[Any], float] | None = None,
701
+ max_latency: float = 10.0,
702
+ ) -> list[RequestResponsePair]:
703
+ """Correlate request and response messages.
704
+
705
+ Implements RE-SEQ-003: Request-Response Correlation.
706
+
707
+ Args:
708
+ messages: All messages.
709
+ request_filter: Function to identify requests.
710
+ response_filter: Function to identify responses.
711
+ timestamp_key: Function to extract timestamp.
712
+ max_latency: Maximum time between request and response.
713
+
714
+ Returns:
715
+ List of correlated pairs.
716
+
717
+ Example:
718
+ >>> pairs = correlate_requests(
719
+ ... messages,
720
+ ... request_filter=lambda m: m['dir'] == 'out',
721
+ ... response_filter=lambda m: m['dir'] == 'in',
722
+ ... timestamp_key=lambda m: m['ts']
723
+ ... )
724
+ """
725
+ correlator = RequestResponseCorrelator(max_latency=max_latency)
726
+ return correlator.correlate(messages, request_filter, response_filter, timestamp_key)
727
+
728
+
729
+ def find_message_dependencies(
730
+ messages: Sequence[Any],
731
+ key: Callable[[Any], Any],
732
+ timestamp_key: Callable[[Any], float] | None = None,
733
+ ) -> dict[Any, list[Any]]:
734
+ """Find dependencies between message types.
735
+
736
+ Implements RE-SEQ-002: Dependency detection.
737
+
738
+ Args:
739
+ messages: Sequence of messages.
740
+ key: Function to extract message type.
741
+ timestamp_key: Function to extract timestamp.
742
+
743
+ Returns:
744
+ Dictionary mapping message types to their typical successors.
745
+
746
+ Example:
747
+ >>> deps = find_message_dependencies(messages, key=lambda m: m['type'])
748
+ >>> deps['REQ'] # ['RSP', 'ACK']
749
+ """
750
+ dependencies: dict[Any, list[Any]] = defaultdict(list)
751
+
752
+ for i in range(len(messages) - 1):
753
+ current = key(messages[i])
754
+ next_msg = key(messages[i + 1])
755
+
756
+ if next_msg not in dependencies[current]:
757
+ dependencies[current].append(next_msg)
758
+
759
+ return dict(dependencies)
760
+
761
+
762
+ def calculate_latency_stats(
763
+ pairs: Sequence[RequestResponsePair],
764
+ ) -> dict[str, float]:
765
+ """Calculate latency statistics for request-response pairs.
766
+
767
+ Implements RE-SEQ-003: Latency analysis.
768
+
769
+ Args:
770
+ pairs: List of request-response pairs.
771
+
772
+ Returns:
773
+ Dictionary with min, max, mean, median, std latencies.
774
+ """
775
+ if not pairs:
776
+ return {
777
+ "min": 0.0,
778
+ "max": 0.0,
779
+ "mean": 0.0,
780
+ "median": 0.0,
781
+ "std": 0.0,
782
+ }
783
+
784
+ latencies = [p.latency for p in pairs]
785
+ arr = np.array(latencies)
786
+
787
+ return {
788
+ "min": float(np.min(arr)),
789
+ "max": float(np.max(arr)),
790
+ "mean": float(np.mean(arr)),
791
+ "median": float(np.median(arr)),
792
+ "std": float(np.std(arr)),
793
+ }
794
+
795
+
796
+ __all__ = [
797
+ "CommunicationFlow",
798
+ "RequestResponseCorrelator",
799
+ "RequestResponsePair",
800
+ # Data classes
801
+ "SequencePattern",
802
+ # Classes
803
+ "SequencePatternDetector",
804
+ "calculate_latency_stats",
805
+ "correlate_requests",
806
+ # Functions
807
+ "detect_sequence_patterns",
808
+ "find_message_dependencies",
809
+ ]