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.
- oscura/__init__.py +169 -167
- oscura/analyzers/__init__.py +3 -0
- oscura/analyzers/classification.py +659 -0
- oscura/analyzers/digital/edges.py +325 -65
- oscura/analyzers/digital/quality.py +293 -166
- oscura/analyzers/digital/timing.py +260 -115
- oscura/analyzers/digital/timing_numba.py +334 -0
- oscura/analyzers/entropy.py +605 -0
- oscura/analyzers/eye/diagram.py +176 -109
- oscura/analyzers/eye/metrics.py +5 -5
- oscura/analyzers/jitter/__init__.py +6 -4
- oscura/analyzers/jitter/ber.py +52 -52
- oscura/analyzers/jitter/classification.py +156 -0
- oscura/analyzers/jitter/decomposition.py +163 -113
- oscura/analyzers/jitter/spectrum.py +80 -64
- oscura/analyzers/ml/__init__.py +39 -0
- oscura/analyzers/ml/features.py +600 -0
- oscura/analyzers/ml/signal_classifier.py +604 -0
- oscura/analyzers/packet/daq.py +246 -158
- oscura/analyzers/packet/parser.py +12 -1
- oscura/analyzers/packet/payload.py +50 -2110
- oscura/analyzers/packet/payload_analysis.py +361 -181
- oscura/analyzers/packet/payload_patterns.py +133 -70
- oscura/analyzers/packet/stream.py +84 -23
- oscura/analyzers/patterns/__init__.py +26 -5
- oscura/analyzers/patterns/anomaly_detection.py +908 -0
- oscura/analyzers/patterns/clustering.py +169 -108
- oscura/analyzers/patterns/clustering_optimized.py +227 -0
- oscura/analyzers/patterns/discovery.py +1 -1
- oscura/analyzers/patterns/matching.py +581 -197
- oscura/analyzers/patterns/pattern_mining.py +778 -0
- oscura/analyzers/patterns/periodic.py +121 -38
- oscura/analyzers/patterns/sequences.py +175 -78
- oscura/analyzers/power/conduction.py +1 -1
- oscura/analyzers/power/soa.py +6 -6
- oscura/analyzers/power/switching.py +250 -110
- oscura/analyzers/protocol/__init__.py +17 -1
- oscura/analyzers/protocols/base.py +6 -6
- oscura/analyzers/protocols/ble/__init__.py +38 -0
- oscura/analyzers/protocols/ble/analyzer.py +809 -0
- oscura/analyzers/protocols/ble/uuids.py +288 -0
- oscura/analyzers/protocols/can.py +257 -127
- oscura/analyzers/protocols/can_fd.py +107 -80
- oscura/analyzers/protocols/flexray.py +139 -80
- oscura/analyzers/protocols/hdlc.py +93 -58
- oscura/analyzers/protocols/i2c.py +247 -106
- oscura/analyzers/protocols/i2s.py +138 -86
- oscura/analyzers/protocols/industrial/__init__.py +40 -0
- oscura/analyzers/protocols/industrial/bacnet/__init__.py +33 -0
- oscura/analyzers/protocols/industrial/bacnet/analyzer.py +708 -0
- oscura/analyzers/protocols/industrial/bacnet/encoding.py +412 -0
- oscura/analyzers/protocols/industrial/bacnet/services.py +622 -0
- oscura/analyzers/protocols/industrial/ethercat/__init__.py +30 -0
- oscura/analyzers/protocols/industrial/ethercat/analyzer.py +474 -0
- oscura/analyzers/protocols/industrial/ethercat/mailbox.py +339 -0
- oscura/analyzers/protocols/industrial/ethercat/topology.py +166 -0
- oscura/analyzers/protocols/industrial/modbus/__init__.py +31 -0
- oscura/analyzers/protocols/industrial/modbus/analyzer.py +525 -0
- oscura/analyzers/protocols/industrial/modbus/crc.py +79 -0
- oscura/analyzers/protocols/industrial/modbus/functions.py +436 -0
- oscura/analyzers/protocols/industrial/opcua/__init__.py +21 -0
- oscura/analyzers/protocols/industrial/opcua/analyzer.py +552 -0
- oscura/analyzers/protocols/industrial/opcua/datatypes.py +446 -0
- oscura/analyzers/protocols/industrial/opcua/services.py +264 -0
- oscura/analyzers/protocols/industrial/profinet/__init__.py +23 -0
- oscura/analyzers/protocols/industrial/profinet/analyzer.py +441 -0
- oscura/analyzers/protocols/industrial/profinet/dcp.py +263 -0
- oscura/analyzers/protocols/industrial/profinet/ptcp.py +200 -0
- oscura/analyzers/protocols/jtag.py +180 -98
- oscura/analyzers/protocols/lin.py +219 -114
- oscura/analyzers/protocols/manchester.py +4 -4
- oscura/analyzers/protocols/onewire.py +253 -149
- oscura/analyzers/protocols/parallel_bus/__init__.py +20 -0
- oscura/analyzers/protocols/parallel_bus/centronics.py +92 -0
- oscura/analyzers/protocols/parallel_bus/gpib.py +137 -0
- oscura/analyzers/protocols/spi.py +192 -95
- oscura/analyzers/protocols/swd.py +321 -167
- oscura/analyzers/protocols/uart.py +267 -125
- oscura/analyzers/protocols/usb.py +235 -131
- oscura/analyzers/side_channel/power.py +17 -12
- oscura/analyzers/signal/__init__.py +15 -0
- oscura/analyzers/signal/timing_analysis.py +1086 -0
- oscura/analyzers/signal_integrity/__init__.py +4 -1
- oscura/analyzers/signal_integrity/sparams.py +2 -19
- oscura/analyzers/spectral/chunked.py +129 -60
- oscura/analyzers/spectral/chunked_fft.py +300 -94
- oscura/analyzers/spectral/chunked_wavelet.py +100 -80
- oscura/analyzers/statistical/checksum.py +376 -217
- oscura/analyzers/statistical/classification.py +229 -107
- oscura/analyzers/statistical/entropy.py +78 -53
- oscura/analyzers/statistics/correlation.py +407 -211
- oscura/analyzers/statistics/outliers.py +2 -2
- oscura/analyzers/statistics/streaming.py +30 -5
- oscura/analyzers/validation.py +216 -101
- oscura/analyzers/waveform/measurements.py +9 -0
- oscura/analyzers/waveform/measurements_with_uncertainty.py +31 -15
- oscura/analyzers/waveform/spectral.py +500 -228
- oscura/api/__init__.py +31 -5
- oscura/api/dsl/__init__.py +582 -0
- oscura/{dsl → api/dsl}/commands.py +43 -76
- oscura/{dsl → api/dsl}/interpreter.py +26 -51
- oscura/{dsl → api/dsl}/parser.py +107 -77
- oscura/{dsl → api/dsl}/repl.py +2 -2
- oscura/api/dsl.py +1 -1
- oscura/{integrations → api/integrations}/__init__.py +1 -1
- oscura/{integrations → api/integrations}/llm.py +201 -102
- oscura/api/operators.py +3 -3
- oscura/api/optimization.py +144 -30
- oscura/api/rest_server.py +921 -0
- oscura/api/server/__init__.py +17 -0
- oscura/api/server/dashboard.py +850 -0
- oscura/api/server/static/README.md +34 -0
- oscura/api/server/templates/base.html +181 -0
- oscura/api/server/templates/export.html +120 -0
- oscura/api/server/templates/home.html +284 -0
- oscura/api/server/templates/protocols.html +58 -0
- oscura/api/server/templates/reports.html +43 -0
- oscura/api/server/templates/session_detail.html +89 -0
- oscura/api/server/templates/sessions.html +83 -0
- oscura/api/server/templates/waveforms.html +73 -0
- oscura/automotive/__init__.py +8 -1
- oscura/automotive/can/__init__.py +10 -0
- oscura/automotive/can/checksum.py +3 -1
- oscura/automotive/can/dbc_generator.py +590 -0
- oscura/automotive/can/message_wrapper.py +121 -74
- oscura/automotive/can/patterns.py +98 -21
- oscura/automotive/can/session.py +292 -56
- oscura/automotive/can/state_machine.py +6 -3
- oscura/automotive/can/stimulus_response.py +97 -75
- oscura/automotive/dbc/__init__.py +10 -2
- oscura/automotive/dbc/generator.py +84 -56
- oscura/automotive/dbc/parser.py +6 -6
- oscura/automotive/dtc/data.json +17 -102
- oscura/automotive/dtc/database.py +2 -2
- oscura/automotive/flexray/__init__.py +31 -0
- oscura/automotive/flexray/analyzer.py +504 -0
- oscura/automotive/flexray/crc.py +185 -0
- oscura/automotive/flexray/fibex.py +449 -0
- oscura/automotive/j1939/__init__.py +45 -8
- oscura/automotive/j1939/analyzer.py +605 -0
- oscura/automotive/j1939/spns.py +326 -0
- oscura/automotive/j1939/transport.py +306 -0
- oscura/automotive/lin/__init__.py +47 -0
- oscura/automotive/lin/analyzer.py +612 -0
- oscura/automotive/loaders/blf.py +13 -2
- oscura/automotive/loaders/csv_can.py +143 -72
- oscura/automotive/loaders/dispatcher.py +50 -2
- oscura/automotive/loaders/mdf.py +86 -45
- oscura/automotive/loaders/pcap.py +111 -61
- oscura/automotive/uds/__init__.py +4 -0
- oscura/automotive/uds/analyzer.py +725 -0
- oscura/automotive/uds/decoder.py +140 -58
- oscura/automotive/uds/models.py +7 -1
- oscura/automotive/visualization.py +1 -1
- oscura/cli/analyze.py +348 -0
- oscura/cli/batch.py +142 -122
- oscura/cli/benchmark.py +275 -0
- oscura/cli/characterize.py +137 -82
- oscura/cli/compare.py +224 -131
- oscura/cli/completion.py +250 -0
- oscura/cli/config_cmd.py +361 -0
- oscura/cli/decode.py +164 -87
- oscura/cli/export.py +286 -0
- oscura/cli/main.py +115 -31
- oscura/{onboarding → cli/onboarding}/__init__.py +3 -3
- oscura/{onboarding → cli/onboarding}/help.py +80 -58
- oscura/{onboarding → cli/onboarding}/tutorials.py +97 -72
- oscura/{onboarding → cli/onboarding}/wizard.py +55 -36
- oscura/cli/progress.py +147 -0
- oscura/cli/shell.py +157 -135
- oscura/cli/validate_cmd.py +204 -0
- oscura/cli/visualize.py +158 -0
- oscura/convenience.py +125 -79
- oscura/core/__init__.py +4 -2
- oscura/core/backend_selector.py +3 -3
- oscura/core/cache.py +126 -15
- oscura/core/cancellation.py +1 -1
- oscura/{config → core/config}/__init__.py +20 -11
- oscura/{config → core/config}/defaults.py +1 -1
- oscura/{config → core/config}/loader.py +7 -5
- oscura/{config → core/config}/memory.py +5 -5
- oscura/{config → core/config}/migration.py +1 -1
- oscura/{config → core/config}/pipeline.py +99 -23
- oscura/{config → core/config}/preferences.py +1 -1
- oscura/{config → core/config}/protocol.py +3 -3
- oscura/{config → core/config}/schema.py +426 -272
- oscura/{config → core/config}/settings.py +1 -1
- oscura/{config → core/config}/thresholds.py +195 -153
- oscura/core/correlation.py +5 -6
- oscura/core/cross_domain.py +0 -2
- oscura/core/debug.py +9 -5
- oscura/{extensibility → core/extensibility}/docs.py +158 -70
- oscura/{extensibility → core/extensibility}/extensions.py +160 -76
- oscura/{extensibility → core/extensibility}/logging.py +1 -1
- oscura/{extensibility → core/extensibility}/measurements.py +1 -1
- oscura/{extensibility → core/extensibility}/plugins.py +1 -1
- oscura/{extensibility → core/extensibility}/templates.py +73 -3
- oscura/{extensibility → core/extensibility}/validation.py +1 -1
- oscura/core/gpu_backend.py +11 -7
- oscura/core/log_query.py +101 -11
- oscura/core/logging.py +126 -54
- oscura/core/logging_advanced.py +5 -5
- oscura/core/memory_limits.py +108 -70
- oscura/core/memory_monitor.py +2 -2
- oscura/core/memory_progress.py +7 -7
- oscura/core/memory_warnings.py +1 -1
- oscura/core/numba_backend.py +13 -13
- oscura/{plugins → core/plugins}/__init__.py +9 -9
- oscura/{plugins → core/plugins}/base.py +7 -7
- oscura/{plugins → core/plugins}/cli.py +3 -3
- oscura/{plugins → core/plugins}/discovery.py +186 -106
- oscura/{plugins → core/plugins}/lifecycle.py +1 -1
- oscura/{plugins → core/plugins}/manager.py +7 -7
- oscura/{plugins → core/plugins}/registry.py +3 -3
- oscura/{plugins → core/plugins}/versioning.py +1 -1
- oscura/core/progress.py +16 -1
- oscura/core/provenance.py +8 -2
- oscura/{schemas → core/schemas}/__init__.py +2 -2
- oscura/{schemas → core/schemas}/device_mapping.json +2 -8
- oscura/{schemas → core/schemas}/packet_format.json +4 -24
- oscura/{schemas → core/schemas}/protocol_definition.json +2 -12
- oscura/core/types.py +4 -0
- oscura/core/uncertainty.py +3 -3
- oscura/correlation/__init__.py +52 -0
- oscura/correlation/multi_protocol.py +811 -0
- oscura/discovery/auto_decoder.py +117 -35
- oscura/discovery/comparison.py +191 -86
- oscura/discovery/quality_validator.py +155 -68
- oscura/discovery/signal_detector.py +196 -79
- oscura/export/__init__.py +18 -8
- oscura/export/kaitai_struct.py +513 -0
- oscura/export/scapy_layer.py +801 -0
- oscura/export/wireshark/generator.py +1 -1
- oscura/export/wireshark/templates/dissector.lua.j2 +2 -2
- oscura/export/wireshark_dissector.py +746 -0
- oscura/guidance/wizard.py +207 -111
- oscura/hardware/__init__.py +19 -0
- oscura/{acquisition → hardware/acquisition}/__init__.py +4 -4
- oscura/{acquisition → hardware/acquisition}/file.py +2 -2
- oscura/{acquisition → hardware/acquisition}/hardware.py +7 -7
- oscura/{acquisition → hardware/acquisition}/saleae.py +15 -12
- oscura/{acquisition → hardware/acquisition}/socketcan.py +1 -1
- oscura/{acquisition → hardware/acquisition}/streaming.py +2 -2
- oscura/{acquisition → hardware/acquisition}/synthetic.py +3 -3
- oscura/{acquisition → hardware/acquisition}/visa.py +33 -11
- oscura/hardware/firmware/__init__.py +29 -0
- oscura/hardware/firmware/pattern_recognition.py +874 -0
- oscura/hardware/hal_detector.py +736 -0
- oscura/hardware/security/__init__.py +37 -0
- oscura/hardware/security/side_channel_detector.py +1126 -0
- oscura/inference/__init__.py +4 -0
- oscura/inference/active_learning/observation_table.py +4 -1
- oscura/inference/alignment.py +216 -123
- oscura/inference/bayesian.py +113 -33
- oscura/inference/crc_reverse.py +101 -55
- oscura/inference/logic.py +6 -2
- oscura/inference/message_format.py +342 -183
- oscura/inference/protocol.py +95 -44
- oscura/inference/protocol_dsl.py +180 -82
- oscura/inference/signal_intelligence.py +1439 -706
- oscura/inference/spectral.py +99 -57
- oscura/inference/state_machine.py +810 -158
- oscura/inference/stream.py +270 -110
- oscura/iot/__init__.py +34 -0
- oscura/iot/coap/__init__.py +32 -0
- oscura/iot/coap/analyzer.py +668 -0
- oscura/iot/coap/options.py +212 -0
- oscura/iot/lorawan/__init__.py +21 -0
- oscura/iot/lorawan/crypto.py +206 -0
- oscura/iot/lorawan/decoder.py +801 -0
- oscura/iot/lorawan/mac_commands.py +341 -0
- oscura/iot/mqtt/__init__.py +27 -0
- oscura/iot/mqtt/analyzer.py +999 -0
- oscura/iot/mqtt/properties.py +315 -0
- oscura/iot/zigbee/__init__.py +31 -0
- oscura/iot/zigbee/analyzer.py +615 -0
- oscura/iot/zigbee/security.py +153 -0
- oscura/iot/zigbee/zcl.py +349 -0
- oscura/jupyter/display.py +125 -45
- oscura/{exploratory → jupyter/exploratory}/__init__.py +8 -8
- oscura/{exploratory → jupyter/exploratory}/error_recovery.py +298 -141
- oscura/jupyter/exploratory/fuzzy.py +746 -0
- oscura/{exploratory → jupyter/exploratory}/fuzzy_advanced.py +258 -100
- oscura/{exploratory → jupyter/exploratory}/legacy.py +464 -242
- oscura/{exploratory → jupyter/exploratory}/parse.py +167 -145
- oscura/{exploratory → jupyter/exploratory}/recovery.py +119 -87
- oscura/jupyter/exploratory/sync.py +612 -0
- oscura/{exploratory → jupyter/exploratory}/unknown.py +299 -176
- oscura/jupyter/magic.py +4 -4
- oscura/{ui → jupyter/ui}/__init__.py +2 -2
- oscura/{ui → jupyter/ui}/formatters.py +3 -3
- oscura/{ui → jupyter/ui}/progressive_display.py +153 -82
- oscura/loaders/__init__.py +183 -67
- oscura/loaders/binary.py +88 -1
- oscura/loaders/chipwhisperer.py +153 -137
- oscura/loaders/configurable.py +208 -86
- oscura/loaders/csv_loader.py +458 -215
- oscura/loaders/hdf5_loader.py +278 -119
- oscura/loaders/lazy.py +87 -54
- oscura/loaders/mmap_loader.py +1 -1
- oscura/loaders/numpy_loader.py +253 -116
- oscura/loaders/pcap.py +226 -151
- oscura/loaders/rigol.py +110 -49
- oscura/loaders/sigrok.py +201 -78
- oscura/loaders/tdms.py +81 -58
- oscura/loaders/tektronix.py +291 -174
- oscura/loaders/touchstone.py +182 -87
- oscura/loaders/tss.py +456 -0
- oscura/loaders/vcd.py +215 -117
- oscura/loaders/wav.py +155 -68
- oscura/reporting/__init__.py +9 -0
- oscura/reporting/analyze.py +352 -146
- oscura/reporting/argument_preparer.py +69 -14
- oscura/reporting/auto_report.py +97 -61
- oscura/reporting/batch.py +131 -58
- oscura/reporting/chart_selection.py +57 -45
- oscura/reporting/comparison.py +63 -17
- oscura/reporting/content/executive.py +76 -24
- oscura/reporting/core_formats/multi_format.py +11 -8
- oscura/reporting/engine.py +312 -158
- oscura/reporting/enhanced_reports.py +949 -0
- oscura/reporting/export.py +86 -43
- oscura/reporting/formatting/numbers.py +69 -42
- oscura/reporting/html.py +139 -58
- oscura/reporting/index.py +137 -65
- oscura/reporting/output.py +158 -67
- oscura/reporting/pdf.py +67 -102
- oscura/reporting/plots.py +191 -112
- oscura/reporting/sections.py +88 -47
- oscura/reporting/standards.py +104 -61
- oscura/reporting/summary_generator.py +75 -55
- oscura/reporting/tables.py +138 -54
- oscura/reporting/templates/enhanced/protocol_re.html +525 -0
- oscura/sessions/__init__.py +14 -23
- oscura/sessions/base.py +3 -3
- oscura/sessions/blackbox.py +106 -10
- oscura/sessions/generic.py +2 -2
- oscura/sessions/legacy.py +783 -0
- oscura/side_channel/__init__.py +63 -0
- oscura/side_channel/dpa.py +1025 -0
- oscura/utils/__init__.py +15 -1
- oscura/utils/bitwise.py +118 -0
- oscura/{builders → utils/builders}/__init__.py +1 -1
- oscura/{comparison → utils/comparison}/__init__.py +6 -6
- oscura/{comparison → utils/comparison}/compare.py +202 -101
- oscura/{comparison → utils/comparison}/golden.py +83 -63
- oscura/{comparison → utils/comparison}/limits.py +313 -89
- oscura/{comparison → utils/comparison}/mask.py +151 -45
- oscura/{comparison → utils/comparison}/trace_diff.py +1 -1
- oscura/{comparison → utils/comparison}/visualization.py +147 -89
- oscura/{component → utils/component}/__init__.py +3 -3
- oscura/{component → utils/component}/impedance.py +122 -58
- oscura/{component → utils/component}/reactive.py +165 -168
- oscura/{component → utils/component}/transmission_line.py +3 -3
- oscura/{filtering → utils/filtering}/__init__.py +6 -6
- oscura/{filtering → utils/filtering}/base.py +1 -1
- oscura/{filtering → utils/filtering}/convenience.py +2 -2
- oscura/{filtering → utils/filtering}/design.py +169 -93
- oscura/{filtering → utils/filtering}/filters.py +2 -2
- oscura/{filtering → utils/filtering}/introspection.py +2 -2
- oscura/utils/geometry.py +31 -0
- oscura/utils/imports.py +184 -0
- oscura/utils/lazy.py +1 -1
- oscura/{math → utils/math}/__init__.py +2 -2
- oscura/{math → utils/math}/arithmetic.py +114 -48
- oscura/{math → utils/math}/interpolation.py +139 -106
- oscura/utils/memory.py +129 -66
- oscura/utils/memory_advanced.py +92 -9
- oscura/utils/memory_extensions.py +10 -8
- oscura/{optimization → utils/optimization}/__init__.py +1 -1
- oscura/{optimization → utils/optimization}/search.py +2 -2
- oscura/utils/performance/__init__.py +58 -0
- oscura/utils/performance/caching.py +889 -0
- oscura/utils/performance/lsh_clustering.py +333 -0
- oscura/utils/performance/memory_optimizer.py +699 -0
- oscura/utils/performance/optimizations.py +675 -0
- oscura/utils/performance/parallel.py +654 -0
- oscura/utils/performance/profiling.py +661 -0
- oscura/{pipeline → utils/pipeline}/base.py +1 -1
- oscura/{pipeline → utils/pipeline}/composition.py +1 -1
- oscura/{pipeline → utils/pipeline}/parallel.py +3 -2
- oscura/{pipeline → utils/pipeline}/pipeline.py +1 -1
- oscura/{pipeline → utils/pipeline}/reverse_engineering.py +412 -221
- oscura/{search → utils/search}/__init__.py +3 -3
- oscura/{search → utils/search}/anomaly.py +188 -58
- oscura/utils/search/context.py +294 -0
- oscura/{search → utils/search}/pattern.py +138 -10
- oscura/utils/serial.py +51 -0
- oscura/utils/storage/__init__.py +61 -0
- oscura/utils/storage/database.py +1166 -0
- oscura/{streaming → utils/streaming}/chunked.py +302 -143
- oscura/{streaming → utils/streaming}/progressive.py +1 -1
- oscura/{streaming → utils/streaming}/realtime.py +3 -2
- oscura/{triggering → utils/triggering}/__init__.py +6 -6
- oscura/{triggering → utils/triggering}/base.py +6 -6
- oscura/{triggering → utils/triggering}/edge.py +2 -2
- oscura/{triggering → utils/triggering}/pattern.py +2 -2
- oscura/{triggering → utils/triggering}/pulse.py +115 -74
- oscura/{triggering → utils/triggering}/window.py +2 -2
- oscura/utils/validation.py +32 -0
- oscura/validation/__init__.py +121 -0
- oscura/{compliance → validation/compliance}/__init__.py +5 -5
- oscura/{compliance → validation/compliance}/advanced.py +5 -5
- oscura/{compliance → validation/compliance}/masks.py +1 -1
- oscura/{compliance → validation/compliance}/reporting.py +127 -53
- oscura/{compliance → validation/compliance}/testing.py +114 -52
- oscura/validation/compliance_tests.py +915 -0
- oscura/validation/fuzzer.py +990 -0
- oscura/validation/grammar_tests.py +596 -0
- oscura/validation/grammar_validator.py +904 -0
- oscura/validation/hil_testing.py +977 -0
- oscura/{quality → validation/quality}/__init__.py +4 -4
- oscura/{quality → validation/quality}/ensemble.py +251 -171
- oscura/{quality → validation/quality}/explainer.py +3 -3
- oscura/{quality → validation/quality}/scoring.py +1 -1
- oscura/{quality → validation/quality}/warnings.py +4 -4
- oscura/validation/regression_suite.py +808 -0
- oscura/validation/replay.py +788 -0
- oscura/{testing → validation/testing}/__init__.py +2 -2
- oscura/{testing → validation/testing}/synthetic.py +5 -5
- oscura/visualization/__init__.py +9 -0
- oscura/visualization/accessibility.py +1 -1
- oscura/visualization/annotations.py +64 -67
- oscura/visualization/colors.py +7 -7
- oscura/visualization/digital.py +180 -81
- oscura/visualization/eye.py +236 -85
- oscura/visualization/interactive.py +320 -143
- oscura/visualization/jitter.py +587 -247
- oscura/visualization/layout.py +169 -134
- oscura/visualization/optimization.py +103 -52
- oscura/visualization/palettes.py +1 -1
- oscura/visualization/power.py +427 -211
- oscura/visualization/power_extended.py +626 -297
- oscura/visualization/presets.py +2 -0
- oscura/visualization/protocols.py +495 -181
- oscura/visualization/render.py +79 -63
- oscura/visualization/reverse_engineering.py +171 -124
- oscura/visualization/signal_integrity.py +460 -279
- oscura/visualization/specialized.py +190 -100
- oscura/visualization/spectral.py +670 -255
- oscura/visualization/thumbnails.py +166 -137
- oscura/visualization/waveform.py +150 -63
- oscura/workflows/__init__.py +3 -0
- oscura/{batch → workflows/batch}/__init__.py +5 -5
- oscura/{batch → workflows/batch}/advanced.py +150 -75
- oscura/workflows/batch/aggregate.py +531 -0
- oscura/workflows/batch/analyze.py +236 -0
- oscura/{batch → workflows/batch}/logging.py +2 -2
- oscura/{batch → workflows/batch}/metrics.py +1 -1
- oscura/workflows/complete_re.py +1144 -0
- oscura/workflows/compliance.py +44 -54
- oscura/workflows/digital.py +197 -51
- oscura/workflows/legacy/__init__.py +12 -0
- oscura/{workflow → workflows/legacy}/dag.py +4 -1
- oscura/workflows/multi_trace.py +9 -9
- oscura/workflows/power.py +42 -62
- oscura/workflows/protocol.py +82 -49
- oscura/workflows/reverse_engineering.py +351 -150
- oscura/workflows/signal_integrity.py +157 -82
- oscura-0.7.0.dist-info/METADATA +661 -0
- oscura-0.7.0.dist-info/RECORD +591 -0
- oscura/batch/aggregate.py +0 -300
- oscura/batch/analyze.py +0 -139
- oscura/dsl/__init__.py +0 -73
- oscura/exceptions.py +0 -59
- oscura/exploratory/fuzzy.py +0 -513
- oscura/exploratory/sync.py +0 -384
- oscura/exporters/__init__.py +0 -94
- oscura/exporters/csv.py +0 -303
- oscura/exporters/exporters.py +0 -44
- oscura/exporters/hdf5.py +0 -217
- oscura/exporters/html_export.py +0 -701
- oscura/exporters/json_export.py +0 -291
- oscura/exporters/markdown_export.py +0 -367
- oscura/exporters/matlab_export.py +0 -354
- oscura/exporters/npz_export.py +0 -219
- oscura/exporters/spice_export.py +0 -210
- oscura/search/context.py +0 -149
- oscura/session/__init__.py +0 -34
- oscura/session/annotations.py +0 -289
- oscura/session/history.py +0 -313
- oscura/session/session.py +0 -520
- oscura/workflow/__init__.py +0 -13
- oscura-0.5.1.dist-info/METADATA +0 -583
- oscura-0.5.1.dist-info/RECORD +0 -481
- /oscura/core/{config.py → config/legacy.py} +0 -0
- /oscura/{extensibility → core/extensibility}/__init__.py +0 -0
- /oscura/{extensibility → core/extensibility}/registry.py +0 -0
- /oscura/{plugins → core/plugins}/isolation.py +0 -0
- /oscura/{schemas → core/schemas}/bus_configuration.json +0 -0
- /oscura/{builders → utils/builders}/signal_builder.py +0 -0
- /oscura/{optimization → utils/optimization}/parallel.py +0 -0
- /oscura/{pipeline → utils/pipeline}/__init__.py +0 -0
- /oscura/{streaming → utils/streaming}/__init__.py +0 -0
- {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/WHEEL +0 -0
- {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/entry_points.txt +0 -0
- {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"]
|