oscura 0.7.0__py3-none-any.whl → 0.10.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. oscura/__init__.py +19 -19
  2. oscura/analyzers/__init__.py +2 -0
  3. oscura/analyzers/digital/extraction.py +2 -3
  4. oscura/analyzers/digital/quality.py +1 -1
  5. oscura/analyzers/digital/timing.py +1 -1
  6. oscura/analyzers/eye/__init__.py +5 -1
  7. oscura/analyzers/eye/generation.py +501 -0
  8. oscura/analyzers/jitter/__init__.py +6 -6
  9. oscura/analyzers/jitter/timing.py +419 -0
  10. oscura/analyzers/patterns/__init__.py +94 -0
  11. oscura/analyzers/patterns/reverse_engineering.py +991 -0
  12. oscura/analyzers/power/__init__.py +35 -12
  13. oscura/analyzers/power/basic.py +3 -3
  14. oscura/analyzers/power/soa.py +1 -1
  15. oscura/analyzers/power/switching.py +3 -3
  16. oscura/analyzers/signal_classification.py +529 -0
  17. oscura/analyzers/signal_integrity/sparams.py +3 -3
  18. oscura/analyzers/statistics/__init__.py +4 -0
  19. oscura/analyzers/statistics/basic.py +152 -0
  20. oscura/analyzers/statistics/correlation.py +47 -6
  21. oscura/analyzers/validation.py +1 -1
  22. oscura/analyzers/waveform/__init__.py +2 -0
  23. oscura/analyzers/waveform/measurements.py +329 -163
  24. oscura/analyzers/waveform/measurements_with_uncertainty.py +91 -35
  25. oscura/analyzers/waveform/spectral.py +498 -54
  26. oscura/api/dsl/commands.py +15 -6
  27. oscura/api/server/templates/base.html +137 -146
  28. oscura/api/server/templates/export.html +84 -110
  29. oscura/api/server/templates/home.html +248 -267
  30. oscura/api/server/templates/protocols.html +44 -48
  31. oscura/api/server/templates/reports.html +27 -35
  32. oscura/api/server/templates/session_detail.html +68 -78
  33. oscura/api/server/templates/sessions.html +62 -72
  34. oscura/api/server/templates/waveforms.html +54 -64
  35. oscura/automotive/__init__.py +1 -1
  36. oscura/automotive/can/session.py +1 -1
  37. oscura/automotive/dbc/generator.py +638 -23
  38. oscura/automotive/dtc/data.json +102 -17
  39. oscura/automotive/uds/decoder.py +99 -6
  40. oscura/cli/analyze.py +8 -2
  41. oscura/cli/batch.py +36 -5
  42. oscura/cli/characterize.py +18 -4
  43. oscura/cli/export.py +47 -5
  44. oscura/cli/main.py +2 -0
  45. oscura/cli/onboarding/wizard.py +10 -6
  46. oscura/cli/pipeline.py +585 -0
  47. oscura/cli/visualize.py +6 -4
  48. oscura/convenience.py +400 -32
  49. oscura/core/config/loader.py +0 -1
  50. oscura/core/measurement_result.py +286 -0
  51. oscura/core/progress.py +1 -1
  52. oscura/core/schemas/device_mapping.json +8 -2
  53. oscura/core/schemas/packet_format.json +24 -4
  54. oscura/core/schemas/protocol_definition.json +12 -2
  55. oscura/core/types.py +300 -199
  56. oscura/correlation/multi_protocol.py +1 -1
  57. oscura/export/legacy/__init__.py +11 -0
  58. oscura/export/legacy/wav.py +75 -0
  59. oscura/exporters/__init__.py +19 -0
  60. oscura/exporters/wireshark.py +809 -0
  61. oscura/hardware/acquisition/file.py +5 -19
  62. oscura/hardware/acquisition/saleae.py +10 -10
  63. oscura/hardware/acquisition/socketcan.py +4 -6
  64. oscura/hardware/acquisition/synthetic.py +1 -5
  65. oscura/hardware/acquisition/visa.py +6 -6
  66. oscura/hardware/security/side_channel_detector.py +5 -508
  67. oscura/inference/message_format.py +686 -1
  68. oscura/jupyter/display.py +2 -2
  69. oscura/jupyter/magic.py +3 -3
  70. oscura/loaders/__init__.py +17 -12
  71. oscura/loaders/binary.py +1 -1
  72. oscura/loaders/chipwhisperer.py +1 -2
  73. oscura/loaders/configurable.py +1 -1
  74. oscura/loaders/csv_loader.py +2 -2
  75. oscura/loaders/hdf5_loader.py +1 -1
  76. oscura/loaders/lazy.py +6 -1
  77. oscura/loaders/mmap_loader.py +0 -1
  78. oscura/loaders/numpy_loader.py +8 -7
  79. oscura/loaders/preprocessing.py +3 -5
  80. oscura/loaders/rigol.py +21 -7
  81. oscura/loaders/sigrok.py +2 -5
  82. oscura/loaders/tdms.py +3 -2
  83. oscura/loaders/tektronix.py +38 -32
  84. oscura/loaders/tss.py +20 -27
  85. oscura/loaders/vcd.py +13 -8
  86. oscura/loaders/wav.py +1 -6
  87. oscura/pipeline/__init__.py +76 -0
  88. oscura/pipeline/handlers/__init__.py +165 -0
  89. oscura/pipeline/handlers/analyzers.py +1045 -0
  90. oscura/pipeline/handlers/decoders.py +899 -0
  91. oscura/pipeline/handlers/exporters.py +1103 -0
  92. oscura/pipeline/handlers/filters.py +891 -0
  93. oscura/pipeline/handlers/loaders.py +640 -0
  94. oscura/pipeline/handlers/transforms.py +768 -0
  95. oscura/reporting/__init__.py +88 -1
  96. oscura/reporting/automation.py +348 -0
  97. oscura/reporting/citations.py +374 -0
  98. oscura/reporting/core.py +54 -0
  99. oscura/reporting/formatting/__init__.py +11 -0
  100. oscura/reporting/formatting/measurements.py +320 -0
  101. oscura/reporting/html.py +57 -0
  102. oscura/reporting/interpretation.py +431 -0
  103. oscura/reporting/summary.py +329 -0
  104. oscura/reporting/templates/enhanced/protocol_re.html +504 -503
  105. oscura/reporting/visualization.py +542 -0
  106. oscura/side_channel/__init__.py +38 -57
  107. oscura/utils/builders/signal_builder.py +5 -5
  108. oscura/utils/comparison/compare.py +7 -9
  109. oscura/utils/comparison/golden.py +1 -1
  110. oscura/utils/filtering/convenience.py +2 -2
  111. oscura/utils/math/arithmetic.py +38 -62
  112. oscura/utils/math/interpolation.py +20 -20
  113. oscura/utils/pipeline/__init__.py +4 -17
  114. oscura/utils/progressive.py +1 -4
  115. oscura/utils/triggering/edge.py +1 -1
  116. oscura/utils/triggering/pattern.py +2 -2
  117. oscura/utils/triggering/pulse.py +2 -2
  118. oscura/utils/triggering/window.py +3 -3
  119. oscura/validation/hil_testing.py +11 -11
  120. oscura/visualization/__init__.py +47 -284
  121. oscura/visualization/batch.py +160 -0
  122. oscura/visualization/plot.py +542 -53
  123. oscura/visualization/styles.py +184 -318
  124. oscura/workflows/__init__.py +2 -0
  125. oscura/workflows/batch/advanced.py +1 -1
  126. oscura/workflows/batch/aggregate.py +7 -8
  127. oscura/workflows/complete_re.py +251 -23
  128. oscura/workflows/digital.py +27 -4
  129. oscura/workflows/multi_trace.py +136 -17
  130. oscura/workflows/waveform.py +788 -0
  131. {oscura-0.7.0.dist-info → oscura-0.10.0.dist-info}/METADATA +59 -79
  132. {oscura-0.7.0.dist-info → oscura-0.10.0.dist-info}/RECORD +135 -149
  133. oscura/side_channel/dpa.py +0 -1025
  134. oscura/utils/optimization/__init__.py +0 -19
  135. oscura/utils/optimization/parallel.py +0 -443
  136. oscura/utils/optimization/search.py +0 -532
  137. oscura/utils/pipeline/base.py +0 -338
  138. oscura/utils/pipeline/composition.py +0 -248
  139. oscura/utils/pipeline/parallel.py +0 -449
  140. oscura/utils/pipeline/pipeline.py +0 -375
  141. oscura/utils/search/__init__.py +0 -16
  142. oscura/utils/search/anomaly.py +0 -424
  143. oscura/utils/search/context.py +0 -294
  144. oscura/utils/search/pattern.py +0 -288
  145. oscura/utils/storage/__init__.py +0 -61
  146. oscura/utils/storage/database.py +0 -1166
  147. oscura/visualization/accessibility.py +0 -526
  148. oscura/visualization/annotations.py +0 -371
  149. oscura/visualization/axis_scaling.py +0 -305
  150. oscura/visualization/colors.py +0 -451
  151. oscura/visualization/digital.py +0 -436
  152. oscura/visualization/eye.py +0 -571
  153. oscura/visualization/histogram.py +0 -281
  154. oscura/visualization/interactive.py +0 -1035
  155. oscura/visualization/jitter.py +0 -1042
  156. oscura/visualization/keyboard.py +0 -394
  157. oscura/visualization/layout.py +0 -400
  158. oscura/visualization/optimization.py +0 -1079
  159. oscura/visualization/palettes.py +0 -446
  160. oscura/visualization/power.py +0 -508
  161. oscura/visualization/power_extended.py +0 -955
  162. oscura/visualization/presets.py +0 -469
  163. oscura/visualization/protocols.py +0 -1246
  164. oscura/visualization/render.py +0 -223
  165. oscura/visualization/rendering.py +0 -444
  166. oscura/visualization/reverse_engineering.py +0 -838
  167. oscura/visualization/signal_integrity.py +0 -989
  168. oscura/visualization/specialized.py +0 -643
  169. oscura/visualization/spectral.py +0 -1226
  170. oscura/visualization/thumbnails.py +0 -340
  171. oscura/visualization/time_axis.py +0 -351
  172. oscura/visualization/waveform.py +0 -454
  173. {oscura-0.7.0.dist-info → oscura-0.10.0.dist-info}/WHEEL +0 -0
  174. {oscura-0.7.0.dist-info → oscura-0.10.0.dist-info}/entry_points.txt +0 -0
  175. {oscura-0.7.0.dist-info → oscura-0.10.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,43 +1,35 @@
1
- {% extends "base.html" %}
2
-
3
- {% block title %}Reports - {{ title }}{% endblock %}
4
-
5
- {% block content %}
1
+ {% extends "base.html" %} {% block title %}Reports - {{ title }}{% endblock %} {% block content %}
6
2
  <div class="row">
7
- <div class="col-12">
8
- <div class="card">
9
- <div class="card-body">
10
- <h2 class="card-title mb-4">
11
- <i class="bi bi-file-text"></i> Analysis Reports
12
- </h2>
13
-
14
- {% if report_path %}
15
- <div class="alert alert-success">
16
- <i class="bi bi-check-circle"></i> Report generated successfully
17
- </div>
18
-
19
- <div class="d-grid gap-2">
20
- <a href="/api/download/{{ session_id }}/report" class="btn btn-primary">
21
- <i class="bi bi-download"></i> Download Full Report
22
- </a>
23
- </div>
24
-
25
- <hr>
3
+ <div class="col-12">
4
+ <div class="card">
5
+ <div class="card-body">
6
+ <h2 class="card-title mb-4"><i class="bi bi-file-text"></i> Analysis Reports</h2>
7
+
8
+ {% if report_path %}
9
+ <div class="alert alert-success"><i class="bi bi-check-circle"></i> Report generated successfully</div>
10
+
11
+ <div class="d-grid gap-2">
12
+ <a href="/api/download/{{ session_id }}/report" class="btn btn-primary">
13
+ <i class="bi bi-download"></i> Download Full Report
14
+ </a>
15
+ </div>
26
16
 
27
- <h5>Report Preview</h5>
28
- <p class="text-muted">Full report available for download above.</p>
17
+ <hr />
29
18
 
30
- {% else %}
31
- <div class="alert alert-warning">
32
- <i class="bi bi-exclamation-triangle"></i> No report available yet. Analysis may still be in progress.
33
- </div>
19
+ <h5>Report Preview</h5>
20
+ <p class="text-muted">Full report available for download above.</p>
34
21
 
35
- <a href="/session/{{ session_id }}" class="btn btn-outline-primary">
36
- <i class="bi bi-arrow-left"></i> Back to Session
37
- </a>
38
- {% endif %}
39
- </div>
22
+ {% else %}
23
+ <div class="alert alert-warning">
24
+ <i class="bi bi-exclamation-triangle"></i> No report available yet. Analysis may still be in progress.
40
25
  </div>
26
+
27
+ <a href="/session/{{ session_id }}" class="btn btn-outline-primary">
28
+ <i class="bi bi-arrow-left"></i> Back to Session
29
+ </a>
30
+ {% endif %}
31
+ </div>
41
32
  </div>
33
+ </div>
42
34
  </div>
43
35
  {% endblock %}
@@ -1,89 +1,79 @@
1
- {% extends "base.html" %}
2
-
3
- {% block title %}Session {{ session.id[:8] }} - {{ title }}{% endblock %}
4
-
5
- {% block content %}
1
+ {% extends "base.html" %} {% block title %}Session {{ session.id[:8] }} - {{ title }}{% endblock %} {% block content %}
6
2
  <div class="row">
7
- <div class="col-lg-8">
8
- <div class="card mb-4">
9
- <div class="card-body">
10
- <h2 class="card-title">
11
- <i class="bi bi-file-earmark-code"></i> {{ session.filename }}
12
- </h2>
13
- <p class="text-muted">Session ID: <code>{{ session.id }}</code></p>
3
+ <div class="col-lg-8">
4
+ <div class="card mb-4">
5
+ <div class="card-body">
6
+ <h2 class="card-title"><i class="bi bi-file-earmark-code"></i> {{ session.filename }}</h2>
7
+ <p class="text-muted">Session ID: <code>{{ session.id }}</code></p>
14
8
 
15
- <div class="row mt-3">
16
- <div class="col-md-6">
17
- <p><strong>Status:</strong>
18
- <span class="session-status status-{{ session.status }}">
19
- {{ session.status }}
20
- </span>
21
- </p>
22
- <p><strong>Created:</strong> {{ session.created_at[:19] }}</p>
23
- <p><strong>Updated:</strong> {{ session.updated_at[:19] }}</p>
24
- </div>
25
- <div class="col-md-6">
26
- <p><strong>File Hash:</strong> <code>{{ session.file_hash[:16] }}...</code></p>
27
- </div>
28
- </div>
9
+ <div class="row mt-3">
10
+ <div class="col-md-6">
11
+ <p>
12
+ <strong>Status:</strong>
13
+ <span class="session-status status-{{ session.status }}"> {{ session.status }} </span>
14
+ </p>
15
+ <p><strong>Created:</strong> {{ session.created_at[:19] }}</p>
16
+ <p><strong>Updated:</strong> {{ session.updated_at[:19] }}</p>
17
+ </div>
18
+ <div class="col-md-6">
19
+ <p><strong>File Hash:</strong> <code>{{ session.file_hash[:16] }}...</code></p>
20
+ </div>
21
+ </div>
29
22
 
30
- {% if protocol_spec %}
31
- <hr>
32
- <h5>Protocol Specification</h5>
33
- <p><strong>Protocol:</strong> {{ protocol_spec.protocol_name }}</p>
34
- <p><strong>Messages:</strong> {{ protocol_spec.message_count }}</p>
35
- <p><strong>Fields:</strong> {{ protocol_spec.field_count }}</p>
23
+ {% if protocol_spec %}
24
+ <hr />
25
+ <h5>Protocol Specification</h5>
26
+ <p><strong>Protocol:</strong> {{ protocol_spec.protocol_name }}</p>
27
+ <p><strong>Messages:</strong> {{ protocol_spec.message_count }}</p>
28
+ <p><strong>Fields:</strong> {{ protocol_spec.field_count }}</p>
36
29
 
37
- {% if protocol_spec.fields %}
38
- <div class="table-responsive mt-3">
39
- <table class="table table-sm">
40
- <thead>
41
- <tr>
42
- <th>Field</th>
43
- <th>Offset</th>
44
- <th>Length</th>
45
- <th>Type</th>
46
- <th>Confidence</th>
47
- </tr>
48
- </thead>
49
- <tbody>
50
- {% for field in protocol_spec.fields %}
51
- <tr>
52
- <td>{{ field.name }}</td>
53
- <td>{{ field.offset }}</td>
54
- <td>{{ field.length }}</td>
55
- <td>{{ field.type }}</td>
56
- <td>{{ (field.confidence * 100)|round(1) }}%</td>
57
- </tr>
58
- {% endfor %}
59
- </tbody>
60
- </table>
61
- </div>
62
- {% endif %}
63
- {% endif %}
64
- </div>
30
+ {% if protocol_spec.fields %}
31
+ <div class="table-responsive mt-3">
32
+ <table class="table table-sm">
33
+ <thead>
34
+ <tr>
35
+ <th>Field</th>
36
+ <th>Offset</th>
37
+ <th>Length</th>
38
+ <th>Type</th>
39
+ <th>Confidence</th>
40
+ </tr>
41
+ </thead>
42
+ <tbody>
43
+ {% for field in protocol_spec.fields %}
44
+ <tr>
45
+ <td>{{ field.name }}</td>
46
+ <td>{{ field.offset }}</td>
47
+ <td>{{ field.length }}</td>
48
+ <td>{{ field.type }}</td>
49
+ <td>{{ (field.confidence * 100)|round(1) }}%</td>
50
+ </tr>
51
+ {% endfor %}
52
+ </tbody>
53
+ </table>
65
54
  </div>
55
+ {% endif %} {% endif %}
56
+ </div>
66
57
  </div>
58
+ </div>
67
59
 
68
- <div class="col-lg-4">
69
- <div class="card mb-4">
70
- <div class="card-body">
71
- <h5 class="card-title">
72
- <i class="bi bi-tools"></i> Actions
73
- </h5>
74
- <div class="d-grid gap-2">
75
- <a href="/waveforms/{{ session.id }}" class="btn btn-outline-primary">
76
- <i class="bi bi-graph-up"></i> View Waveforms
77
- </a>
78
- <a href="/reports/{{ session.id }}" class="btn btn-outline-primary">
79
- <i class="bi bi-file-text"></i> View Reports
80
- </a>
81
- <a href="/export/{{ session.id }}" class="btn btn-outline-primary">
82
- <i class="bi bi-download"></i> Export/Download
83
- </a>
84
- </div>
85
- </div>
60
+ <div class="col-lg-4">
61
+ <div class="card mb-4">
62
+ <div class="card-body">
63
+ <h5 class="card-title"><i class="bi bi-tools"></i> Actions</h5>
64
+ <div class="d-grid gap-2">
65
+ <a href="/waveforms/{{ session.id }}" class="btn btn-outline-primary">
66
+ <i class="bi bi-graph-up"></i> View Waveforms
67
+ </a>
68
+ <a href="/reports/{{ session.id }}" class="btn btn-outline-primary">
69
+ <i class="bi bi-file-text"></i> View Reports
70
+ </a>
71
+ <a href="/export/{{ session.id }}" class="btn btn-outline-primary">
72
+ <i class="bi bi-download"></i> Export/Download
73
+ </a>
86
74
  </div>
75
+ </div>
87
76
  </div>
77
+ </div>
88
78
  </div>
89
79
  {% endblock %}
@@ -1,83 +1,73 @@
1
- {% extends "base.html" %}
2
-
3
- {% block title %}Sessions - {{ title }}{% endblock %}
4
-
5
- {% block content %}
1
+ {% extends "base.html" %} {% block title %}Sessions - {{ title }}{% endblock %} {% block content %}
6
2
  <div class="row">
7
- <div class="col-12">
8
- <div class="card">
9
- <div class="card-body">
10
- <h1 class="card-title mb-4">
11
- <i class="bi bi-list-task"></i> Analysis Sessions
12
- </h1>
3
+ <div class="col-12">
4
+ <div class="card">
5
+ <div class="card-body">
6
+ <h1 class="card-title mb-4"><i class="bi bi-list-task"></i> Analysis Sessions</h1>
13
7
 
14
- {% if sessions %}
15
- <div class="table-responsive">
16
- <table class="table table-hover">
17
- <thead>
18
- <tr>
19
- <th>Session ID</th>
20
- <th>Filename</th>
21
- <th>Status</th>
22
- <th>Created</th>
23
- <th>Updated</th>
24
- <th>Actions</th>
25
- </tr>
26
- </thead>
27
- <tbody>
28
- {% for session in sessions %}
29
- <tr>
30
- <td><code>{{ session.session_id[:8] }}...</code></td>
31
- <td>{{ session.filename }}</td>
32
- <td>
33
- <span class="session-status status-{{ session.status }}">
34
- {{ session.status }}
35
- </span>
36
- </td>
37
- <td>{{ session.created_at[:19] }}</td>
38
- <td>{{ session.updated_at[:19] }}</td>
39
- <td>
40
- <a href="/session/{{ session.session_id }}" class="btn btn-sm btn-primary">
41
- <i class="bi bi-eye"></i> View
42
- </a>
43
- <button class="btn btn-sm btn-danger" onclick="deleteSession('{{ session.session_id }}')">
44
- <i class="bi bi-trash"></i>
45
- </button>
46
- </td>
47
- </tr>
48
- {% endfor %}
49
- </tbody>
50
- </table>
51
- </div>
52
- {% else %}
53
- <div class="alert alert-info">
54
- <i class="bi bi-info-circle"></i> No sessions yet. <a href="/">Upload a file</a> to get started.
55
- </div>
56
- {% endif %}
57
- </div>
8
+ {% if sessions %}
9
+ <div class="table-responsive">
10
+ <table class="table table-hover">
11
+ <thead>
12
+ <tr>
13
+ <th>Session ID</th>
14
+ <th>Filename</th>
15
+ <th>Status</th>
16
+ <th>Created</th>
17
+ <th>Updated</th>
18
+ <th>Actions</th>
19
+ </tr>
20
+ </thead>
21
+ <tbody>
22
+ {% for session in sessions %}
23
+ <tr>
24
+ <td><code>{{ session.session_id[:8] }}...</code></td>
25
+ <td>{{ session.filename }}</td>
26
+ <td>
27
+ <span class="session-status status-{{ session.status }}"> {{ session.status }} </span>
28
+ </td>
29
+ <td>{{ session.created_at[:19] }}</td>
30
+ <td>{{ session.updated_at[:19] }}</td>
31
+ <td>
32
+ <a href="/session/{{ session.session_id }}" class="btn btn-sm btn-primary">
33
+ <i class="bi bi-eye"></i> View
34
+ </a>
35
+ <button class="btn btn-sm btn-danger" onclick="deleteSession('{{ session.session_id }}')">
36
+ <i class="bi bi-trash"></i>
37
+ </button>
38
+ </td>
39
+ </tr>
40
+ {% endfor %}
41
+ </tbody>
42
+ </table>
43
+ </div>
44
+ {% else %}
45
+ <div class="alert alert-info">
46
+ <i class="bi bi-info-circle"></i> No sessions yet. <a href="/">Upload a file</a> to get started.
58
47
  </div>
48
+ {% endif %}
49
+ </div>
59
50
  </div>
51
+ </div>
60
52
  </div>
61
- {% endblock %}
62
-
63
- {% block extra_scripts %}
53
+ {% endblock %} {% block extra_scripts %}
64
54
  <script>
65
- async function deleteSession(sessionId) {
66
- if (!confirm('Delete this session?')) return;
55
+ async function deleteSession(sessionId) {
56
+ if (!confirm('Delete this session?')) return;
67
57
 
68
- try {
69
- const response = await fetch(`/api/session/${sessionId}`, {
70
- method: 'DELETE'
71
- });
58
+ try {
59
+ const response = await fetch(`/api/session/${sessionId}`, {
60
+ method: 'DELETE',
61
+ });
72
62
 
73
- if (response.ok) {
74
- location.reload();
75
- } else {
76
- alert('Failed to delete session');
77
- }
78
- } catch (error) {
79
- alert('Error: ' + error.message);
80
- }
63
+ if (response.ok) {
64
+ location.reload();
65
+ } else {
66
+ alert('Failed to delete session');
67
+ }
68
+ } catch (error) {
69
+ alert('Error: ' + error.message);
81
70
  }
71
+ }
82
72
  </script>
83
73
  {% endblock %}
@@ -1,73 +1,63 @@
1
- {% extends "base.html" %}
2
-
3
- {% block title %}Waveforms - {{ title }}{% endblock %}
4
-
5
- {% block content %}
1
+ {% extends "base.html" %} {% block title %}Waveforms - {{ title }}{% endblock %} {% block content %}
6
2
  <div class="row">
7
- <div class="col-12">
8
- <div class="card mb-4">
9
- <div class="card-body">
10
- <h2 class="card-title">
11
- <i class="bi bi-graph-up"></i> Waveform Viewer
12
- </h2>
13
- <p class="text-muted">{{ filename }}</p>
14
- </div>
15
- </div>
3
+ <div class="col-12">
4
+ <div class="card mb-4">
5
+ <div class="card-body">
6
+ <h2 class="card-title"><i class="bi bi-graph-up"></i> Waveform Viewer</h2>
7
+ <p class="text-muted">{{ filename }}</p>
8
+ </div>
9
+ </div>
16
10
 
17
- <div class="card">
18
- <div class="card-body">
19
- <div id="waveformPlot" style="width: 100%; height: 600px;"></div>
20
- </div>
21
- </div>
11
+ <div class="card">
12
+ <div class="card-body">
13
+ <div id="waveformPlot" style="width: 100%; height: 600px"></div>
14
+ </div>
15
+ </div>
22
16
 
23
- <div class="card mt-4">
24
- <div class="card-body">
25
- <h5 class="card-title">Controls</h5>
26
- <div class="btn-group" role="group">
27
- <button class="btn btn-outline-secondary" onclick="resetZoom()">
28
- <i class="bi bi-zoom-out"></i> Reset Zoom
29
- </button>
30
- <button class="btn btn-outline-secondary" onclick="togglePan()">
31
- <i class="bi bi-arrows-move"></i> Pan
32
- </button>
33
- </div>
34
- </div>
17
+ <div class="card mt-4">
18
+ <div class="card-body">
19
+ <h5 class="card-title">Controls</h5>
20
+ <div class="btn-group" role="group">
21
+ <button class="btn btn-outline-secondary" onclick="resetZoom()">
22
+ <i class="bi bi-zoom-out"></i> Reset Zoom
23
+ </button>
24
+ <button class="btn btn-outline-secondary" onclick="togglePan()"><i class="bi bi-arrows-move"></i> Pan</button>
35
25
  </div>
26
+ </div>
36
27
  </div>
28
+ </div>
37
29
  </div>
38
- {% endblock %}
39
-
40
- {% block extra_scripts %}
30
+ {% endblock %} {% block extra_scripts %}
41
31
  <script>
42
- const sessionId = '{{ session_id }}';
43
- const plotlyConfig = {{ plotly_config|safe }};
44
-
45
- async function loadWaveform() {
46
- try {
47
- const response = await fetch(`/api/session/${sessionId}/waveform`);
48
- const data = await response.json();
49
-
50
- Plotly.newPlot('waveformPlot', data.data, data.layout, plotlyConfig);
51
- } catch (error) {
52
- console.error('Failed to load waveform:', error);
53
- document.getElementById('waveformPlot').innerHTML =
54
- '<div class="alert alert-danger">Failed to load waveform data</div>';
55
- }
56
- }
57
-
58
- function resetZoom() {
59
- Plotly.relayout('waveformPlot', {
60
- 'xaxis.autorange': true,
61
- 'yaxis.autorange': true
62
- });
63
- }
64
-
65
- function togglePan() {
66
- const plot = document.getElementById('waveformPlot');
67
- Plotly.relayout(plot, {dragmode: 'pan'});
68
- }
69
-
70
- // Load waveform on page load
71
- loadWaveform();
32
+ const sessionId = '{{ session_id }}';
33
+ const plotlyConfig = {{ plotly_config|safe }};
34
+
35
+ async function loadWaveform() {
36
+ try {
37
+ const response = await fetch(`/api/session/${sessionId}/waveform`);
38
+ const data = await response.json();
39
+
40
+ Plotly.newPlot('waveformPlot', data.data, data.layout, plotlyConfig);
41
+ } catch (error) {
42
+ console.error('Failed to load waveform:', error);
43
+ document.getElementById('waveformPlot').innerHTML =
44
+ '<div class="alert alert-danger">Failed to load waveform data</div>';
45
+ }
46
+ }
47
+
48
+ function resetZoom() {
49
+ Plotly.relayout('waveformPlot', {
50
+ 'xaxis.autorange': true,
51
+ 'yaxis.autorange': true
52
+ });
53
+ }
54
+
55
+ function togglePan() {
56
+ const plot = document.getElementById('waveformPlot');
57
+ Plotly.relayout(plot, {dragmode: 'pan'});
58
+ }
59
+
60
+ // Load waveform on page load
61
+ loadWaveform();
72
62
  </script>
73
63
  {% endblock %}
@@ -49,7 +49,7 @@ try:
49
49
  __version__ = version("oscura")
50
50
  except Exception:
51
51
  # Fallback for development/testing when package not installed
52
- __version__ = "0.7.0"
52
+ __version__ = "0.10.0"
53
53
 
54
54
  __all__ = [
55
55
  "CANMessage",
@@ -15,7 +15,7 @@ try:
15
15
 
16
16
  _HAS_PANDAS = True
17
17
  except ImportError:
18
- pd = None # type: ignore[assignment]
18
+ pd = None
19
19
  _HAS_PANDAS = False
20
20
 
21
21
  from oscura.automotive.can.analysis import MessageAnalyzer