PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
pdu_decoder.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
6
7#include <algorithm>
8#include <cstring>
9#include <tuple>
10
11namespace kcenon::pacs::network {
12
13namespace {
14
16constexpr size_t PDU_HEADER_SIZE = 6;
17
19constexpr size_t FIXED_PDU_SIZE = 10;
20
23constexpr size_t ASSOCIATE_HEADER_SIZE = 68; // 2 + 2 + 16 + 16 + 32
24
26inline int to_error_code(pdu_decode_error err) {
27 switch (err) {
39 default:
41 }
42}
43
45template<typename T>
46DecodeResult<T> make_error(pdu_decode_error err, const std::string& msg = "") {
47 std::string full_msg = to_string(err);
48 if (!msg.empty()) {
49 full_msg += ": " + msg;
50 }
51 return kcenon::pacs::error_info{to_error_code(err), full_msg, "network"};
52}
53
55template<typename T>
56DecodeResult<T> make_ok(T value) {
57 return kcenon::pacs::ok(std::move(value));
58}
59
60} // namespace
61
62// ============================================================================
63// Helper Functions
64// ============================================================================
65
66uint16_t pdu_decoder::read_uint16_be(std::span<const uint8_t> data, size_t offset) {
67 return static_cast<uint16_t>(
68 (static_cast<uint16_t>(data[offset]) << 8) |
69 static_cast<uint16_t>(data[offset + 1]));
70}
71
72uint32_t pdu_decoder::read_uint32_be(std::span<const uint8_t> data, size_t offset) {
73 return (static_cast<uint32_t>(data[offset]) << 24) |
74 (static_cast<uint32_t>(data[offset + 1]) << 16) |
75 (static_cast<uint32_t>(data[offset + 2]) << 8) |
76 static_cast<uint32_t>(data[offset + 3]);
77}
78
79std::string pdu_decoder::read_ae_title(std::span<const uint8_t> data, size_t offset) {
80 std::string ae_title(reinterpret_cast<const char*>(data.data() + offset),
82 // Trim trailing spaces
83 auto end = ae_title.find_last_not_of(' ');
84 if (end != std::string::npos) {
85 ae_title.resize(end + 1);
86 } else if (ae_title.find_first_not_of(' ') == std::string::npos) {
87 ae_title.clear(); // All spaces
88 }
89 return ae_title;
90}
91
92std::string pdu_decoder::read_uid(std::span<const uint8_t> data, size_t offset,
93 size_t length) {
94 std::string uid(reinterpret_cast<const char*>(data.data() + offset), length);
95 // Trim trailing nulls and spaces
96 while (!uid.empty() && (uid.back() == '\0' || uid.back() == ' ')) {
97 uid.pop_back();
98 }
99 return uid;
100}
101
103 std::span<const uint8_t> data, uint8_t expected_type) {
104
105 if (data.size() < PDU_HEADER_SIZE) {
106 return make_error<uint32_t>(pdu_decode_error::incomplete_header);
107 }
108
109 if (expected_type != 0 && data[0] != expected_type) {
110 return make_error<uint32_t>(pdu_decode_error::invalid_pdu_type,
111 "Expected type " + std::to_string(expected_type) +
112 ", got " + std::to_string(data[0]));
113 }
114
115 const uint32_t pdu_length = read_uint32_be(data, 2);
116
117 // Reject unreasonably large PDUs to prevent memory exhaustion attacks
118 constexpr uint32_t MAX_PDU_LENGTH = 16 * 1024 * 1024; // 16 MB
119 if (pdu_length > MAX_PDU_LENGTH) {
120 return make_error<uint32_t>(pdu_decode_error::malformed_pdu,
121 "PDU length " + std::to_string(pdu_length) +
122 " exceeds maximum allowed " + std::to_string(MAX_PDU_LENGTH));
123 }
124
125 const size_t total_length = PDU_HEADER_SIZE + pdu_length;
126
127 if (data.size() < total_length) {
128 return make_error<uint32_t>(pdu_decode_error::incomplete_pdu,
129 "Need " + std::to_string(total_length) +
130 " bytes, have " + std::to_string(data.size()));
131 }
132
133 return make_ok(pdu_length);
134}
135
136// ============================================================================
137// General Decoding
138// ============================================================================
139
140std::optional<size_t> pdu_decoder::pdu_length(std::span<const uint8_t> data) {
141 if (data.size() < PDU_HEADER_SIZE) {
142 return std::nullopt;
143 }
144
145 const uint32_t length = read_uint32_be(data, 2);
146 const size_t total = PDU_HEADER_SIZE + length;
147
148 if (data.size() < total) {
149 return std::nullopt;
150 }
151
152 return total;
153}
154
155std::optional<pdu_type> pdu_decoder::peek_pdu_type(std::span<const uint8_t> data) {
156 if (data.empty()) {
157 return std::nullopt;
158 }
159
160 switch (data[0]) {
161 case 0x01: return pdu_type::associate_rq;
162 case 0x02: return pdu_type::associate_ac;
163 case 0x03: return pdu_type::associate_rj;
164 case 0x04: return pdu_type::p_data_tf;
165 case 0x05: return pdu_type::release_rq;
166 case 0x06: return pdu_type::release_rp;
167 case 0x07: return pdu_type::abort;
168 default: return std::nullopt;
169 }
170}
171
172DecodeResult<pdu> pdu_decoder::decode(std::span<const uint8_t> data) {
173 if (data.size() < PDU_HEADER_SIZE) {
174 return make_error<pdu>(pdu_decode_error::incomplete_header);
175 }
176
177 const uint8_t type = data[0];
178
179 switch (type) {
180 case 0x01: {
181 auto result = decode_associate_rq(data);
182 if (result.is_ok()) {
183 return make_ok<pdu>(std::move(result.value()));
184 }
185 return result.error();
186 }
187 case 0x02: {
188 auto result = decode_associate_ac(data);
189 if (result.is_ok()) {
190 return make_ok<pdu>(std::move(result.value()));
191 }
192 return result.error();
193 }
194 case 0x03: {
195 auto result = decode_associate_rj(data);
196 if (result.is_ok()) {
197 return make_ok<pdu>(std::move(result.value()));
198 }
199 return result.error();
200 }
201 case 0x04: {
202 auto result = decode_p_data_tf(data);
203 if (result.is_ok()) {
204 return make_ok<pdu>(std::move(result.value()));
205 }
206 return result.error();
207 }
208 case 0x05: {
209 auto result = decode_release_rq(data);
210 if (result.is_ok()) {
211 return make_ok<pdu>(std::move(result.value()));
212 }
213 return result.error();
214 }
215 case 0x06: {
216 auto result = decode_release_rp(data);
217 if (result.is_ok()) {
218 return make_ok<pdu>(std::move(result.value()));
219 }
220 return result.error();
221 }
222 case 0x07: {
223 auto result = decode_abort(data);
224 if (result.is_ok()) {
225 return make_ok<pdu>(std::move(result.value()));
226 }
227 return result.error();
228 }
229 default:
230 return make_error<pdu>(pdu_decode_error::invalid_pdu_type,
231 "Unknown PDU type: " + std::to_string(type));
232 }
233}
234
235// ============================================================================
236// A-ASSOCIATE-RJ Decoder
237// ============================================================================
238
240 std::span<const uint8_t> data) {
241
242 auto header_result = validate_pdu_header(data, 0x03);
243 if (header_result.is_err()) {
244 return header_result.error();
245 }
246
247 // A-ASSOCIATE-RJ is always 10 bytes
248 if (data.size() < FIXED_PDU_SIZE) {
249 return make_error<associate_rj>(pdu_decode_error::incomplete_pdu);
250 }
251
252 associate_rj rj;
253 rj.result = static_cast<reject_result>(data[7]);
254 rj.source = data[8];
255 rj.reason = data[9];
256
257 return make_ok(std::move(rj));
258}
259
260// ============================================================================
261// A-RELEASE-RQ Decoder
262// ============================================================================
263
265 std::span<const uint8_t> data) {
266
267 auto header_result = validate_pdu_header(data, 0x05);
268 if (header_result.is_err()) {
269 return header_result.error();
270 }
271
272 return make_ok(release_rq_pdu{});
273}
274
275// ============================================================================
276// A-RELEASE-RP Decoder
277// ============================================================================
278
280 std::span<const uint8_t> data) {
281
282 auto header_result = validate_pdu_header(data, 0x06);
283 if (header_result.is_err()) {
284 return header_result.error();
285 }
286
287 return make_ok(release_rp_pdu{});
288}
289
290// ============================================================================
291// A-ABORT Decoder
292// ============================================================================
293
295 auto header_result = validate_pdu_header(data, 0x07);
296 if (header_result.is_err()) {
297 return header_result.error();
298 }
299
300 if (data.size() < FIXED_PDU_SIZE) {
301 return make_error<abort_pdu>(pdu_decode_error::incomplete_pdu);
302 }
303
305 abort.source = static_cast<abort_source>(data[8]);
306 abort.reason = static_cast<abort_reason>(data[9]);
307
308 return make_ok(std::move(abort));
309}
310
311// ============================================================================
312// P-DATA-TF Decoder
313// ============================================================================
314
316 std::span<const uint8_t> data) {
317
318 auto header_result = validate_pdu_header(data, 0x04);
319 if (header_result.is_err()) {
320 return header_result.error();
321 }
322
323 const uint32_t pdu_length = header_result.value();
324 const size_t pdu_end = PDU_HEADER_SIZE + pdu_length;
325
326 p_data_tf_pdu result;
327 size_t pos = PDU_HEADER_SIZE;
328
329 while (pos < pdu_end) {
330 // Each PDV item has:
331 // - 4-byte length
332 // - 1-byte presentation context ID
333 // - 1-byte message control header
334 // - variable data
335
336 if (pos + 4 > pdu_end) {
337 return make_error<p_data_tf_pdu>(pdu_decode_error::malformed_pdu,
338 "Incomplete PDV item length");
339 }
340
341 const uint32_t pdv_item_length = read_uint32_be(data, pos);
342 pos += 4;
343
344 if (pdv_item_length < 2) {
345 return make_error<p_data_tf_pdu>(pdu_decode_error::malformed_pdu,
346 "PDV item length too small");
347 }
348
349 if (pos + pdv_item_length > pdu_end) {
350 return make_error<p_data_tf_pdu>(pdu_decode_error::buffer_overflow,
351 "PDV item exceeds PDU bounds");
352 }
353
355 pdv.context_id = data[pos];
356 pos += 1;
357
358 const uint8_t control = data[pos];
359 pos += 1;
360
361 pdv.is_command = (control & 0x01) != 0;
362 pdv.is_last = (control & 0x02) != 0;
363
364 // Data length = item length - context_id (1) - control (1)
365 const size_t data_length = pdv_item_length - 2;
366 pdv.data.assign(data.begin() + static_cast<ptrdiff_t>(pos),
367 data.begin() + static_cast<ptrdiff_t>(pos + data_length));
368 pos += data_length;
369
370 result.pdvs.push_back(std::move(pdv));
371 }
372
373 return make_ok(std::move(result));
374}
375
376// ============================================================================
377// Variable Items Decoder (for ASSOCIATE-RQ/AC)
378// ============================================================================
379
380DecodeResult<std::tuple<
381 std::string,
382 std::vector<presentation_context_rq>,
383 std::vector<presentation_context_ac>,
385>> pdu_decoder::decode_variable_items(std::span<const uint8_t> data, bool is_rq) {
386 std::string application_context;
387 std::vector<presentation_context_rq> pcs_rq;
388 std::vector<presentation_context_ac> pcs_ac;
389 user_information user_info;
390
391 size_t pos = 0;
392
393 while (pos < data.size()) {
394 if (pos + 4 > data.size()) {
395 return make_error<std::tuple<std::string,
396 std::vector<presentation_context_rq>,
397 std::vector<presentation_context_ac>,
399 "Incomplete item header");
400 }
401
402 const uint8_t item_type_byte = data[pos];
403 // Reserved byte at pos + 1
404 const uint16_t item_length = read_uint16_be(data, pos + 2);
405 pos += 4;
406
407 if (pos + item_length > data.size()) {
408 return make_error<std::tuple<std::string,
409 std::vector<presentation_context_rq>,
410 std::vector<presentation_context_ac>,
412 "Item length exceeds buffer");
413 }
414
415 switch (item_type_byte) {
416 case 0x10: // Application Context
417 application_context = read_uid(data, pos, item_length);
418 break;
419
420 case 0x20: // Presentation Context (RQ)
421 if (is_rq) {
422 // Parse presentation context RQ
424 pc.id = data[pos];
425 // 3 reserved bytes
426
427 size_t sub_pos = pos + 4;
428 const size_t pc_end = pos + item_length;
429
430 while (sub_pos < pc_end) {
431 if (sub_pos + 4 > pc_end) break;
432
433 const uint8_t sub_type = data[sub_pos];
434 const uint16_t sub_length = read_uint16_be(data, sub_pos + 2);
435 sub_pos += 4;
436
437 if (sub_pos + sub_length > pc_end) break;
438
439 if (sub_type == 0x30) { // Abstract Syntax
440 pc.abstract_syntax = read_uid(data, sub_pos, sub_length);
441 } else if (sub_type == 0x40) { // Transfer Syntax
442 pc.transfer_syntaxes.push_back(
443 read_uid(data, sub_pos, sub_length));
444 }
445 sub_pos += sub_length;
446 }
447 pcs_rq.push_back(std::move(pc));
448 }
449 break;
450
451 case 0x21: // Presentation Context (AC)
452 if (!is_rq) {
454 pc.id = data[pos];
455 // Reserved byte at pos + 1
456 pc.result = static_cast<presentation_context_result>(data[pos + 2]);
457 // Reserved byte at pos + 3
458
459 size_t sub_pos = pos + 4;
460 const size_t pc_end = pos + item_length;
461
462 while (sub_pos < pc_end) {
463 if (sub_pos + 4 > pc_end) break;
464
465 const uint8_t sub_type = data[sub_pos];
466 const uint16_t sub_length = read_uint16_be(data, sub_pos + 2);
467 sub_pos += 4;
468
469 if (sub_pos + sub_length > pc_end) break;
470
471 if (sub_type == 0x40) { // Transfer Syntax
472 pc.transfer_syntax = read_uid(data, sub_pos, sub_length);
473 }
474 sub_pos += sub_length;
475 }
476 pcs_ac.push_back(std::move(pc));
477 }
478 break;
479
480 case 0x50: { // User Information
481 auto ui_result = decode_user_info_item(
482 data.subspan(pos, item_length));
483 if (ui_result.is_ok()) {
484 user_info = std::move(ui_result.value());
485 }
486 break;
487 }
488
489 default:
490 // Skip unknown item types
491 break;
492 }
493
494 pos += item_length;
495 }
496
497 return make_ok(std::make_tuple(
498 std::move(application_context),
499 std::move(pcs_rq),
500 std::move(pcs_ac),
501 std::move(user_info)));
502}
503
504// ============================================================================
505// User Information Decoder
506// ============================================================================
507
509 std::span<const uint8_t> data) {
510
512 size_t pos = 0;
513
514 while (pos < data.size()) {
515 if (pos + 4 > data.size()) break;
516
517 const uint8_t sub_type = data[pos];
518 // Reserved byte at pos + 1
519 const uint16_t sub_length = read_uint16_be(data, pos + 2);
520 pos += 4;
521
522 if (pos + sub_length > data.size()) break;
523
524 switch (sub_type) {
525 case 0x51: // Maximum Length
526 if (sub_length >= 4) {
527 ui.max_pdu_length = read_uint32_be(data, pos);
528 }
529 break;
530
531 case 0x52: // Implementation Class UID
532 ui.implementation_class_uid = read_uid(data, pos, sub_length);
533 break;
534
535 case 0x55: // Implementation Version Name
536 ui.implementation_version_name = read_uid(data, pos, sub_length);
537 break;
538
539 case 0x54: { // SCP/SCU Role Selection
540 if (sub_length >= 4) {
541 const uint16_t uid_length = read_uint16_be(data, pos);
542 // UID is padded to even length in encoder
543 const uint16_t padded_uid_length = uid_length + (uid_length % 2);
544 if (pos + 2 + padded_uid_length + 2 <= pos + sub_length) {
546 role.sop_class_uid = read_uid(data, pos + 2, uid_length);
547 role.scu_role = (data[pos + 2 + padded_uid_length] != 0);
548 role.scp_role = (data[pos + 2 + padded_uid_length + 1] != 0);
549 ui.role_selections.push_back(std::move(role));
550 }
551 }
552 break;
553 }
554
555 default:
556 // Skip unknown sub-items
557 break;
558 }
559
560 pos += sub_length;
561 }
562
563 return make_ok(std::move(ui));
564}
565
566// ============================================================================
567// A-ASSOCIATE-RQ Decoder
568// ============================================================================
569
571 std::span<const uint8_t> data) {
572
573 auto header_result = validate_pdu_header(data, 0x01);
574 if (header_result.is_err()) {
575 return header_result.error();
576 }
577
578 const uint32_t pdu_length = header_result.value();
579
580 // Check minimum size for ASSOCIATE header
581 if (pdu_length < ASSOCIATE_HEADER_SIZE) {
582 return make_error<associate_rq>(pdu_decode_error::malformed_pdu,
583 "PDU too short for ASSOCIATE-RQ");
584 }
585
586 // Check protocol version (bytes 6-7)
587 const uint16_t protocol_version = read_uint16_be(data, 6);
588 if (protocol_version != DICOM_PROTOCOL_VERSION) {
589 return make_error<associate_rq>(pdu_decode_error::invalid_protocol_version,
590 "Expected version 1, got " + std::to_string(protocol_version));
591 }
592
593 associate_rq rq;
594
595 // Called AE Title (bytes 10-25)
596 rq.called_ae_title = read_ae_title(data, 10);
597
598 // Calling AE Title (bytes 26-41)
599 rq.calling_ae_title = read_ae_title(data, 26);
600
601 // Reserved bytes 42-73 (32 bytes)
602 // Variable items start at byte 74
603
604 const size_t variable_start = PDU_HEADER_SIZE + ASSOCIATE_HEADER_SIZE;
605 const size_t variable_length = pdu_length - ASSOCIATE_HEADER_SIZE;
606
607 if (variable_length > 0 && variable_start + variable_length <= data.size()) {
608 auto var_result = decode_variable_items(
609 data.subspan(variable_start, variable_length), true);
610
611 if (var_result.is_ok()) {
612 auto& [app_ctx, pcs_rq, pcs_ac, user_info] = var_result.value();
613 rq.application_context = std::move(app_ctx);
614 rq.presentation_contexts = std::move(pcs_rq);
615 rq.user_info = std::move(user_info);
616 }
617 }
618
619 return make_ok(std::move(rq));
620}
621
622// ============================================================================
623// A-ASSOCIATE-AC Decoder
624// ============================================================================
625
627 std::span<const uint8_t> data) {
628
629 auto header_result = validate_pdu_header(data, 0x02);
630 if (header_result.is_err()) {
631 return header_result.error();
632 }
633
634 const uint32_t pdu_length = header_result.value();
635
636 // Check minimum size
637 if (pdu_length < ASSOCIATE_HEADER_SIZE) {
638 return make_error<associate_ac>(pdu_decode_error::malformed_pdu,
639 "PDU too short for ASSOCIATE-AC");
640 }
641
642 // Check protocol version
643 const uint16_t protocol_version = read_uint16_be(data, 6);
644 if (protocol_version != DICOM_PROTOCOL_VERSION) {
645 return make_error<associate_ac>(pdu_decode_error::invalid_protocol_version,
646 "Expected version 1, got " + std::to_string(protocol_version));
647 }
648
649 associate_ac ac;
650
651 // Called AE Title
652 ac.called_ae_title = read_ae_title(data, 10);
653
654 // Calling AE Title
655 ac.calling_ae_title = read_ae_title(data, 26);
656
657 // Variable items
658 const size_t variable_start = PDU_HEADER_SIZE + ASSOCIATE_HEADER_SIZE;
659 const size_t variable_length = pdu_length - ASSOCIATE_HEADER_SIZE;
660
661 if (variable_length > 0 && variable_start + variable_length <= data.size()) {
662 auto var_result = decode_variable_items(
663 data.subspan(variable_start, variable_length), false);
664
665 if (var_result.is_ok()) {
666 auto& [app_ctx, pcs_rq, pcs_ac, user_info] = var_result.value();
667 ac.application_context = std::move(app_ctx);
668 ac.presentation_contexts = std::move(pcs_ac);
669 ac.user_info = std::move(user_info);
670 }
671 }
672
673 return make_ok(std::move(ac));
674}
675
676} // namespace kcenon::pacs::network
static DecodeResult< std::tuple< std::string, std::vector< presentation_context_rq >, std::vector< presentation_context_ac >, user_information > > decode_variable_items(std::span< const uint8_t > data, bool is_rq)
Decode variable items from ASSOCIATE-RQ/AC PDUs.
static DecodeResult< user_information > decode_user_info_item(std::span< const uint8_t > data)
Decode User Information sub-items.
static DecodeResult< associate_ac > decode_associate_ac(std::span< const uint8_t > data)
Decode an A-ASSOCIATE-AC PDU.
static DecodeResult< associate_rq > decode_associate_rq(std::span< const uint8_t > data)
Decode an A-ASSOCIATE-RQ PDU.
static DecodeResult< release_rp_pdu > decode_release_rp(std::span< const uint8_t > data)
Decode an A-RELEASE-RP PDU.
static DecodeResult< pdu > decode(std::span< const uint8_t > data)
Decode any PDU from bytes.
static uint16_t read_uint16_be(std::span< const uint8_t > data, size_t offset)
Read a 16-bit unsigned integer in big-endian format.
static std::string read_ae_title(std::span< const uint8_t > data, size_t offset)
Read an AE Title (16 bytes, space-trimmed).
static DecodeResult< abort_pdu > decode_abort(std::span< const uint8_t > data)
Decode an A-ABORT PDU.
static uint32_t read_uint32_be(std::span< const uint8_t > data, size_t offset)
Read a 32-bit unsigned integer in big-endian format.
static std::string read_uid(std::span< const uint8_t > data, size_t offset, size_t length)
Read a UID string (trim trailing null/space padding).
static DecodeResult< uint32_t > validate_pdu_header(std::span< const uint8_t > data, uint8_t expected_type=0)
Validate PDU header and extract length.
static DecodeResult< release_rq_pdu > decode_release_rq(std::span< const uint8_t > data)
Decode an A-RELEASE-RQ PDU.
static DecodeResult< associate_rj > decode_associate_rj(std::span< const uint8_t > data)
Decode an A-ASSOCIATE-RJ PDU.
static std::optional< size_t > pdu_length(std::span< const uint8_t > data)
Check if a complete PDU is available in the buffer.
static std::optional< pdu_type > peek_pdu_type(std::span< const uint8_t > data)
Get the PDU type from buffer without full decoding.
static DecodeResult< p_data_tf_pdu > decode_p_data_tf(std::span< const uint8_t > data)
Decode a P-DATA-TF PDU.
constexpr int pdu_decoding_error
Definition result.h:109
constexpr int malformed_pdu
Definition result.h:112
constexpr int pdu_error
Definition result.h:91
constexpr int invalid_pdu_type
Definition result.h:111
constexpr int incomplete_pdu
Definition result.h:110
reject_result
Reject result values.
Definition pdu_types.h:92
@ 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)
@ application_context
Application Context Item.
constexpr uint16_t DICOM_PROTOCOL_VERSION
DICOM Protocol Version.
Definition pdu_types.h:270
abort_reason
Abort reason values when source is service-provider.
Definition pdu_types.h:79
kcenon::pacs::Result< T > DecodeResult
Result type alias for PDU decoding operations using standardized kcenon::pacs::Result<T>
constexpr const char * to_string(association_state state) noexcept
Convert association_state to string representation.
Definition association.h:86
constexpr size_t AE_TITLE_LENGTH
AE Title length (fixed 16 characters, space-padded)
Definition pdu_types.h:273
abort_source
Abort source values.
Definition pdu_types.h:70
pdu_decode_error
Error codes for PDU decoding errors.
Definition pdu_decoder.h:75
@ incomplete_header
Less than 6 bytes available.
@ buffer_overflow
Item length exceeds PDU bounds.
@ malformed_pdu
PDU structure is invalid.
@ incomplete_pdu
PDU length exceeds available data.
@ invalid_item_type
Unknown item type in variable items.
@ invalid_protocol_version
Unsupported protocol version.
@ invalid_pdu_type
Unknown PDU type byte.
presentation_context_result
Result values for A-ASSOCIATE-AC presentation context.
Definition pdu_types.h:59
kcenon::common::error_info error_info
Error information type.
Definition result.h:40
abort_source source
Source of abort.
Definition pdu_decoder.h:39
A-ASSOCIATE-AC PDU data.
Definition pdu_types.h:218
std::string calling_ae_title
Calling AE Title (16 chars max)
Definition pdu_types.h:220
user_information user_info
User Information.
Definition pdu_types.h:223
std::string called_ae_title
Called AE Title (16 chars max)
Definition pdu_types.h:219
std::string application_context
Application Context Name UID.
Definition pdu_types.h:221
std::vector< presentation_context_ac > presentation_contexts
Presentation Contexts.
Definition pdu_types.h:222
A-ASSOCIATE-RJ PDU data.
Definition pdu_types.h:231
uint8_t reason
Reason/Diagnostic.
Definition pdu_types.h:234
reject_result result
Result (1=permanent, 2=transient)
Definition pdu_types.h:232
A-ASSOCIATE-RQ PDU data.
Definition pdu_types.h:205
std::string called_ae_title
Called AE Title (16 chars max)
Definition pdu_types.h:206
std::string application_context
Application Context Name UID.
Definition pdu_types.h:208
user_information user_info
User Information.
Definition pdu_types.h:210
std::string calling_ae_title
Calling AE Title (16 chars max)
Definition pdu_types.h:207
std::vector< presentation_context_rq > presentation_contexts
Presentation Contexts.
Definition pdu_types.h:209
std::vector< presentation_data_value > pdvs
Presentation Data Values.
Definition pdu_decoder.h:52
Presentation Context for A-ASSOCIATE-AC.
Definition pdu_types.h:165
presentation_context_result result
Result/Reason.
Definition pdu_types.h:167
uint8_t id
Presentation Context ID.
Definition pdu_types.h:166
std::string transfer_syntax
Accepted Transfer Syntax UID.
Definition pdu_types.h:168
Presentation Context for A-ASSOCIATE-RQ.
Definition pdu_types.h:149
uint8_t id
Presentation Context ID (odd number 1-255)
Definition pdu_types.h:150
std::string abstract_syntax
Abstract Syntax UID (SOP Class)
Definition pdu_types.h:151
std::vector< std::string > transfer_syntaxes
Proposed Transfer Syntaxes.
Definition pdu_types.h:152
Presentation Data Value (PDV) item for P-DATA-TF.
Definition pdu_types.h:135
bool is_command
true if Command message, false if Data
Definition pdu_types.h:137
uint8_t context_id
Presentation Context ID (odd number 1-255)
Definition pdu_types.h:136
std::vector< uint8_t > data
Fragment data.
Definition pdu_types.h:139
A-RELEASE-RP has no data fields.
Definition pdu_decoder.h:33
PDU variant type representing any DICOM Upper Layer PDU.A-RELEASE-RQ has no data fields.
Definition pdu_decoder.h:32
SCP/SCU Role Selection Sub-item.
Definition pdu_types.h:180
std::string sop_class_uid
SOP Class UID.
Definition pdu_types.h:181
User Information for A-ASSOCIATE-RQ/AC.
Definition pdu_types.h:193
std::string implementation_class_uid
Implementation Class UID.
Definition pdu_types.h:195
std::string implementation_version_name
Implementation Version Name (optional)
Definition pdu_types.h:196
std::vector< scp_scu_role_selection > role_selections
Role selections (optional)
Definition pdu_types.h:197
uint32_t max_pdu_length
Maximum Length of P-DATA-TF PDUs.
Definition pdu_types.h:194
std::string_view uid