oscura 0.0.1__py3-none-any.whl → 0.1.1__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 +813 -8
- oscura/__main__.py +392 -0
- oscura/analyzers/__init__.py +37 -0
- oscura/analyzers/digital/__init__.py +177 -0
- oscura/analyzers/digital/bus.py +691 -0
- oscura/analyzers/digital/clock.py +805 -0
- oscura/analyzers/digital/correlation.py +720 -0
- oscura/analyzers/digital/edges.py +632 -0
- oscura/analyzers/digital/extraction.py +413 -0
- oscura/analyzers/digital/quality.py +878 -0
- oscura/analyzers/digital/signal_quality.py +877 -0
- oscura/analyzers/digital/thresholds.py +708 -0
- oscura/analyzers/digital/timing.py +1104 -0
- oscura/analyzers/eye/__init__.py +46 -0
- oscura/analyzers/eye/diagram.py +434 -0
- oscura/analyzers/eye/metrics.py +555 -0
- oscura/analyzers/jitter/__init__.py +83 -0
- oscura/analyzers/jitter/ber.py +333 -0
- oscura/analyzers/jitter/decomposition.py +759 -0
- oscura/analyzers/jitter/measurements.py +413 -0
- oscura/analyzers/jitter/spectrum.py +220 -0
- oscura/analyzers/measurements.py +40 -0
- oscura/analyzers/packet/__init__.py +171 -0
- oscura/analyzers/packet/daq.py +1077 -0
- oscura/analyzers/packet/metrics.py +437 -0
- oscura/analyzers/packet/parser.py +327 -0
- oscura/analyzers/packet/payload.py +2156 -0
- oscura/analyzers/packet/payload_analysis.py +1312 -0
- oscura/analyzers/packet/payload_extraction.py +236 -0
- oscura/analyzers/packet/payload_patterns.py +670 -0
- oscura/analyzers/packet/stream.py +359 -0
- oscura/analyzers/patterns/__init__.py +266 -0
- oscura/analyzers/patterns/clustering.py +1036 -0
- oscura/analyzers/patterns/discovery.py +539 -0
- oscura/analyzers/patterns/learning.py +797 -0
- oscura/analyzers/patterns/matching.py +1091 -0
- oscura/analyzers/patterns/periodic.py +650 -0
- oscura/analyzers/patterns/sequences.py +767 -0
- oscura/analyzers/power/__init__.py +116 -0
- oscura/analyzers/power/ac_power.py +391 -0
- oscura/analyzers/power/basic.py +383 -0
- oscura/analyzers/power/conduction.py +314 -0
- oscura/analyzers/power/efficiency.py +297 -0
- oscura/analyzers/power/ripple.py +356 -0
- oscura/analyzers/power/soa.py +372 -0
- oscura/analyzers/power/switching.py +479 -0
- oscura/analyzers/protocol/__init__.py +150 -0
- oscura/analyzers/protocols/__init__.py +150 -0
- oscura/analyzers/protocols/base.py +500 -0
- oscura/analyzers/protocols/can.py +620 -0
- oscura/analyzers/protocols/can_fd.py +448 -0
- oscura/analyzers/protocols/flexray.py +405 -0
- oscura/analyzers/protocols/hdlc.py +399 -0
- oscura/analyzers/protocols/i2c.py +368 -0
- oscura/analyzers/protocols/i2s.py +296 -0
- oscura/analyzers/protocols/jtag.py +393 -0
- oscura/analyzers/protocols/lin.py +445 -0
- oscura/analyzers/protocols/manchester.py +333 -0
- oscura/analyzers/protocols/onewire.py +501 -0
- oscura/analyzers/protocols/spi.py +334 -0
- oscura/analyzers/protocols/swd.py +325 -0
- oscura/analyzers/protocols/uart.py +393 -0
- oscura/analyzers/protocols/usb.py +495 -0
- oscura/analyzers/signal_integrity/__init__.py +63 -0
- oscura/analyzers/signal_integrity/embedding.py +294 -0
- oscura/analyzers/signal_integrity/equalization.py +370 -0
- oscura/analyzers/signal_integrity/sparams.py +484 -0
- oscura/analyzers/spectral/__init__.py +53 -0
- oscura/analyzers/spectral/chunked.py +273 -0
- oscura/analyzers/spectral/chunked_fft.py +571 -0
- oscura/analyzers/spectral/chunked_wavelet.py +391 -0
- oscura/analyzers/spectral/fft.py +92 -0
- oscura/analyzers/statistical/__init__.py +250 -0
- oscura/analyzers/statistical/checksum.py +923 -0
- oscura/analyzers/statistical/chunked_corr.py +228 -0
- oscura/analyzers/statistical/classification.py +778 -0
- oscura/analyzers/statistical/entropy.py +1113 -0
- oscura/analyzers/statistical/ngrams.py +614 -0
- oscura/analyzers/statistics/__init__.py +119 -0
- oscura/analyzers/statistics/advanced.py +885 -0
- oscura/analyzers/statistics/basic.py +263 -0
- oscura/analyzers/statistics/correlation.py +630 -0
- oscura/analyzers/statistics/distribution.py +298 -0
- oscura/analyzers/statistics/outliers.py +463 -0
- oscura/analyzers/statistics/streaming.py +93 -0
- oscura/analyzers/statistics/trend.py +520 -0
- oscura/analyzers/validation.py +598 -0
- oscura/analyzers/waveform/__init__.py +36 -0
- oscura/analyzers/waveform/measurements.py +943 -0
- oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
- oscura/analyzers/waveform/spectral.py +1689 -0
- oscura/analyzers/waveform/wavelets.py +298 -0
- oscura/api/__init__.py +62 -0
- oscura/api/dsl.py +538 -0
- oscura/api/fluent.py +571 -0
- oscura/api/operators.py +498 -0
- oscura/api/optimization.py +392 -0
- oscura/api/profiling.py +396 -0
- oscura/automotive/__init__.py +73 -0
- oscura/automotive/can/__init__.py +52 -0
- oscura/automotive/can/analysis.py +356 -0
- oscura/automotive/can/checksum.py +250 -0
- oscura/automotive/can/correlation.py +212 -0
- oscura/automotive/can/discovery.py +355 -0
- oscura/automotive/can/message_wrapper.py +375 -0
- oscura/automotive/can/models.py +385 -0
- oscura/automotive/can/patterns.py +381 -0
- oscura/automotive/can/session.py +452 -0
- oscura/automotive/can/state_machine.py +300 -0
- oscura/automotive/can/stimulus_response.py +461 -0
- oscura/automotive/dbc/__init__.py +15 -0
- oscura/automotive/dbc/generator.py +156 -0
- oscura/automotive/dbc/parser.py +146 -0
- oscura/automotive/dtc/__init__.py +30 -0
- oscura/automotive/dtc/database.py +3036 -0
- oscura/automotive/j1939/__init__.py +14 -0
- oscura/automotive/j1939/decoder.py +745 -0
- oscura/automotive/loaders/__init__.py +35 -0
- oscura/automotive/loaders/asc.py +98 -0
- oscura/automotive/loaders/blf.py +77 -0
- oscura/automotive/loaders/csv_can.py +136 -0
- oscura/automotive/loaders/dispatcher.py +136 -0
- oscura/automotive/loaders/mdf.py +331 -0
- oscura/automotive/loaders/pcap.py +132 -0
- oscura/automotive/obd/__init__.py +14 -0
- oscura/automotive/obd/decoder.py +707 -0
- oscura/automotive/uds/__init__.py +48 -0
- oscura/automotive/uds/decoder.py +265 -0
- oscura/automotive/uds/models.py +64 -0
- oscura/automotive/visualization.py +369 -0
- oscura/batch/__init__.py +55 -0
- oscura/batch/advanced.py +627 -0
- oscura/batch/aggregate.py +300 -0
- oscura/batch/analyze.py +139 -0
- oscura/batch/logging.py +487 -0
- oscura/batch/metrics.py +556 -0
- oscura/builders/__init__.py +41 -0
- oscura/builders/signal_builder.py +1131 -0
- oscura/cli/__init__.py +14 -0
- oscura/cli/batch.py +339 -0
- oscura/cli/characterize.py +273 -0
- oscura/cli/compare.py +775 -0
- oscura/cli/decode.py +551 -0
- oscura/cli/main.py +247 -0
- oscura/cli/shell.py +350 -0
- oscura/comparison/__init__.py +66 -0
- oscura/comparison/compare.py +397 -0
- oscura/comparison/golden.py +487 -0
- oscura/comparison/limits.py +391 -0
- oscura/comparison/mask.py +434 -0
- oscura/comparison/trace_diff.py +30 -0
- oscura/comparison/visualization.py +481 -0
- oscura/compliance/__init__.py +70 -0
- oscura/compliance/advanced.py +756 -0
- oscura/compliance/masks.py +363 -0
- oscura/compliance/reporting.py +483 -0
- oscura/compliance/testing.py +298 -0
- oscura/component/__init__.py +38 -0
- oscura/component/impedance.py +365 -0
- oscura/component/reactive.py +598 -0
- oscura/component/transmission_line.py +312 -0
- oscura/config/__init__.py +191 -0
- oscura/config/defaults.py +254 -0
- oscura/config/loader.py +348 -0
- oscura/config/memory.py +271 -0
- oscura/config/migration.py +458 -0
- oscura/config/pipeline.py +1077 -0
- oscura/config/preferences.py +530 -0
- oscura/config/protocol.py +875 -0
- oscura/config/schema.py +713 -0
- oscura/config/settings.py +420 -0
- oscura/config/thresholds.py +599 -0
- oscura/convenience.py +457 -0
- oscura/core/__init__.py +299 -0
- oscura/core/audit.py +457 -0
- oscura/core/backend_selector.py +405 -0
- oscura/core/cache.py +590 -0
- oscura/core/cancellation.py +439 -0
- oscura/core/confidence.py +225 -0
- oscura/core/config.py +506 -0
- oscura/core/correlation.py +216 -0
- oscura/core/cross_domain.py +422 -0
- oscura/core/debug.py +301 -0
- oscura/core/edge_cases.py +541 -0
- oscura/core/exceptions.py +535 -0
- oscura/core/gpu_backend.py +523 -0
- oscura/core/lazy.py +832 -0
- oscura/core/log_query.py +540 -0
- oscura/core/logging.py +931 -0
- oscura/core/logging_advanced.py +952 -0
- oscura/core/memoize.py +171 -0
- oscura/core/memory_check.py +274 -0
- oscura/core/memory_guard.py +290 -0
- oscura/core/memory_limits.py +336 -0
- oscura/core/memory_monitor.py +453 -0
- oscura/core/memory_progress.py +465 -0
- oscura/core/memory_warnings.py +315 -0
- oscura/core/numba_backend.py +362 -0
- oscura/core/performance.py +352 -0
- oscura/core/progress.py +524 -0
- oscura/core/provenance.py +358 -0
- oscura/core/results.py +331 -0
- oscura/core/types.py +504 -0
- oscura/core/uncertainty.py +383 -0
- oscura/discovery/__init__.py +52 -0
- oscura/discovery/anomaly_detector.py +672 -0
- oscura/discovery/auto_decoder.py +415 -0
- oscura/discovery/comparison.py +497 -0
- oscura/discovery/quality_validator.py +528 -0
- oscura/discovery/signal_detector.py +769 -0
- oscura/dsl/__init__.py +73 -0
- oscura/dsl/commands.py +246 -0
- oscura/dsl/interpreter.py +455 -0
- oscura/dsl/parser.py +689 -0
- oscura/dsl/repl.py +172 -0
- oscura/exceptions.py +59 -0
- oscura/exploratory/__init__.py +111 -0
- oscura/exploratory/error_recovery.py +642 -0
- oscura/exploratory/fuzzy.py +513 -0
- oscura/exploratory/fuzzy_advanced.py +786 -0
- oscura/exploratory/legacy.py +831 -0
- oscura/exploratory/parse.py +358 -0
- oscura/exploratory/recovery.py +275 -0
- oscura/exploratory/sync.py +382 -0
- oscura/exploratory/unknown.py +707 -0
- oscura/export/__init__.py +25 -0
- oscura/export/wireshark/README.md +265 -0
- oscura/export/wireshark/__init__.py +47 -0
- oscura/export/wireshark/generator.py +312 -0
- oscura/export/wireshark/lua_builder.py +159 -0
- oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
- oscura/export/wireshark/type_mapping.py +165 -0
- oscura/export/wireshark/validator.py +105 -0
- oscura/exporters/__init__.py +94 -0
- oscura/exporters/csv.py +303 -0
- oscura/exporters/exporters.py +44 -0
- oscura/exporters/hdf5.py +219 -0
- oscura/exporters/html_export.py +701 -0
- oscura/exporters/json_export.py +291 -0
- oscura/exporters/markdown_export.py +367 -0
- oscura/exporters/matlab_export.py +354 -0
- oscura/exporters/npz_export.py +219 -0
- oscura/exporters/spice_export.py +210 -0
- oscura/extensibility/__init__.py +131 -0
- oscura/extensibility/docs.py +752 -0
- oscura/extensibility/extensions.py +1125 -0
- oscura/extensibility/logging.py +259 -0
- oscura/extensibility/measurements.py +485 -0
- oscura/extensibility/plugins.py +414 -0
- oscura/extensibility/registry.py +346 -0
- oscura/extensibility/templates.py +913 -0
- oscura/extensibility/validation.py +651 -0
- oscura/filtering/__init__.py +89 -0
- oscura/filtering/base.py +563 -0
- oscura/filtering/convenience.py +564 -0
- oscura/filtering/design.py +725 -0
- oscura/filtering/filters.py +32 -0
- oscura/filtering/introspection.py +605 -0
- oscura/guidance/__init__.py +24 -0
- oscura/guidance/recommender.py +429 -0
- oscura/guidance/wizard.py +518 -0
- oscura/inference/__init__.py +251 -0
- oscura/inference/active_learning/README.md +153 -0
- oscura/inference/active_learning/__init__.py +38 -0
- oscura/inference/active_learning/lstar.py +257 -0
- oscura/inference/active_learning/observation_table.py +230 -0
- oscura/inference/active_learning/oracle.py +78 -0
- oscura/inference/active_learning/teachers/__init__.py +15 -0
- oscura/inference/active_learning/teachers/simulator.py +192 -0
- oscura/inference/adaptive_tuning.py +453 -0
- oscura/inference/alignment.py +653 -0
- oscura/inference/bayesian.py +943 -0
- oscura/inference/binary.py +1016 -0
- oscura/inference/crc_reverse.py +711 -0
- oscura/inference/logic.py +288 -0
- oscura/inference/message_format.py +1305 -0
- oscura/inference/protocol.py +417 -0
- oscura/inference/protocol_dsl.py +1084 -0
- oscura/inference/protocol_library.py +1230 -0
- oscura/inference/sequences.py +809 -0
- oscura/inference/signal_intelligence.py +1509 -0
- oscura/inference/spectral.py +215 -0
- oscura/inference/state_machine.py +634 -0
- oscura/inference/stream.py +918 -0
- oscura/integrations/__init__.py +59 -0
- oscura/integrations/llm.py +1827 -0
- oscura/jupyter/__init__.py +32 -0
- oscura/jupyter/display.py +268 -0
- oscura/jupyter/magic.py +334 -0
- oscura/loaders/__init__.py +526 -0
- oscura/loaders/binary.py +69 -0
- oscura/loaders/configurable.py +1255 -0
- oscura/loaders/csv.py +26 -0
- oscura/loaders/csv_loader.py +473 -0
- oscura/loaders/hdf5.py +9 -0
- oscura/loaders/hdf5_loader.py +510 -0
- oscura/loaders/lazy.py +370 -0
- oscura/loaders/mmap_loader.py +583 -0
- oscura/loaders/numpy_loader.py +436 -0
- oscura/loaders/pcap.py +432 -0
- oscura/loaders/preprocessing.py +368 -0
- oscura/loaders/rigol.py +287 -0
- oscura/loaders/sigrok.py +321 -0
- oscura/loaders/tdms.py +367 -0
- oscura/loaders/tektronix.py +711 -0
- oscura/loaders/validation.py +584 -0
- oscura/loaders/vcd.py +464 -0
- oscura/loaders/wav.py +233 -0
- oscura/math/__init__.py +45 -0
- oscura/math/arithmetic.py +824 -0
- oscura/math/interpolation.py +413 -0
- oscura/onboarding/__init__.py +39 -0
- oscura/onboarding/help.py +498 -0
- oscura/onboarding/tutorials.py +405 -0
- oscura/onboarding/wizard.py +466 -0
- oscura/optimization/__init__.py +19 -0
- oscura/optimization/parallel.py +440 -0
- oscura/optimization/search.py +532 -0
- oscura/pipeline/__init__.py +43 -0
- oscura/pipeline/base.py +338 -0
- oscura/pipeline/composition.py +242 -0
- oscura/pipeline/parallel.py +448 -0
- oscura/pipeline/pipeline.py +375 -0
- oscura/pipeline/reverse_engineering.py +1119 -0
- oscura/plugins/__init__.py +122 -0
- oscura/plugins/base.py +272 -0
- oscura/plugins/cli.py +497 -0
- oscura/plugins/discovery.py +411 -0
- oscura/plugins/isolation.py +418 -0
- oscura/plugins/lifecycle.py +959 -0
- oscura/plugins/manager.py +493 -0
- oscura/plugins/registry.py +421 -0
- oscura/plugins/versioning.py +372 -0
- oscura/py.typed +0 -0
- oscura/quality/__init__.py +65 -0
- oscura/quality/ensemble.py +740 -0
- oscura/quality/explainer.py +338 -0
- oscura/quality/scoring.py +616 -0
- oscura/quality/warnings.py +456 -0
- oscura/reporting/__init__.py +248 -0
- oscura/reporting/advanced.py +1234 -0
- oscura/reporting/analyze.py +448 -0
- oscura/reporting/argument_preparer.py +596 -0
- oscura/reporting/auto_report.py +507 -0
- oscura/reporting/batch.py +615 -0
- oscura/reporting/chart_selection.py +223 -0
- oscura/reporting/comparison.py +330 -0
- oscura/reporting/config.py +615 -0
- oscura/reporting/content/__init__.py +39 -0
- oscura/reporting/content/executive.py +127 -0
- oscura/reporting/content/filtering.py +191 -0
- oscura/reporting/content/minimal.py +257 -0
- oscura/reporting/content/verbosity.py +162 -0
- oscura/reporting/core.py +508 -0
- oscura/reporting/core_formats/__init__.py +17 -0
- oscura/reporting/core_formats/multi_format.py +210 -0
- oscura/reporting/engine.py +836 -0
- oscura/reporting/export.py +366 -0
- oscura/reporting/formatting/__init__.py +129 -0
- oscura/reporting/formatting/emphasis.py +81 -0
- oscura/reporting/formatting/numbers.py +403 -0
- oscura/reporting/formatting/standards.py +55 -0
- oscura/reporting/formatting.py +466 -0
- oscura/reporting/html.py +578 -0
- oscura/reporting/index.py +590 -0
- oscura/reporting/multichannel.py +296 -0
- oscura/reporting/output.py +379 -0
- oscura/reporting/pdf.py +373 -0
- oscura/reporting/plots.py +731 -0
- oscura/reporting/pptx_export.py +360 -0
- oscura/reporting/renderers/__init__.py +11 -0
- oscura/reporting/renderers/pdf.py +94 -0
- oscura/reporting/sections.py +471 -0
- oscura/reporting/standards.py +680 -0
- oscura/reporting/summary_generator.py +368 -0
- oscura/reporting/tables.py +397 -0
- oscura/reporting/template_system.py +724 -0
- oscura/reporting/templates/__init__.py +15 -0
- oscura/reporting/templates/definition.py +205 -0
- oscura/reporting/templates/index.html +649 -0
- oscura/reporting/templates/index.md +173 -0
- oscura/schemas/__init__.py +158 -0
- oscura/schemas/bus_configuration.json +322 -0
- oscura/schemas/device_mapping.json +182 -0
- oscura/schemas/packet_format.json +418 -0
- oscura/schemas/protocol_definition.json +363 -0
- oscura/search/__init__.py +16 -0
- oscura/search/anomaly.py +292 -0
- oscura/search/context.py +149 -0
- oscura/search/pattern.py +160 -0
- oscura/session/__init__.py +34 -0
- oscura/session/annotations.py +289 -0
- oscura/session/history.py +313 -0
- oscura/session/session.py +445 -0
- oscura/streaming/__init__.py +43 -0
- oscura/streaming/chunked.py +611 -0
- oscura/streaming/progressive.py +393 -0
- oscura/streaming/realtime.py +622 -0
- oscura/testing/__init__.py +54 -0
- oscura/testing/synthetic.py +808 -0
- oscura/triggering/__init__.py +68 -0
- oscura/triggering/base.py +229 -0
- oscura/triggering/edge.py +353 -0
- oscura/triggering/pattern.py +344 -0
- oscura/triggering/pulse.py +581 -0
- oscura/triggering/window.py +453 -0
- oscura/ui/__init__.py +48 -0
- oscura/ui/formatters.py +526 -0
- oscura/ui/progressive_display.py +340 -0
- oscura/utils/__init__.py +99 -0
- oscura/utils/autodetect.py +338 -0
- oscura/utils/buffer.py +389 -0
- oscura/utils/lazy.py +407 -0
- oscura/utils/lazy_imports.py +147 -0
- oscura/utils/memory.py +836 -0
- oscura/utils/memory_advanced.py +1326 -0
- oscura/utils/memory_extensions.py +465 -0
- oscura/utils/progressive.py +352 -0
- oscura/utils/windowing.py +362 -0
- oscura/visualization/__init__.py +321 -0
- oscura/visualization/accessibility.py +526 -0
- oscura/visualization/annotations.py +374 -0
- oscura/visualization/axis_scaling.py +305 -0
- oscura/visualization/colors.py +453 -0
- oscura/visualization/digital.py +337 -0
- oscura/visualization/eye.py +420 -0
- oscura/visualization/histogram.py +281 -0
- oscura/visualization/interactive.py +858 -0
- oscura/visualization/jitter.py +702 -0
- oscura/visualization/keyboard.py +394 -0
- oscura/visualization/layout.py +365 -0
- oscura/visualization/optimization.py +1028 -0
- oscura/visualization/palettes.py +446 -0
- oscura/visualization/plot.py +92 -0
- oscura/visualization/power.py +290 -0
- oscura/visualization/power_extended.py +626 -0
- oscura/visualization/presets.py +467 -0
- oscura/visualization/protocols.py +932 -0
- oscura/visualization/render.py +207 -0
- oscura/visualization/rendering.py +444 -0
- oscura/visualization/reverse_engineering.py +791 -0
- oscura/visualization/signal_integrity.py +808 -0
- oscura/visualization/specialized.py +553 -0
- oscura/visualization/spectral.py +811 -0
- oscura/visualization/styles.py +381 -0
- oscura/visualization/thumbnails.py +311 -0
- oscura/visualization/time_axis.py +351 -0
- oscura/visualization/waveform.py +367 -0
- oscura/workflow/__init__.py +13 -0
- oscura/workflow/dag.py +377 -0
- oscura/workflows/__init__.py +58 -0
- oscura/workflows/compliance.py +280 -0
- oscura/workflows/digital.py +272 -0
- oscura/workflows/multi_trace.py +502 -0
- oscura/workflows/power.py +178 -0
- oscura/workflows/protocol.py +492 -0
- oscura/workflows/reverse_engineering.py +639 -0
- oscura/workflows/signal_integrity.py +227 -0
- oscura-0.1.1.dist-info/METADATA +300 -0
- oscura-0.1.1.dist-info/RECORD +463 -0
- oscura-0.1.1.dist-info/entry_points.txt +2 -0
- {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/licenses/LICENSE +1 -1
- oscura-0.0.1.dist-info/METADATA +0 -63
- oscura-0.0.1.dist-info/RECORD +0 -5
- {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
"""Packet validation and integrity checking.
|
|
2
|
+
|
|
3
|
+
This module provides comprehensive packet validation including sync markers,
|
|
4
|
+
sequence numbers, checksums, and structural integrity verification.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
>>> from oscura.loaders.validation import PacketValidator
|
|
9
|
+
>>> validator = PacketValidator(sync_marker=0xFA, checksum_type="crc16")
|
|
10
|
+
>>> result = validator.validate_packet(packet_data)
|
|
11
|
+
>>> if result.is_valid:
|
|
12
|
+
... print("Packet valid")
|
|
13
|
+
>>> stats = validator.get_statistics()
|
|
14
|
+
>>> print(f"Pass rate: {stats.pass_rate:.1%}")
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import logging
|
|
20
|
+
from dataclasses import dataclass, field
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
# Logger for debug output
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class SequenceGap:
|
|
29
|
+
"""Sequence gap information.
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
Attributes:
|
|
34
|
+
position: Packet position where gap was detected.
|
|
35
|
+
expected: Expected sequence number.
|
|
36
|
+
got: Actual sequence number received.
|
|
37
|
+
gap_size: Size of the gap (number of missing packets).
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
position: int
|
|
41
|
+
expected: int
|
|
42
|
+
got: int
|
|
43
|
+
gap_size: int
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class SequenceValidation:
|
|
48
|
+
"""Sequence validation results.
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
Attributes:
|
|
53
|
+
total_packets: Total number of packets validated.
|
|
54
|
+
sequence_gaps: List of detected sequence gaps.
|
|
55
|
+
duplicates: Number of duplicate sequence numbers.
|
|
56
|
+
valid: Whether sequence validation passed overall.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
total_packets: int = 0
|
|
60
|
+
sequence_gaps: list[SequenceGap] = field(default_factory=list)
|
|
61
|
+
duplicates: int = 0
|
|
62
|
+
valid: bool = True
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def gap_count(self) -> int:
|
|
66
|
+
"""Number of sequence gaps detected.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
Number of gaps.
|
|
70
|
+
"""
|
|
71
|
+
return len(self.sequence_gaps)
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def total_missing_packets(self) -> int:
|
|
75
|
+
"""Total number of missing packets across all gaps.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Sum of all gap sizes.
|
|
79
|
+
"""
|
|
80
|
+
return sum(gap.gap_size for gap in self.sequence_gaps)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass
|
|
84
|
+
class ValidationResult:
|
|
85
|
+
"""Result of packet validation.
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
Attributes:
|
|
90
|
+
is_valid: Whether packet passed all validation checks.
|
|
91
|
+
sync_valid: Sync marker validation result.
|
|
92
|
+
sequence_valid: Sequence number validation result.
|
|
93
|
+
checksum_valid: Checksum validation result.
|
|
94
|
+
errors: List of validation error messages.
|
|
95
|
+
warnings: List of validation warnings.
|
|
96
|
+
packet_index: Index of validated packet.
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
is_valid: bool = True
|
|
100
|
+
sync_valid: bool = True
|
|
101
|
+
sequence_valid: bool = True
|
|
102
|
+
checksum_valid: bool = True
|
|
103
|
+
errors: list[str] = field(default_factory=list)
|
|
104
|
+
warnings: list[str] = field(default_factory=list)
|
|
105
|
+
packet_index: int = 0
|
|
106
|
+
|
|
107
|
+
def add_error(self, message: str) -> None:
|
|
108
|
+
"""Add validation error.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
message: Error message.
|
|
112
|
+
"""
|
|
113
|
+
self.errors.append(message)
|
|
114
|
+
self.is_valid = False
|
|
115
|
+
|
|
116
|
+
def add_warning(self, message: str) -> None:
|
|
117
|
+
"""Add validation warning.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
message: Warning message.
|
|
121
|
+
"""
|
|
122
|
+
self.warnings.append(message)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@dataclass
|
|
126
|
+
class ValidationStats:
|
|
127
|
+
"""Aggregate validation statistics.
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
Attributes:
|
|
132
|
+
total_packets: Total number of packets validated.
|
|
133
|
+
valid_packets: Number of packets that passed all checks.
|
|
134
|
+
sync_failures: Number of sync marker failures.
|
|
135
|
+
sequence_gaps: Number of sequence gaps detected.
|
|
136
|
+
sequence_duplicates: Number of duplicate sequences detected.
|
|
137
|
+
checksum_failures: Number of checksum failures.
|
|
138
|
+
error_types: Dictionary of error type counts.
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
total_packets: int = 0
|
|
142
|
+
valid_packets: int = 0
|
|
143
|
+
sync_failures: int = 0
|
|
144
|
+
sequence_gaps: int = 0
|
|
145
|
+
sequence_duplicates: int = 0
|
|
146
|
+
checksum_failures: int = 0
|
|
147
|
+
error_types: dict[str, int] = field(default_factory=dict)
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def pass_rate(self) -> float:
|
|
151
|
+
"""Calculate validation pass rate.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
Fraction of packets that passed validation (0.0 to 1.0).
|
|
155
|
+
"""
|
|
156
|
+
if self.total_packets == 0:
|
|
157
|
+
return 0.0
|
|
158
|
+
return self.valid_packets / self.total_packets
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def fail_rate(self) -> float:
|
|
162
|
+
"""Calculate validation fail rate.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
Fraction of packets that failed validation (0.0 to 1.0).
|
|
166
|
+
"""
|
|
167
|
+
return 1.0 - self.pass_rate
|
|
168
|
+
|
|
169
|
+
def add_error_type(self, error_type: str) -> None:
|
|
170
|
+
"""Increment error type counter.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
error_type: Type of error (e.g., "sync_mismatch", "checksum_fail").
|
|
174
|
+
"""
|
|
175
|
+
self.error_types[error_type] = self.error_types.get(error_type, 0) + 1
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class PacketValidator:
|
|
179
|
+
"""Validate packet integrity and structure.
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
Performs comprehensive validation including:
|
|
184
|
+
- Sync/magic byte verification
|
|
185
|
+
- Sequence number gap/duplicate detection
|
|
186
|
+
- Checksum verification (CRC-8/16/32, sum, XOR)
|
|
187
|
+
- Field value range validation
|
|
188
|
+
|
|
189
|
+
Attributes:
|
|
190
|
+
sync_marker: Expected sync marker value (optional).
|
|
191
|
+
sync_field: Name of sync field in packet header (optional).
|
|
192
|
+
sequence_field: Name of sequence field in packet header (optional).
|
|
193
|
+
checksum_type: Checksum algorithm ("crc8", "crc16", "crc32", "sum", "xor", optional).
|
|
194
|
+
checksum_field: Name of checksum field in packet header (optional).
|
|
195
|
+
strictness: Validation strictness level ("strict", "normal", "lenient").
|
|
196
|
+
stats: Validation statistics.
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
def __init__(
|
|
200
|
+
self,
|
|
201
|
+
*,
|
|
202
|
+
sync_marker: int | bytes | None = None,
|
|
203
|
+
sync_field: str = "sync_marker",
|
|
204
|
+
sequence_field: str = "sequence",
|
|
205
|
+
checksum_type: str | None = None,
|
|
206
|
+
checksum_field: str = "checksum",
|
|
207
|
+
strictness: str = "normal",
|
|
208
|
+
) -> None:
|
|
209
|
+
"""Initialize packet validator.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
sync_marker: Expected sync marker value (optional).
|
|
213
|
+
sync_field: Name of sync field in packet header (default: "sync_marker").
|
|
214
|
+
sequence_field: Name of sequence field in packet header (default: "sequence").
|
|
215
|
+
checksum_type: Checksum algorithm (optional).
|
|
216
|
+
checksum_field: Name of checksum field in packet header (default: "checksum").
|
|
217
|
+
strictness: Validation strictness ("strict", "normal", "lenient").
|
|
218
|
+
"""
|
|
219
|
+
self.sync_marker = sync_marker
|
|
220
|
+
self.sync_field = sync_field
|
|
221
|
+
self.sequence_field = sequence_field
|
|
222
|
+
self.checksum_type = checksum_type
|
|
223
|
+
self.checksum_field = checksum_field
|
|
224
|
+
self.strictness = strictness
|
|
225
|
+
|
|
226
|
+
self.stats = ValidationStats()
|
|
227
|
+
self._last_sequence: int | None = None
|
|
228
|
+
|
|
229
|
+
def validate_packet(
|
|
230
|
+
self, packet: dict[str, Any], packet_data: bytes | None = None
|
|
231
|
+
) -> ValidationResult:
|
|
232
|
+
"""Validate a single packet.
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
Args:
|
|
237
|
+
packet: Parsed packet dictionary.
|
|
238
|
+
packet_data: Raw packet bytes (required for checksum validation).
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
ValidationResult with validation outcome.
|
|
242
|
+
|
|
243
|
+
Example:
|
|
244
|
+
>>> validator = PacketValidator(sync_marker=0xFA)
|
|
245
|
+
>>> result = validator.validate_packet(packet)
|
|
246
|
+
>>> if not result.is_valid:
|
|
247
|
+
... print(f"Errors: {result.errors}")
|
|
248
|
+
"""
|
|
249
|
+
result = ValidationResult(packet_index=packet.get("index", 0))
|
|
250
|
+
|
|
251
|
+
header = packet.get("header", {})
|
|
252
|
+
|
|
253
|
+
# Validate sync marker
|
|
254
|
+
if self.sync_marker is not None:
|
|
255
|
+
result.sync_valid = self._validate_sync(header, result)
|
|
256
|
+
|
|
257
|
+
# Validate sequence number
|
|
258
|
+
if self.sequence_field in header:
|
|
259
|
+
result.sequence_valid = self._validate_sequence(header, result)
|
|
260
|
+
|
|
261
|
+
# Validate checksum
|
|
262
|
+
if self.checksum_type is not None and packet_data is not None:
|
|
263
|
+
result.checksum_valid = self._validate_checksum(header, packet_data, result)
|
|
264
|
+
|
|
265
|
+
# Update statistics
|
|
266
|
+
self.stats.total_packets += 1
|
|
267
|
+
if result.is_valid:
|
|
268
|
+
self.stats.valid_packets += 1
|
|
269
|
+
|
|
270
|
+
return result
|
|
271
|
+
|
|
272
|
+
def _validate_sync(self, header: dict[str, Any], result: ValidationResult) -> bool:
|
|
273
|
+
"""Validate sync marker.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
header: Packet header dictionary.
|
|
277
|
+
result: Validation result to update.
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
True if sync is valid.
|
|
281
|
+
"""
|
|
282
|
+
if self.sync_field not in header:
|
|
283
|
+
if self.strictness == "strict":
|
|
284
|
+
result.add_error(f"Missing sync field: {self.sync_field}")
|
|
285
|
+
self.stats.sync_failures += 1
|
|
286
|
+
self.stats.add_error_type("sync_missing")
|
|
287
|
+
return False
|
|
288
|
+
else:
|
|
289
|
+
result.add_warning(f"Missing sync field: {self.sync_field}")
|
|
290
|
+
return True
|
|
291
|
+
|
|
292
|
+
sync_value = header[self.sync_field]
|
|
293
|
+
|
|
294
|
+
if sync_value != self.sync_marker:
|
|
295
|
+
# Convert bytes to int if needed for formatting
|
|
296
|
+
if isinstance(sync_value, int):
|
|
297
|
+
sync_val_hex = sync_value
|
|
298
|
+
elif isinstance(sync_value, bytes):
|
|
299
|
+
sync_val_hex = int.from_bytes(sync_value, "big")
|
|
300
|
+
else:
|
|
301
|
+
sync_val_hex = int.from_bytes(bytes([sync_value]), "big")
|
|
302
|
+
|
|
303
|
+
# Convert sync_marker to int if needed for formatting
|
|
304
|
+
if isinstance(self.sync_marker, int):
|
|
305
|
+
expected_hex = self.sync_marker
|
|
306
|
+
elif isinstance(self.sync_marker, bytes):
|
|
307
|
+
expected_hex = int.from_bytes(self.sync_marker, "big")
|
|
308
|
+
else:
|
|
309
|
+
expected_hex = 0
|
|
310
|
+
|
|
311
|
+
msg = f"Sync marker mismatch: expected {expected_hex:#x}, got {sync_val_hex:#x}"
|
|
312
|
+
if self.strictness == "strict":
|
|
313
|
+
result.add_error(msg)
|
|
314
|
+
else:
|
|
315
|
+
result.add_warning(msg)
|
|
316
|
+
|
|
317
|
+
self.stats.sync_failures += 1
|
|
318
|
+
self.stats.add_error_type("sync_mismatch")
|
|
319
|
+
return False
|
|
320
|
+
|
|
321
|
+
return True
|
|
322
|
+
|
|
323
|
+
def _validate_sequence(self, header: dict[str, Any], result: ValidationResult) -> bool:
|
|
324
|
+
"""Validate sequence number.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
header: Packet header dictionary.
|
|
328
|
+
result: Validation result to update.
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
True if sequence is valid.
|
|
332
|
+
"""
|
|
333
|
+
sequence = header.get(self.sequence_field)
|
|
334
|
+
if sequence is None:
|
|
335
|
+
return True # No sequence to validate
|
|
336
|
+
|
|
337
|
+
if self._last_sequence is not None:
|
|
338
|
+
expected = (self._last_sequence + 1) & 0xFFFFFFFF # Handle rollover
|
|
339
|
+
|
|
340
|
+
if sequence == self._last_sequence:
|
|
341
|
+
# Duplicate sequence
|
|
342
|
+
msg = f"Duplicate sequence number: {sequence}"
|
|
343
|
+
result.add_warning(msg)
|
|
344
|
+
self.stats.sequence_duplicates += 1
|
|
345
|
+
self.stats.add_error_type("sequence_duplicate")
|
|
346
|
+
return False
|
|
347
|
+
|
|
348
|
+
elif sequence != expected:
|
|
349
|
+
# Sequence gap
|
|
350
|
+
gap = (sequence - expected) & 0xFFFFFFFF
|
|
351
|
+
msg = f"Sequence gap detected: expected {expected}, got {sequence} (gap: {gap})"
|
|
352
|
+
|
|
353
|
+
if self.strictness == "strict":
|
|
354
|
+
result.add_error(msg)
|
|
355
|
+
else:
|
|
356
|
+
result.add_warning(msg)
|
|
357
|
+
|
|
358
|
+
self.stats.sequence_gaps += 1
|
|
359
|
+
self.stats.add_error_type("sequence_gap")
|
|
360
|
+
|
|
361
|
+
if self.strictness == "strict":
|
|
362
|
+
self._last_sequence = sequence
|
|
363
|
+
return False
|
|
364
|
+
|
|
365
|
+
self._last_sequence = sequence
|
|
366
|
+
return True
|
|
367
|
+
|
|
368
|
+
def _validate_checksum(
|
|
369
|
+
self, header: dict[str, Any], packet_data: bytes, result: ValidationResult
|
|
370
|
+
) -> bool:
|
|
371
|
+
"""Validate packet checksum.
|
|
372
|
+
|
|
373
|
+
Args:
|
|
374
|
+
header: Packet header dictionary.
|
|
375
|
+
packet_data: Raw packet bytes.
|
|
376
|
+
result: Validation result to update.
|
|
377
|
+
|
|
378
|
+
Returns:
|
|
379
|
+
True if checksum is valid.
|
|
380
|
+
"""
|
|
381
|
+
if self.checksum_field not in header:
|
|
382
|
+
if self.strictness == "strict":
|
|
383
|
+
result.add_error(f"Missing checksum field: {self.checksum_field}")
|
|
384
|
+
self.stats.checksum_failures += 1
|
|
385
|
+
self.stats.add_error_type("checksum_missing")
|
|
386
|
+
return False
|
|
387
|
+
return True
|
|
388
|
+
|
|
389
|
+
expected_checksum = header[self.checksum_field]
|
|
390
|
+
computed_checksum = self._compute_checksum(packet_data)
|
|
391
|
+
|
|
392
|
+
if computed_checksum != expected_checksum:
|
|
393
|
+
msg = f"Checksum mismatch: expected {expected_checksum:#x}, got {computed_checksum:#x}"
|
|
394
|
+
|
|
395
|
+
if self.strictness == "strict":
|
|
396
|
+
result.add_error(msg)
|
|
397
|
+
else:
|
|
398
|
+
result.add_warning(msg)
|
|
399
|
+
|
|
400
|
+
self.stats.checksum_failures += 1
|
|
401
|
+
self.stats.add_error_type("checksum_fail")
|
|
402
|
+
return False
|
|
403
|
+
|
|
404
|
+
return True
|
|
405
|
+
|
|
406
|
+
def _compute_checksum(self, data: bytes) -> int:
|
|
407
|
+
"""Compute checksum using configured algorithm.
|
|
408
|
+
|
|
409
|
+
Args:
|
|
410
|
+
data: Data to checksum.
|
|
411
|
+
|
|
412
|
+
Returns:
|
|
413
|
+
Computed checksum value.
|
|
414
|
+
"""
|
|
415
|
+
if self.checksum_type == "crc8":
|
|
416
|
+
return self._crc8(data)
|
|
417
|
+
elif self.checksum_type == "crc16":
|
|
418
|
+
return self._crc16(data)
|
|
419
|
+
elif self.checksum_type == "crc32":
|
|
420
|
+
return self._crc32(data)
|
|
421
|
+
elif self.checksum_type == "sum":
|
|
422
|
+
return sum(data) & 0xFF
|
|
423
|
+
elif self.checksum_type == "xor":
|
|
424
|
+
result = 0
|
|
425
|
+
for byte in data:
|
|
426
|
+
result ^= byte
|
|
427
|
+
return result
|
|
428
|
+
else:
|
|
429
|
+
logger.warning("Unknown checksum type: %s", self.checksum_type)
|
|
430
|
+
return 0
|
|
431
|
+
|
|
432
|
+
@staticmethod
|
|
433
|
+
def _crc8(data: bytes, poly: int = 0x07) -> int:
|
|
434
|
+
"""Compute CRC-8 checksum.
|
|
435
|
+
|
|
436
|
+
Args:
|
|
437
|
+
data: Data to checksum.
|
|
438
|
+
poly: CRC polynomial (default: 0x07).
|
|
439
|
+
|
|
440
|
+
Returns:
|
|
441
|
+
CRC-8 value.
|
|
442
|
+
"""
|
|
443
|
+
crc = 0
|
|
444
|
+
for byte in data:
|
|
445
|
+
crc ^= byte
|
|
446
|
+
for _ in range(8):
|
|
447
|
+
if crc & 0x80:
|
|
448
|
+
crc = (crc << 1) ^ poly
|
|
449
|
+
else:
|
|
450
|
+
crc <<= 1
|
|
451
|
+
crc &= 0xFF
|
|
452
|
+
return crc
|
|
453
|
+
|
|
454
|
+
@staticmethod
|
|
455
|
+
def _crc16(data: bytes, poly: int = 0x1021) -> int:
|
|
456
|
+
"""Compute CRC-16 checksum.
|
|
457
|
+
|
|
458
|
+
Args:
|
|
459
|
+
data: Data to checksum.
|
|
460
|
+
poly: CRC polynomial (default: 0x1021 for CRC-16-CCITT).
|
|
461
|
+
|
|
462
|
+
Returns:
|
|
463
|
+
CRC-16 value.
|
|
464
|
+
"""
|
|
465
|
+
crc = 0xFFFF
|
|
466
|
+
for byte in data:
|
|
467
|
+
crc ^= byte << 8
|
|
468
|
+
for _ in range(8):
|
|
469
|
+
if crc & 0x8000:
|
|
470
|
+
crc = (crc << 1) ^ poly
|
|
471
|
+
else:
|
|
472
|
+
crc <<= 1
|
|
473
|
+
crc &= 0xFFFF
|
|
474
|
+
return crc
|
|
475
|
+
|
|
476
|
+
@staticmethod
|
|
477
|
+
def _crc32(data: bytes, poly: int = 0xEDB88320) -> int:
|
|
478
|
+
"""Compute CRC-32 checksum.
|
|
479
|
+
|
|
480
|
+
Args:
|
|
481
|
+
data: Data to checksum.
|
|
482
|
+
poly: CRC polynomial (default: 0xEDB88320 for CRC-32).
|
|
483
|
+
|
|
484
|
+
Returns:
|
|
485
|
+
CRC-32 value.
|
|
486
|
+
"""
|
|
487
|
+
crc = 0xFFFFFFFF
|
|
488
|
+
for byte in data:
|
|
489
|
+
crc ^= byte
|
|
490
|
+
for _ in range(8):
|
|
491
|
+
if crc & 1:
|
|
492
|
+
crc = (crc >> 1) ^ poly
|
|
493
|
+
else:
|
|
494
|
+
crc >>= 1
|
|
495
|
+
return crc ^ 0xFFFFFFFF
|
|
496
|
+
|
|
497
|
+
def get_statistics(self) -> ValidationStats:
|
|
498
|
+
"""Get aggregate validation statistics.
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
Returns:
|
|
503
|
+
ValidationStats with cumulative validation results.
|
|
504
|
+
|
|
505
|
+
Example:
|
|
506
|
+
>>> validator = PacketValidator()
|
|
507
|
+
>>> # ... validate packets ...
|
|
508
|
+
>>> stats = validator.get_statistics()
|
|
509
|
+
>>> print(f"Pass rate: {stats.pass_rate:.1%}")
|
|
510
|
+
>>> print(f"Sync failures: {stats.sync_failures}")
|
|
511
|
+
"""
|
|
512
|
+
return self.stats
|
|
513
|
+
|
|
514
|
+
def validate_sequence(self, packets: list[dict[str, Any]]) -> SequenceValidation:
|
|
515
|
+
"""Validate sequence numbers across multiple packets.
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
Args:
|
|
520
|
+
packets: List of parsed packets with headers.
|
|
521
|
+
|
|
522
|
+
Returns:
|
|
523
|
+
SequenceValidation with gap and duplicate detection results.
|
|
524
|
+
|
|
525
|
+
Example:
|
|
526
|
+
>>> validator = PacketValidator(sequence_field="sequence")
|
|
527
|
+
>>> seq_validation = validator.validate_sequence(packets)
|
|
528
|
+
>>> if seq_validation.gap_count > 0:
|
|
529
|
+
... print(f"Found {seq_validation.gap_count} sequence gaps")
|
|
530
|
+
"""
|
|
531
|
+
result = SequenceValidation(total_packets=len(packets))
|
|
532
|
+
|
|
533
|
+
if not packets:
|
|
534
|
+
return result
|
|
535
|
+
|
|
536
|
+
last_seq: int | None = None
|
|
537
|
+
|
|
538
|
+
for i, packet in enumerate(packets):
|
|
539
|
+
header = packet.get("header", {})
|
|
540
|
+
seq = header.get(self.sequence_field)
|
|
541
|
+
|
|
542
|
+
if seq is None:
|
|
543
|
+
continue
|
|
544
|
+
|
|
545
|
+
if last_seq is not None:
|
|
546
|
+
expected = (last_seq + 1) & 0xFFFFFFFF
|
|
547
|
+
|
|
548
|
+
if seq == last_seq:
|
|
549
|
+
# Duplicate
|
|
550
|
+
result.duplicates += 1
|
|
551
|
+
result.valid = False
|
|
552
|
+
|
|
553
|
+
elif seq != expected:
|
|
554
|
+
# Gap detected
|
|
555
|
+
gap_size = (seq - expected) & 0xFFFFFFFF
|
|
556
|
+
gap = SequenceGap(
|
|
557
|
+
position=i,
|
|
558
|
+
expected=expected,
|
|
559
|
+
got=seq,
|
|
560
|
+
gap_size=gap_size,
|
|
561
|
+
)
|
|
562
|
+
result.sequence_gaps.append(gap)
|
|
563
|
+
result.valid = False
|
|
564
|
+
|
|
565
|
+
last_seq = seq
|
|
566
|
+
|
|
567
|
+
return result
|
|
568
|
+
|
|
569
|
+
def reset_statistics(self) -> None:
|
|
570
|
+
"""Reset validation statistics.
|
|
571
|
+
|
|
572
|
+
Useful for validating multiple files or resetting state.
|
|
573
|
+
"""
|
|
574
|
+
self.stats = ValidationStats()
|
|
575
|
+
self._last_sequence = None
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
__all__ = [
|
|
579
|
+
"PacketValidator",
|
|
580
|
+
"SequenceGap",
|
|
581
|
+
"SequenceValidation",
|
|
582
|
+
"ValidationResult",
|
|
583
|
+
"ValidationStats",
|
|
584
|
+
]
|