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,449 @@
|
|
|
1
|
+
"""FIBEX (FlexRay Interface Bus Exchange) format support.
|
|
2
|
+
|
|
3
|
+
This module implements FIBEX XML import and export for FlexRay network
|
|
4
|
+
configuration, frame definitions, and signal definitions.
|
|
5
|
+
|
|
6
|
+
References:
|
|
7
|
+
ASAM FIBEX 4.0.0 Specification
|
|
8
|
+
FlexRay Communications System Protocol Specification Version 3.0.1
|
|
9
|
+
|
|
10
|
+
Example:
|
|
11
|
+
>>> from oscura.automotive.flexray import FIBEXExporter, FlexRayAnalyzer
|
|
12
|
+
>>> analyzer = FlexRayAnalyzer()
|
|
13
|
+
>>> # ... parse frames and add signals ...
|
|
14
|
+
>>> exporter = FIBEXExporter(analyzer)
|
|
15
|
+
>>> exporter.export(Path("network.xml"))
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import xml.etree.ElementTree as ET
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import TYPE_CHECKING, Any
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from oscura.automotive.flexray.analyzer import FlexRayAnalyzer, FlexRaySignal
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class FIBEXExporter:
|
|
29
|
+
"""FIBEX XML exporter for FlexRay networks.
|
|
30
|
+
|
|
31
|
+
Exports FlexRay network configuration as FIBEX 4.0 XML format.
|
|
32
|
+
|
|
33
|
+
Attributes:
|
|
34
|
+
analyzer: FlexRay analyzer with frames and signals.
|
|
35
|
+
|
|
36
|
+
Example:
|
|
37
|
+
>>> exporter = FIBEXExporter(analyzer)
|
|
38
|
+
>>> exporter.export(Path("flexray_network.xml"))
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
FIBEX_NAMESPACE = "http://www.asam.net/xml/fbx"
|
|
42
|
+
FIBEX_VERSION = "4.0.0"
|
|
43
|
+
|
|
44
|
+
def __init__(self, analyzer: FlexRayAnalyzer) -> None:
|
|
45
|
+
"""Initialize FIBEX exporter.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
analyzer: FlexRay analyzer containing parsed frames and signals.
|
|
49
|
+
"""
|
|
50
|
+
self.analyzer = analyzer
|
|
51
|
+
|
|
52
|
+
def export(self, output_path: Path) -> None:
|
|
53
|
+
"""Export FIBEX XML file.
|
|
54
|
+
|
|
55
|
+
Creates a FIBEX XML file containing:
|
|
56
|
+
- Project information
|
|
57
|
+
- Cluster configuration
|
|
58
|
+
- Frame definitions
|
|
59
|
+
- Signal definitions
|
|
60
|
+
- Coding information
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
output_path: Output file path for FIBEX XML.
|
|
64
|
+
|
|
65
|
+
Example:
|
|
66
|
+
>>> exporter.export(Path("output/flexray_network.xml"))
|
|
67
|
+
"""
|
|
68
|
+
# Create root element
|
|
69
|
+
root = ET.Element("FIBEX")
|
|
70
|
+
root.set("xmlns", self.FIBEX_NAMESPACE)
|
|
71
|
+
root.set("VERSION", self.FIBEX_VERSION)
|
|
72
|
+
|
|
73
|
+
# Add project
|
|
74
|
+
project = ET.SubElement(root, "PROJECT")
|
|
75
|
+
project.set("ID", "FlexRay_Network")
|
|
76
|
+
|
|
77
|
+
project_name = ET.SubElement(project, "SHORT-NAME")
|
|
78
|
+
project_name.text = "FlexRay Network"
|
|
79
|
+
|
|
80
|
+
# Add cluster
|
|
81
|
+
clusters = ET.SubElement(project, "CLUSTERS")
|
|
82
|
+
cluster = ET.SubElement(clusters, "CLUSTER")
|
|
83
|
+
cluster.set("ID", "FlexRay_Cluster_1")
|
|
84
|
+
|
|
85
|
+
cluster_name = ET.SubElement(cluster, "SHORT-NAME")
|
|
86
|
+
cluster_name.text = "FlexRay_Cluster"
|
|
87
|
+
|
|
88
|
+
# Add cluster parameters
|
|
89
|
+
self._add_cluster_parameters(cluster)
|
|
90
|
+
|
|
91
|
+
# Add channels
|
|
92
|
+
self._add_channels(cluster)
|
|
93
|
+
|
|
94
|
+
# Add frames
|
|
95
|
+
self._add_frames(cluster)
|
|
96
|
+
|
|
97
|
+
# Add signals
|
|
98
|
+
self._add_signals(project)
|
|
99
|
+
|
|
100
|
+
# Write XML with pretty formatting
|
|
101
|
+
self._indent(root)
|
|
102
|
+
tree = ET.ElementTree(root)
|
|
103
|
+
tree.write(output_path, encoding="utf-8", xml_declaration=True)
|
|
104
|
+
|
|
105
|
+
def _add_cluster_parameters(self, cluster: ET.Element) -> None:
|
|
106
|
+
"""Add cluster parameters to FIBEX.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
cluster: Cluster XML element.
|
|
110
|
+
"""
|
|
111
|
+
params = ET.SubElement(cluster, "CLUSTER-PARAMS")
|
|
112
|
+
|
|
113
|
+
# Get configuration
|
|
114
|
+
config = self.analyzer.cluster_config
|
|
115
|
+
|
|
116
|
+
# Speed
|
|
117
|
+
speed = ET.SubElement(params, "SPEED")
|
|
118
|
+
speed.text = "10000" # 10 Mbps
|
|
119
|
+
|
|
120
|
+
# Static slots
|
|
121
|
+
static_slots = ET.SubElement(params, "NUMBER-OF-STATIC-SLOTS")
|
|
122
|
+
static_slots.text = str(config.get("static_slot_count", 100))
|
|
123
|
+
|
|
124
|
+
# Dynamic slots
|
|
125
|
+
dynamic_slots = ET.SubElement(params, "NUMBER-OF-DYNAMIC-SLOTS")
|
|
126
|
+
dynamic_slots.text = str(config.get("dynamic_slot_count", 50))
|
|
127
|
+
|
|
128
|
+
# Cycle length
|
|
129
|
+
cycle_length = ET.SubElement(params, "CYCLE-LENGTH-IN-MACROTICKS")
|
|
130
|
+
cycle_length.text = str(config.get("cycle_length", 5000))
|
|
131
|
+
|
|
132
|
+
def _add_channels(self, cluster: ET.Element) -> None:
|
|
133
|
+
"""Add channel definitions to FIBEX.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
cluster: Cluster XML element.
|
|
137
|
+
"""
|
|
138
|
+
channels = ET.SubElement(cluster, "CHANNELS")
|
|
139
|
+
|
|
140
|
+
# Get unique channels from frames
|
|
141
|
+
unique_channels = {frame.channel for frame in self.analyzer.frames}
|
|
142
|
+
|
|
143
|
+
for channel_id in sorted(unique_channels):
|
|
144
|
+
channel = ET.SubElement(channels, "CHANNEL")
|
|
145
|
+
channel.set("ID", f"Channel_{channel_id}")
|
|
146
|
+
|
|
147
|
+
channel_name = ET.SubElement(channel, "SHORT-NAME")
|
|
148
|
+
channel_name.text = f"Channel_{channel_id}"
|
|
149
|
+
|
|
150
|
+
def _add_frames(self, cluster: ET.Element) -> None:
|
|
151
|
+
"""Add frame definitions to FIBEX.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
cluster: Cluster XML element.
|
|
155
|
+
"""
|
|
156
|
+
frames_elem = ET.SubElement(cluster, "FRAMES")
|
|
157
|
+
|
|
158
|
+
# Get unique frame IDs
|
|
159
|
+
unique_frame_ids = sorted({frame.header.frame_id for frame in self.analyzer.frames})
|
|
160
|
+
|
|
161
|
+
for frame_id in unique_frame_ids:
|
|
162
|
+
# Get representative frame for this ID
|
|
163
|
+
frame_example = next(f for f in self.analyzer.frames if f.header.frame_id == frame_id)
|
|
164
|
+
|
|
165
|
+
frame_elem = ET.SubElement(frames_elem, "FRAME")
|
|
166
|
+
frame_elem.set("ID", f"Frame_{frame_id}")
|
|
167
|
+
|
|
168
|
+
# Frame name
|
|
169
|
+
frame_name = ET.SubElement(frame_elem, "SHORT-NAME")
|
|
170
|
+
frame_name.text = f"Frame_{frame_id}"
|
|
171
|
+
|
|
172
|
+
# Slot ID
|
|
173
|
+
slot_id_elem = ET.SubElement(frame_elem, "SLOT-ID")
|
|
174
|
+
slot_id_elem.text = str(frame_id)
|
|
175
|
+
|
|
176
|
+
# Frame length
|
|
177
|
+
frame_length = ET.SubElement(frame_elem, "FRAME-LENGTH")
|
|
178
|
+
frame_length.text = str(frame_example.header.payload_length)
|
|
179
|
+
|
|
180
|
+
# Segment type
|
|
181
|
+
segment_type = ET.SubElement(frame_elem, "SEGMENT-TYPE")
|
|
182
|
+
segment_type.text = frame_example.segment_type.upper()
|
|
183
|
+
|
|
184
|
+
# Add signals in this frame
|
|
185
|
+
frame_signals = [s for s in self.analyzer.signals if s.frame_id == frame_id]
|
|
186
|
+
if frame_signals:
|
|
187
|
+
signals_elem = ET.SubElement(frame_elem, "SIGNALS")
|
|
188
|
+
for signal in frame_signals:
|
|
189
|
+
signal_ref = ET.SubElement(signals_elem, "SIGNAL-REF")
|
|
190
|
+
signal_ref.set("ID-REF", signal.name)
|
|
191
|
+
|
|
192
|
+
def _add_signals(self, project: ET.Element) -> None:
|
|
193
|
+
"""Add signal definitions to FIBEX.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
project: Project XML element.
|
|
197
|
+
"""
|
|
198
|
+
if not self.analyzer.signals:
|
|
199
|
+
return
|
|
200
|
+
|
|
201
|
+
signals_elem = ET.SubElement(project, "SIGNALS")
|
|
202
|
+
|
|
203
|
+
for signal in self.analyzer.signals:
|
|
204
|
+
signal_elem = ET.SubElement(signals_elem, "SIGNAL")
|
|
205
|
+
signal_elem.set("ID", signal.name)
|
|
206
|
+
|
|
207
|
+
# Signal name
|
|
208
|
+
signal_name = ET.SubElement(signal_elem, "SHORT-NAME")
|
|
209
|
+
signal_name.text = signal.name
|
|
210
|
+
|
|
211
|
+
# Bit position
|
|
212
|
+
bit_position = ET.SubElement(signal_elem, "BIT-POSITION")
|
|
213
|
+
bit_position.text = str(signal.start_bit)
|
|
214
|
+
|
|
215
|
+
# Bit length
|
|
216
|
+
bit_length = ET.SubElement(signal_elem, "BIT-LENGTH")
|
|
217
|
+
bit_length.text = str(signal.bit_length)
|
|
218
|
+
|
|
219
|
+
# Byte order
|
|
220
|
+
byte_order = ET.SubElement(signal_elem, "BYTE-ORDER")
|
|
221
|
+
byte_order.text = "BIG-ENDIAN" if signal.byte_order == "big_endian" else "LITTLE-ENDIAN"
|
|
222
|
+
|
|
223
|
+
# Coding
|
|
224
|
+
coding = ET.SubElement(signal_elem, "CODING")
|
|
225
|
+
|
|
226
|
+
# Factor
|
|
227
|
+
factor_elem = ET.SubElement(coding, "FACTOR")
|
|
228
|
+
factor_elem.text = str(signal.factor)
|
|
229
|
+
|
|
230
|
+
# Offset
|
|
231
|
+
offset_elem = ET.SubElement(coding, "OFFSET")
|
|
232
|
+
offset_elem.text = str(signal.offset)
|
|
233
|
+
|
|
234
|
+
# Unit
|
|
235
|
+
if signal.unit:
|
|
236
|
+
unit_elem = ET.SubElement(coding, "UNIT")
|
|
237
|
+
unit_elem.text = signal.unit
|
|
238
|
+
|
|
239
|
+
def _indent(self, elem: ET.Element, level: int = 0) -> None:
|
|
240
|
+
"""Add pretty-printing indentation to XML tree.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
elem: XML element to indent.
|
|
244
|
+
level: Current indentation level.
|
|
245
|
+
"""
|
|
246
|
+
indent = " "
|
|
247
|
+
i = "\n" + level * indent
|
|
248
|
+
if len(elem):
|
|
249
|
+
if not elem.text or not elem.text.strip():
|
|
250
|
+
elem.text = i + indent
|
|
251
|
+
if not elem.tail or not elem.tail.strip():
|
|
252
|
+
elem.tail = i
|
|
253
|
+
for child in elem:
|
|
254
|
+
self._indent(child, level + 1)
|
|
255
|
+
if elem and (not elem[-1].tail or not elem[-1].tail.strip()):
|
|
256
|
+
elem[-1].tail = i
|
|
257
|
+
else:
|
|
258
|
+
if level and (not elem.tail or not elem.tail.strip()):
|
|
259
|
+
elem.tail = i
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
class FIBEXImporter:
|
|
263
|
+
"""FIBEX XML importer for FlexRay networks.
|
|
264
|
+
|
|
265
|
+
Imports FlexRay network configuration from FIBEX 4.0 XML format.
|
|
266
|
+
|
|
267
|
+
Example:
|
|
268
|
+
>>> importer = FIBEXImporter()
|
|
269
|
+
>>> cluster_config, signals = importer.load(Path("network.xml"))
|
|
270
|
+
>>> analyzer = FlexRayAnalyzer(cluster_config=cluster_config)
|
|
271
|
+
>>> for signal in signals:
|
|
272
|
+
... analyzer.add_signal(signal)
|
|
273
|
+
"""
|
|
274
|
+
|
|
275
|
+
def __init__(self) -> None:
|
|
276
|
+
"""Initialize FIBEX importer."""
|
|
277
|
+
|
|
278
|
+
def load(self, fibex_path: Path) -> tuple[dict[str, Any], list[FlexRaySignal]]:
|
|
279
|
+
"""Load FIBEX XML file.
|
|
280
|
+
|
|
281
|
+
Args:
|
|
282
|
+
fibex_path: Path to FIBEX XML file.
|
|
283
|
+
|
|
284
|
+
Returns:
|
|
285
|
+
Tuple of (cluster_config, signals) where:
|
|
286
|
+
- cluster_config: Dictionary with cluster parameters
|
|
287
|
+
- signals: List of FlexRaySignal definitions
|
|
288
|
+
|
|
289
|
+
Raises:
|
|
290
|
+
FileNotFoundError: If FIBEX file not found.
|
|
291
|
+
ValueError: If FIBEX file is invalid.
|
|
292
|
+
|
|
293
|
+
Example:
|
|
294
|
+
>>> config, signals = importer.load(Path("flexray_network.xml"))
|
|
295
|
+
>>> print(f"Loaded {len(signals)} signals")
|
|
296
|
+
"""
|
|
297
|
+
# Import here to avoid circular dependency
|
|
298
|
+
|
|
299
|
+
if not fibex_path.exists():
|
|
300
|
+
raise FileNotFoundError(f"FIBEX file not found: {fibex_path}")
|
|
301
|
+
|
|
302
|
+
tree = ET.parse(fibex_path)
|
|
303
|
+
root = tree.getroot()
|
|
304
|
+
|
|
305
|
+
# Extract cluster configuration
|
|
306
|
+
cluster_config = self._parse_cluster_config(root)
|
|
307
|
+
|
|
308
|
+
# Extract signal definitions
|
|
309
|
+
signals = self._parse_signals(root)
|
|
310
|
+
|
|
311
|
+
return cluster_config, signals
|
|
312
|
+
|
|
313
|
+
def _parse_cluster_config(self, root: ET.Element) -> dict[str, Any]:
|
|
314
|
+
"""Parse cluster configuration from FIBEX.
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
root: FIBEX root element.
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
Cluster configuration dictionary.
|
|
321
|
+
"""
|
|
322
|
+
config: dict[str, Any] = {}
|
|
323
|
+
|
|
324
|
+
# Find cluster parameters
|
|
325
|
+
cluster_params = root.find(".//{*}CLUSTER-PARAMS")
|
|
326
|
+
if cluster_params is not None:
|
|
327
|
+
# Static slots
|
|
328
|
+
static_slots = cluster_params.find("{*}NUMBER-OF-STATIC-SLOTS")
|
|
329
|
+
if static_slots is not None and static_slots.text:
|
|
330
|
+
config["static_slot_count"] = int(static_slots.text)
|
|
331
|
+
|
|
332
|
+
# Dynamic slots
|
|
333
|
+
dynamic_slots = cluster_params.find("{*}NUMBER-OF-DYNAMIC-SLOTS")
|
|
334
|
+
if dynamic_slots is not None and dynamic_slots.text:
|
|
335
|
+
config["dynamic_slot_count"] = int(dynamic_slots.text)
|
|
336
|
+
|
|
337
|
+
# Cycle length
|
|
338
|
+
cycle_length = cluster_params.find("{*}CYCLE-LENGTH-IN-MACROTICKS")
|
|
339
|
+
if cycle_length is not None and cycle_length.text:
|
|
340
|
+
config["cycle_length"] = int(cycle_length.text)
|
|
341
|
+
|
|
342
|
+
return config
|
|
343
|
+
|
|
344
|
+
def _parse_signals(self, root: ET.Element) -> list[FlexRaySignal]:
|
|
345
|
+
"""Parse signal definitions from FIBEX.
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
root: FIBEX root element.
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
List of FlexRay signal definitions.
|
|
352
|
+
"""
|
|
353
|
+
# Import here to avoid circular dependency
|
|
354
|
+
from oscura.automotive.flexray.analyzer import FlexRaySignal
|
|
355
|
+
|
|
356
|
+
signals: list[FlexRaySignal] = []
|
|
357
|
+
|
|
358
|
+
# Find all signal elements
|
|
359
|
+
for signal_elem in root.findall(".//{*}SIGNAL"):
|
|
360
|
+
signal_id = signal_elem.get("ID", "")
|
|
361
|
+
|
|
362
|
+
# Signal name
|
|
363
|
+
name_elem = signal_elem.find("{*}SHORT-NAME")
|
|
364
|
+
name = name_elem.text if name_elem is not None and name_elem.text else signal_id
|
|
365
|
+
|
|
366
|
+
# Bit position
|
|
367
|
+
bit_pos_elem = signal_elem.find("{*}BIT-POSITION")
|
|
368
|
+
start_bit = (
|
|
369
|
+
int(bit_pos_elem.text) if bit_pos_elem is not None and bit_pos_elem.text else 0
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
# Bit length
|
|
373
|
+
bit_len_elem = signal_elem.find("{*}BIT-LENGTH")
|
|
374
|
+
bit_length = (
|
|
375
|
+
int(bit_len_elem.text) if bit_len_elem is not None and bit_len_elem.text else 8
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
# Byte order
|
|
379
|
+
byte_order_elem = signal_elem.find("{*}BYTE-ORDER")
|
|
380
|
+
byte_order = "big_endian"
|
|
381
|
+
if byte_order_elem is not None and byte_order_elem.text:
|
|
382
|
+
byte_order = (
|
|
383
|
+
"big_endian" if byte_order_elem.text == "BIG-ENDIAN" else "little_endian"
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
# Coding
|
|
387
|
+
coding_elem = signal_elem.find("{*}CODING")
|
|
388
|
+
factor = 1.0
|
|
389
|
+
offset = 0.0
|
|
390
|
+
unit = ""
|
|
391
|
+
|
|
392
|
+
if coding_elem is not None:
|
|
393
|
+
factor_elem = coding_elem.find("{*}FACTOR")
|
|
394
|
+
if factor_elem is not None and factor_elem.text:
|
|
395
|
+
factor = float(factor_elem.text)
|
|
396
|
+
|
|
397
|
+
offset_elem = coding_elem.find("{*}OFFSET")
|
|
398
|
+
if offset_elem is not None and offset_elem.text:
|
|
399
|
+
offset = float(offset_elem.text)
|
|
400
|
+
|
|
401
|
+
unit_elem = coding_elem.find("{*}UNIT")
|
|
402
|
+
if unit_elem is not None and unit_elem.text:
|
|
403
|
+
unit = unit_elem.text
|
|
404
|
+
|
|
405
|
+
# Find frame ID (need to search for frame containing this signal)
|
|
406
|
+
frame_id = self._find_frame_id_for_signal(root, signal_id)
|
|
407
|
+
|
|
408
|
+
signal = FlexRaySignal(
|
|
409
|
+
name=name,
|
|
410
|
+
frame_id=frame_id,
|
|
411
|
+
start_bit=start_bit,
|
|
412
|
+
bit_length=bit_length,
|
|
413
|
+
byte_order=byte_order,
|
|
414
|
+
factor=factor,
|
|
415
|
+
offset=offset,
|
|
416
|
+
unit=unit,
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
signals.append(signal)
|
|
420
|
+
|
|
421
|
+
return signals
|
|
422
|
+
|
|
423
|
+
def _find_frame_id_for_signal(self, root: ET.Element, signal_id: str) -> int:
|
|
424
|
+
"""Find frame ID containing a signal.
|
|
425
|
+
|
|
426
|
+
Args:
|
|
427
|
+
root: FIBEX root element.
|
|
428
|
+
signal_id: Signal ID to find.
|
|
429
|
+
|
|
430
|
+
Returns:
|
|
431
|
+
Frame ID (slot ID) containing the signal, or 0 if not found.
|
|
432
|
+
"""
|
|
433
|
+
# Find frame containing this signal reference
|
|
434
|
+
for frame_elem in root.findall(".//{*}FRAME"):
|
|
435
|
+
signal_refs = frame_elem.findall(".//{*}SIGNAL-REF")
|
|
436
|
+
for sig_ref in signal_refs:
|
|
437
|
+
if sig_ref.get("ID-REF") == signal_id:
|
|
438
|
+
# Found frame, get slot ID
|
|
439
|
+
slot_id_elem = frame_elem.find("{*}SLOT-ID")
|
|
440
|
+
if slot_id_elem is not None and slot_id_elem.text:
|
|
441
|
+
return int(slot_id_elem.text)
|
|
442
|
+
|
|
443
|
+
return 0 # Default if not found
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
__all__ = [
|
|
447
|
+
"FIBEXExporter",
|
|
448
|
+
"FIBEXImporter",
|
|
449
|
+
]
|
|
@@ -1,12 +1,49 @@
|
|
|
1
|
-
"""J1939
|
|
1
|
+
"""J1939 (SAE J1939) protocol support for heavy-duty vehicles.
|
|
2
2
|
|
|
3
|
-
This module provides J1939 protocol
|
|
4
|
-
|
|
3
|
+
This module provides comprehensive J1939 protocol analysis including:
|
|
4
|
+
- Message decoding and PGN extraction
|
|
5
|
+
- Transport protocol (TP.CM, TP.DT, BAM) multi-packet reassembly
|
|
6
|
+
- Suspect Parameter Number (SPN) decoding
|
|
7
|
+
- Standard SPN definitions
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
>>> from oscura.automotive.j1939 import J1939Analyzer, J1939SPN
|
|
11
|
+
>>> analyzer = J1939Analyzer()
|
|
12
|
+
>>> msg = analyzer.parse_message(0x0CF00400, b'\\xff' * 8)
|
|
13
|
+
>>> print(msg.identifier.pgn)
|
|
14
|
+
61444
|
|
5
15
|
"""
|
|
6
16
|
|
|
7
|
-
|
|
17
|
+
from oscura.automotive.j1939.analyzer import (
|
|
18
|
+
J1939SPN,
|
|
19
|
+
J1939Analyzer,
|
|
20
|
+
J1939Identifier,
|
|
21
|
+
J1939Message,
|
|
22
|
+
)
|
|
23
|
+
from oscura.automotive.j1939.decoder import (
|
|
24
|
+
J1939Decoder,
|
|
25
|
+
extract_pgn,
|
|
26
|
+
)
|
|
27
|
+
from oscura.automotive.j1939.decoder import (
|
|
28
|
+
J1939Message as DecoderMessage,
|
|
29
|
+
)
|
|
30
|
+
from oscura.automotive.j1939.spns import STANDARD_SPNS, get_standard_spns
|
|
31
|
+
from oscura.automotive.j1939.transport import TransportProtocol, TransportSession
|
|
8
32
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
33
|
+
__all__ = [
|
|
34
|
+
"J1939SPN",
|
|
35
|
+
# SPN definitions
|
|
36
|
+
"STANDARD_SPNS",
|
|
37
|
+
"DecoderMessage",
|
|
38
|
+
# Analyzer (new comprehensive analyzer)
|
|
39
|
+
"J1939Analyzer",
|
|
40
|
+
# Decoder (legacy decoder)
|
|
41
|
+
"J1939Decoder",
|
|
42
|
+
"J1939Identifier",
|
|
43
|
+
"J1939Message",
|
|
44
|
+
# Transport protocol
|
|
45
|
+
"TransportProtocol",
|
|
46
|
+
"TransportSession",
|
|
47
|
+
"extract_pgn",
|
|
48
|
+
"get_standard_spns",
|
|
49
|
+
]
|