102 {
103
105 xa_pacs_server server(port, "XA_SCP");
106 REQUIRE(server.initialize());
107 REQUIRE(server.start());
108
109 SECTION("Scenario 1: Basic XA Storage") {
110
112 auto instance_uid = ds.get_string(tags::sop_instance_uid);
113
114
116 "127.0.0.1", port, "XA_SCP", "TEST_SCU",
117 {"1.2.840.10008.5.1.4.1.1.12.1", "1.2.840.10008.1.1"});
118 REQUIRE(assoc_result.is_ok());
119
120 auto association = std::move(assoc_result.value());
121
122
124 if (!context_id_opt) {
125
126 WARN("Verification SOP Class not accepted (Global Issue)");
127 } else {
130 CHECK(send_result.is_ok());
132 CHECK(recv_result.is_ok());
133 }
134
139 }
141
142
143 auto stored_path = server.storage_path() / (instance_uid + ".dcm");
144 CHECK(std::filesystem::exists(stored_path));
145
146
148 }
149
150 SECTION("Scenario 2: XA IOD Validation") {
151
153
154
156 "127.0.0.1", port, "XA_SCP", "TEST_SCU",
157 {"1.2.840.10008.5.1.4.1.1.12.1"});
158 REQUIRE(assoc_result.is_ok());
159 auto association = std::move(assoc_result.value());
160
161
164 if (result_valid.is_err()) {
165 FAIL("Store valid failed: " << result_valid.error().message);
166 }
167 CHECK(result_valid.value().is_success());
168
169
171
172
173 }
174
175 SECTION("Scenario 3: Multi-frame XA Storage") {
178
179 std::vector<uint16_t>
pixel_data(512 * 512 * 10, 128);
181 std::span<const uint8_t>(
reinterpret_cast<const uint8_t*
>(
pixel_data.data()),
pixel_data.size() *
sizeof(uint16_t))));
182
184 "127.0.0.1", port, "XA_SCP", "TEST_SCU",
185 {"1.2.840.10008.5.1.4.1.1.12.1"});
186 REQUIRE(assoc_result.is_ok());
187 auto association = std::move(assoc_result.value());
188
191 if (result.is_err()) {
192 FAIL("Multi-frame store failed: " << result.error().message);
193 }
194 CHECK(result.value().is_success());
195
196
198 }
199
200 SECTION("Scenario 4: XA Specific Attributes") {
202
203
205 "127.0.0.1", port, "XA_SCP", "TEST_SCU",
206 {"1.2.840.10008.5.1.4.1.1.12.1"});
207 REQUIRE(assoc_result.is_ok());
208 auto association = std::move(assoc_result.value());
209
212 if (result.is_err()) {
213 FAIL("XA attributes store failed: " << result.error().message);
214 }
215 CHECK(result.value().is_success());
216
217
219 }
220
221
222 server.stop();
223}
static network::Result< network::association > connect(const std::string &host, uint16_t port, const std::string &called_ae, const std::string &calling_ae=test_scu_ae_title, const std::vector< std::string > &sop_classes={"1.2.840.10008.1.1"})
Connect to a test server.
Result< std::monostate > send_dimse(uint8_t context_id, const dimse::dimse_message &msg)
Send a DIMSE message.
Result< std::monostate > release(duration timeout=default_timeout)
Gracefully release the association.
Result< std::pair< uint8_t, dimse::dimse_message > > receive_dimse(duration timeout=default_timeout)
Receive a DIMSE message.
std::optional< uint8_t > accepted_context_id(std::string_view abstract_syntax) const
Get the presentation context ID for an abstract syntax.
network::Result< store_result > store(network::association &assoc, const core::dicom_dataset &dataset)
Store a single DICOM dataset.
uint16_t find_available_port(uint16_t start=default_test_port, int max_attempts=200)
Find an available port for testing.
core::dicom_dataset generate_xa_dataset(const std::string &study_uid="")
Generate a XA (X-Ray Angiographic) image dataset for testing.
auto make_c_echo_rq(uint16_t message_id, std::string_view sop_class_uid="1.2.840.10008.1.1") -> dimse_message
Create a C-ECHO request message.
constexpr std::string_view verification_sop_class_uid
Verification SOP Class UID (1.2.840.10008.1.1)
Result of a C-STORE operation.
bool is_success() const noexcept
Check if the store operation was successful.
constexpr dicom_tag number_of_frames