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,913 @@
|
|
|
1
|
+
"""Plugin template generation for creating new Oscura plugins.
|
|
2
|
+
|
|
3
|
+
This module provides tools for generating plugin skeletons with all necessary
|
|
4
|
+
boilerplate code, tests, and documentation.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import textwrap
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from typing import TYPE_CHECKING, Literal
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
# Plugin type definitions
|
|
17
|
+
PluginType = Literal["analyzer", "loader", "exporter", "decoder"]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class PluginTemplate:
|
|
22
|
+
"""Configuration for plugin template generation.
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
name: Plugin name (e.g., 'my_custom_decoder').
|
|
26
|
+
plugin_type: Type of plugin ('analyzer', 'loader', 'exporter', 'decoder').
|
|
27
|
+
output_dir: Directory where plugin will be generated.
|
|
28
|
+
author: Plugin author name.
|
|
29
|
+
description: Brief description of plugin functionality.
|
|
30
|
+
version: Initial plugin version (default: '0.1.0').
|
|
31
|
+
|
|
32
|
+
Example:
|
|
33
|
+
>>> template = PluginTemplate(
|
|
34
|
+
... name='flexray_decoder',
|
|
35
|
+
... plugin_type='decoder',
|
|
36
|
+
... output_dir=Path('plugins/flexray'),
|
|
37
|
+
... author='John Doe',
|
|
38
|
+
... description='FlexRay protocol decoder'
|
|
39
|
+
... )
|
|
40
|
+
|
|
41
|
+
References:
|
|
42
|
+
PLUG-008: Plugin Template Generator
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
name: str
|
|
46
|
+
plugin_type: PluginType
|
|
47
|
+
output_dir: Path
|
|
48
|
+
author: str = "Plugin Author"
|
|
49
|
+
description: str = "Custom Oscura plugin"
|
|
50
|
+
version: str = "0.1.0"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def generate_plugin_template(
|
|
54
|
+
name: str,
|
|
55
|
+
plugin_type: PluginType,
|
|
56
|
+
output_dir: Path,
|
|
57
|
+
*,
|
|
58
|
+
author: str = "Plugin Author",
|
|
59
|
+
description: str | None = None,
|
|
60
|
+
version: str = "0.1.0",
|
|
61
|
+
) -> Path:
|
|
62
|
+
"""Generate a plugin skeleton with all necessary boilerplate.
|
|
63
|
+
|
|
64
|
+
Creates a complete plugin package structure including:
|
|
65
|
+
- __init__.py with plugin metadata
|
|
66
|
+
- Main module with stub implementation
|
|
67
|
+
- tests/ directory with test stubs
|
|
68
|
+
- README.md with usage instructions
|
|
69
|
+
- pyproject.toml for packaging
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
name: Plugin name (will be converted to snake_case).
|
|
73
|
+
plugin_type: Type of plugin to generate.
|
|
74
|
+
output_dir: Directory where plugin will be created.
|
|
75
|
+
author: Plugin author name.
|
|
76
|
+
description: Plugin description (auto-generated if None).
|
|
77
|
+
version: Initial plugin version.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Path to the generated plugin directory.
|
|
81
|
+
|
|
82
|
+
Raises:
|
|
83
|
+
ValueError: If plugin_type is invalid.
|
|
84
|
+
|
|
85
|
+
Example:
|
|
86
|
+
>>> from pathlib import Path
|
|
87
|
+
>>> plugin_dir = generate_plugin_template(
|
|
88
|
+
... name='flexray_decoder',
|
|
89
|
+
... plugin_type='decoder',
|
|
90
|
+
... output_dir=Path('plugins/flexray'),
|
|
91
|
+
... author='John Doe',
|
|
92
|
+
... description='FlexRay protocol decoder'
|
|
93
|
+
... )
|
|
94
|
+
>>> print(f"Plugin generated at {plugin_dir}")
|
|
95
|
+
|
|
96
|
+
Plugin Structure:
|
|
97
|
+
```
|
|
98
|
+
plugins/flexray/
|
|
99
|
+
├── __init__.py # Plugin metadata and entry point
|
|
100
|
+
├── flexray_decoder.py # Main implementation
|
|
101
|
+
├── tests/
|
|
102
|
+
│ ├── __init__.py
|
|
103
|
+
│ └── test_flexray_decoder.py
|
|
104
|
+
├── README.md # Usage documentation
|
|
105
|
+
└── pyproject.toml # Packaging configuration
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
References:
|
|
109
|
+
PLUG-008: Plugin Template Generator
|
|
110
|
+
"""
|
|
111
|
+
# Validate plugin type
|
|
112
|
+
valid_types: set[PluginType] = {"analyzer", "loader", "exporter", "decoder"}
|
|
113
|
+
if plugin_type not in valid_types:
|
|
114
|
+
raise ValueError(
|
|
115
|
+
f"Invalid plugin_type '{plugin_type}'. Must be one of: {', '.join(valid_types)}"
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# Generate default description if not provided
|
|
119
|
+
if description is None:
|
|
120
|
+
description = f"Custom {plugin_type} plugin for Oscura"
|
|
121
|
+
|
|
122
|
+
# Create template configuration
|
|
123
|
+
template = PluginTemplate(
|
|
124
|
+
name=name,
|
|
125
|
+
plugin_type=plugin_type,
|
|
126
|
+
output_dir=output_dir,
|
|
127
|
+
author=author,
|
|
128
|
+
description=description,
|
|
129
|
+
version=version,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Generate plugin directory structure
|
|
133
|
+
_generate_plugin_structure(template)
|
|
134
|
+
|
|
135
|
+
return output_dir
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _generate_plugin_structure(template: PluginTemplate) -> None:
|
|
139
|
+
"""Generate complete plugin directory structure.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
template: Plugin template configuration.
|
|
143
|
+
|
|
144
|
+
Raises:
|
|
145
|
+
FileExistsError: If plugin directory already exists.
|
|
146
|
+
"""
|
|
147
|
+
output_dir = template.output_dir
|
|
148
|
+
|
|
149
|
+
# Check if directory exists
|
|
150
|
+
if output_dir.exists():
|
|
151
|
+
raise FileExistsError(
|
|
152
|
+
f"Plugin directory already exists: {output_dir}\n"
|
|
153
|
+
f"Remove it or choose a different output_dir."
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# Create directory structure
|
|
157
|
+
output_dir.mkdir(parents=True, exist_ok=False)
|
|
158
|
+
tests_dir = output_dir / "tests"
|
|
159
|
+
tests_dir.mkdir(exist_ok=False)
|
|
160
|
+
|
|
161
|
+
# Generate files
|
|
162
|
+
_write_init_py(template)
|
|
163
|
+
_write_main_module(template)
|
|
164
|
+
_write_test_init(template)
|
|
165
|
+
_write_test_module(template)
|
|
166
|
+
_write_readme(template)
|
|
167
|
+
_write_pyproject_toml(template)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _write_init_py(template: PluginTemplate) -> None:
|
|
171
|
+
"""Write plugin __init__.py with metadata.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
template: Plugin template configuration.
|
|
175
|
+
"""
|
|
176
|
+
content = textwrap.dedent(f'''\
|
|
177
|
+
"""{template.description}
|
|
178
|
+
|
|
179
|
+
This plugin integrates with Oscura via entry points.
|
|
180
|
+
|
|
181
|
+
Plugin Metadata:
|
|
182
|
+
Name: {template.name}
|
|
183
|
+
Type: {template.plugin_type}
|
|
184
|
+
Version: {template.version}
|
|
185
|
+
Author: {template.author}
|
|
186
|
+
|
|
187
|
+
Installation:
|
|
188
|
+
pip install -e .
|
|
189
|
+
|
|
190
|
+
Usage:
|
|
191
|
+
import oscura as osc
|
|
192
|
+
# Plugin auto-discovered via entry points
|
|
193
|
+
# See README.md for usage examples
|
|
194
|
+
|
|
195
|
+
References:
|
|
196
|
+
PLUG-008: Plugin Template Generator
|
|
197
|
+
"""
|
|
198
|
+
from .{template.name} import {_get_class_name(template)}
|
|
199
|
+
|
|
200
|
+
__version__ = "{template.version}"
|
|
201
|
+
|
|
202
|
+
__all__ = [
|
|
203
|
+
"{_get_class_name(template)}",
|
|
204
|
+
]
|
|
205
|
+
''')
|
|
206
|
+
|
|
207
|
+
(template.output_dir / "__init__.py").write_text(content)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _write_main_module(template: PluginTemplate) -> None:
|
|
211
|
+
"""Write main plugin module with stub implementation.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
template: Plugin template configuration.
|
|
215
|
+
"""
|
|
216
|
+
class_name = _get_class_name(template)
|
|
217
|
+
|
|
218
|
+
if template.plugin_type == "decoder":
|
|
219
|
+
content = _generate_decoder_stub(template, class_name)
|
|
220
|
+
elif template.plugin_type == "analyzer":
|
|
221
|
+
content = _generate_analyzer_stub(template, class_name)
|
|
222
|
+
elif template.plugin_type == "loader":
|
|
223
|
+
content = _generate_loader_stub(template, class_name)
|
|
224
|
+
elif template.plugin_type == "exporter":
|
|
225
|
+
content = _generate_exporter_stub(template, class_name)
|
|
226
|
+
else:
|
|
227
|
+
# Fallback generic stub
|
|
228
|
+
content = _generate_generic_stub(template, class_name) # type: ignore[unreachable]
|
|
229
|
+
|
|
230
|
+
(template.output_dir / f"{template.name}.py").write_text(content)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _write_test_init(template: PluginTemplate) -> None:
|
|
234
|
+
"""Write tests/__init__.py.
|
|
235
|
+
|
|
236
|
+
Args:
|
|
237
|
+
template: Plugin template configuration.
|
|
238
|
+
"""
|
|
239
|
+
content = '"""Test suite for plugin."""\n'
|
|
240
|
+
(template.output_dir / "tests" / "__init__.py").write_text(content)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _write_test_module(template: PluginTemplate) -> None:
|
|
244
|
+
"""Write test module with example tests.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
template: Plugin template configuration.
|
|
248
|
+
"""
|
|
249
|
+
class_name = _get_class_name(template)
|
|
250
|
+
|
|
251
|
+
content = textwrap.dedent(f'''\
|
|
252
|
+
"""Tests for {template.name} plugin.
|
|
253
|
+
|
|
254
|
+
This module contains unit tests for the plugin implementation.
|
|
255
|
+
"""
|
|
256
|
+
import numpy as np
|
|
257
|
+
import pytest
|
|
258
|
+
|
|
259
|
+
from {template.name} import {class_name}
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def test_{template.name}_initialization():
|
|
263
|
+
"""Test plugin can be instantiated."""
|
|
264
|
+
plugin = {class_name}()
|
|
265
|
+
assert plugin is not None
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def test_{template.name}_basic_functionality():
|
|
269
|
+
"""Test basic plugin functionality."""
|
|
270
|
+
plugin = {class_name}()
|
|
271
|
+
|
|
272
|
+
# USER: Implement test for your plugin's main functionality
|
|
273
|
+
# Example:
|
|
274
|
+
# result = plugin.process(test_data)
|
|
275
|
+
# assert result is not None
|
|
276
|
+
|
|
277
|
+
# Placeholder assertion - replace with actual tests
|
|
278
|
+
assert True
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def test_{template.name}_error_handling():
|
|
282
|
+
"""Test plugin handles errors gracefully."""
|
|
283
|
+
plugin = {class_name}()
|
|
284
|
+
|
|
285
|
+
# USER: Implement error condition tests
|
|
286
|
+
# Example:
|
|
287
|
+
# with pytest.raises(ValueError):
|
|
288
|
+
# plugin.process(invalid_data)
|
|
289
|
+
|
|
290
|
+
# Placeholder assertion - replace with actual tests
|
|
291
|
+
assert True
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
@pytest.mark.parametrize("param", [1, 2, 3])
|
|
295
|
+
def test_{template.name}_parametrized(param):
|
|
296
|
+
"""Example parametrized test."""
|
|
297
|
+
plugin = {class_name}()
|
|
298
|
+
|
|
299
|
+
# USER: Implement parametrized test logic
|
|
300
|
+
assert param > 0
|
|
301
|
+
''')
|
|
302
|
+
|
|
303
|
+
(template.output_dir / "tests" / f"test_{template.name}.py").write_text(content)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def _write_readme(template: PluginTemplate) -> None:
|
|
307
|
+
"""Write README.md with usage instructions.
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
template: Plugin template configuration.
|
|
311
|
+
"""
|
|
312
|
+
class_name = _get_class_name(template)
|
|
313
|
+
entry_point_group = _get_entry_point_group(template.plugin_type)
|
|
314
|
+
|
|
315
|
+
content = textwrap.dedent(f"""\
|
|
316
|
+
# {class_name}
|
|
317
|
+
|
|
318
|
+
{template.description}
|
|
319
|
+
|
|
320
|
+
## Installation
|
|
321
|
+
|
|
322
|
+
Install in development mode:
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
cd {template.output_dir.name}
|
|
326
|
+
pip install -e .
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## Usage
|
|
330
|
+
|
|
331
|
+
The plugin integrates automatically with Oscura via entry points:
|
|
332
|
+
|
|
333
|
+
```python
|
|
334
|
+
import oscura as osc
|
|
335
|
+
|
|
336
|
+
# Plugin is automatically discovered
|
|
337
|
+
# USER: Add usage examples specific to your plugin
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Direct Usage
|
|
341
|
+
|
|
342
|
+
```python
|
|
343
|
+
from {template.name} import {class_name}
|
|
344
|
+
|
|
345
|
+
# Create instance
|
|
346
|
+
plugin = {class_name}()
|
|
347
|
+
|
|
348
|
+
# USER: Add direct usage examples
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## CLI Integration
|
|
352
|
+
|
|
353
|
+
After installation, the plugin is available in Oscura CLI:
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
# List installed plugins
|
|
357
|
+
oscura plugin list
|
|
358
|
+
|
|
359
|
+
# Show plugin info
|
|
360
|
+
oscura plugin info {template.name}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
## Development
|
|
364
|
+
|
|
365
|
+
### Running Tests
|
|
366
|
+
|
|
367
|
+
```bash
|
|
368
|
+
pytest tests/
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Code Quality
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
# Linting
|
|
375
|
+
ruff check {template.name}.py
|
|
376
|
+
|
|
377
|
+
# Formatting
|
|
378
|
+
ruff format {template.name}.py
|
|
379
|
+
|
|
380
|
+
# Type checking
|
|
381
|
+
mypy {template.name}.py
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
## Plugin Type: {template.plugin_type}
|
|
385
|
+
|
|
386
|
+
This is a **{template.plugin_type}** plugin for Oscura.
|
|
387
|
+
|
|
388
|
+
### Entry Point
|
|
389
|
+
|
|
390
|
+
Registered in `{entry_point_group}` entry point group.
|
|
391
|
+
|
|
392
|
+
## Requirements
|
|
393
|
+
|
|
394
|
+
- Oscura >= 0.1.0
|
|
395
|
+
- Python >= 3.12
|
|
396
|
+
|
|
397
|
+
## License
|
|
398
|
+
|
|
399
|
+
MIT
|
|
400
|
+
|
|
401
|
+
## Author
|
|
402
|
+
|
|
403
|
+
{template.author}
|
|
404
|
+
|
|
405
|
+
## Version
|
|
406
|
+
|
|
407
|
+
{template.version}
|
|
408
|
+
""")
|
|
409
|
+
|
|
410
|
+
(template.output_dir / "README.md").write_text(content)
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def _write_pyproject_toml(template: PluginTemplate) -> None:
|
|
414
|
+
"""Write pyproject.toml for plugin packaging.
|
|
415
|
+
|
|
416
|
+
Args:
|
|
417
|
+
template: Plugin template configuration.
|
|
418
|
+
"""
|
|
419
|
+
entry_point_group = _get_entry_point_group(template.plugin_type)
|
|
420
|
+
class_name = _get_class_name(template)
|
|
421
|
+
|
|
422
|
+
content = textwrap.dedent(f'''\
|
|
423
|
+
[project]
|
|
424
|
+
name = "{template.name}"
|
|
425
|
+
version = "{template.version}"
|
|
426
|
+
description = "{template.description}"
|
|
427
|
+
readme = "README.md"
|
|
428
|
+
license = {{ text = "MIT" }}
|
|
429
|
+
requires-python = ">=3.12"
|
|
430
|
+
authors = [
|
|
431
|
+
{{ name = "{template.author}" }}
|
|
432
|
+
]
|
|
433
|
+
keywords = ["oscura", "plugin", "{template.plugin_type}"]
|
|
434
|
+
classifiers = [
|
|
435
|
+
"Development Status :: 3 - Alpha",
|
|
436
|
+
"Intended Audience :: Developers",
|
|
437
|
+
"License :: OSI Approved :: MIT License",
|
|
438
|
+
"Programming Language :: Python :: 3",
|
|
439
|
+
"Programming Language :: Python :: 3.12",
|
|
440
|
+
"Programming Language :: Python :: 3.13",
|
|
441
|
+
]
|
|
442
|
+
|
|
443
|
+
dependencies = [
|
|
444
|
+
"oscura>=0.1.0",
|
|
445
|
+
"numpy>=1.26.0",
|
|
446
|
+
]
|
|
447
|
+
|
|
448
|
+
[project.optional-dependencies]
|
|
449
|
+
dev = [
|
|
450
|
+
"pytest>=8.3.0",
|
|
451
|
+
"pytest-cov>=6.0.0",
|
|
452
|
+
"ruff>=0.8.0",
|
|
453
|
+
"mypy>=1.13.0",
|
|
454
|
+
]
|
|
455
|
+
|
|
456
|
+
# Oscura plugin entry point
|
|
457
|
+
[project.entry-points."{entry_point_group}"]
|
|
458
|
+
{template.name} = "{template.name}:{class_name}"
|
|
459
|
+
|
|
460
|
+
[build-system]
|
|
461
|
+
requires = ["hatchling"]
|
|
462
|
+
build-backend = "hatchling.build"
|
|
463
|
+
|
|
464
|
+
[tool.pytest.ini_options]
|
|
465
|
+
testpaths = ["tests"]
|
|
466
|
+
python_files = ["test_*.py"]
|
|
467
|
+
python_classes = ["Test*"]
|
|
468
|
+
python_functions = ["test_*"]
|
|
469
|
+
|
|
470
|
+
[tool.ruff]
|
|
471
|
+
line-length = 88
|
|
472
|
+
target-version = "py312"
|
|
473
|
+
|
|
474
|
+
[tool.ruff.lint]
|
|
475
|
+
select = ["E", "F", "W", "I", "N", "UP", "YTT", "B", "A", "C4", "T10", "RUF"]
|
|
476
|
+
|
|
477
|
+
[tool.mypy]
|
|
478
|
+
python_version = "3.12"
|
|
479
|
+
warn_return_any = true
|
|
480
|
+
warn_unused_configs = true
|
|
481
|
+
disallow_untyped_defs = true
|
|
482
|
+
''')
|
|
483
|
+
|
|
484
|
+
(template.output_dir / "pyproject.toml").write_text(content)
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def _get_class_name(template: PluginTemplate) -> str:
|
|
488
|
+
"""Generate class name from plugin name.
|
|
489
|
+
|
|
490
|
+
Args:
|
|
491
|
+
template: Plugin template configuration.
|
|
492
|
+
|
|
493
|
+
Returns:
|
|
494
|
+
PascalCase class name.
|
|
495
|
+
|
|
496
|
+
Example:
|
|
497
|
+
>>> template = PluginTemplate('my_decoder', 'decoder', Path('.'))
|
|
498
|
+
>>> _get_class_name(template)
|
|
499
|
+
'MyDecoder'
|
|
500
|
+
"""
|
|
501
|
+
# Convert snake_case to PascalCase
|
|
502
|
+
parts = template.name.split("_")
|
|
503
|
+
return "".join(word.capitalize() for word in parts)
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
def _get_entry_point_group(plugin_type: PluginType) -> str:
|
|
507
|
+
"""Get entry point group for plugin type.
|
|
508
|
+
|
|
509
|
+
Args:
|
|
510
|
+
plugin_type: Type of plugin.
|
|
511
|
+
|
|
512
|
+
Returns:
|
|
513
|
+
Entry point group name.
|
|
514
|
+
"""
|
|
515
|
+
return f"oscura.{plugin_type}s"
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
def _generate_decoder_stub(template: PluginTemplate, class_name: str) -> str:
|
|
519
|
+
"""Generate decoder plugin stub.
|
|
520
|
+
|
|
521
|
+
Args:
|
|
522
|
+
template: Plugin template configuration.
|
|
523
|
+
class_name: Class name for the decoder.
|
|
524
|
+
|
|
525
|
+
Returns:
|
|
526
|
+
Python source code for decoder stub.
|
|
527
|
+
"""
|
|
528
|
+
return textwrap.dedent(f'''\
|
|
529
|
+
"""{template.description}
|
|
530
|
+
|
|
531
|
+
This decoder implements protocol decoding for Oscura.
|
|
532
|
+
|
|
533
|
+
References:
|
|
534
|
+
PLUG-008: Plugin Template Generator
|
|
535
|
+
"""
|
|
536
|
+
from __future__ import annotations
|
|
537
|
+
|
|
538
|
+
import numpy as np
|
|
539
|
+
from numpy.typing import NDArray
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
class {class_name}:
|
|
543
|
+
"""Protocol decoder implementation.
|
|
544
|
+
|
|
545
|
+
Attributes:
|
|
546
|
+
sample_rate: Sample rate of input signal in Hz.
|
|
547
|
+
|
|
548
|
+
Example:
|
|
549
|
+
>>> decoder = {class_name}(sample_rate=1_000_000)
|
|
550
|
+
>>> frames = decoder.decode(digital_signal)
|
|
551
|
+
|
|
552
|
+
References:
|
|
553
|
+
PLUG-008: Plugin Template Generator
|
|
554
|
+
"""
|
|
555
|
+
def __init__(self, *, sample_rate: float = 1_000_000.0) -> None:
|
|
556
|
+
"""Initialize decoder.
|
|
557
|
+
|
|
558
|
+
Args:
|
|
559
|
+
sample_rate: Sample rate in Hz.
|
|
560
|
+
"""
|
|
561
|
+
self.sample_rate = sample_rate
|
|
562
|
+
|
|
563
|
+
def decode(
|
|
564
|
+
self,
|
|
565
|
+
signal: NDArray[np.uint8],
|
|
566
|
+
) -> list[dict[str, object]]:
|
|
567
|
+
"""Decode protocol frames from digital signal.
|
|
568
|
+
|
|
569
|
+
Args:
|
|
570
|
+
signal: Digital signal (0/1 values).
|
|
571
|
+
|
|
572
|
+
Returns:
|
|
573
|
+
List of decoded frames, each a dictionary with frame data.
|
|
574
|
+
|
|
575
|
+
Raises:
|
|
576
|
+
ValueError: If signal is empty or invalid.
|
|
577
|
+
|
|
578
|
+
Example:
|
|
579
|
+
>>> signal = np.array([0, 1, 1, 0, 1], dtype=np.uint8)
|
|
580
|
+
>>> frames = decoder.decode(signal)
|
|
581
|
+
"""
|
|
582
|
+
if len(signal) == 0:
|
|
583
|
+
raise ValueError("Signal cannot be empty")
|
|
584
|
+
|
|
585
|
+
# USER: Implement protocol decoding logic here
|
|
586
|
+
# This stub returns an empty list - replace with actual decoding
|
|
587
|
+
frames: list[dict[str, object]] = []
|
|
588
|
+
|
|
589
|
+
return frames
|
|
590
|
+
|
|
591
|
+
def configure(self, **params: object) -> None:
|
|
592
|
+
"""Configure decoder parameters.
|
|
593
|
+
|
|
594
|
+
Args:
|
|
595
|
+
**params: Decoder-specific parameters.
|
|
596
|
+
|
|
597
|
+
Example:
|
|
598
|
+
>>> decoder.configure(baudrate=115200, parity='none')
|
|
599
|
+
"""
|
|
600
|
+
# USER: Implement configuration logic here
|
|
601
|
+
# Store parameters as instance attributes
|
|
602
|
+
for key, value in params.items():
|
|
603
|
+
setattr(self, key, value)
|
|
604
|
+
''')
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
def _generate_analyzer_stub(template: PluginTemplate, class_name: str) -> str:
|
|
608
|
+
"""Generate analyzer plugin stub.
|
|
609
|
+
|
|
610
|
+
Args:
|
|
611
|
+
template: Plugin template configuration.
|
|
612
|
+
class_name: Class name for the analyzer.
|
|
613
|
+
|
|
614
|
+
Returns:
|
|
615
|
+
Python source code for analyzer stub.
|
|
616
|
+
"""
|
|
617
|
+
return textwrap.dedent(f'''\
|
|
618
|
+
"""{template.description}
|
|
619
|
+
|
|
620
|
+
This analyzer implements custom signal analysis for Oscura.
|
|
621
|
+
|
|
622
|
+
References:
|
|
623
|
+
PLUG-008: Plugin Template Generator
|
|
624
|
+
"""
|
|
625
|
+
from __future__ import annotations
|
|
626
|
+
|
|
627
|
+
import numpy as np
|
|
628
|
+
from numpy.typing import NDArray
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
class {class_name}:
|
|
632
|
+
"""Signal analyzer implementation.
|
|
633
|
+
|
|
634
|
+
Example:
|
|
635
|
+
>>> analyzer = {class_name}()
|
|
636
|
+
>>> result = analyzer.analyze(signal)
|
|
637
|
+
|
|
638
|
+
References:
|
|
639
|
+
PLUG-008: Plugin Template Generator
|
|
640
|
+
"""
|
|
641
|
+
def __init__(self) -> None:
|
|
642
|
+
"""Initialize analyzer."""
|
|
643
|
+
pass
|
|
644
|
+
|
|
645
|
+
def analyze(
|
|
646
|
+
self,
|
|
647
|
+
signal: NDArray[np.float64],
|
|
648
|
+
*,
|
|
649
|
+
sample_rate: float = 1_000_000.0,
|
|
650
|
+
) -> dict[str, object]:
|
|
651
|
+
"""Analyze signal and extract features.
|
|
652
|
+
|
|
653
|
+
Args:
|
|
654
|
+
signal: Input signal array.
|
|
655
|
+
sample_rate: Sample rate in Hz.
|
|
656
|
+
|
|
657
|
+
Returns:
|
|
658
|
+
Dictionary containing analysis results.
|
|
659
|
+
|
|
660
|
+
Raises:
|
|
661
|
+
ValueError: If signal is empty or invalid.
|
|
662
|
+
|
|
663
|
+
Example:
|
|
664
|
+
>>> signal = np.sin(2 * np.pi * 1000 * np.linspace(0, 1, 1000))
|
|
665
|
+
>>> result = analyzer.analyze(signal, sample_rate=1000)
|
|
666
|
+
"""
|
|
667
|
+
if len(signal) == 0:
|
|
668
|
+
raise ValueError("Signal cannot be empty")
|
|
669
|
+
|
|
670
|
+
# USER: Implement analysis logic here
|
|
671
|
+
# This stub returns placeholder results - replace with actual analysis
|
|
672
|
+
result: dict[str, object] = {{
|
|
673
|
+
"status": "not_implemented",
|
|
674
|
+
"sample_count": len(signal),
|
|
675
|
+
"sample_rate": sample_rate,
|
|
676
|
+
}}
|
|
677
|
+
|
|
678
|
+
return result
|
|
679
|
+
''')
|
|
680
|
+
|
|
681
|
+
|
|
682
|
+
def _generate_loader_stub(template: PluginTemplate, class_name: str) -> str:
|
|
683
|
+
"""Generate loader plugin stub.
|
|
684
|
+
|
|
685
|
+
Args:
|
|
686
|
+
template: Plugin template configuration.
|
|
687
|
+
class_name: Class name for the loader.
|
|
688
|
+
|
|
689
|
+
Returns:
|
|
690
|
+
Python source code for loader stub.
|
|
691
|
+
"""
|
|
692
|
+
return textwrap.dedent(f'''\
|
|
693
|
+
"""{template.description}
|
|
694
|
+
|
|
695
|
+
This loader implements file format loading for Oscura.
|
|
696
|
+
|
|
697
|
+
References:
|
|
698
|
+
PLUG-008: Plugin Template Generator
|
|
699
|
+
"""
|
|
700
|
+
from __future__ import annotations
|
|
701
|
+
|
|
702
|
+
import numpy as np
|
|
703
|
+
from numpy.typing import NDArray
|
|
704
|
+
from pathlib import Path
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
class {class_name}:
|
|
708
|
+
"""File format loader implementation.
|
|
709
|
+
|
|
710
|
+
Example:
|
|
711
|
+
>>> loader = {class_name}()
|
|
712
|
+
>>> data = loader.load(Path("capture.dat"))
|
|
713
|
+
|
|
714
|
+
References:
|
|
715
|
+
PLUG-008: Plugin Template Generator
|
|
716
|
+
"""
|
|
717
|
+
def __init__(self) -> None:
|
|
718
|
+
"""Initialize loader."""
|
|
719
|
+
pass
|
|
720
|
+
|
|
721
|
+
def load(self, file_path: Path) -> dict[str, NDArray[np.float64]]:
|
|
722
|
+
"""Load data from file.
|
|
723
|
+
|
|
724
|
+
Args:
|
|
725
|
+
file_path: Path to file to load.
|
|
726
|
+
|
|
727
|
+
Returns:
|
|
728
|
+
Dictionary mapping channel names to signal arrays.
|
|
729
|
+
|
|
730
|
+
Raises:
|
|
731
|
+
FileNotFoundError: If file does not exist.
|
|
732
|
+
ValueError: If file format is invalid.
|
|
733
|
+
|
|
734
|
+
Example:
|
|
735
|
+
>>> data = loader.load(Path("capture.dat"))
|
|
736
|
+
>>> print(f"Loaded {{len(data)}} channels")
|
|
737
|
+
"""
|
|
738
|
+
if not file_path.exists():
|
|
739
|
+
raise FileNotFoundError(f"File not found: {{file_path}}")
|
|
740
|
+
|
|
741
|
+
# USER: Implement file loading logic here
|
|
742
|
+
# This stub returns empty data - replace with actual loading
|
|
743
|
+
data: dict[str, NDArray[np.float64]] = {{}}
|
|
744
|
+
|
|
745
|
+
return data
|
|
746
|
+
|
|
747
|
+
@staticmethod
|
|
748
|
+
def can_load(file_path: Path) -> bool:
|
|
749
|
+
"""Check if this loader can handle the file.
|
|
750
|
+
|
|
751
|
+
Args:
|
|
752
|
+
file_path: Path to file.
|
|
753
|
+
|
|
754
|
+
Returns:
|
|
755
|
+
True if loader can handle this file format.
|
|
756
|
+
|
|
757
|
+
Example:
|
|
758
|
+
>>> if loader.can_load(Path("capture.dat")):
|
|
759
|
+
... data = loader.load(Path("capture.dat"))
|
|
760
|
+
"""
|
|
761
|
+
# USER: Implement format detection here
|
|
762
|
+
# Check file extension, magic bytes, etc.
|
|
763
|
+
return file_path.suffix == ".dat"
|
|
764
|
+
''')
|
|
765
|
+
|
|
766
|
+
|
|
767
|
+
def _generate_exporter_stub(template: PluginTemplate, class_name: str) -> str:
|
|
768
|
+
"""Generate exporter plugin stub.
|
|
769
|
+
|
|
770
|
+
Args:
|
|
771
|
+
template: Plugin template configuration.
|
|
772
|
+
class_name: Class name for the exporter.
|
|
773
|
+
|
|
774
|
+
Returns:
|
|
775
|
+
Python source code for exporter stub.
|
|
776
|
+
"""
|
|
777
|
+
return textwrap.dedent(f'''\
|
|
778
|
+
"""{template.description}
|
|
779
|
+
|
|
780
|
+
This exporter implements custom export format for Oscura.
|
|
781
|
+
|
|
782
|
+
References:
|
|
783
|
+
PLUG-008: Plugin Template Generator
|
|
784
|
+
"""
|
|
785
|
+
from __future__ import annotations
|
|
786
|
+
|
|
787
|
+
import numpy as np
|
|
788
|
+
from numpy.typing import NDArray
|
|
789
|
+
from pathlib import Path
|
|
790
|
+
|
|
791
|
+
|
|
792
|
+
class {class_name}:
|
|
793
|
+
"""Export format implementation.
|
|
794
|
+
|
|
795
|
+
Example:
|
|
796
|
+
>>> exporter = {class_name}()
|
|
797
|
+
>>> exporter.export(data, Path("output.dat"))
|
|
798
|
+
|
|
799
|
+
References:
|
|
800
|
+
PLUG-008: Plugin Template Generator
|
|
801
|
+
"""
|
|
802
|
+
def __init__(self) -> None:
|
|
803
|
+
"""Initialize exporter."""
|
|
804
|
+
pass
|
|
805
|
+
|
|
806
|
+
def export(
|
|
807
|
+
self,
|
|
808
|
+
data: dict[str, NDArray[np.float64]],
|
|
809
|
+
output_path: Path,
|
|
810
|
+
) -> None:
|
|
811
|
+
"""Export data to file.
|
|
812
|
+
|
|
813
|
+
Args:
|
|
814
|
+
data: Dictionary mapping channel names to signal arrays.
|
|
815
|
+
output_path: Path where file will be written.
|
|
816
|
+
|
|
817
|
+
Raises:
|
|
818
|
+
ValueError: If data is invalid.
|
|
819
|
+
OSError: If file cannot be written.
|
|
820
|
+
|
|
821
|
+
Example:
|
|
822
|
+
>>> data = {{"ch1": np.sin(np.linspace(0, 10, 100))}}
|
|
823
|
+
>>> exporter.export(data, Path("output.dat"))
|
|
824
|
+
"""
|
|
825
|
+
if not data:
|
|
826
|
+
raise ValueError("Data dictionary cannot be empty")
|
|
827
|
+
|
|
828
|
+
# USER: Implement export logic here
|
|
829
|
+
# Write data to output_path in your custom format
|
|
830
|
+
|
|
831
|
+
# Placeholder implementation - replace with actual export
|
|
832
|
+
with output_path.open("w") as f:
|
|
833
|
+
f.write("# USER: Implement export format\\n")
|
|
834
|
+
for name, values in data.items():
|
|
835
|
+
f.write(f"# Channel: {{name}}, samples: {{len(values)}}\\n")
|
|
836
|
+
|
|
837
|
+
@staticmethod
|
|
838
|
+
def supports_format(format_name: str) -> bool:
|
|
839
|
+
"""Check if this exporter supports the format.
|
|
840
|
+
|
|
841
|
+
Args:
|
|
842
|
+
format_name: Name of export format.
|
|
843
|
+
|
|
844
|
+
Returns:
|
|
845
|
+
True if format is supported.
|
|
846
|
+
|
|
847
|
+
Example:
|
|
848
|
+
>>> if exporter.supports_format("custom"):
|
|
849
|
+
... exporter.export(data, path)
|
|
850
|
+
"""
|
|
851
|
+
# USER: Implement format support detection here
|
|
852
|
+
return format_name == "custom"
|
|
853
|
+
''')
|
|
854
|
+
|
|
855
|
+
|
|
856
|
+
def _generate_generic_stub(template: PluginTemplate, class_name: str) -> str:
|
|
857
|
+
"""Generate generic plugin stub.
|
|
858
|
+
|
|
859
|
+
Args:
|
|
860
|
+
template: Plugin template configuration.
|
|
861
|
+
class_name: Class name for the plugin.
|
|
862
|
+
|
|
863
|
+
Returns:
|
|
864
|
+
Python source code for generic stub.
|
|
865
|
+
"""
|
|
866
|
+
return textwrap.dedent(f'''\
|
|
867
|
+
"""{template.description}
|
|
868
|
+
|
|
869
|
+
This is a generic plugin implementation for Oscura.
|
|
870
|
+
|
|
871
|
+
References:
|
|
872
|
+
PLUG-008: Plugin Template Generator
|
|
873
|
+
"""
|
|
874
|
+
from __future__ import annotations
|
|
875
|
+
|
|
876
|
+
|
|
877
|
+
class {class_name}:
|
|
878
|
+
"""Generic plugin implementation.
|
|
879
|
+
|
|
880
|
+
Example:
|
|
881
|
+
>>> plugin = {class_name}()
|
|
882
|
+
>>> result = plugin.process()
|
|
883
|
+
|
|
884
|
+
References:
|
|
885
|
+
PLUG-008: Plugin Template Generator
|
|
886
|
+
"""
|
|
887
|
+
def __init__(self) -> None:
|
|
888
|
+
"""Initialize plugin."""
|
|
889
|
+
pass
|
|
890
|
+
|
|
891
|
+
def process(self) -> dict[str, object]:
|
|
892
|
+
"""Process data or perform plugin function.
|
|
893
|
+
|
|
894
|
+
Returns:
|
|
895
|
+
Dictionary containing results.
|
|
896
|
+
|
|
897
|
+
Example:
|
|
898
|
+
>>> result = plugin.process()
|
|
899
|
+
"""
|
|
900
|
+
# USER: Implement plugin logic here
|
|
901
|
+
result: dict[str, object] = {{
|
|
902
|
+
"status": "not_implemented",
|
|
903
|
+
}}
|
|
904
|
+
|
|
905
|
+
return result
|
|
906
|
+
''')
|
|
907
|
+
|
|
908
|
+
|
|
909
|
+
__all__ = [
|
|
910
|
+
"PluginTemplate",
|
|
911
|
+
"PluginType",
|
|
912
|
+
"generate_plugin_template",
|
|
913
|
+
]
|