PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
pdu_decode_job.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2021-2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
15
16#include <chrono>
17
19
21 std::vector<uint8_t> raw_data,
22 decode_callback on_decoded,
23 error_callback on_error)
24 : raw_data_(std::move(raw_data))
25 , on_decoded_(std::move(on_decoded))
26 , on_error_(std::move(on_error)) {
27
28 context_.session_id = session_id;
31 context_.enqueue_time_ns = static_cast<uint64_t>(
32 std::chrono::duration_cast<std::chrono::nanoseconds>(
33 std::chrono::steady_clock::now().time_since_epoch()
34 ).count()
35 );
36}
37
39 // Validate we have data
40 if (raw_data_.size() < 6) { // Minimum PDU header size
41 if (on_error_) {
42 on_error_(context_.session_id, "PDU data too short");
43 }
45 "PDU data too short for header");
46 }
47
48 // Decode the PDU
49 auto decode_result = decode_pdu();
50 if (!decode_result.is_ok()) {
51 if (on_error_) {
52 auto err = decode_result.error();
53 on_error_(context_.session_id, err.message);
54 }
55 return VoidResult(decode_result.error());
56 }
57
58 auto pdu = decode_result.value();
59
60 // Invoke callback if set
61 if (on_decoded_) {
62 on_decoded_(pdu);
63 }
64
65 // Update context based on PDU type
72 context_.category = job_category::association;
73 }
74
75 // Create DIMSE process job and submit to next stage
76 auto dimse_job = std::make_unique<dimse_process_job>(std::move(pdu));
77
78 // Copy context to DIMSE job
79 dimse_job->get_context() = context_;
80 dimse_job->get_context().stage = pipeline_stage::dimse_process;
81
82 return coordinator.submit_to_stage(
84 std::move(dimse_job)
85 );
86}
87
88auto pdu_decode_job::get_context() const noexcept -> const job_context& {
89 return context_;
90}
91
93 return context_;
94}
95
96auto pdu_decode_job::get_name() const -> std::string {
97 return "pdu_decode_job[session=" +
98 std::to_string(context_.session_id) +
99 ", bytes=" + std::to_string(raw_data_.size()) + "]";
100}
101
102auto pdu_decode_job::get_raw_data() const noexcept
103 -> const std::vector<uint8_t>& {
104 return raw_data_;
105}
106
108 decoded_pdu result;
109 result.session_id = context_.session_id;
110
111 // PDU Header: Type (1 byte) + Reserved (1 byte) + Length (4 bytes)
112 if (raw_data_.size() < 6) {
114 "Insufficient data for PDU header");
115 }
116
117 // Extract PDU type
118 result.type = static_cast<kcenon::pacs::network::pdu_type>(raw_data_[0]);
119
120 // Extract length (big-endian, 4 bytes starting at offset 2)
121 uint32_t length = (static_cast<uint32_t>(raw_data_[2]) << 24) |
122 (static_cast<uint32_t>(raw_data_[3]) << 16) |
123 (static_cast<uint32_t>(raw_data_[4]) << 8) |
124 static_cast<uint32_t>(raw_data_[5]);
125
126 // Validate we have complete PDU
127 if (raw_data_.size() < 6 + length) {
129 "Incomplete PDU data");
130 }
131
132 // Copy PDU data (excluding header)
133 result.data.assign(raw_data_.begin() + 6,
134 raw_data_.begin() + 6 + length);
135
136 // For P-DATA-TF, extract presentation context ID and flags
137 if (result.type == kcenon::pacs::network::pdu_type::p_data_tf && !result.data.empty()) {
138 // P-DATA-TF contains PDV items
139 // PDV Item: Length (4) + Presentation Context ID (1) + Message Control Header (1) + Data
140 if (result.data.size() >= 6) {
141 result.presentation_context_id = result.data[4];
142 uint8_t control_header = result.data[5];
143 result.is_last_fragment = (control_header & 0x02) != 0;
144 }
145 }
146
147 return ok(std::move(result));
148}
149
150} // namespace kcenon::pacs::network::pipeline
std::function< void(const decoded_pdu &pdu)> decode_callback
Callback type for decoded PDU.
auto get_name() const -> std::string override
Get the job name.
auto get_raw_data() const noexcept -> const std::vector< uint8_t > &
Get the raw PDU data.
auto decode_pdu() -> Result< decoded_pdu >
Internal decode method.
auto execute(pipeline_coordinator &coordinator) -> VoidResult override
Execute the decode job.
pdu_decode_job(uint64_t session_id, std::vector< uint8_t > raw_data, decode_callback on_decoded=nullptr, error_callback on_error=nullptr)
Construct a decode job.
std::function< void(uint64_t session_id, const std::string &error)> error_callback
Callback type for decode errors.
auto get_context() const noexcept -> const job_context &override
Get the job context.
Coordinates the 6-stage DICOM I/O pipeline.
DIMSE processing job for Stage 3 of the pipeline.
constexpr int insufficient_data
Definition result.h:82
constexpr int incomplete_pdu
Definition result.h:110
@ move
C-MOVE move request/response.
@ association
Association management (A-ASSOCIATE, A-RELEASE, A-ABORT)
@ dimse_process
Stage 3: Process DIMSE messages and route requests.
@ pdu_decode
Stage 2: Decode PDU bytes into structured data.
pdu_type
PDU (Protocol Data Unit) types as defined in DICOM PS3.8.
Definition pdu_types.h:25
@ associate_rj
A-ASSOCIATE-RJ (Association Reject)
@ associate_ac
A-ASSOCIATE-AC (Association Accept)
@ p_data_tf
P-DATA-TF (Data Transfer)
@ release_rq
A-RELEASE-RQ (Release Request)
@ release_rp
A-RELEASE-RP (Release Response)
@ associate_rq
A-ASSOCIATE-RQ (Association Request)
std::variant< associate_rq, associate_ac, associate_rj, p_data_tf_pdu, release_rq_pdu, release_rp_pdu, abort_pdu > pdu
Variant type that can hold any PDU.
Definition pdu_decoder.h:62
kcenon::pacs::VoidResult VoidResult
VoidResult type alias for operations without return value.
Definition association.h:59
VoidResult pacs_void_error(int code, const std::string &message, const std::string &details="")
Create a PACS void error result.
Definition result.h:249
Result< T > pacs_error(int code, const std::string &message, const std::string &details="")
Create a PACS error result with module context.
Definition result.h:234
PDU decoding job for Stage 2 of the pipeline.
Result of PDU decoding containing the PDU type and data.
uint64_t session_id
Session this PDU belongs to.
std::vector< uint8_t > data
Raw PDU data for further processing.
kcenon::pacs::network::pdu_type type
The type of PDU that was decoded.
bool is_last_fragment
Whether this is the last fragment (for P-DATA-TF)
uint8_t presentation_context_id
Presentation context ID (for P-DATA-TF)
Context information attached to pipeline jobs for tracking.
pipeline_stage stage
Current pipeline stage.
job_category category
Job category for metrics.
uint64_t enqueue_time_ns
Timestamp when job entered the pipeline (nanoseconds since epoch)
uint64_t session_id
Session/association identifier.