PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
waveform_ps_iod_validator.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
13
15
16using namespace kcenon::pacs::core;
17
18// =============================================================================
19// Waveform PS-specific DICOM Tags
20// =============================================================================
21
22namespace waveform_tags {
23
24// Referenced Waveform Sequence
25constexpr dicom_tag referenced_series_sequence{0x0008, 0x1115};
26
27// Waveform Presentation State Module
28constexpr dicom_tag waveform_presentation_sequence{0x0070, 0x0001};
29
30// Waveform Annotation Module
31constexpr dicom_tag waveform_annotation_sequence{0x0040, 0xB020};
32
33// General Equipment Module
34constexpr dicom_tag manufacturer{0x0008, 0x0070};
35
36} // namespace waveform_tags
37
38// =============================================================================
39// waveform_ps_iod_validator Implementation
40// =============================================================================
41
45
47 const core::dicom_dataset& dataset) const {
48 validation_result result;
49 result.is_valid = true;
50
51 // Validate mandatory modules
53 validate_patient_module(dataset, result);
54 validate_general_study_module(dataset, result);
55 validate_general_series_module(dataset, result);
56 validate_general_equipment_module(dataset, result);
57 validate_sop_common_module(dataset, result);
58 }
59
60 // Determine if this is a Presentation State or Annotation
61 auto sop_class = dataset.get_string(tags::sop_class_uid);
64 validate_waveform_ps_module(dataset, result);
65 }
69 }
70 }
71
73 auto ref_result = validate_references(dataset);
74 result.findings.insert(result.findings.end(),
75 ref_result.findings.begin(),
76 ref_result.findings.end());
77 }
78
79 // Check for errors
80 for (const auto& finding : result.findings) {
81 if (finding.severity == validation_severity::error) {
82 result.is_valid = false;
83 break;
84 }
85 if (options_.strict_mode && finding.severity == validation_severity::warning) {
86 result.is_valid = false;
87 break;
88 }
89 }
90
91 return result;
92}
93
95 const core::dicom_dataset& dataset) const {
96 validation_result result;
97 result.is_valid = true;
98
100 result.findings.push_back({
103 "ReferencedSeriesSequence (0008,1115) should be present for waveform references",
104 "WF-REF-WARN-001"
105 });
106 }
107
108 for (const auto& finding : result.findings) {
109 if (finding.severity == validation_severity::error) {
110 result.is_valid = false;
111 break;
112 }
113 }
114
115 return result;
116}
117
119 const core::dicom_dataset& dataset) const {
120 // Check essential Type 1 attributes
121 if (!dataset.contains(tags::study_instance_uid)) return false;
122 if (!dataset.contains(tags::series_instance_uid)) return false;
123 if (!dataset.contains(tags::sop_class_uid)) return false;
124 if (!dataset.contains(tags::sop_instance_uid)) return false;
125
126 // Verify SOP Class is a waveform PS or annotation class
127 auto sop_class = dataset.get_string(tags::sop_class_uid);
130 return false;
131 }
132
133 return true;
134}
135
138 return options_;
139}
140
145
146// =============================================================================
147// Module Validation Methods
148// =============================================================================
149
151 const core::dicom_dataset& dataset,
152 validation_result& result) const {
153
154 if (options_.check_type2) {
155 check_type2_attribute(dataset, tags::patient_name, "PatientName", result);
156 check_type2_attribute(dataset, tags::patient_id, "PatientID", result);
157 check_type2_attribute(dataset, tags::patient_birth_date, "PatientBirthDate", result);
158 check_type2_attribute(dataset, tags::patient_sex, "PatientSex", result);
159 }
160}
161
163 const core::dicom_dataset& dataset,
164 validation_result& result) const {
165
166 if (options_.check_type1) {
167 check_type1_attribute(dataset, tags::study_instance_uid, "StudyInstanceUID", result);
168 }
169
170 if (options_.check_type2) {
171 check_type2_attribute(dataset, tags::study_date, "StudyDate", result);
172 check_type2_attribute(dataset, tags::study_time, "StudyTime", result);
173 check_type2_attribute(dataset, tags::referring_physician_name, "ReferringPhysicianName", result);
174 check_type2_attribute(dataset, tags::study_id, "StudyID", result);
175 check_type2_attribute(dataset, tags::accession_number, "AccessionNumber", result);
176 }
177}
178
180 const core::dicom_dataset& dataset,
181 validation_result& result) const {
182
183 if (options_.check_type1) {
184 check_type1_attribute(dataset, tags::modality, "Modality", result);
185 check_type1_attribute(dataset, tags::series_instance_uid, "SeriesInstanceUID", result);
186 }
187
188 if (options_.check_type2) {
189 check_type2_attribute(dataset, tags::series_number, "SeriesNumber", result);
190 }
191}
192
194 const core::dicom_dataset& dataset,
195 validation_result& result) const {
196
197 if (options_.check_type2) {
198 check_type2_attribute(dataset, waveform_tags::manufacturer, "Manufacturer", result);
199 }
200}
201
203 const core::dicom_dataset& dataset,
204 validation_result& result) const {
205
206 if (options_.check_type1) {
207 check_type1_attribute(dataset, tags::sop_class_uid, "SOPClassUID", result);
208 check_type1_attribute(dataset, tags::sop_instance_uid, "SOPInstanceUID", result);
209 }
210
211 // Validate SOP Class UID is a waveform PS or annotation class
212 if (dataset.contains(tags::sop_class_uid)) {
213 auto sop_class = dataset.get_string(tags::sop_class_uid);
216 result.findings.push_back({
219 "SOPClassUID is not a recognized Waveform PS/Annotation SOP Class: " + sop_class,
220 "WF-ERR-001"
221 });
222 }
223 }
224}
225
227 const core::dicom_dataset& dataset,
228 validation_result& result) const {
229
230 // Waveform Presentation Sequence is expected for PS objects
233 result.findings.push_back({
236 "WaveformPresentationSequence (0070,0001) should be present for "
237 "Waveform Presentation State objects",
238 "WF-PS-WARN-001"
239 });
240 }
241 }
242}
243
245 const core::dicom_dataset& dataset,
246 validation_result& result) const {
247
248 // Waveform Annotation Sequence is expected for annotation objects
251 result.findings.push_back({
254 "WaveformAnnotationSequence (0040,B020) should be present for "
255 "Waveform Annotation objects",
256 "WF-ANN-WARN-001"
257 });
258 }
259 }
260}
261
262// =============================================================================
263// Attribute Validation Helpers
264// =============================================================================
265
267 const core::dicom_dataset& dataset,
268 const core::dicom_tag& tag,
269 const std::string& description,
270 validation_result& result) const {
271
272 if (!dataset.contains(tag)) {
273 result.findings.push_back({
275 tag,
276 "Type 1 attribute missing: " + description +
277 " (" + tag.to_string() + ")",
278 "WF-TYPE1-MISSING"
279 });
280 } else {
281 const auto* element = dataset.get(tag);
282 if (element != nullptr && !element->is_sequence()) {
283 auto value = dataset.get_string(tag);
284 if (value.empty()) {
285 result.findings.push_back({
287 tag,
288 "Type 1 attribute has empty value: " + description +
289 " (" + tag.to_string() + ")",
290 "WF-TYPE1-EMPTY"
291 });
292 }
293 }
294 }
295}
296
298 const core::dicom_dataset& dataset,
299 const core::dicom_tag& tag,
300 const std::string& description,
301 validation_result& result) const {
302
303 if (!dataset.contains(tag)) {
304 result.findings.push_back({
306 tag,
307 "Type 2 attribute missing: " + description +
308 " (" + tag.to_string() + ")",
309 "WF-TYPE2-MISSING"
310 });
311 }
312}
313
314} // namespace kcenon::pacs::services::validation
auto get(dicom_tag tag) noexcept -> dicom_element *
Get a pointer to the element with the given tag.
auto contains(dicom_tag tag) const noexcept -> bool
Check if the dataset contains an element with the given tag.
auto get_string(dicom_tag tag, std::string_view default_value="") const -> std::string
Get the string value of an element.
validation_result validate(const core::dicom_dataset &dataset) const
Validate a complete Waveform Presentation State or Annotation dataset.
void validate_sop_common_module(const core::dicom_dataset &dataset, validation_result &result) const
void validate_general_equipment_module(const core::dicom_dataset &dataset, validation_result &result) const
bool quick_check(const core::dicom_dataset &dataset) const
Quick validation of essential Type 1 attributes only.
void validate_waveform_annotation_module(const core::dicom_dataset &dataset, validation_result &result) const
void validate_patient_module(const core::dicom_dataset &dataset, validation_result &result) const
void validate_general_study_module(const core::dicom_dataset &dataset, validation_result &result) const
const waveform_ps_validation_options & options() const noexcept
Get current validation options.
void validate_general_series_module(const core::dicom_dataset &dataset, validation_result &result) const
validation_result validate_references(const core::dicom_dataset &dataset) const
Validate referenced waveform sequences.
void check_type1_attribute(const core::dicom_dataset &dataset, const core::dicom_tag &tag, const std::string &description, validation_result &result) const
void check_type2_attribute(const core::dicom_dataset &dataset, const core::dicom_tag &tag, const std::string &description, validation_result &result) const
void set_options(const waveform_ps_validation_options &options)
Set validation options.
void validate_waveform_ps_module(const core::dicom_dataset &dataset, validation_result &result) const
Compile-time constants for commonly used DICOM tags.
constexpr dicom_tag referring_physician_name
Referring Physician's Name.
constexpr dicom_tag patient_id
Patient ID.
constexpr dicom_tag sop_instance_uid
SOP Instance UID.
constexpr dicom_tag patient_birth_date
Patient's Birth Date.
constexpr dicom_tag accession_number
Accession Number.
constexpr dicom_tag modality
Modality.
constexpr dicom_tag study_time
Study Time.
constexpr dicom_tag patient_sex
Patient's Sex.
constexpr dicom_tag study_instance_uid
Study Instance UID.
constexpr dicom_tag series_number
Series Number.
constexpr dicom_tag sop_class_uid
SOP Class UID.
constexpr dicom_tag study_id
Study ID.
constexpr dicom_tag patient_name
Patient's Name.
constexpr dicom_tag study_date
Study Date.
constexpr dicom_tag series_instance_uid
Series Instance UID.
bool is_waveform_presentation_state_sop_class(std::string_view uid) noexcept
Check if a SOP Class UID is a Waveform Presentation State SOP Class.
bool is_waveform_annotation_sop_class(std::string_view uid) noexcept
Check if a SOP Class UID is a Waveform Annotation SOP Class.
@ warning
Non-critical - IOD may have issues.
std::vector< validation_finding > findings
All findings during validation.
Options for Waveform Presentation State / Annotation IOD validation.
bool validate_annotations
Validate annotation sequences (for Waveform Annotation)
bool check_conditional
Check Type 1C/2C (conditionally required) attributes.
bool check_type2
Check Type 2 (required, can be empty) attributes.
bool validate_display_config
Validate presentation state display configuration.
Waveform Presentation State IOD Validator.
Waveform Presentation State and Annotation Storage SOP Classes.