chronaxis 0.1.0.dev0__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 (917) hide show
  1. chronaxis/__init__.py +72 -0
  2. chronaxis/__main__.py +12 -0
  3. chronaxis/analysis_cli.py +181 -0
  4. chronaxis/batch/__init__.py +14 -0
  5. chronaxis/batch/api.py +46 -0
  6. chronaxis/batch/cli.py +48 -0
  7. chronaxis/batch/config.py +37 -0
  8. chronaxis/batch/models.py +202 -0
  9. chronaxis/batch/process_transport.py +149 -0
  10. chronaxis/batch/process_tree.py +72 -0
  11. chronaxis/batch/publication.py +41 -0
  12. chronaxis/batch/runner.py +236 -0
  13. chronaxis/batch/templates.py +42 -0
  14. chronaxis/can_dependencies.py +42 -0
  15. chronaxis/cli.py +193 -0
  16. chronaxis/configuration/__init__.py +46 -0
  17. chronaxis/configuration/cli.py +41 -0
  18. chronaxis/configuration/diagnostics.py +107 -0
  19. chronaxis/configuration/explanation.py +233 -0
  20. chronaxis/configuration/file_tools.py +140 -0
  21. chronaxis/configuration/imports.py +144 -0
  22. chronaxis/configuration/initialization.py +36 -0
  23. chronaxis/configuration/json_parser.py +114 -0
  24. chronaxis/configuration/models.py +74 -0
  25. chronaxis/configuration/origins.py +90 -0
  26. chronaxis/configuration/overrides.py +55 -0
  27. chronaxis/configuration/resolution.py +150 -0
  28. chronaxis/configuration/template_catalog.py +68 -0
  29. chronaxis/configuration/templates/batch/analysis-annotated.yaml +18 -0
  30. chronaxis/configuration/templates/batch/analysis-minimal.yaml +18 -0
  31. chronaxis/configuration/templates/batch/annotated.yaml +6 -0
  32. chronaxis/configuration/templates/batch/minimal.yaml +6 -0
  33. chronaxis/configuration/templates/can-asc/annotated.yaml +18 -0
  34. chronaxis/configuration/templates/can-asc/minimal.yaml +18 -0
  35. chronaxis/configuration/templates/can-blf/annotated.yaml +18 -0
  36. chronaxis/configuration/templates/can-blf/minimal.yaml +18 -0
  37. chronaxis/configuration/templates/can-export/annotated.yaml +23 -0
  38. chronaxis/configuration/templates/can-export/minimal.yaml +23 -0
  39. chronaxis/configuration/templates/catalog.json +233 -0
  40. chronaxis/configuration/templates/charge-session/annotated.yaml +38 -0
  41. chronaxis/configuration/templates/charge-session/minimal.yaml +38 -0
  42. chronaxis/configuration/templates/command-response/annotated.yaml +32 -0
  43. chronaxis/configuration/templates/command-response/minimal.yaml +36 -0
  44. chronaxis/configuration/templates/fault-window/annotated.yaml +25 -0
  45. chronaxis/configuration/templates/fault-window/minimal.yaml +27 -0
  46. chronaxis/configuration/templates/ini-mapping/annotated.yaml +20 -0
  47. chronaxis/configuration/templates/ini-mapping/minimal.yaml +20 -0
  48. chronaxis/configuration/templates/raw-can-csv/annotated.yaml +34 -0
  49. chronaxis/configuration/templates/raw-can-csv/minimal.yaml +35 -0
  50. chronaxis/configuration/templates/shared/charge-session.asc +5 -0
  51. chronaxis/configuration/templates/shared/command-response.asc +8 -0
  52. chronaxis/configuration/templates/shared/fault-window.asc +5 -0
  53. chronaxis/configuration/templates/shared/power.asc +7 -0
  54. chronaxis/configuration/templates/shared/sample.asc +6 -0
  55. chronaxis/configuration/templates/shared/sample.blf +0 -0
  56. chronaxis/configuration/templates/shared/sample.csv +5 -0
  57. chronaxis/configuration/templates/shared/sample.dbc +11 -0
  58. chronaxis/configuration/templates/shared/signals.csv +6 -0
  59. chronaxis/configuration/templates/shared/source.ini +5 -0
  60. chronaxis/configuration/templates/shared/stability.asc +9 -0
  61. chronaxis/configuration/templates/shared/teaching.dbc +27 -0
  62. chronaxis/configuration/templates/shared/threshold.asc +7 -0
  63. chronaxis/configuration/templates/signal-csv/annotated.yaml +27 -0
  64. chronaxis/configuration/templates/signal-csv/minimal.yaml +27 -0
  65. chronaxis/configuration/templates/signal-export/annotated.yaml +27 -0
  66. chronaxis/configuration/templates/signal-export/minimal.yaml +29 -0
  67. chronaxis/configuration/templates/stability/annotated.yaml +35 -0
  68. chronaxis/configuration/templates/stability/minimal.yaml +35 -0
  69. chronaxis/configuration/templates/threshold/annotated.yaml +22 -0
  70. chronaxis/configuration/templates/threshold/minimal.yaml +22 -0
  71. chronaxis/configuration/yaml_parser.py +125 -0
  72. chronaxis/core/__init__.py +71 -0
  73. chronaxis/core/binding.py +449 -0
  74. chronaxis/core/calculation_series_join.py +106 -0
  75. chronaxis/core/calculations.py +435 -0
  76. chronaxis/core/calculations_memory_numpy.py +136 -0
  77. chronaxis/core/calculations_memory_reference.py +165 -0
  78. chronaxis/core/calculations_numpy.py +298 -0
  79. chronaxis/core/calculations_reference.py +294 -0
  80. chronaxis/core/condition_evidence.py +182 -0
  81. chronaxis/core/conditional_integral_result.py +43 -0
  82. chronaxis/core/conditional_integral_terms.py +146 -0
  83. chronaxis/core/conditional_integral_terms_numpy.py +148 -0
  84. chronaxis/core/conditional_integral_terms_reference.py +111 -0
  85. chronaxis/core/conditional_time_reduction.py +118 -0
  86. chronaxis/core/conditional_time_reduction_numpy.py +223 -0
  87. chronaxis/core/conditional_time_reduction_reference.py +213 -0
  88. chronaxis/core/declarations.py +192 -0
  89. chronaxis/core/decoded_binding.py +197 -0
  90. chronaxis/core/direct_message.py +387 -0
  91. chronaxis/core/export_interpolation.py +54 -0
  92. chronaxis/core/expression.py +642 -0
  93. chronaxis/core/expression_numpy.py +314 -0
  94. chronaxis/core/expression_reference.py +272 -0
  95. chronaxis/core/integral_result.py +53 -0
  96. chronaxis/core/integral_sum.py +77 -0
  97. chronaxis/core/integral_sum_numpy.py +224 -0
  98. chronaxis/core/integral_sum_reference.py +148 -0
  99. chronaxis/core/integral_terms.py +119 -0
  100. chronaxis/core/integral_terms_numpy.py +127 -0
  101. chronaxis/core/integral_terms_reference.py +91 -0
  102. chronaxis/core/mean_weights_numpy.py +29 -0
  103. chronaxis/core/mean_weights_reference.py +27 -0
  104. chronaxis/core/message_binding.py +305 -0
  105. chronaxis/core/models.py +485 -0
  106. chronaxis/core/normalize_numpy.py +396 -0
  107. chronaxis/core/numeric.py +472 -0
  108. chronaxis/core/numeric_numpy.py +440 -0
  109. chronaxis/core/record_intervals.py +115 -0
  110. chronaxis/core/sample_reduction.py +161 -0
  111. chronaxis/core/sample_reduction_numpy.py +160 -0
  112. chronaxis/core/sample_reduction_reference.py +154 -0
  113. chronaxis/core/selection.py +74 -0
  114. chronaxis/core/sequence.py +160 -0
  115. chronaxis/core/sequence_reference.py +248 -0
  116. chronaxis/core/stability.py +249 -0
  117. chronaxis/core/start_end.py +169 -0
  118. chronaxis/core/state.py +494 -0
  119. chronaxis/core/state_numpy.py +296 -0
  120. chronaxis/core/temporal.py +373 -0
  121. chronaxis/core/temporal_clock.py +25 -0
  122. chronaxis/core/temporal_memory_numpy.py +163 -0
  123. chronaxis/core/temporal_memory_reference.py +161 -0
  124. chronaxis/core/temporal_numpy.py +345 -0
  125. chronaxis/core/temporal_reference.py +304 -0
  126. chronaxis/core/time_reduction.py +190 -0
  127. chronaxis/core/time_reduction_numpy.py +205 -0
  128. chronaxis/core/time_reduction_reference.py +203 -0
  129. chronaxis/core/types.py +105 -0
  130. chronaxis/core/units.py +81 -0
  131. chronaxis/core/validation.py +94 -0
  132. chronaxis/core/window_delta.py +123 -0
  133. chronaxis/core/window_delta_numpy.py +78 -0
  134. chronaxis/core/window_delta_reference.py +69 -0
  135. chronaxis/core/windows.py +224 -0
  136. chronaxis/core/windows_numpy.py +78 -0
  137. chronaxis/core/windows_reference.py +61 -0
  138. chronaxis/execution/__init__.py +6 -0
  139. chronaxis/execution/batch_signal_groups.py +122 -0
  140. chronaxis/execution/bindings.py +296 -0
  141. chronaxis/execution/boundaries.py +568 -0
  142. chronaxis/execution/calculation_blocks.py +338 -0
  143. chronaxis/execution/calculation_values.py +55 -0
  144. chronaxis/execution/can_batch_reader.py +356 -0
  145. chronaxis/execution/can_reader.py +948 -0
  146. chronaxis/execution/can_reference_template.py +61 -0
  147. chronaxis/execution/condition_evidence.py +370 -0
  148. chronaxis/execution/conditional_integral_facts.py +310 -0
  149. chronaxis/execution/conditional_integral_reduction.py +216 -0
  150. chronaxis/execution/csv_observation.py +524 -0
  151. chronaxis/execution/csv_reader.py +662 -0
  152. chronaxis/execution/current_message_calculation.py +225 -0
  153. chronaxis/execution/driver_expression.py +274 -0
  154. chronaxis/execution/expressions.py +618 -0
  155. chronaxis/execution/frame_capture.py +245 -0
  156. chronaxis/execution/groups.py +236 -0
  157. chronaxis/execution/historical_conditional_blocks.py +264 -0
  158. chronaxis/execution/historical_conditional_integral.py +226 -0
  159. chronaxis/execution/historical_conditional_time_calculation.py +368 -0
  160. chronaxis/execution/historical_integral.py +219 -0
  161. chronaxis/execution/historical_observation.py +372 -0
  162. chronaxis/execution/historical_sample_calculation.py +338 -0
  163. chronaxis/execution/historical_selection_rows.py +300 -0
  164. chronaxis/execution/historical_time_calculation.py +341 -0
  165. chronaxis/execution/history.py +572 -0
  166. chronaxis/execution/history_records.py +225 -0
  167. chronaxis/execution/hold.py +448 -0
  168. chronaxis/execution/integral_facts.py +311 -0
  169. chronaxis/execution/integral_reduction.py +216 -0
  170. chronaxis/execution/legacy_frame_samples.py +94 -0
  171. chronaxis/execution/memory_reader.py +247 -0
  172. chronaxis/execution/message_bindings.py +293 -0
  173. chronaxis/execution/message_delivery.py +37 -0
  174. chronaxis/execution/observed_ingestion.py +551 -0
  175. chronaxis/execution/raw_signal_groups.py +192 -0
  176. chronaxis/execution/record_alarm_duration.py +114 -0
  177. chronaxis/execution/record_delta_condition.py +167 -0
  178. chronaxis/execution/record_diagnostics.py +87 -0
  179. chronaxis/execution/record_event_durations.py +143 -0
  180. chronaxis/execution/record_export_samples.py +94 -0
  181. chronaxis/execution/record_exports.py +155 -0
  182. chronaxis/execution/record_integral.py +196 -0
  183. chronaxis/execution/record_point_buffer.py +101 -0
  184. chronaxis/execution/record_rule_timeline.py +344 -0
  185. chronaxis/execution/record_sample.py +46 -0
  186. chronaxis/execution/record_sample_series.py +29 -0
  187. chronaxis/execution/record_scope_calculations.py +290 -0
  188. chronaxis/execution/record_selection.py +62 -0
  189. chronaxis/execution/record_sequences.py +199 -0
  190. chronaxis/execution/record_session_calculations.py +778 -0
  191. chronaxis/execution/record_signal_export.py +133 -0
  192. chronaxis/execution/record_source_cursor.py +295 -0
  193. chronaxis/execution/record_time_calculation.py +322 -0
  194. chronaxis/execution/record_time_indexes.py +143 -0
  195. chronaxis/execution/record_value_history.py +160 -0
  196. chronaxis/execution/record_windows.py +229 -0
  197. chronaxis/execution/required_expression.py +52 -0
  198. chronaxis/execution/retained_metadata.py +71 -0
  199. chronaxis/execution/run_session.py +402 -0
  200. chronaxis/execution/scanned_signal_proof.py +550 -0
  201. chronaxis/execution/signal_validity.py +40 -0
  202. chronaxis/execution/state.py +280 -0
  203. chronaxis/execution/temporal_buffer.py +508 -0
  204. chronaxis/execution/text_can_reader.py +359 -0
  205. chronaxis/execution/timeline.py +309 -0
  206. chronaxis/execution/timeline_multi.py +415 -0
  207. chronaxis/execution/window_delta_events.py +220 -0
  208. chronaxis/execution/window_replay.py +350 -0
  209. chronaxis/execution/windows.py +173 -0
  210. chronaxis/guide/README.md +70 -0
  211. chronaxis/guide/api/AnalysisError.md +14 -0
  212. chronaxis/guide/api/AnalysisPlan.md +96 -0
  213. chronaxis/guide/api/AnalysisResult.md +398 -0
  214. chronaxis/guide/api/AnalysisRunner.md +155 -0
  215. chronaxis/guide/api/BatchJob.md +19 -0
  216. chronaxis/guide/api/BatchJobResult.md +61 -0
  217. chronaxis/guide/api/BatchPlan.md +48 -0
  218. chronaxis/guide/api/BatchReport.md +108 -0
  219. chronaxis/guide/api/BatchRunner.md +84 -0
  220. chronaxis/guide/api/OutputManifest.md +23 -0
  221. chronaxis/guide/api/PlanFragment.md +47 -0
  222. chronaxis/guide/api/PlanValidationError.md +98 -0
  223. chronaxis/guide/api/PresetSpec.md +63 -0
  224. chronaxis/guide/api/ResultClosedError.md +14 -0
  225. chronaxis/guide/api/RunMetadata.md +100 -0
  226. chronaxis/guide/api/TimeConversion.md +29 -0
  227. chronaxis/guide/api/TimeOverflowError.md +17 -0
  228. chronaxis/guide/api/build_preset.md +36 -0
  229. chronaxis/guide/api/checked_add_ns.md +30 -0
  230. chronaxis/guide/api/convert_time.md +40 -0
  231. chronaxis/guide/api/explain_analysis_plan.md +38 -0
  232. chronaxis/guide/api/index.json +185 -0
  233. chronaxis/guide/api/load_analysis_plan.md +34 -0
  234. chronaxis/guide/api/load_batch_plan.md +36 -0
  235. chronaxis/guide/api/parse_datetime_ns.md +35 -0
  236. chronaxis/guide/api/parse_duration.md +30 -0
  237. chronaxis/guide/api/resolve_preset_roles.md +37 -0
  238. chronaxis/guide/api/run_batch.md +38 -0
  239. chronaxis/guide/api/shift_times_reference.md +33 -0
  240. chronaxis/guide/api/time_deltas_reference.md +37 -0
  241. chronaxis/guide/api/validate_analysis_plan.md +38 -0
  242. chronaxis/guide/en/README.md +74 -0
  243. chronaxis/guide/en/api/AnalysisError.md +14 -0
  244. chronaxis/guide/en/api/AnalysisPlan.md +93 -0
  245. chronaxis/guide/en/api/AnalysisResult.md +398 -0
  246. chronaxis/guide/en/api/AnalysisRunner.md +191 -0
  247. chronaxis/guide/en/api/BatchJob.md +17 -0
  248. chronaxis/guide/en/api/BatchJobResult.md +43 -0
  249. chronaxis/guide/en/api/BatchPlan.md +37 -0
  250. chronaxis/guide/en/api/BatchReport.md +52 -0
  251. chronaxis/guide/en/api/BatchRunner.md +51 -0
  252. chronaxis/guide/en/api/OutputManifest.md +23 -0
  253. chronaxis/guide/en/api/PlanFragment.md +33 -0
  254. chronaxis/guide/en/api/PlanValidationError.md +58 -0
  255. chronaxis/guide/en/api/PresetSpec.md +51 -0
  256. chronaxis/guide/en/api/ResultClosedError.md +14 -0
  257. chronaxis/guide/en/api/RunMetadata.md +100 -0
  258. chronaxis/guide/en/api/TimeConversion.md +23 -0
  259. chronaxis/guide/en/api/TimeOverflowError.md +14 -0
  260. chronaxis/guide/en/api/build_preset.md +23 -0
  261. chronaxis/guide/en/api/checked_add_ns.md +21 -0
  262. chronaxis/guide/en/api/convert_time.md +25 -0
  263. chronaxis/guide/en/api/explain_analysis_plan.md +30 -0
  264. chronaxis/guide/en/api/index.json +185 -0
  265. chronaxis/guide/en/api/load_analysis_plan.md +27 -0
  266. chronaxis/guide/en/api/load_batch_plan.md +28 -0
  267. chronaxis/guide/en/api/parse_datetime_ns.md +23 -0
  268. chronaxis/guide/en/api/parse_duration.md +20 -0
  269. chronaxis/guide/en/api/resolve_preset_roles.md +23 -0
  270. chronaxis/guide/en/api/run_batch.md +39 -0
  271. chronaxis/guide/en/api/shift_times_reference.md +21 -0
  272. chronaxis/guide/en/api/time_deltas_reference.md +22 -0
  273. chronaxis/guide/en/api/validate_analysis_plan.md +29 -0
  274. chronaxis/guide/en/fields/analysis/calculations-all-1.json +345 -0
  275. chronaxis/guide/en/fields/analysis/calculations-all-2.json +300 -0
  276. chronaxis/guide/en/fields/analysis/calculations-all-3.json +84 -0
  277. chronaxis/guide/en/fields/analysis/config_version-all-1.json +24 -0
  278. chronaxis/guide/en/fields/analysis/dataset-all-1.json +344 -0
  279. chronaxis/guide/en/fields/analysis/derived-all-1.json +147 -0
  280. chronaxis/guide/en/fields/analysis/diagnostics-all-1.json +69 -0
  281. chronaxis/guide/en/fields/analysis/execution-all-1.json +167 -0
  282. chronaxis/guide/en/fields/analysis/imports-all-1.json +39 -0
  283. chronaxis/guide/en/fields/analysis/intervals-all-1.json +173 -0
  284. chronaxis/guide/en/fields/analysis/message_calculations-all-1.json +345 -0
  285. chronaxis/guide/en/fields/analysis/message_calculations-all-2.json +270 -0
  286. chronaxis/guide/en/fields/analysis/metadata-all-1.json +24 -0
  287. chronaxis/guide/en/fields/analysis/outputs-all-1.json +332 -0
  288. chronaxis/guide/en/fields/analysis/outputs-all-2.json +319 -0
  289. chronaxis/guide/en/fields/analysis/outputs-all-3.json +321 -0
  290. chronaxis/guide/en/fields/analysis/outputs-all-4.json +236 -0
  291. chronaxis/guide/en/fields/analysis/outputs-all-5.json +246 -0
  292. chronaxis/guide/en/fields/analysis/outputs-all-6.json +214 -0
  293. chronaxis/guide/en/fields/analysis/overrides-all-1.json +104 -0
  294. chronaxis/guide/en/fields/analysis/presets-charge_session-1.json +398 -0
  295. chronaxis/guide/en/fields/analysis/presets-charge_session-2.json +114 -0
  296. chronaxis/guide/en/fields/analysis/presets-command_response-1.json +376 -0
  297. chronaxis/guide/en/fields/analysis/presets-fault_context-1.json +285 -0
  298. chronaxis/guide/en/fields/analysis/presets-threshold_excursion-1.json +317 -0
  299. chronaxis/guide/en/fields/analysis/rules-condition_duration-1.json +344 -0
  300. chronaxis/guide/en/fields/analysis/rules-condition_duration-2.json +27 -0
  301. chronaxis/guide/en/fields/analysis/rules-debounce-1.json +335 -0
  302. chronaxis/guide/en/fields/analysis/rules-falling_edge-1.json +317 -0
  303. chronaxis/guide/en/fields/analysis/rules-hysteresis-1.json +334 -0
  304. chronaxis/guide/en/fields/analysis/rules-hysteresis-2.json +27 -0
  305. chronaxis/guide/en/fields/analysis/rules-rising_edge-1.json +317 -0
  306. chronaxis/guide/en/fields/analysis/rules-start_end-1.json +333 -0
  307. chronaxis/guide/en/fields/analysis/rules-start_end-2.json +60 -0
  308. chronaxis/guide/en/fields/analysis/rules-start_only-1.json +334 -0
  309. chronaxis/guide/en/fields/analysis/rules-window_delta-1.json +338 -0
  310. chronaxis/guide/en/fields/analysis/rules-window_delta-2.json +96 -0
  311. chronaxis/guide/en/fields/analysis/sequences-all-1.json +378 -0
  312. chronaxis/guide/en/fields/analysis/sequences-all-2.json +70 -0
  313. chronaxis/guide/en/fields/analysis/signals-variant-1-1.json +349 -0
  314. chronaxis/guide/en/fields/analysis/signals-variant-1-2.json +297 -0
  315. chronaxis/guide/en/fields/analysis/signals-variant-1-3.json +323 -0
  316. chronaxis/guide/en/fields/analysis/signals-variant-1-4.json +192 -0
  317. chronaxis/guide/en/fields/analysis/signals-variant-2-1.json +327 -0
  318. chronaxis/guide/en/fields/analysis/signals-variant-2-2.json +312 -0
  319. chronaxis/guide/en/fields/analysis/signals-variant-2-3.json +294 -0
  320. chronaxis/guide/en/fields/analysis/signals-variant-2-4.json +253 -0
  321. chronaxis/guide/en/fields/analysis/signals-variant-2-5.json +44 -0
  322. chronaxis/guide/en/fields/analysis/sources-can_asc-1.json +318 -0
  323. chronaxis/guide/en/fields/analysis/sources-can_asc-2.json +310 -0
  324. chronaxis/guide/en/fields/analysis/sources-can_asc-3.json +311 -0
  325. chronaxis/guide/en/fields/analysis/sources-can_asc-4.json +69 -0
  326. chronaxis/guide/en/fields/analysis/sources-can_blf-1.json +317 -0
  327. chronaxis/guide/en/fields/analysis/sources-can_blf-2.json +310 -0
  328. chronaxis/guide/en/fields/analysis/sources-can_blf-3.json +317 -0
  329. chronaxis/guide/en/fields/analysis/sources-memory-1.json +332 -0
  330. chronaxis/guide/en/fields/analysis/sources-memory-2.json +125 -0
  331. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-1.json +330 -0
  332. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-10.json +233 -0
  333. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-11.json +225 -0
  334. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-12.json +228 -0
  335. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-13.json +226 -0
  336. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-14.json +230 -0
  337. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-15.json +227 -0
  338. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-16.json +230 -0
  339. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-17.json +238 -0
  340. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-18.json +239 -0
  341. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-19.json +233 -0
  342. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-2.json +310 -0
  343. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-20.json +242 -0
  344. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-21.json +230 -0
  345. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-22.json +228 -0
  346. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-23.json +230 -0
  347. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-24.json +223 -0
  348. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-25.json +290 -0
  349. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-26.json +131 -0
  350. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-3.json +297 -0
  351. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-4.json +226 -0
  352. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-5.json +230 -0
  353. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-6.json +227 -0
  354. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-7.json +230 -0
  355. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-8.json +238 -0
  356. chronaxis/guide/en/fields/analysis/sources-raw_can_csv-9.json +239 -0
  357. chronaxis/guide/en/fields/analysis/sources-signal_csv-1.json +331 -0
  358. chronaxis/guide/en/fields/analysis/sources-signal_csv-2.json +307 -0
  359. chronaxis/guide/en/fields/analysis/sources-signal_csv-3.json +315 -0
  360. chronaxis/guide/en/fields/analysis/sources-variant-6-1.json +319 -0
  361. chronaxis/guide/en/fields/analysis/sources-variant-6-2.json +307 -0
  362. chronaxis/guide/en/fields/analysis/sources-variant-6-3.json +312 -0
  363. chronaxis/guide/en/fields/analysis/sources-variant-6-4.json +113 -0
  364. chronaxis/guide/en/fields/analysis/windows-all-1.json +361 -0
  365. chronaxis/guide/en/fields/analysis/windows-all-2.json +187 -0
  366. chronaxis/guide/en/fields/batch/backend-all-1.json +26 -0
  367. chronaxis/guide/en/fields/batch/batch_version-all-1.json +22 -0
  368. chronaxis/guide/en/fields/batch/fail_fast-all-1.json +22 -0
  369. chronaxis/guide/en/fields/batch/inputs-all-1.json +161 -0
  370. chronaxis/guide/en/fields/batch/jobs-all-1.json +58 -0
  371. chronaxis/guide/en/fields/batch/process_count-all-1.json +23 -0
  372. chronaxis/guide/en/fields/batch/source-all-1.json +22 -0
  373. chronaxis/guide/en/fields/batch/template-all-1.json +22 -0
  374. chronaxis/guide/en/fields/definitions/derived-all-1.json +121 -0
  375. chronaxis/guide/en/fields/definitions/signals-all-1.json +336 -0
  376. chronaxis/guide/en/fields/definitions/signals-all-2.json +307 -0
  377. chronaxis/guide/en/fields/definitions/signals-all-3.json +290 -0
  378. chronaxis/guide/en/fields/definitions/signals-all-4.json +347 -0
  379. chronaxis/guide/en/fields/definitions/signals-all-5.json +297 -0
  380. chronaxis/guide/en/fields/definitions/signals-all-6.json +259 -0
  381. chronaxis/guide/en/fields/definitions/signals-all-7.json +117 -0
  382. chronaxis/guide/en/fields/definitions/sources-all-1.json +332 -0
  383. chronaxis/guide/en/fields/definitions/sources-all-10.json +306 -0
  384. chronaxis/guide/en/fields/definitions/sources-all-11.json +294 -0
  385. chronaxis/guide/en/fields/definitions/sources-all-12.json +212 -0
  386. chronaxis/guide/en/fields/definitions/sources-all-13.json +217 -0
  387. chronaxis/guide/en/fields/definitions/sources-all-14.json +213 -0
  388. chronaxis/guide/en/fields/definitions/sources-all-15.json +214 -0
  389. chronaxis/guide/en/fields/definitions/sources-all-16.json +215 -0
  390. chronaxis/guide/en/fields/definitions/sources-all-17.json +213 -0
  391. chronaxis/guide/en/fields/definitions/sources-all-18.json +214 -0
  392. chronaxis/guide/en/fields/definitions/sources-all-19.json +213 -0
  393. chronaxis/guide/en/fields/definitions/sources-all-2.json +323 -0
  394. chronaxis/guide/en/fields/definitions/sources-all-20.json +217 -0
  395. chronaxis/guide/en/fields/definitions/sources-all-21.json +210 -0
  396. chronaxis/guide/en/fields/definitions/sources-all-22.json +214 -0
  397. chronaxis/guide/en/fields/definitions/sources-all-23.json +218 -0
  398. chronaxis/guide/en/fields/definitions/sources-all-24.json +209 -0
  399. chronaxis/guide/en/fields/definitions/sources-all-25.json +215 -0
  400. chronaxis/guide/en/fields/definitions/sources-all-26.json +218 -0
  401. chronaxis/guide/en/fields/definitions/sources-all-27.json +224 -0
  402. chronaxis/guide/en/fields/definitions/sources-all-28.json +215 -0
  403. chronaxis/guide/en/fields/definitions/sources-all-29.json +205 -0
  404. chronaxis/guide/en/fields/definitions/sources-all-3.json +303 -0
  405. chronaxis/guide/en/fields/definitions/sources-all-30.json +298 -0
  406. chronaxis/guide/en/fields/definitions/sources-all-31.json +315 -0
  407. chronaxis/guide/en/fields/definitions/sources-all-32.json +306 -0
  408. chronaxis/guide/en/fields/definitions/sources-all-33.json +209 -0
  409. chronaxis/guide/en/fields/definitions/sources-all-4.json +336 -0
  410. chronaxis/guide/en/fields/definitions/sources-all-5.json +299 -0
  411. chronaxis/guide/en/fields/definitions/sources-all-6.json +307 -0
  412. chronaxis/guide/en/fields/definitions/sources-all-7.json +320 -0
  413. chronaxis/guide/en/fields/definitions/sources-all-8.json +303 -0
  414. chronaxis/guide/en/fields/definitions/sources-all-9.json +315 -0
  415. chronaxis/guide/en/fields/index.json +3450 -0
  416. chronaxis/guide/en/fields/mapping/aliases-all-1.json +91 -0
  417. chronaxis/guide/en/fields/mapping/analysis-all-1.json +382 -0
  418. chronaxis/guide/en/fields/mapping/analysis-all-10.json +301 -0
  419. chronaxis/guide/en/fields/mapping/analysis-all-11.json +312 -0
  420. chronaxis/guide/en/fields/mapping/analysis-all-12.json +311 -0
  421. chronaxis/guide/en/fields/mapping/analysis-all-13.json +314 -0
  422. chronaxis/guide/en/fields/mapping/analysis-all-14.json +310 -0
  423. chronaxis/guide/en/fields/mapping/analysis-all-15.json +297 -0
  424. chronaxis/guide/en/fields/mapping/analysis-all-16.json +226 -0
  425. chronaxis/guide/en/fields/mapping/analysis-all-17.json +230 -0
  426. chronaxis/guide/en/fields/mapping/analysis-all-18.json +227 -0
  427. chronaxis/guide/en/fields/mapping/analysis-all-19.json +230 -0
  428. chronaxis/guide/en/fields/mapping/analysis-all-2.json +323 -0
  429. chronaxis/guide/en/fields/mapping/analysis-all-20.json +221 -0
  430. chronaxis/guide/en/fields/mapping/analysis-all-21.json +237 -0
  431. chronaxis/guide/en/fields/mapping/analysis-all-22.json +233 -0
  432. chronaxis/guide/en/fields/mapping/analysis-all-23.json +222 -0
  433. chronaxis/guide/en/fields/mapping/analysis-all-24.json +231 -0
  434. chronaxis/guide/en/fields/mapping/analysis-all-25.json +225 -0
  435. chronaxis/guide/en/fields/mapping/analysis-all-26.json +230 -0
  436. chronaxis/guide/en/fields/mapping/analysis-all-27.json +228 -0
  437. chronaxis/guide/en/fields/mapping/analysis-all-28.json +230 -0
  438. chronaxis/guide/en/fields/mapping/analysis-all-29.json +223 -0
  439. chronaxis/guide/en/fields/mapping/analysis-all-3.json +318 -0
  440. chronaxis/guide/en/fields/mapping/analysis-all-30.json +233 -0
  441. chronaxis/guide/en/fields/mapping/analysis-all-31.json +218 -0
  442. chronaxis/guide/en/fields/mapping/analysis-all-32.json +239 -0
  443. chronaxis/guide/en/fields/mapping/analysis-all-33.json +233 -0
  444. chronaxis/guide/en/fields/mapping/analysis-all-34.json +225 -0
  445. chronaxis/guide/en/fields/mapping/analysis-all-35.json +228 -0
  446. chronaxis/guide/en/fields/mapping/analysis-all-36.json +226 -0
  447. chronaxis/guide/en/fields/mapping/analysis-all-37.json +244 -0
  448. chronaxis/guide/en/fields/mapping/analysis-all-38.json +326 -0
  449. chronaxis/guide/en/fields/mapping/analysis-all-39.json +299 -0
  450. chronaxis/guide/en/fields/mapping/analysis-all-4.json +297 -0
  451. chronaxis/guide/en/fields/mapping/analysis-all-40.json +311 -0
  452. chronaxis/guide/en/fields/mapping/analysis-all-41.json +310 -0
  453. chronaxis/guide/en/fields/mapping/analysis-all-42.json +346 -0
  454. chronaxis/guide/en/fields/mapping/analysis-all-43.json +267 -0
  455. chronaxis/guide/en/fields/mapping/analysis-all-44.json +327 -0
  456. chronaxis/guide/en/fields/mapping/analysis-all-45.json +279 -0
  457. chronaxis/guide/en/fields/mapping/analysis-all-46.json +327 -0
  458. chronaxis/guide/en/fields/mapping/analysis-all-47.json +312 -0
  459. chronaxis/guide/en/fields/mapping/analysis-all-48.json +294 -0
  460. chronaxis/guide/en/fields/mapping/analysis-all-49.json +253 -0
  461. chronaxis/guide/en/fields/mapping/analysis-all-5.json +312 -0
  462. chronaxis/guide/en/fields/mapping/analysis-all-50.json +346 -0
  463. chronaxis/guide/en/fields/mapping/analysis-all-51.json +324 -0
  464. chronaxis/guide/en/fields/mapping/analysis-all-52.json +335 -0
  465. chronaxis/guide/en/fields/mapping/analysis-all-53.json +317 -0
  466. chronaxis/guide/en/fields/mapping/analysis-all-54.json +316 -0
  467. chronaxis/guide/en/fields/mapping/analysis-all-55.json +302 -0
  468. chronaxis/guide/en/fields/mapping/analysis-all-56.json +319 -0
  469. chronaxis/guide/en/fields/mapping/analysis-all-57.json +334 -0
  470. chronaxis/guide/en/fields/mapping/analysis-all-58.json +333 -0
  471. chronaxis/guide/en/fields/mapping/analysis-all-59.json +359 -0
  472. chronaxis/guide/en/fields/mapping/analysis-all-6.json +327 -0
  473. chronaxis/guide/en/fields/mapping/analysis-all-60.json +303 -0
  474. chronaxis/guide/en/fields/mapping/analysis-all-61.json +326 -0
  475. chronaxis/guide/en/fields/mapping/analysis-all-62.json +301 -0
  476. chronaxis/guide/en/fields/mapping/analysis-all-63.json +326 -0
  477. chronaxis/guide/en/fields/mapping/analysis-all-64.json +367 -0
  478. chronaxis/guide/en/fields/mapping/analysis-all-65.json +334 -0
  479. chronaxis/guide/en/fields/mapping/analysis-all-66.json +358 -0
  480. chronaxis/guide/en/fields/mapping/analysis-all-67.json +349 -0
  481. chronaxis/guide/en/fields/mapping/analysis-all-68.json +331 -0
  482. chronaxis/guide/en/fields/mapping/analysis-all-69.json +304 -0
  483. chronaxis/guide/en/fields/mapping/analysis-all-7.json +303 -0
  484. chronaxis/guide/en/fields/mapping/analysis-all-70.json +349 -0
  485. chronaxis/guide/en/fields/mapping/analysis-all-71.json +288 -0
  486. chronaxis/guide/en/fields/mapping/analysis-all-72.json +316 -0
  487. chronaxis/guide/en/fields/mapping/analysis-all-73.json +266 -0
  488. chronaxis/guide/en/fields/mapping/analysis-all-74.json +227 -0
  489. chronaxis/guide/en/fields/mapping/analysis-all-75.json +239 -0
  490. chronaxis/guide/en/fields/mapping/analysis-all-76.json +274 -0
  491. chronaxis/guide/en/fields/mapping/analysis-all-77.json +380 -0
  492. chronaxis/guide/en/fields/mapping/analysis-all-78.json +313 -0
  493. chronaxis/guide/en/fields/mapping/analysis-all-79.json +338 -0
  494. chronaxis/guide/en/fields/mapping/analysis-all-8.json +292 -0
  495. chronaxis/guide/en/fields/mapping/analysis-all-80.json +304 -0
  496. chronaxis/guide/en/fields/mapping/analysis-all-81.json +140 -0
  497. chronaxis/guide/en/fields/mapping/analysis-all-9.json +332 -0
  498. chronaxis/guide/en/fields/mapping/io_policy-all-1.json +19 -0
  499. chronaxis/guide/en/fields/mapping/mapping_version-all-1.json +19 -0
  500. chronaxis/guide/en/fields/mapping/path_base-all-1.json +19 -0
  501. chronaxis/guide/en/fields/mapping/sections-all-1.json +402 -0
  502. chronaxis/guide/en/fields/mapping/sections-all-2.json +232 -0
  503. chronaxis/guide/en/semantics.md +70 -0
  504. chronaxis/guide/en/tasks.json +290 -0
  505. chronaxis/guide/en/workflow.md +41 -0
  506. chronaxis/guide/examples/analysis-imports.yaml +22 -0
  507. chronaxis/guide/examples/analysis.yaml +23 -0
  508. chronaxis/guide/examples/batch-minimal.yaml +3 -0
  509. chronaxis/guide/examples/conditional-energy.yaml +36 -0
  510. chronaxis/guide/examples/data/activity.csv +6 -0
  511. chronaxis/guide/examples/data/can/activity.asc +11 -0
  512. chronaxis/guide/examples/data/can/calculation-minimal.asc +6 -0
  513. chronaxis/guide/examples/data/can/conditional-energy/part-1.asc +92 -0
  514. chronaxis/guide/examples/data/can/conditional-energy/part-2.asc +36 -0
  515. chronaxis/guide/examples/data/can/conditional-energy/signals.dbc +10 -0
  516. chronaxis/guide/examples/data/can/execution-comparison.asc +12 -0
  517. chronaxis/guide/examples/data/can/execution-report.asc +4 -0
  518. chronaxis/guide/examples/data/can/expression-extended.asc +11 -0
  519. chronaxis/guide/examples/data/can/interval-actual.asc +6 -0
  520. chronaxis/guide/examples/data/can/preset-charge-energy.asc +15 -0
  521. chronaxis/guide/examples/data/can/preset-charge-minimal.asc +11 -0
  522. chronaxis/guide/examples/data/can/preset-command-automatic.asc +11 -0
  523. chronaxis/guide/examples/data/can/preset-fault-context.asc +7 -0
  524. chronaxis/guide/examples/data/can/preset-threshold-delay.asc +7 -0
  525. chronaxis/guide/examples/data/can/preset-threshold-partial.asc +7 -0
  526. chronaxis/guide/examples/data/can/quality.asc +6 -0
  527. chronaxis/guide/examples/data/can/quickstart-parts/part1.asc +4 -0
  528. chronaxis/guide/examples/data/can/quickstart-parts/part2.asc +3 -0
  529. chronaxis/guide/examples/data/can/quickstart.asc +6 -0
  530. chronaxis/guide/examples/data/can/rule-condition_duration-extended.asc +35 -0
  531. chronaxis/guide/examples/data/can/rule-condition_duration-minimal.asc +19 -0
  532. chronaxis/guide/examples/data/can/rule-debounce-extended.asc +19 -0
  533. chronaxis/guide/examples/data/can/rule-falling_edge-extended.asc +35 -0
  534. chronaxis/guide/examples/data/can/rule-hysteresis-extended.asc +19 -0
  535. chronaxis/guide/examples/data/can/rule-start_end-extended.asc +35 -0
  536. chronaxis/guide/examples/data/can/rule-start_only-extended.asc +19 -0
  537. chronaxis/guide/examples/data/can/rule-window_delta-extended.asc +19 -0
  538. chronaxis/guide/examples/data/can/sequence-outcomes.asc +19 -0
  539. chronaxis/guide/examples/data/can/signal-types.asc +13 -0
  540. chronaxis/guide/examples/data/can/start-end.asc +30 -0
  541. chronaxis/guide/examples/data/can/teaching-base.asc +58 -0
  542. chronaxis/guide/examples/data/can/teaching.dbc +53 -0
  543. chronaxis/guide/examples/data/joint-current.csv +5 -0
  544. chronaxis/guide/examples/data/joint.asc +5 -0
  545. chronaxis/guide/examples/data/joint.dbc +7 -0
  546. chronaxis/guide/examples/data/message-clean.asc +6 -0
  547. chronaxis/guide/examples/data/message-direction.asc +7 -0
  548. chronaxis/guide/examples/data/message-direction.dbc +7 -0
  549. chronaxis/guide/examples/data/power.blf +0 -0
  550. chronaxis/guide/examples/data/power.dbc +6 -0
  551. chronaxis/guide/examples/data/quality.csv +5 -0
  552. chronaxis/guide/examples/data/raw-can-example.csv +5 -0
  553. chronaxis/guide/examples/data/signal-types.csv +5 -0
  554. chronaxis/guide/examples/data/signals.csv +6 -0
  555. chronaxis/guide/examples/data/timing.asc +6 -0
  556. chronaxis/guide/examples/data/timing.dbc +11 -0
  557. chronaxis/guide/examples/export-can-minimal.yaml +24 -0
  558. chronaxis/guide/examples/input-csv-minimal.yaml +23 -0
  559. chronaxis/guide/examples/preset-charge-energy.yaml +41 -0
  560. chronaxis/guide/examples/preset-charge-minimal.yaml +31 -0
  561. chronaxis/guide/examples/preset-fault-context.yaml +27 -0
  562. chronaxis/guide/examples/quality.yaml +19 -0
  563. chronaxis/guide/examples/sequence-outcomes.yaml +39 -0
  564. chronaxis/guide/examples/shared/definitions.yaml +14 -0
  565. chronaxis/guide/examples/signal-enum-minimal.yaml +19 -0
  566. chronaxis/guide/examples/signal-number-conversion.yaml +24 -0
  567. chronaxis/guide/examples/window-merged.yaml +28 -0
  568. chronaxis/guide/fields/analysis/calculations-all-1.json +360 -0
  569. chronaxis/guide/fields/analysis/calculations-all-2.json +299 -0
  570. chronaxis/guide/fields/analysis/calculations-all-3.json +70 -0
  571. chronaxis/guide/fields/analysis/config_version-all-1.json +24 -0
  572. chronaxis/guide/fields/analysis/dataset-all-1.json +344 -0
  573. chronaxis/guide/fields/analysis/derived-all-1.json +147 -0
  574. chronaxis/guide/fields/analysis/diagnostics-all-1.json +69 -0
  575. chronaxis/guide/fields/analysis/execution-all-1.json +167 -0
  576. chronaxis/guide/fields/analysis/imports-all-1.json +39 -0
  577. chronaxis/guide/fields/analysis/intervals-all-1.json +173 -0
  578. chronaxis/guide/fields/analysis/message_calculations-all-1.json +360 -0
  579. chronaxis/guide/fields/analysis/message_calculations-all-2.json +255 -0
  580. chronaxis/guide/fields/analysis/metadata-all-1.json +24 -0
  581. chronaxis/guide/fields/analysis/outputs-all-1.json +349 -0
  582. chronaxis/guide/fields/analysis/outputs-all-2.json +302 -0
  583. chronaxis/guide/fields/analysis/outputs-all-3.json +321 -0
  584. chronaxis/guide/fields/analysis/outputs-all-4.json +253 -0
  585. chronaxis/guide/fields/analysis/outputs-all-5.json +244 -0
  586. chronaxis/guide/fields/analysis/outputs-all-6.json +199 -0
  587. chronaxis/guide/fields/analysis/overrides-all-1.json +104 -0
  588. chronaxis/guide/fields/analysis/presets-charge_session-1.json +398 -0
  589. chronaxis/guide/fields/analysis/presets-charge_session-2.json +114 -0
  590. chronaxis/guide/fields/analysis/presets-command_response-1.json +376 -0
  591. chronaxis/guide/fields/analysis/presets-fault_context-1.json +285 -0
  592. chronaxis/guide/fields/analysis/presets-threshold_excursion-1.json +317 -0
  593. chronaxis/guide/fields/analysis/rules-condition_duration-1.json +344 -0
  594. chronaxis/guide/fields/analysis/rules-condition_duration-2.json +27 -0
  595. chronaxis/guide/fields/analysis/rules-debounce-1.json +335 -0
  596. chronaxis/guide/fields/analysis/rules-falling_edge-1.json +317 -0
  597. chronaxis/guide/fields/analysis/rules-hysteresis-1.json +334 -0
  598. chronaxis/guide/fields/analysis/rules-hysteresis-2.json +27 -0
  599. chronaxis/guide/fields/analysis/rules-rising_edge-1.json +317 -0
  600. chronaxis/guide/fields/analysis/rules-start_end-1.json +349 -0
  601. chronaxis/guide/fields/analysis/rules-start_end-2.json +44 -0
  602. chronaxis/guide/fields/analysis/rules-start_only-1.json +334 -0
  603. chronaxis/guide/fields/analysis/rules-window_delta-1.json +356 -0
  604. chronaxis/guide/fields/analysis/rules-window_delta-2.json +78 -0
  605. chronaxis/guide/fields/analysis/sequences-all-1.json +394 -0
  606. chronaxis/guide/fields/analysis/sequences-all-2.json +54 -0
  607. chronaxis/guide/fields/analysis/signals-variant-1-1.json +349 -0
  608. chronaxis/guide/fields/analysis/signals-variant-1-2.json +297 -0
  609. chronaxis/guide/fields/analysis/signals-variant-1-3.json +340 -0
  610. chronaxis/guide/fields/analysis/signals-variant-1-4.json +175 -0
  611. chronaxis/guide/fields/analysis/signals-variant-2-1.json +327 -0
  612. chronaxis/guide/fields/analysis/signals-variant-2-2.json +335 -0
  613. chronaxis/guide/fields/analysis/signals-variant-2-3.json +289 -0
  614. chronaxis/guide/fields/analysis/signals-variant-2-4.json +270 -0
  615. chronaxis/guide/fields/analysis/sources-can_asc-1.json +318 -0
  616. chronaxis/guide/fields/analysis/sources-can_asc-2.json +310 -0
  617. chronaxis/guide/fields/analysis/sources-can_asc-3.json +311 -0
  618. chronaxis/guide/fields/analysis/sources-can_asc-4.json +69 -0
  619. chronaxis/guide/fields/analysis/sources-can_blf-1.json +317 -0
  620. chronaxis/guide/fields/analysis/sources-can_blf-2.json +310 -0
  621. chronaxis/guide/fields/analysis/sources-can_blf-3.json +317 -0
  622. chronaxis/guide/fields/analysis/sources-memory-1.json +347 -0
  623. chronaxis/guide/fields/analysis/sources-memory-2.json +110 -0
  624. chronaxis/guide/fields/analysis/sources-raw_can_csv-1.json +330 -0
  625. chronaxis/guide/fields/analysis/sources-raw_can_csv-10.json +233 -0
  626. chronaxis/guide/fields/analysis/sources-raw_can_csv-11.json +225 -0
  627. chronaxis/guide/fields/analysis/sources-raw_can_csv-12.json +228 -0
  628. chronaxis/guide/fields/analysis/sources-raw_can_csv-13.json +226 -0
  629. chronaxis/guide/fields/analysis/sources-raw_can_csv-14.json +230 -0
  630. chronaxis/guide/fields/analysis/sources-raw_can_csv-15.json +227 -0
  631. chronaxis/guide/fields/analysis/sources-raw_can_csv-16.json +230 -0
  632. chronaxis/guide/fields/analysis/sources-raw_can_csv-17.json +238 -0
  633. chronaxis/guide/fields/analysis/sources-raw_can_csv-18.json +239 -0
  634. chronaxis/guide/fields/analysis/sources-raw_can_csv-19.json +233 -0
  635. chronaxis/guide/fields/analysis/sources-raw_can_csv-2.json +310 -0
  636. chronaxis/guide/fields/analysis/sources-raw_can_csv-20.json +242 -0
  637. chronaxis/guide/fields/analysis/sources-raw_can_csv-21.json +230 -0
  638. chronaxis/guide/fields/analysis/sources-raw_can_csv-22.json +228 -0
  639. chronaxis/guide/fields/analysis/sources-raw_can_csv-23.json +230 -0
  640. chronaxis/guide/fields/analysis/sources-raw_can_csv-24.json +223 -0
  641. chronaxis/guide/fields/analysis/sources-raw_can_csv-25.json +290 -0
  642. chronaxis/guide/fields/analysis/sources-raw_can_csv-26.json +131 -0
  643. chronaxis/guide/fields/analysis/sources-raw_can_csv-3.json +297 -0
  644. chronaxis/guide/fields/analysis/sources-raw_can_csv-4.json +226 -0
  645. chronaxis/guide/fields/analysis/sources-raw_can_csv-5.json +230 -0
  646. chronaxis/guide/fields/analysis/sources-raw_can_csv-6.json +227 -0
  647. chronaxis/guide/fields/analysis/sources-raw_can_csv-7.json +230 -0
  648. chronaxis/guide/fields/analysis/sources-raw_can_csv-8.json +238 -0
  649. chronaxis/guide/fields/analysis/sources-raw_can_csv-9.json +239 -0
  650. chronaxis/guide/fields/analysis/sources-signal_csv-1.json +331 -0
  651. chronaxis/guide/fields/analysis/sources-signal_csv-2.json +327 -0
  652. chronaxis/guide/fields/analysis/sources-signal_csv-3.json +295 -0
  653. chronaxis/guide/fields/analysis/sources-variant-6-1.json +319 -0
  654. chronaxis/guide/fields/analysis/sources-variant-6-2.json +327 -0
  655. chronaxis/guide/fields/analysis/sources-variant-6-3.json +314 -0
  656. chronaxis/guide/fields/analysis/sources-variant-6-4.json +91 -0
  657. chronaxis/guide/fields/analysis/windows-all-1.json +361 -0
  658. chronaxis/guide/fields/analysis/windows-all-2.json +187 -0
  659. chronaxis/guide/fields/batch/backend-all-1.json +26 -0
  660. chronaxis/guide/fields/batch/batch_version-all-1.json +22 -0
  661. chronaxis/guide/fields/batch/fail_fast-all-1.json +22 -0
  662. chronaxis/guide/fields/batch/inputs-all-1.json +161 -0
  663. chronaxis/guide/fields/batch/jobs-all-1.json +58 -0
  664. chronaxis/guide/fields/batch/process_count-all-1.json +23 -0
  665. chronaxis/guide/fields/batch/source-all-1.json +22 -0
  666. chronaxis/guide/fields/batch/template-all-1.json +22 -0
  667. chronaxis/guide/fields/definitions/derived-all-1.json +121 -0
  668. chronaxis/guide/fields/definitions/signals-all-1.json +336 -0
  669. chronaxis/guide/fields/definitions/signals-all-2.json +307 -0
  670. chronaxis/guide/fields/definitions/signals-all-3.json +305 -0
  671. chronaxis/guide/fields/definitions/signals-all-4.json +352 -0
  672. chronaxis/guide/fields/definitions/signals-all-5.json +311 -0
  673. chronaxis/guide/fields/definitions/signals-all-6.json +254 -0
  674. chronaxis/guide/fields/definitions/signals-all-7.json +88 -0
  675. chronaxis/guide/fields/definitions/sources-all-1.json +332 -0
  676. chronaxis/guide/fields/definitions/sources-all-10.json +307 -0
  677. chronaxis/guide/fields/definitions/sources-all-11.json +280 -0
  678. chronaxis/guide/fields/definitions/sources-all-12.json +209 -0
  679. chronaxis/guide/fields/definitions/sources-all-13.json +219 -0
  680. chronaxis/guide/fields/definitions/sources-all-14.json +213 -0
  681. chronaxis/guide/fields/definitions/sources-all-15.json +212 -0
  682. chronaxis/guide/fields/definitions/sources-all-16.json +232 -0
  683. chronaxis/guide/fields/definitions/sources-all-17.json +213 -0
  684. chronaxis/guide/fields/definitions/sources-all-18.json +212 -0
  685. chronaxis/guide/fields/definitions/sources-all-19.json +217 -0
  686. chronaxis/guide/fields/definitions/sources-all-2.json +336 -0
  687. chronaxis/guide/fields/definitions/sources-all-20.json +228 -0
  688. chronaxis/guide/fields/definitions/sources-all-21.json +212 -0
  689. chronaxis/guide/fields/definitions/sources-all-22.json +232 -0
  690. chronaxis/guide/fields/definitions/sources-all-23.json +213 -0
  691. chronaxis/guide/fields/definitions/sources-all-24.json +212 -0
  692. chronaxis/guide/fields/definitions/sources-all-25.json +217 -0
  693. chronaxis/guide/fields/definitions/sources-all-26.json +228 -0
  694. chronaxis/guide/fields/definitions/sources-all-27.json +212 -0
  695. chronaxis/guide/fields/definitions/sources-all-28.json +217 -0
  696. chronaxis/guide/fields/definitions/sources-all-29.json +227 -0
  697. chronaxis/guide/fields/definitions/sources-all-3.json +306 -0
  698. chronaxis/guide/fields/definitions/sources-all-30.json +333 -0
  699. chronaxis/guide/fields/definitions/sources-all-31.json +302 -0
  700. chronaxis/guide/fields/definitions/sources-all-32.json +319 -0
  701. chronaxis/guide/fields/definitions/sources-all-33.json +89 -0
  702. chronaxis/guide/fields/definitions/sources-all-4.json +336 -0
  703. chronaxis/guide/fields/definitions/sources-all-5.json +313 -0
  704. chronaxis/guide/fields/definitions/sources-all-6.json +292 -0
  705. chronaxis/guide/fields/definitions/sources-all-7.json +319 -0
  706. chronaxis/guide/fields/definitions/sources-all-8.json +315 -0
  707. chronaxis/guide/fields/definitions/sources-all-9.json +318 -0
  708. chronaxis/guide/fields/index.json +3432 -0
  709. chronaxis/guide/fields/mapping/aliases-all-1.json +91 -0
  710. chronaxis/guide/fields/mapping/analysis-all-1.json +382 -0
  711. chronaxis/guide/fields/mapping/analysis-all-10.json +307 -0
  712. chronaxis/guide/fields/mapping/analysis-all-11.json +311 -0
  713. chronaxis/guide/fields/mapping/analysis-all-12.json +329 -0
  714. chronaxis/guide/fields/mapping/analysis-all-13.json +294 -0
  715. chronaxis/guide/fields/mapping/analysis-all-14.json +311 -0
  716. chronaxis/guide/fields/mapping/analysis-all-15.json +280 -0
  717. chronaxis/guide/fields/mapping/analysis-all-16.json +227 -0
  718. chronaxis/guide/fields/mapping/analysis-all-17.json +230 -0
  719. chronaxis/guide/fields/mapping/analysis-all-18.json +221 -0
  720. chronaxis/guide/fields/mapping/analysis-all-19.json +237 -0
  721. chronaxis/guide/fields/mapping/analysis-all-2.json +342 -0
  722. chronaxis/guide/fields/mapping/analysis-all-20.json +233 -0
  723. chronaxis/guide/fields/mapping/analysis-all-21.json +244 -0
  724. chronaxis/guide/fields/mapping/analysis-all-22.json +228 -0
  725. chronaxis/guide/fields/mapping/analysis-all-23.json +226 -0
  726. chronaxis/guide/fields/mapping/analysis-all-24.json +230 -0
  727. chronaxis/guide/fields/mapping/analysis-all-25.json +227 -0
  728. chronaxis/guide/fields/mapping/analysis-all-26.json +230 -0
  729. chronaxis/guide/fields/mapping/analysis-all-27.json +221 -0
  730. chronaxis/guide/fields/mapping/analysis-all-28.json +237 -0
  731. chronaxis/guide/fields/mapping/analysis-all-29.json +233 -0
  732. chronaxis/guide/fields/mapping/analysis-all-3.json +322 -0
  733. chronaxis/guide/fields/mapping/analysis-all-30.json +244 -0
  734. chronaxis/guide/fields/mapping/analysis-all-31.json +228 -0
  735. chronaxis/guide/fields/mapping/analysis-all-32.json +226 -0
  736. chronaxis/guide/fields/mapping/analysis-all-33.json +230 -0
  737. chronaxis/guide/fields/mapping/analysis-all-34.json +227 -0
  738. chronaxis/guide/fields/mapping/analysis-all-35.json +230 -0
  739. chronaxis/guide/fields/mapping/analysis-all-36.json +221 -0
  740. chronaxis/guide/fields/mapping/analysis-all-37.json +291 -0
  741. chronaxis/guide/fields/mapping/analysis-all-38.json +315 -0
  742. chronaxis/guide/fields/mapping/analysis-all-39.json +304 -0
  743. chronaxis/guide/fields/mapping/analysis-all-4.json +312 -0
  744. chronaxis/guide/fields/mapping/analysis-all-40.json +307 -0
  745. chronaxis/guide/fields/mapping/analysis-all-41.json +292 -0
  746. chronaxis/guide/fields/mapping/analysis-all-42.json +333 -0
  747. chronaxis/guide/fields/mapping/analysis-all-43.json +296 -0
  748. chronaxis/guide/fields/mapping/analysis-all-44.json +322 -0
  749. chronaxis/guide/fields/mapping/analysis-all-45.json +298 -0
  750. chronaxis/guide/fields/mapping/analysis-all-46.json +347 -0
  751. chronaxis/guide/fields/mapping/analysis-all-47.json +302 -0
  752. chronaxis/guide/fields/mapping/analysis-all-48.json +263 -0
  753. chronaxis/guide/fields/mapping/analysis-all-49.json +301 -0
  754. chronaxis/guide/fields/mapping/analysis-all-5.json +313 -0
  755. chronaxis/guide/fields/mapping/analysis-all-50.json +336 -0
  756. chronaxis/guide/fields/mapping/analysis-all-51.json +335 -0
  757. chronaxis/guide/fields/mapping/analysis-all-52.json +335 -0
  758. chronaxis/guide/fields/mapping/analysis-all-53.json +334 -0
  759. chronaxis/guide/fields/mapping/analysis-all-54.json +339 -0
  760. chronaxis/guide/fields/mapping/analysis-all-55.json +335 -0
  761. chronaxis/guide/fields/mapping/analysis-all-56.json +336 -0
  762. chronaxis/guide/fields/mapping/analysis-all-57.json +335 -0
  763. chronaxis/guide/fields/mapping/analysis-all-58.json +333 -0
  764. chronaxis/guide/fields/mapping/analysis-all-59.json +325 -0
  765. chronaxis/guide/fields/mapping/analysis-all-6.json +323 -0
  766. chronaxis/guide/fields/mapping/analysis-all-60.json +334 -0
  767. chronaxis/guide/fields/mapping/analysis-all-61.json +311 -0
  768. chronaxis/guide/fields/mapping/analysis-all-62.json +289 -0
  769. chronaxis/guide/fields/mapping/analysis-all-63.json +383 -0
  770. chronaxis/guide/fields/mapping/analysis-all-64.json +357 -0
  771. chronaxis/guide/fields/mapping/analysis-all-65.json +375 -0
  772. chronaxis/guide/fields/mapping/analysis-all-66.json +351 -0
  773. chronaxis/guide/fields/mapping/analysis-all-67.json +346 -0
  774. chronaxis/guide/fields/mapping/analysis-all-68.json +317 -0
  775. chronaxis/guide/fields/mapping/analysis-all-69.json +313 -0
  776. chronaxis/guide/fields/mapping/analysis-all-7.json +306 -0
  777. chronaxis/guide/fields/mapping/analysis-all-70.json +326 -0
  778. chronaxis/guide/fields/mapping/analysis-all-71.json +316 -0
  779. chronaxis/guide/fields/mapping/analysis-all-72.json +264 -0
  780. chronaxis/guide/fields/mapping/analysis-all-73.json +228 -0
  781. chronaxis/guide/fields/mapping/analysis-all-74.json +241 -0
  782. chronaxis/guide/fields/mapping/analysis-all-75.json +273 -0
  783. chronaxis/guide/fields/mapping/analysis-all-76.json +382 -0
  784. chronaxis/guide/fields/mapping/analysis-all-77.json +328 -0
  785. chronaxis/guide/fields/mapping/analysis-all-78.json +338 -0
  786. chronaxis/guide/fields/mapping/analysis-all-79.json +304 -0
  787. chronaxis/guide/fields/mapping/analysis-all-8.json +309 -0
  788. chronaxis/guide/fields/mapping/analysis-all-80.json +140 -0
  789. chronaxis/guide/fields/mapping/analysis-all-9.json +329 -0
  790. chronaxis/guide/fields/mapping/io_policy-all-1.json +19 -0
  791. chronaxis/guide/fields/mapping/mapping_version-all-1.json +19 -0
  792. chronaxis/guide/fields/mapping/path_base-all-1.json +19 -0
  793. chronaxis/guide/fields/mapping/sections-all-1.json +402 -0
  794. chronaxis/guide/fields/mapping/sections-all-2.json +232 -0
  795. chronaxis/guide/lookup.py +90 -0
  796. chronaxis/guide/manifest.json +614 -0
  797. chronaxis/guide/run_analysis.py +34 -0
  798. chronaxis/guide/schemas/analysis.schema.json +8451 -0
  799. chronaxis/guide/schemas/batch-v1.schema.json +172 -0
  800. chronaxis/guide/schemas/definitions.schema.json +29 -0
  801. chronaxis/guide/schemas/legacy-import-v1.schema.json +143 -0
  802. chronaxis/guide/semantics.md +62 -0
  803. chronaxis/guide/tasks.json +290 -0
  804. chronaxis/guide/workflow.md +38 -0
  805. chronaxis/legacy/__init__.py +46 -0
  806. chronaxis/legacy/batch_read.py +412 -0
  807. chronaxis/legacy/batch_validation.py +134 -0
  808. chronaxis/legacy/batch_wire.py +264 -0
  809. chronaxis/legacy/batch_worker.py +417 -0
  810. chronaxis/legacy/binding.py +154 -0
  811. chronaxis/legacy/dbc_cycles.py +47 -0
  812. chronaxis/legacy/decode_policy.py +28 -0
  813. chronaxis/legacy/disjunction.py +79 -0
  814. chronaxis/legacy/export_worker.py +147 -0
  815. chronaxis/legacy/export_write.py +378 -0
  816. chronaxis/legacy/message_store.py +562 -0
  817. chronaxis/legacy/process_read.py +526 -0
  818. chronaxis/legacy/raw_store.py +341 -0
  819. chronaxis/legacy/records.py +709 -0
  820. chronaxis/legacy/required_records.py +122 -0
  821. chronaxis/legacy/sum.py +403 -0
  822. chronaxis/legacy/sum_numpy.py +43 -0
  823. chronaxis/legacy/updates.py +277 -0
  824. chronaxis/legacy/vendor/sclicer_functions_v1/can_processor.py +138 -0
  825. chronaxis/legacy/vendor/sclicer_functions_v1/file_processor.py +218 -0
  826. chronaxis/legacy/vendor/sclicer_functions_v1/utils.py +447 -0
  827. chronaxis/legacy/worker.py +352 -0
  828. chronaxis/migration/__init__.py +11 -0
  829. chronaxis/migration/api.py +270 -0
  830. chronaxis/migration/ini.py +110 -0
  831. chronaxis/migration/legacy_sum.py +304 -0
  832. chronaxis/migration/models.py +83 -0
  833. chronaxis/migration/publication.py +230 -0
  834. chronaxis/migration/sclicer.py +241 -0
  835. chronaxis/migration/slope.py +78 -0
  836. chronaxis/migration/start_end.py +48 -0
  837. chronaxis/planning/__init__.py +19 -0
  838. chronaxis/planning/channels.py +30 -0
  839. chronaxis/planning/checks.py +447 -0
  840. chronaxis/planning/config_groups.py +215 -0
  841. chronaxis/planning/dbc_defaults.py +88 -0
  842. chronaxis/planning/export_options.py +247 -0
  843. chronaxis/planning/input_defaults.py +62 -0
  844. chronaxis/planning/input_paths.py +124 -0
  845. chronaxis/planning/input_requirements.py +250 -0
  846. chronaxis/planning/inputs.py +306 -0
  847. chronaxis/planning/intervals.py +242 -0
  848. chronaxis/planning/legacy_execution.py +38 -0
  849. chronaxis/planning/message_calculations.py +171 -0
  850. chronaxis/planning/models.py +369 -0
  851. chronaxis/planning/nodes.py +355 -0
  852. chronaxis/planning/plan.py +294 -0
  853. chronaxis/planning/preset_models.py +211 -0
  854. chronaxis/planning/record_execution.py +76 -0
  855. chronaxis/planning/reports.py +523 -0
  856. chronaxis/planning/sequences.py +91 -0
  857. chronaxis/planning/signal_groups.py +61 -0
  858. chronaxis/planning/signal_validity.py +46 -0
  859. chronaxis/planning/text_inputs.py +110 -0
  860. chronaxis/presets.py +270 -0
  861. chronaxis/results/__init__.py +34 -0
  862. chronaxis/results/analysis_outcome.py +67 -0
  863. chronaxis/results/artifact_exports.py +212 -0
  864. chronaxis/results/binding_metadata.py +54 -0
  865. chronaxis/results/budget.py +138 -0
  866. chronaxis/results/can_metadata.py +247 -0
  867. chronaxis/results/codec.py +246 -0
  868. chronaxis/results/errors.py +25 -0
  869. chronaxis/results/export_files.py +216 -0
  870. chronaxis/results/frame_exports.py +299 -0
  871. chronaxis/results/frame_patch.py +186 -0
  872. chronaxis/results/frame_rebuild.py +458 -0
  873. chronaxis/results/frame_sort.py +223 -0
  874. chronaxis/results/input_diagnostics.py +51 -0
  875. chronaxis/results/manifest.py +343 -0
  876. chronaxis/results/merge_runs.py +207 -0
  877. chronaxis/results/owned_directory.py +32 -0
  878. chronaxis/results/raw_exports.py +689 -0
  879. chronaxis/results/readable.py +355 -0
  880. chronaxis/results/readable_format.py +94 -0
  881. chronaxis/results/result.py +734 -0
  882. chronaxis/results/rows.py +57 -0
  883. chronaxis/results/signal_exports.py +209 -0
  884. chronaxis/results/signal_grid.py +219 -0
  885. chronaxis/results/spool.py +421 -0
  886. chronaxis/results/summary.py +350 -0
  887. chronaxis/results/text_can_metadata.py +64 -0
  888. chronaxis/results/threshold_metadata.py +105 -0
  889. chronaxis/runner.py +121 -0
  890. chronaxis/sources/__init__.py +23 -0
  891. chronaxis/sources/asc.py +412 -0
  892. chronaxis/sources/blf.py +101 -0
  893. chronaxis/sources/can_spec.py +179 -0
  894. chronaxis/sources/can_updates.py +160 -0
  895. chronaxis/sources/csv_spec.py +133 -0
  896. chronaxis/sources/descriptions.py +65 -0
  897. chronaxis/sources/memory.py +299 -0
  898. chronaxis/sources/models.py +497 -0
  899. chronaxis/sources/packing.py +170 -0
  900. chronaxis/sources/protocol.py +221 -0
  901. chronaxis/sources/raw_can.py +62 -0
  902. chronaxis/sources/raw_csv.py +470 -0
  903. chronaxis/sources/raw_csv_layout.py +310 -0
  904. chronaxis/sources/signal_csv.py +608 -0
  905. chronaxis/sources/text_can_decode.py +421 -0
  906. chronaxis/sources/text_can_ref.py +48 -0
  907. chronaxis/sources/text_can_spec.py +61 -0
  908. chronaxis/timebase/__init__.py +19 -0
  909. chronaxis/timebase/conversion.py +364 -0
  910. chronaxis/timebase/numpy_ops.py +104 -0
  911. chronaxis/user_guide.py +81 -0
  912. chronaxis-0.1.0.dev0.dist-info/METADATA +172 -0
  913. chronaxis-0.1.0.dev0.dist-info/RECORD +917 -0
  914. chronaxis-0.1.0.dev0.dist-info/WHEEL +5 -0
  915. chronaxis-0.1.0.dev0.dist-info/entry_points.txt +2 -0
  916. chronaxis-0.1.0.dev0.dist-info/licenses/LICENSE +21 -0
  917. chronaxis-0.1.0.dev0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,734 @@
1
+ """从封口暂存提供流式结果,并在完整关闭输出后原子发布新目录。
2
+
3
+ 输入是已排序ResultSpool与运行元数据,输出是行迭代器和OutputManifest。
4
+ CSV与JSONL使用同一套平坦摘要字段,不保留旧row_json或结果包装副本。
5
+ 成功生成表、质量记录及完成清单后才安装目录;已有目标拒绝,失败保留诊断临时目录。
6
+ close关闭所有活跃迭代器且幂等;默认清理自有暂存,保留原输入和已发布文件。无旧reader/decoder/writer调用。
7
+ 当前原子安装通过Windows不覆盖rename实现;其他平台明确拒绝,尚未验收的能力不静默承诺。
8
+ 元数据、读槽和发布workspace分别记录实际RAM费用,预算更新后取消仍可恢复清账。
9
+ 读取器真实handle.close失败保留句柄并拒绝继续读;结果close失败可重试,不提前标closed。
10
+ 上述重试保证只覆盖托管的结果资源,不覆盖任意局部文件句柄的持续关闭失败。
11
+ """
12
+
13
+ # 发布只操作新目录;标准库逐行读写,禁止全量结果列表或目录覆盖。
14
+ import csv
15
+ from contextlib import nullcontext
16
+ from collections.abc import Iterator
17
+ from datetime import datetime, timezone
18
+ import hashlib
19
+ import json
20
+ import os
21
+ from pathlib import Path
22
+ import platform
23
+ import tempfile
24
+ import time
25
+ from .codec import decode_row
26
+ from .errors import (AnalysisError, BudgetExceededError, ResultClosedError, ResultFormatError,
27
+ ContributionsUnavailableError)
28
+ from .manifest import RunMetadata, OutputManifest
29
+ from .artifact_exports import ArtifactExportStore
30
+ from .rows import ResultRow, row_table
31
+ from .summary import (SummaryCalculationRow, SummaryEventRow, SummarySequenceRow, SummarySegmentRow,
32
+ SummaryContributionRow, SummarySelectionRow, SummaryInputRow,
33
+ summary_fields, SUMMARY_MODELS, SUMMARY_COLUMNS)
34
+ from .owned_directory import directory_identity, remove_owned_directory
35
+ from .spool import ResultSpool, _reserve_owned_ram, _release_owned_ram, _cleanup_owner
36
+
37
+
38
+ def _json(value) -> str:
39
+ """生成标准紧凑JSON文本,拒绝非有限普通数字。
40
+
41
+ 参数: value为受控JSON结构。返回: str。异常: ValueError为非法数值。
42
+ """
43
+ try:
44
+ return json.dumps(value, ensure_ascii=False, allow_nan=False, sort_keys=True, separators=(',', ':'))
45
+ finally:
46
+ # 原异常/TB和返回值保持;只断本层临时引用,不清调用方帧。
47
+ value = None
48
+
49
+
50
+ def _row_fields(row) -> dict:
51
+ """提取当前平坦结果的CSV字段;数组单元格使用有限JSON文本。
52
+
53
+ 参数: row为当前登记的结果行。返回: 独立字段字典。异常: 未支持模型或非有限值错误。
54
+ """
55
+ try:
56
+ result = summary_fields(row)
57
+ if 'parent_event_ids' in result:
58
+ result['parent_event_ids'] = _json(result['parent_event_ids'])
59
+ return result
60
+ finally:
61
+ row = result = None
62
+
63
+
64
+ class _DigestOutput:
65
+ """借用本次已打开的输出流,同一次写入计算请求的摘要,不拥有关闭责任。"""
66
+ def __init__(self,stream):
67
+ """建立固定状态。参数: 文本或二进制流。返回: owner。异常: 哈希构造错。"""
68
+ self.stream=stream;self.digest=hashlib.sha256();self.size=0
69
+
70
+ def write(self,payload):
71
+ """完整写出有限payload后更新摘要。参数: str/bytes。返回: 原长度。异常: 短写/IO。"""
72
+ count=self.stream.write(payload)
73
+ if count!=len(payload): raise OSError('结果写入不完整')
74
+ encoded=payload.encode('utf-8') if isinstance(payload,str) else payload
75
+ self.digest.update(encoded);self.size+=len(encoded)
76
+ return count
77
+
78
+ def info(self,name):
79
+ """返回已关闭文件的有限事实。参数: 文件名。返回: dict。异常: 无。"""
80
+ return dict(name=name,bytes=self.size,sha256=self.digest.hexdigest())
81
+
82
+
83
+ class _RowIterator:
84
+ """独立文件位置的有限行迭代器,实际读槽与文件关闭完成后才closed。
85
+
86
+ _reader_cost为实际已付槽位,_held_cost仅含本迭代器费用。关闭中的迭代器仍在
87
+ owner登记中,成功关handle后再抵销费用、解除登记;失败可由本对象或父close重试。
88
+ handle及内部发布模式的_index_handle保留真实文件,所有实际FD关闭后才抵销读槽。
89
+ """
90
+
91
+ def __init__(self, owner, table: str):
92
+ """先预留解码槽位,再打开只读暂存并登记活跃资源。
93
+
94
+ 参数: owner为AnalysisResult,table为固定表名;publication只供内部legacy JSONL发布。
95
+ 返回: 公共typed行迭代器,内部发布模式返回已核完整原行对应的有限质量字段。
96
+ 异常: 预算/IO/取消原样传播;持续清理失败附row_iterator_cleanup_owner,父登记仍可达。
97
+ """
98
+ owner._require_open()
99
+ self.owner, self.table, self.closed = owner, table, False
100
+ # 在首次预约前准备可清理字段;父元数据覆盖这个尚无读槽的小登记引用。
101
+ self._budget, self._held_cost, self._reader_cost = owner.spool.budget, 0, 0
102
+ self.handle, self._handle_closed, self._closing = None, True, False
103
+ self._payload = None
104
+ if table not in owner.spool.tables:
105
+ if table not in SUMMARY_COLUMNS:
106
+ raise ValueError('未注册结果表')
107
+ # 未配置legacy是实际无文件的空游标;不登记FD、不申请读槽,但保close接口。
108
+ self.closed = True
109
+ return
110
+ try:
111
+ owner._iterators.add(self)
112
+ # 所有表都按成功追加实长付款;原编码硬限和读前长度检查继续保留。
113
+ self.max_row_bytes = owner.spool.row_limit(table)
114
+ _reserve_owned_ram(self, owner.spool.reader_cost_for(table), '_reader_cost')
115
+ self.handle = owner.spool.files[table].open('rb', buffering=8192)
116
+ self._handle_closed = False
117
+ except BaseException as exc:
118
+ _cleanup_owner(self, exc, 'row_iterator_cleanup_owner', '结果读取器构造')
119
+ raise
120
+
121
+ def __iter__(self):
122
+ """保持当前读取位置,不重读输入。
123
+
124
+ 参数: 无。返回: self。异常: 无。
125
+ """
126
+ return self
127
+
128
+ def __next__(self) -> ResultRow | dict:
129
+ """读取一条有限行,EOF或异常关闭资源。
130
+
131
+ 参数: 无。返回: 公共typed行或内部认证发布的质量字段。异常: StopIteration/关闭/格式/IO错误。
132
+ """
133
+ self.owner._require_open()
134
+ # 只借出当前严格解码过的一行原字节;下一次推进前释放,不建立结果缓存。
135
+ self._payload = None
136
+ if self.closed:
137
+ raise StopIteration
138
+ # 真实关闭失败或读槽未抵销时禁止继续借出半关闭输入。
139
+ if self._closing:
140
+ raise ResultClosedError('结果读取器正在清理')
141
+ try:
142
+ payload = self.handle.readline(self.max_row_bytes + 1)
143
+ if not payload:
144
+ self.close()
145
+ raise StopIteration
146
+ if (len(payload) > self.max_row_bytes or len(payload) > self.owner.spool.max_row_bytes
147
+ or not payload.endswith(b'\n')):
148
+ raise ResultFormatError('结果行超限或未完整结束')
149
+ row = decode_row(payload, self.max_row_bytes)
150
+ if row_table(row) != self.table:
151
+ raise ResultFormatError('结果暂存表与行类型不一致')
152
+ self._payload = payload
153
+ return row
154
+ except BaseException as exc:
155
+ # 解码/读取原异常优先;清理仍失败时父登记和恢复属性保存实际读取器。
156
+ payload = row = entry = None
157
+ try:
158
+ self.close()
159
+ except BaseException as cleanup:
160
+ exc.add_note(f'结果读取器异常清理附加失败: {type(cleanup).__name__}')
161
+ if not self.closed:
162
+ exc.row_iterator_cleanup_owner = self
163
+ raise
164
+ finally:
165
+ # return已先求值;原TB不额外持有本层行和读取bytes。
166
+ payload = row = entry = None
167
+
168
+ def close(self) -> None:
169
+ """抵销槽位、取消登记并关闭句柄;重复调用安全。
170
+
171
+ 参数: 无。返回: None。异常: 原文件关闭/预算取消传播;失败句柄与未付清费用留待重试。
172
+ """
173
+ if self.closed:
174
+ return
175
+ self._closing = True
176
+ self._payload = None
177
+ try:
178
+ # FD尚未关完时保留handle及读槽;关闭后抛取消也根据真实handle.closed记录状态。
179
+ if self.handle is not None and not self._handle_closed:
180
+ try:
181
+ self.handle.close()
182
+ finally:
183
+ if self.handle.closed:
184
+ self._handle_closed = True
185
+ _release_owned_ram(self, self._reader_cost, '_reader_cost')
186
+ # 未关闭资源的迭代器不能先从父登记消失;解除登记失败可由父再次close。
187
+ self.owner._iterators.discard(self)
188
+ finally:
189
+ if self._handle_closed and self._held_cost == 0 and self not in self.owner._iterators:
190
+ self.closed = True
191
+
192
+
193
+ class AnalysisResult:
194
+ """保存一次分析的结果,提供可关闭的逐行迭代器和结果发布方法。
195
+
196
+ 内部状态初始analysis_complete,发布失败为output_failed/cancelled。
197
+ write可重新导出到全新目录;每次都只读暂存,不重读旧输入。
198
+ 单线程使用;执行输入和旧writer成功关闭的前置条件由AnalysisRunner负责。
199
+ _metadata_cost/_writer_cost为实际已付费用;自有读取器各自持有读槽,spool持有其
200
+ 固定/缓冲/归并费用。close进入_closing后拒绝读取或发布,所有层成功关闭才_closed。
201
+ """
202
+
203
+ def __init__(self, spool: ResultSpool, metadata: RunMetadata, table_format: str = 'csv', *, artifact_exports=None,
204
+ owned_work=None):
205
+ """接管已封口暂存并写analysis_complete内部清单。
206
+
207
+ 参数: spool为已完成暂存,metadata为真实运行事实,table_format为csv/jsonl/readable_csv;
208
+ 低层构造器保留csv默认以兼容现有调用,普通配置与AnalysisRunner默认readable_csv。
209
+ artifact_exports为独立信号/报文导出仓;同样接管、按索引复制并校验、关闭。
210
+ owned_work仅为新会话移交的私有根及创建身份,须为spool直接父目录;默认只接管spool目录。
211
+ 返回: None;初始化实例状态。异常: 配置拒绝前不接管spool;资源失败保原取消或原
212
+ AnalysisError包装,持续清理失败附analysis_result_cleanup_owner供实际恢复。
213
+ """
214
+ if (not isinstance(spool, ResultSpool) or spool.state != 'analysis_complete'
215
+ or spool._closed or spool._closing):
216
+ raise AnalysisError('AnalysisResult需要已封口且未关闭的暂存')
217
+ if not isinstance(metadata, RunMetadata) or table_format not in ('csv', 'jsonl', 'readable_csv'):
218
+ raise ValueError('结果需RunMetadata和csv/jsonl/readable_csv格式')
219
+ if owned_work is not None:
220
+ if ((type(owned_work) is not tuple) or (len(owned_work) != 2) or (Path(owned_work[0]).resolve() != spool.path.resolve().parent) or (directory_identity(owned_work[0]) != owned_work[1])):
221
+ raise ValueError('结果只能接管同次新会话登记的spool直接父目录')
222
+ if (set(spool.tables) - set(SUMMARY_COLUMNS) or spool._summary_row_count != sum(spool.counts.values())
223
+ or spool._directory_identity is None):
224
+ raise ValueError('当前结果只接受已登记的精简表及对应行,不能混入旧完整结果')
225
+ if artifact_exports is not None and (type(artifact_exports) is not ArtifactExportStore
226
+ or artifact_exports._budget is not spool.budget or artifact_exports.closed or artifact_exports.state!='sealed'
227
+ or artifact_exports.session_id not in (set(metadata.session_ids))):
228
+ raise ValueError('新导出仓需同预算、同会话且封口')
229
+ # 校验通过后才接管spool;首次预约前安装全部清理字段,已付费用从0开始。
230
+ self.spool, self.metadata, self.table_format = spool, metadata, table_format
231
+ self._work_removed = (metadata.retain_work)
232
+ self._owned_work = owned_work or (spool.path, spool._directory_identity)
233
+ self._pending_output_cleanup = None
234
+ self._readable_writer = None # 发布失败时保留真实索引所有者,供close重试。
235
+ self._published_counts = None
236
+ self._presentation = None
237
+ self._contributions_omitted = 'contributions' not in spool.tables
238
+ self._output_tables = spool.tables
239
+ self._artifact_exports=artifact_exports
240
+ self._budget, self._held_cost, self._metadata_cost, self._writer_cost = spool.budget, 0, 0, 0
241
+ self._iterators = set()
242
+ self._closed = self._closing = False
243
+ self._failure_count = 0
244
+ self.status = 'analysis_complete'
245
+ self.manifest_path = spool.path / 'analysis_manifest.json'
246
+ if (not (metadata.detailed_diagnostics or metadata.retain_work)):
247
+ self.manifest_path = None
248
+ try:
249
+ expected = metadata.estimated_bytes()
250
+ if owned_work is not None:
251
+ expected += 4096 + 64 * len(str(owned_work[0]))
252
+ if artifact_exports is not None:
253
+ expected+=65536+64*len(str(artifact_exports.path))
254
+ _reserve_owned_ram(self, expected, '_metadata_cost')
255
+ self._write_state(self.manifest_path, self.status)
256
+ except BaseException as exc:
257
+ spool._fail(exc)
258
+ _cleanup_owner(self, exc, 'analysis_result_cleanup_owner', '结果对象构造')
259
+ if isinstance(exc, (KeyboardInterrupt, SystemExit)):
260
+ raise
261
+ # 保持原非取消异常包装;未返回owner同时挂在外层,调用方无需猜测异常链。
262
+ error = AnalysisError('结果内部清单写入失败')
263
+ recovery = getattr(exc, 'analysis_result_cleanup_owner', None)
264
+ if recovery is not None:
265
+ error.analysis_result_cleanup_owner = recovery
266
+ error.add_note('释放恢复后调用analysis_result_cleanup_owner.close()')
267
+ raise error from exc
268
+
269
+ def _require_open(self) -> None:
270
+ """拒绝关闭后迭代/发布,不自动重新打开已关闭对象。
271
+
272
+ 参数: 无。返回: None。异常: ResultClosedError。
273
+ """
274
+ if self._closed or self._closing or self.spool._closed or self.spool._closing:
275
+ raise ResultClosedError('AnalysisResult已关闭或正在清理')
276
+
277
+ def _manifest(self, status: str, outputs=(), coverage=None) -> dict:
278
+ """构造有界清单副本,分开分析状态与发布状态。
279
+
280
+ 参数: status为实际状态,outputs为固定文件记录,coverage为固定状态计数。
281
+ 返回: dict;元数据转换成本已申请。异常: 无预期异常。
282
+ """
283
+ manifest = {'status': status, 'run': self.metadata.to_dict(), 'python_version': platform.python_version(),
284
+ 'ended_at': datetime.now(timezone.utc).isoformat(), 'error_counts': {'output': self._failure_count},
285
+ 'row_counts': {table: self.spool.counts[table] for table in self._output_tables},
286
+ 'resources': self.spool.budget.snapshot(),
287
+ 'result_buffer_rows': self.spool.result_buffer_rows, 'peak_buffer_rows': self.spool.peak_buffer_rows,
288
+ 'table_format': self.table_format, 'outputs': list(outputs)}
289
+ if (self.metadata.retain_work):
290
+ manifest['spool_path'] = str(self.spool.path)
291
+ manifest['spool_files'] = {table: str(self.spool.files[table]) for table in self._output_tables}
292
+ if (self.metadata.detailed_diagnostics):
293
+ manifest['coverage'] = coverage or {}
294
+ defaults={table:version for table,_,version in reversed(tuple(SUMMARY_MODELS.values()))}
295
+ versions={table:sorted(self.spool._summary_versions.get(table,{defaults[table]})) for table in self.spool.tables}
296
+ versions={table:values[0] if len(values)==1 else values for table,values in versions.items()}
297
+ if all(type(v) is int for v in versions.values()) and len(set(versions.values())) == 1:
298
+ manifest['row_schema_version'] = next(iter(versions.values()))
299
+ else:
300
+ manifest['row_schema_versions'] = versions
301
+ if self._artifact_exports is not None:
302
+ manifest['exports']={'index_file':'exports.jsonl','directory':'exports',
303
+ 'record_count':self._artifact_exports.count,'device_label':self._artifact_exports.device_label}
304
+ if self.table_format == 'readable_csv':
305
+ from .readable_format import seconds, outcome_messages
306
+ # 可读表不是旧行模型的逐行副本;以实际发布行数和专用表示版本为准。
307
+ manifest.pop('row_schema_version', None)
308
+ manifest.pop('row_schema_versions', None)
309
+ if self._published_counts is not None:
310
+ manifest['row_counts'] = dict(self._published_counts)
311
+ if self._presentation is not None:
312
+ manifest['presentation'] = self._presentation
313
+ manifest['result_messages'] = outcome_messages(self.metadata.analysis_outcome)
314
+ run = manifest['run']
315
+ if 'session_boundaries' in run:
316
+ run['session_boundaries_seconds'] = [(name, seconds(start), seconds(end))
317
+ for name, start, end in run.pop('session_boundaries')]
318
+ # 调试资源仍在显式诊断中提供,普通结果先呈现实际文件和业务概况。
319
+ if not self.metadata.detailed_diagnostics:
320
+ for key in ('python_version', 'resources', 'result_buffer_rows', 'peak_buffer_rows'):
321
+ manifest.pop(key, None)
322
+ return manifest
323
+
324
+ @property
325
+ def run_manifest(self) -> dict:
326
+ """返回当前状态快照,发布失败诊断路径另见manifest_path。
327
+
328
+ 参数: 无。返回: 独立小型dict。异常: 关闭后仍可查看诊断,不读文件。
329
+ """
330
+ return self._manifest(self.status)
331
+
332
+ def _write_state(self, path: Path, status: str) -> None:
333
+ """追加状态清单到暂存,每份保留且计入累计配额。
334
+
335
+ 参数: path为本任务全新清单路径,status为实际状态。返回: None。
336
+ 异常: 配额/IO失败;不能生成虚假completed。
337
+ """
338
+ try:
339
+ if (not (self.metadata.detailed_diagnostics or self.metadata.retain_work)):
340
+ return
341
+ payload = (_json(self._manifest(status)) + '\n').encode('utf-8')
342
+ if len(payload) > self.spool.max_row_bytes:
343
+ raise BudgetExceededError('运行清单超过有限元数据大小')
344
+ with path.open('xb', buffering=0) as handle:
345
+ self.spool._write_bytes(handle, payload)
346
+ finally:
347
+ # 失败状态诊断也只断本层文本/输入,保原IO错误与实际写次数。
348
+ payload = self = path = status = None
349
+
350
+ def iter_events(self) -> Iterator[SummaryEventRow]:
351
+ """创建独立事件流,不装入全量列表。
352
+
353
+ 参数: 无。返回: SummaryEventRow迭代器。异常: ResultClosedError表示结果已关闭或正在关闭;BudgetExceededError表示无法为游标分配预算。
354
+ 创建或迭代期间OSError表示暂存文件读取失败,ResultFormatError表示暂存行格式损坏。
355
+ """
356
+ return _RowIterator(self, 'events')
357
+
358
+ def iter_sequences(self) -> Iterator[SummarySequenceRow]:
359
+ """逐行读取请求配对结果;未配置配对时返回可关闭空游标。
360
+
361
+ 参数: 无。返回: SummarySequenceRow迭代器。异常: ResultClosedError表示结果已关闭或正在关闭;BudgetExceededError表示无法为游标分配预算。
362
+ 创建或迭代期间OSError表示暂存文件读取失败,ResultFormatError表示暂存行格式损坏。
363
+ """
364
+ return _RowIterator(self, 'sequences')
365
+
366
+ def iter_segments(self) -> Iterator[SummarySegmentRow]:
367
+ """创建独立片段流,保留全部父事件关联。
368
+
369
+ 参数: 无。返回: SummarySegmentRow迭代器。异常: ResultClosedError表示结果已关闭或正在关闭;BudgetExceededError表示无法为游标分配预算。
370
+ 创建或迭代期间OSError表示暂存文件读取失败,ResultFormatError表示暂存行格式损坏。
371
+ """
372
+ return _RowIterator(self, 'segments')
373
+
374
+ def iter_calculations(self) -> Iterator[SummaryCalculationRow]:
375
+ """创建计算摘要行流;同时检查value、unit、status、reason和quality。
376
+
377
+ 参数: 无。返回: SummaryCalculationRow迭代器。异常: ResultClosedError表示结果已关闭或正在关闭;BudgetExceededError表示无法为游标分配预算。
378
+ 创建或迭代期间OSError表示暂存文件读取失败,ResultFormatError表示暂存行格式损坏。
379
+ """
380
+ return _RowIterator(self, 'calculations')
381
+
382
+
383
+ def iter_contributions(self) -> Iterator[SummaryContributionRow]:
384
+ """逐行回查真实贡献与skip,原序号和新求值序号分别保留。
385
+
386
+ 参数: 无。返回: 独立有预算游标;逐行返回SummaryContributionRow。
387
+ 异常: ContributionsUnavailableError表示本次明确不生成明细;关闭、预算、
388
+ IO或格式错误仍保留。原输入/解码器/求值器均不再调用。
389
+ """
390
+ self._require_open()
391
+ if self._contributions_omitted:
392
+ raise ContributionsUnavailableError('本次未生成贡献明细;请将outputs.contributions设为full后重新运行')
393
+ return _RowIterator(self, 'contributions')
394
+
395
+ def iter_selections(self) -> Iterator[SummarySelectionRow]:
396
+ """读取明确请求的真实样本选择行。参数: 无。返回: 有预算游标,未请求为空。异常: ResultClosedError表示结果关闭;BudgetExceededError表示游标预算不足;
397
+ 迭代时ResultFormatError表示暂存格式损坏,OSError表示文件读取失败。"""
398
+ return _RowIterator(self,'selections')
399
+
400
+ def iter_input_trace(self) -> Iterator[SummaryInputRow]:
401
+ """读取显式请求的所需输入追溯。参数: 无。返回: 按行产生SummaryInputRow的可关闭游标。
402
+ 异常: ResultClosedError表示结果已关闭;AnalysisError表示未请求diagnostics.trace。
403
+ 迭代期间可能因暂存损坏报告ResultFormatError,或传播OSError及预算错误。"""
404
+ self._require_open()
405
+ if 'input_trace' not in self.spool.tables:
406
+ raise AnalysisError('本次未生成输入追溯;请设置diagnostics.trace后重新运行')
407
+ return _RowIterator(self,'input_trace')
408
+
409
+ @property
410
+ def contributions_available(self) -> bool:
411
+ """说明本结果是否允许查询逐消息贡献,不将未生成和零行混为一谈。
412
+
413
+ 参数: 无。返回: bool;默认未配置逐消息指标仍允许原空游标。
414
+ 异常: 无;关闭后可查看这项固定运行事实,但不能重新创建游标。
415
+ """
416
+ return not self._contributions_omitted
417
+
418
+ def _write_table(self, table: str, destination: Path, quality_handle, coverage: dict):
419
+ """逐行生成单表和质量记录,异常或EOF关闭读取句柄。
420
+
421
+ 参数: table为固定表,destination为新文件,quality_handle为质量输出。
422
+ 返回: 请求摘要时为文件事实,否则None。异常: 预算/编码/写入错误传播,输出with保证关闭。
423
+ """
424
+ iterator = _RowIterator(self, table)
425
+ count = 0
426
+ failure = None
427
+ digest_output = None
428
+ try:
429
+ mode = 'x' if self.table_format == 'csv' else 'xb'
430
+ options = {'encoding': 'utf-8', 'newline': ''} if mode == 'x' else {}
431
+ with destination.open(mode, **options) as output:
432
+ # 新版只在明确请求时随写摘要;CSV使用newline空串,无平台换行二次转换。
433
+ if (self.metadata.integrity_diagnostics):
434
+ digest_output=_DigestOutput(output);output=digest_output
435
+ columns = SUMMARY_COLUMNS[table]
436
+ if (10 in self.spool._summary_versions.get(table,())):
437
+ columns=columns+('coverage_status',)
438
+ writer = csv.DictWriter(output, fieldnames=columns) if mode == 'x' else None
439
+ if writer is not None:
440
+ writer.writeheader()
441
+ for row in iterator:
442
+ # 普通/CSV路径已完整解码;内部legacy发布已核本次完整行证据。
443
+ # 直接复用原字节,公共iterator仍交付严格模型,全部业务字段原样保留。
444
+ payload = iterator._payload
445
+ fields = ((_row_fields(row) if writer is not None else summary_fields(row)) if (writer is not None or quality_handle is not None)
446
+ else None)
447
+ if writer is not None:
448
+ writer.writerow(fields)
449
+ else:
450
+ output.write(payload)
451
+ # 状态/覆盖分布仅使用固定有限键,不按任意signal/rule建立无限dict。
452
+ if quality_handle is not None:
453
+ state_field = {'selections': 'selection', 'contributions': 'reason',
454
+ 'input_trace': 'record_type'}.get(table, 'status')
455
+ state_key = table + ':' + fields[state_field]
456
+ coverage[state_key] = coverage.get(state_key, 0) + 1
457
+ quality_record = {key: fields[key] for key in ('sequence', 'dataset_id', 'entity_id', 'session_id', 'quality', state_field)}
458
+ quality_record['table'] = table
459
+ for key in ('selection', 'value_coverage', 'condition_coverage', 'requested_coverage', 'coverage_status'):
460
+ if key in fields:
461
+ quality_record[key] = fields[key]
462
+ quality_handle.write(_json(quality_record) + '\n')
463
+ count += 1
464
+ if count != self.spool.counts[table]:
465
+ raise ResultFormatError('输出表行数与分析暂存不一致')
466
+ return digest_output.info(destination.name) if digest_output is not None else None
467
+ except BaseException as exc:
468
+ failure = exc
469
+ raise
470
+ finally:
471
+ # 输出/编码原错误不能被读槽收尾取消覆盖,失败读取器仍留父登记供close。
472
+ row = fields = payload = quality_record = None
473
+ try:
474
+ iterator.close()
475
+ except BaseException as cleanup:
476
+ if failure is None:
477
+ raise
478
+ failure.add_note(f'发布表读取器清理附加失败: {type(cleanup).__name__}')
479
+
480
+ def _file_info(self, path: Path) -> dict:
481
+ """以固定读取块计算新输出哈希,不保留全文件内容。
482
+
483
+ 参数: path为已关闭输出文件。返回: 名称/bytes/SHA256。
484
+ 异常: IO错误;调用侧已预留发布临时槽位。
485
+ """
486
+ if (not self.metadata.detailed_diagnostics):
487
+ return {'name': path.name, 'bytes': path.stat().st_size}
488
+ digest = hashlib.sha256()
489
+ with path.open('rb') as handle:
490
+ while chunk := handle.read(65536):
491
+ digest.update(chunk)
492
+ return {'name': path.name, 'bytes': path.stat().st_size, 'sha256': digest.hexdigest()}
493
+
494
+ def _install_directory(self, temporary: Path, destination: Path) -> None:
495
+ """在Windows同文件系统安装,短暂占用时有限等待并始终拒绝覆盖。
496
+
497
+ 参数: temporary为目标父目录临时目录,destination为全新目标。
498
+ 返回: None。异常: AnalysisError为未支持平台;FileExistsError/IO错误为安装失败。
499
+ 边界: 仅原rename的Win5/32最多尝试六次,五次等待合计0.77秒;
500
+ 系统调用本身不受该等待时长约束。其他错误立即传播,最终保留原异常与源目录。
501
+ 副作用: 只重试目录安装,不重新读取、分析或生成任何输出文件。
502
+ """
503
+ # Windows rename对已存在目录拒绝;POSIX普通rename可覆盖空目录,不能静默复用。
504
+ if os.name != 'nt':
505
+ raise AnalysisError('原子目录安装目前只验收Windows;其他平台需不覆盖安装实现')
506
+ delays = (.02, .05, .1, .2, .4)
507
+ for attempt in range(len(delays) + 1):
508
+ # 每次等待后重新保护新出现的目标;检查之后的竞争仍由Windows rename拒绝。
509
+ if destination.exists():
510
+ raise FileExistsError(destination)
511
+ try:
512
+ os.rename(temporary, destination)
513
+ return
514
+ except PermissionError as exc:
515
+ # 仅恢复短暂访问/共享冲突;权限长期不允许时仍保最后真实OS错误及现场。
516
+ if getattr(exc, 'winerror', None) not in (5, 32):
517
+ raise
518
+ if attempt == len(delays):
519
+ exc.add_note('Windows目录安装六次均被拒绝;已等待0.77秒,保留临时目录供诊断')
520
+ raise
521
+ time.sleep(delays[attempt])
522
+
523
+ def write(self, output_dir: str | Path) -> OutputManifest:
524
+ """从暂存导出到全新目录,completed最后写入并原子安装。
525
+
526
+ 参数: output_dir为新输出目录;格式取本结果table_format。
527
+ 返回: 安装成功后的OutputManifest。异常: AnalysisError包含原错误链;关闭异常独立。
528
+ 副作用: 成功时创建正式输出目录;失败时按retain_work决定是否保留本次临时目录。
529
+ 不覆盖已有目标,不重新读取输入日志;关闭后的结果不能发布。
530
+ """
531
+ self._require_open()
532
+ temporary = None
533
+ temporary_identity = None
534
+ failure = None
535
+ try:
536
+ destination = Path(output_dir).absolute()
537
+ if (not self.metadata.retain_work) and (destination.resolve().is_relative_to(Path(self._owned_work[0]).resolve())):
538
+ raise ValueError('发布目录不能放在关闭后删除的本次私有工作目录内')
539
+ if destination.exists():
540
+ raise FileExistsError(destination)
541
+ if os.name != 'nt':
542
+ raise AnalysisError('当前未实现该平台的不覆盖原子目录安装')
543
+ # 转换/CSV/质量/hash临时对象也计入账本,不只计算读取数组。
544
+ _reserve_owned_ram(self, self.spool.reader_cost, '_writer_cost')
545
+ destination.parent.mkdir(parents=True, exist_ok=True)
546
+ temporary = Path(tempfile.mkdtemp(prefix=f'.{destination.name}.incomplete-', dir=destination.parent))
547
+ if (not self.metadata.retain_work):
548
+ temporary_identity = directory_identity(temporary)
549
+ coverage = {}
550
+ suffix = '.' + self.table_format
551
+ # 仅发布实际请求的表;none不写空贡献文件,也不会打开其内部空暂存。
552
+ paths = tuple(temporary / (table + suffix) for table in self._output_tables)
553
+ quality_path = temporary / 'quality.jsonl'
554
+ detailed = (self.metadata.detailed_diagnostics)
555
+ quality_paths = (quality_path,) if detailed else ()
556
+ written_infos={}
557
+ with (quality_path.open('x', encoding='utf-8', newline='') if detailed else nullcontext(None)) as quality:
558
+ if self.table_format == 'readable_csv':
559
+ from .readable import ReadableWriter
560
+ self._readable_writer = ReadableWriter(self, temporary, _RowIterator)
561
+ paths = self._readable_writer.write(quality, coverage)
562
+ written_infos.update(self._readable_writer.infos)
563
+ self._published_counts = dict(self._readable_writer.counts)
564
+ self._presentation = self._readable_writer.presentation()
565
+ self._readable_writer.close()
566
+ self._readable_writer = None
567
+ else:
568
+ for table, path in zip(self._output_tables, paths):
569
+ info=self._write_table(table, path, quality, coverage)
570
+ if info is not None: written_infos[path.name]=info
571
+ # 原writer在run内已关闭;发布只复制并核验现成BLF/索引,无旧I/O调用。
572
+ extra_paths = ()
573
+ if self._artifact_exports is not None:
574
+ extra_paths+=(self._artifact_exports.copy_to(temporary),)
575
+ written_infos['exports.jsonl']=self._artifact_exports.index_info
576
+ # 所有表和质量句柄已成功关闭;清单作为最后写入内容。
577
+ infos = tuple(written_infos[path.name] if path.name in written_infos else self._file_info(path)
578
+ for path in (*paths, *quality_paths, *extra_paths))
579
+ completed = self._manifest('completed', infos, coverage)
580
+ completed['manifest_file'] = 'run_manifest.json'
581
+ with (temporary / 'run_manifest.json').open('x', encoding='utf-8', newline='') as handle:
582
+ handle.write((json.dumps(completed, ensure_ascii=False, allow_nan=False, indent=2)
583
+ if self.table_format == 'readable_csv' else _json(completed)) + '\n')
584
+ # 返回模型也在安装前完成校验;安装成功后仅返回,避免校验错误遗留已发布completed。
585
+ manifest = OutputManifest(self.metadata.run_id, destination, destination / 'run_manifest.json',
586
+ tuple(destination / path.name for path in (*paths, *quality_paths, *extra_paths)),
587
+ tuple(self._published_counts.items()) if self.table_format == 'readable_csv'
588
+ else tuple((table, self.spool.counts[table]) for table in self._output_tables))
589
+ self._install_directory(temporary, destination)
590
+ self.status = 'analysis_complete'
591
+ return manifest
592
+ except BaseException as exc:
593
+ failure = exc
594
+ if self._readable_writer is not None:
595
+ try:
596
+ self._readable_writer.close()
597
+ self._readable_writer = None
598
+ except BaseException as cleanup:
599
+ self._closing = True
600
+ exc.analysis_result_cleanup_owner = self
601
+ exc.add_note('可读关联索引关闭失败:' + type(cleanup).__name__)
602
+ raw_failure = False
603
+ raw_failure=raw_failure or (self._artifact_exports is not None and
604
+ getattr(exc,'artifact_export_store_cleanup_owner',None) is self._artifact_exports)
605
+ if raw_failure:
606
+ # 取消也必须保最高结果owner;不能到普通异常包装后才补这条真实责任。
607
+ exc.analysis_result_cleanup_owner = self
608
+ self.status = 'cancelled' if isinstance(exc, (KeyboardInterrupt, SystemExit)) else 'output_failed'
609
+ self._failure_count += 1
610
+ # 安装失败时移除临时completed名称,把现存清单改标incomplete;只操作本次新文件。
611
+ if (temporary is not None) and (not self.metadata.retain_work):
612
+ self._pending_output_cleanup = (temporary, temporary_identity)
613
+ try:
614
+ remove_owned_directory(*self._pending_output_cleanup)
615
+ self._pending_output_cleanup = None
616
+ except BaseException as cleanup:
617
+ self._closing = True
618
+ exc.analysis_result_cleanup_owner = self
619
+ exc.add_note('输出临时目录清理失败,可重试close:'+type(cleanup).__name__)
620
+ elif temporary is not None:
621
+ marker = temporary / 'run_manifest.json'
622
+ try:
623
+ if marker.exists():
624
+ with marker.open('w', encoding='utf-8') as handle:
625
+ handle.write(_json({'status': self.status, 'run_id': self.metadata.run_id}) + '\n')
626
+ with (temporary / 'failure.json').open('x', encoding='utf-8') as handle:
627
+ handle.write(_json({'status': self.status, 'error_type': type(exc).__name__}) + '\n')
628
+ except BaseException as diagnostic:
629
+ # 诊断写入失败不替换发布原错;原磁盘失败限制及incomplete目录保持。
630
+ if not isinstance(diagnostic, OSError):
631
+ exc.add_note(f'发布临时诊断附加失败: {type(diagnostic).__name__}')
632
+ try:
633
+ path = self.spool.path / f'output_failure-{self._failure_count:06d}.json'
634
+ self._write_state(path, self.status)
635
+ self.manifest_path = (None if (not (self.metadata.detailed_diagnostics or self.metadata.retain_work)) else path)
636
+ except BaseException as diagnostic:
637
+ if not isinstance(diagnostic, (OSError, BudgetExceededError)):
638
+ exc.add_note(f'发布状态诊断附加失败: {type(diagnostic).__name__}')
639
+ if isinstance(exc, (KeyboardInterrupt, SystemExit)):
640
+ raise
641
+ error = AnalysisError(f'结果发布失败,状态{self.status}')
642
+ if raw_failure or self._pending_output_cleanup is not None or self._readable_writer is not None:
643
+ # 原始复制的实际FD仍由本结果的仓持有;包装不得丢最高恢复入口。
644
+ error.analysis_result_cleanup_owner = self
645
+ failure = error
646
+ raise error from exc
647
+ finally:
648
+ # 局部with已关闭真实输出;发布槽的实际余额由同一owner保存供失败后close。
649
+ try:
650
+ _release_owned_ram(self, self._writer_cost, '_writer_cost')
651
+ except BaseException as cleanup:
652
+ if self._writer_cost:
653
+ self._closing = True
654
+ target = cleanup if failure is None else failure
655
+ target.analysis_result_cleanup_owner = self
656
+ if failure is None:
657
+ raise
658
+ failure.add_note(f'发布workspace清理附加失败: {type(cleanup).__name__}')
659
+
660
+ def close(self) -> None:
661
+ """关闭活跃游标并释放资源;默认清理本次自有暂存,保留原始输入与已发布结果。
662
+
663
+ 参数: 无。返回: None;重复调用安全。diagnostics.retain_work为True时保留工作现场。
664
+ 异常: 关闭失败仍尝试其余句柄,最后抛首个原错误;未完成清理时可重试close。
665
+ """
666
+ if self._closed:
667
+ return
668
+ # 清理中拒绝新读取/发布;尝试全部读取器,但不提前丢失失败句柄或自有费用。
669
+ self._closing = True
670
+ error = None
671
+ for iterator in tuple(self._iterators):
672
+ try:
673
+ iterator.close()
674
+ except BaseException as exc:
675
+ if error is None:
676
+ error = exc
677
+ else:
678
+ error.add_note(f'其他结果读取器关闭失败: {type(exc).__name__}')
679
+ if error is not None:
680
+ # 原首错和关闭顺序不变,最高结果仍保留失败的真实游标供恢复。
681
+ error.analysis_result_cleanup_owner = self
682
+ raise error
683
+ try:
684
+ # 各层只释放自己的实际申请,子spool从不抵销本层或调用方的借用余额。
685
+ if self._readable_writer is not None:
686
+ self._readable_writer.close()
687
+ self._readable_writer = None
688
+ if self._artifact_exports is not None:
689
+ self._artifact_exports.close()
690
+ if not self._artifact_exports.closed:
691
+ raise AnalysisError('新导出仓没有真实关闭')
692
+ _release_owned_ram(self, self._writer_cost, '_writer_cost')
693
+ _release_owned_ram(self, self._metadata_cost, '_metadata_cost')
694
+ self.spool.close()
695
+ if self._pending_output_cleanup is not None:
696
+ remove_owned_directory(*self._pending_output_cleanup)
697
+ self._pending_output_cleanup = None
698
+ if not self._work_removed:
699
+ remove_owned_directory(*self._owned_work)
700
+ self._work_removed = True
701
+ except BaseException as exc:
702
+ # 新原始仓持续关闭失败仍从本结果恢复,不要求调用方绕过其真实父。
703
+ exc.analysis_result_cleanup_owner = self
704
+ raise
705
+ finally:
706
+ if (not self._iterators and self._held_cost == 0 and self.spool._closed
707
+ and self._work_removed and self._pending_output_cleanup is None
708
+ and (self._artifact_exports is None or self._artifact_exports.closed)):
709
+ self._closed = True
710
+
711
+ def __enter__(self) -> 'AnalysisResult':
712
+ """进入结果上下文,不再次执行输入读取。
713
+
714
+ 参数: 无。返回: self。异常: ResultClosedError为已关闭。
715
+ """
716
+ self._require_open()
717
+ return self
718
+
719
+ def __exit__(self, exc_type, exc_value, traceback):
720
+ """退出上下文时释放资源,保留业务原异常。
721
+
722
+ 参数: exc_type/exc_value/traceback为上下文异常。返回: False,不吞异常。
723
+ 异常: 无原业务异常时关闭错误传播,否则原业务异常优先。
724
+ """
725
+ try:
726
+ self.close()
727
+ except BaseException as cleanup:
728
+ if exc_value is None:
729
+ raise
730
+ # 原上下文异常优先,但持续关闭失败仍说明实际恢复入口。
731
+ exc_value.add_note(f'结果上下文清理附加失败: {type(cleanup).__name__}')
732
+ if not self._closed:
733
+ exc_value.analysis_result_cleanup_owner = self
734
+ return False