[med-svn] [odil] 01/04: Imported Upstream version 0.7.1
Julien Lamy
lamy-guest at moszumanska.debian.org
Fri Jul 8 16:22:05 UTC 2016
This is an automated email from the git hooks/post-receive script.
lamy-guest pushed a commit to branch master
in repository odil.
commit ff5fc36d0ba8c992e08d2bc9583dd2790bf5bc28
Author: Julien Lamy <lamy at unistra.fr>
Date: Fri Jul 8 17:50:06 2016 +0200
Imported Upstream version 0.7.1
---
.gitlab-ci.yml | 12 ++
CMakeLists.txt | 4 +-
applications/get.py | 125 ++++++++++++++++++++-
src/odil/EchoSCP.cpp | 14 ++-
src/odil/EchoSCP.h | 5 +-
src/odil/Exception.cpp | 4 +-
src/odil/Exception.h | 4 +-
src/odil/FindSCP.cpp | 20 ++--
src/odil/GetSCP.cpp | 39 +++----
src/odil/MoveSCP.cpp | 39 +++----
src/odil/Reader.cpp | 242 +++++++++++++++++++++-------------------
src/odil/Reader.h | 17 +++
src/odil/Reader.txx | 59 ++++++++++
src/odil/SCP.cpp | 15 +++
src/odil/SCP.h | 26 ++++-
src/odil/StoreSCP.cpp | 14 ++-
src/odil/StoreSCP.h | 5 +-
src/odil/VR.cpp | 4 +-
src/odil/Writer.cpp | 152 +++++++++++--------------
src/odil/Writer.h | 17 ++-
src/odil/Writer.txx | 56 ++++++++++
src/odil/dcmtk/Exception.cpp | 2 +-
src/odil/json_converter.cpp | 2 +-
src/odil/message/Response.cpp | 17 +++
src/odil/message/Response.h | 14 +++
src/odil/xml_converter.cpp | 2 +-
tests/code/message/Response.cpp | 14 +++
27 files changed, 640 insertions(+), 284 deletions(-)
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..2d0abf9
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,12 @@
+before_script:
+ - apt-get update -qq && apt-get install -y -qq build-essential pkg-config cmake ninja-build libdcmtk2-dev libwrap0-dev libjsoncpp-dev libicu-dev zlib1g-dev libboost-dev libboost-filesystem-dev libboost-python-dev libboost-regex-dev libboost-test-dev liblog4cpp5-dev dcmtk python-minimal python-nose
+ - mkdir build && cd build
+ - cmake -G Ninja -D CMAKE_CXX_FLAGS=-std=c++11 ../
+
+trusty:
+ image: ubuntu:trusty
+ script: ninja && ../tests/run --no-network
+
+xenial:
+ image: ubuntu:xenial
+ script: ninja && ../tests/run --no-network
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c5a1276..c3d7bf3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 2.8)
project("odil")
set(odil_MAJOR_VERSION 0)
set(odil_MINOR_VERSION 7)
-set(odil_PATCH_VERSION 0)
+set(odil_PATCH_VERSION 1)
set(odil_VERSION
${odil_MAJOR_VERSION}.${odil_MINOR_VERSION}.${odil_PATCH_VERSION})
@@ -58,7 +58,7 @@ endif()
add_custom_target(
CIIntegration ${CMAKE_COMMAND} -E echo "CI Integration"
- SOURCES appveyor.yml .travis.yml)
+ SOURCES appveyor.yml .gitlab-ci.yml .travis.yml)
set_target_properties(CIIntegration PROPERTIES FOLDER "Utils")
add_custom_target(
diff --git a/applications/get.py b/applications/get.py
index 30fb046..813c96b 100644
--- a/applications/get.py
+++ b/applications/get.py
@@ -2,10 +2,12 @@ from __future__ import print_function
import argparse
import logging
import os
+import re
import odil
from print_ import find_max_name_length, print_data_set
+from dicomdir import create_dicomdir
def add_subparser(subparsers):
parser = subparsers.add_parser(
@@ -24,10 +26,40 @@ def add_subparser(subparsers):
parser.add_argument(
"--directory", "-d", default=os.getcwd(),
help="Directory where the output files will be stored")
+ parser.add_argument(
+ "--iso-9660", "-I", action="store_true",
+ help="Save file names using ISO-9660 compatible file names")
+ parser.add_argument(
+ "--layout", "-l", choices=["flat", "tree"], default="flat",
+ help="Save files in the same directory (flat) or in a "
+ "patient/study/series tree (hierarchical)")
+ parser.add_argument(
+ "--dicomdir", "-D", action="store_true",
+ help="Create a DICOMDIR from the retrieved files")
+ parser.add_argument(
+ "--patient-key", "-p", default=[], action="append",
+ help="User-defined keys for PATIENT-level records, "
+ "expressed as KEYWORD[:TYPE]. TYPE defaults to 3.")
+ parser.add_argument(
+ "--study-key", "-S", default=[], action="append",
+ help="User-defined keys for STUDY-level records")
+ parser.add_argument(
+ "--series-key", "-s", default=[], action="append",
+ help="User-defined keys for SERIES-level records")
+ parser.add_argument(
+ "--image-key", "-i", default=[], action="append",
+ help="User-defined keys for IMAGE-level records")
parser.set_defaults(function=get)
return parser
-def get(host, port, calling_ae_title, called_ae_title, level, keys, directory):
+def get(
+ host, port, calling_ae_title, called_ae_title, level, keys, directory,
+ iso_9660, layout,
+ dicomdir, patient_key, study_key, series_key, image_key):
+
+ if dicomdir and not iso_9660:
+ raise Exception("Cannot create a DICOMDIR without ISO-9660 filenames")
+
query = odil.DataSet()
for key in keys:
key, value = key.split("=", 1)
@@ -88,10 +120,84 @@ def get(host, port, calling_ae_title, called_ae_title, level, keys, directory):
self.remaining = 0
self.failed = 0
self.warning = 0
+ self.stored = {}
+ self.files = []
+ self._study_ids = {}
+ self._series_ids = {}
def store(self, data_set):
- uid = data_set.as_string("SOPInstanceUID")[0]
- odil.write(data_set, os.path.join(self.directory, uid))
+ if layout == "flat":
+ directory = ""
+ elif layout == "tree":
+ # Patient directory: <PatientName> or <PatientID>.
+ patient_directory = None
+ if "PatientName" in data_set and data_set.as_string("PatientName"):
+ patient_directory = data_set.as_string("PatientName")[0]
+ else:
+
+ patient_directory = data_set.as_string("PatientID")[0]
+
+ # Study directory: <StudyID>_<StudyDescription>, both parts are
+ # optional. If both tags are missing or empty, default to a
+ # numeric index based on StudyInstanceUID.
+ study_directory = []
+ if "StudyID" in data_set and data_set.as_string("StudyID"):
+ study_directory.append(data_set.as_string("StudyID")[0])
+ if ("StudyDescription" in data_set and
+ data_set.as_string("StudyDescription")):
+ study_directory.append(
+ data_set.as_string("StudyDescription")[0])
+
+ if not study_directory:
+ study_instance_uid = data_set.as_string("StudyInstanceUID")[0]
+ study_directory.append(
+ self._study_ids.setdefault(
+ study_instance_uid, 1+len(self._study_ids)))
+
+ study_directory = "_".join(study_directory)
+
+ # Study directory: <SeriesNumber>_<SeriesDescription>, both
+ # parts are optional. If both tags are missing or empty, default
+ # to a numeric index based on SeriesInstanceUID.
+ series_directory = []
+ if "SeriesNumber" in data_set and data_set.as_int("SeriesNumber"):
+ series_directory.append(str(data_set.as_int("SeriesNumber")[0]))
+ if ("SeriesDescription" in data_set and
+ data_set.as_string("SeriesDescription")):
+ series_directory.append(
+ data_set.as_string("SeriesDescription")[0])
+
+ if not series_directory:
+ series_instance_uid = data_set.as_string("series_instance_uid")[0]
+ series_directory.append(
+ self._series_ids.setdefault(
+ series_instance_uid, 1+len(self._series_ids)))
+
+ series_directory = "_".join(series_directory)
+
+ if iso_9660:
+ patient_directory = to_iso_9660(patient_directory)
+ study_directory = to_iso_9660(study_directory)
+ series_directory = to_iso_9660(series_directory)
+ directory = os.path.join(
+ patient_directory, study_directory, series_directory)
+ if not os.path.isdir(os.path.join(self.directory, directory)):
+ os.makedirs(os.path.join(self.directory, directory))
+ else:
+ raise NotImplementedError()
+
+ self.stored.setdefault(directory, 0)
+
+ if iso_9660:
+ filename = "IM{:06d}".format(1+self.stored[directory])
+ else:
+ filename = data_set.as_string("SOPInstanceUID")[0]
+
+ odil.write(
+ data_set, os.path.join(self.directory, directory, filename))
+
+ self.stored[directory] += 1
+ self.files.append(os.path.join(directory, filename))
def get(self, message):
for type_ in ["completed", "remaining", "failed", "warning"]:
@@ -105,6 +211,8 @@ def get(host, port, calling_ae_title, called_ae_title, level, keys, directory):
if not os.path.isdir(directory):
os.makedirs(directory)
+ if len(os.listdir(directory)):
+ logging.warning("{} is not empty".format(directory))
callback = Callback(directory)
get.get(query, callback.store, callback.get)
@@ -115,3 +223,14 @@ def get(host, port, calling_ae_title, called_ae_title, level, keys, directory):
association.release()
logging.info("Association released")
+
+ if dicomdir:
+ logging.info("Creating DICOMDIR")
+ create_dicomdir(
+ [os.path.join(directory, x) for x in callback.files],
+ directory, patient_key, study_key, series_key, image_key)
+
+def to_iso_9660(value):
+ value = value[:8].upper()
+ value = re.sub(r"[^A-Z0-9_]", "_", value)
+ return value
diff --git a/src/odil/EchoSCP.cpp b/src/odil/EchoSCP.cpp
index f663c77..e80e24b 100644
--- a/src/odil/EchoSCP.cpp
+++ b/src/odil/EchoSCP.cpp
@@ -60,21 +60,25 @@ EchoSCP
message::CEchoRequest const request(message);
Value::Integer status=message::CEchoResponse::Success;
+ DataSet status_fields;
try
{
status = this->_callback(request);
}
- catch(Exception const &)
+ catch(SCP::Exception const & e)
+ {
+ status = e.status;
+ status_fields = e.status_fields;
+ }
+ catch(odil::Exception const &)
{
status = message::CEchoResponse::ProcessingFailure;
- // Error Comment
- // Error ID
- // Affected SOP Class UID
}
- message::CEchoResponse const response(
+ message::CEchoResponse response(
request.get_message_id(), status, request.get_affected_sop_class_uid());
+ response.set_status_fields(status_fields);
this->_association.send_message(
response, request.get_affected_sop_class_uid());
}
diff --git a/src/odil/EchoSCP.h b/src/odil/EchoSCP.h
index ef9680b..5fa0433 100644
--- a/src/odil/EchoSCP.h
+++ b/src/odil/EchoSCP.h
@@ -24,7 +24,10 @@ namespace odil
class EchoSCP: public SCP
{
public:
- /// @brief Callback called when a request is received.
+ /**
+ * @brief Callback called when a request is received, shall throw an
+ * SCP::Exception on error.
+ */
typedef std::function<Value::Integer(message::CEchoRequest const &)> Callback;
/// @brief Constructor.
diff --git a/src/odil/Exception.cpp b/src/odil/Exception.cpp
index ce8ae19..334a995 100644
--- a/src/odil/Exception.cpp
+++ b/src/odil/Exception.cpp
@@ -22,14 +22,14 @@ Exception
}
Exception
-::~Exception() throw()
+::~Exception() noexcept
{
// Nothing to do.
}
char const *
Exception
-::what() const throw()
+::what() const noexcept
{
return this->_message.c_str();
}
diff --git a/src/odil/Exception.h b/src/odil/Exception.h
index 2e01b2c..89583d6 100644
--- a/src/odil/Exception.h
+++ b/src/odil/Exception.h
@@ -23,10 +23,10 @@ public:
Exception(std::string const & message="");
/// @brief Destructor.
- virtual ~Exception() throw();
+ virtual ~Exception() noexcept;
/// @brief Return the reason for the exception.
- virtual const char* what() const throw();
+ virtual const char* what() const noexcept;
protected:
/// @brief Message of the exception.
diff --git a/src/odil/FindSCP.cpp b/src/odil/FindSCP.cpp
index bc7badb..97be4c3 100644
--- a/src/odil/FindSCP.cpp
+++ b/src/odil/FindSCP.cpp
@@ -62,6 +62,9 @@ FindSCP
{
message::CFindRequest const request(message);
+ Value::Integer final_status = message::CFindResponse::Success;
+ DataSet status_fields;
+
try
{
this->_generator->initialize(request);
@@ -76,17 +79,18 @@ FindSCP
this->_generator->next();
}
}
- catch(Exception const & e)
+ catch(SCP::Exception const & e)
+ {
+ final_status = e.status;
+ status_fields = e.status_fields;
+ }
+ catch(odil::Exception const & e)
{
- message::CFindResponse response(
- request.get_message_id(), message::CFindResponse::UnableToProcess);
- this->_association.send_message(
- response, request.get_affected_sop_class_uid());
- return;
+ final_status = message::CFindResponse::UnableToProcess;
}
- message::CFindResponse response(
- request.get_message_id(), message::CFindResponse::Success);
+ message::CFindResponse response(request.get_message_id(), final_status);
+ response.set_status_fields(status_fields);
this->_association.send_message(
response, request.get_affected_sop_class_uid());
}
diff --git a/src/odil/GetSCP.cpp b/src/odil/GetSCP.cpp
index a72262c..75354a4 100644
--- a/src/odil/GetSCP.cpp
+++ b/src/odil/GetSCP.cpp
@@ -65,6 +65,8 @@ GetSCP
StoreSCU store_scu(this->_association);
+ Value::Integer final_status = message::CGetResponse::Success;
+ DataSet status_fields;
unsigned int remaining_sub_operations = 0;
unsigned int completed_sub_operations=0;
unsigned int failed_sub_operations=0;
@@ -107,33 +109,22 @@ GetSCP
this->_generator->next();
}
}
- catch(Exception const &)
+ catch(SCP::Exception const & e)
{
- message::CGetResponse response(
- request.get_message_id(), message::CGetResponse::UnableToProcess);
- response.set_number_of_remaining_sub_operations(
- remaining_sub_operations);
- response.set_number_of_completed_sub_operations(
- completed_sub_operations);
- response.set_number_of_failed_sub_operations(
- failed_sub_operations);
- response.set_number_of_warning_sub_operations(
- warning_sub_operations);
- this->_association.send_message(
- response, request.get_affected_sop_class_uid());
- return;
+ final_status = e.status;
+ status_fields = e.status_fields;
+ }
+ catch(odil::Exception const &)
+ {
+ final_status = message::CGetResponse::UnableToProcess;
}
- message::CGetResponse response(
- request.get_message_id(), message::CGetResponse::Success);
- response.set_number_of_remaining_sub_operations(
- remaining_sub_operations);
- response.set_number_of_completed_sub_operations(
- completed_sub_operations);
- response.set_number_of_failed_sub_operations(
- failed_sub_operations);
- response.set_number_of_warning_sub_operations(
- warning_sub_operations);
+ message::CGetResponse response(request.get_message_id(), final_status);
+ response.set_status_fields(status_fields);
+ response.set_number_of_remaining_sub_operations(remaining_sub_operations);
+ response.set_number_of_completed_sub_operations(completed_sub_operations);
+ response.set_number_of_failed_sub_operations(failed_sub_operations);
+ response.set_number_of_warning_sub_operations(warning_sub_operations);
this->_association.send_message(
response, request.get_affected_sop_class_uid());
}
diff --git a/src/odil/MoveSCP.cpp b/src/odil/MoveSCP.cpp
index 739925a..ee26243 100644
--- a/src/odil/MoveSCP.cpp
+++ b/src/odil/MoveSCP.cpp
@@ -70,6 +70,8 @@ MoveSCP
move_association.associate();
StoreSCU store_scu(move_association);
+ Value::Integer final_status = message::CMoveResponse::Success;
+ DataSet status_fields;
unsigned int remaining_sub_operations = 0;
unsigned int completed_sub_operations=0;
unsigned int failed_sub_operations=0;
@@ -116,33 +118,22 @@ MoveSCP
this->_generator->next();
}
}
- catch(Exception const &)
+ catch(SCP::Exception const & e)
{
- message::CMoveResponse response(
- request.get_message_id(), message::CMoveResponse::UnableToProcess);
- response.set_number_of_remaining_sub_operations(
- remaining_sub_operations);
- response.set_number_of_completed_sub_operations(
- completed_sub_operations);
- response.set_number_of_failed_sub_operations(
- failed_sub_operations);
- response.set_number_of_warning_sub_operations(
- warning_sub_operations);
- this->_association.send_message(
- response, request.get_affected_sop_class_uid());
- return;
+ final_status = e.status;
+ status_fields = e.status_fields;
+ }
+ catch(odil::Exception const &)
+ {
+ final_status = message::CMoveResponse::UnableToProcess;
}
- message::CMoveResponse response(
- request.get_message_id(), message::CMoveResponse::Success);
- response.set_number_of_remaining_sub_operations(
- remaining_sub_operations);
- response.set_number_of_completed_sub_operations(
- completed_sub_operations);
- response.set_number_of_failed_sub_operations(
- failed_sub_operations);
- response.set_number_of_warning_sub_operations(
- warning_sub_operations);
+ message::CMoveResponse response(request.get_message_id(), final_status);
+ response.set_status_fields(status_fields);
+ response.set_number_of_remaining_sub_operations(remaining_sub_operations);
+ response.set_number_of_completed_sub_operations(completed_sub_operations);
+ response.set_number_of_failed_sub_operations(failed_sub_operations);
+ response.set_number_of_warning_sub_operations(warning_sub_operations);
this->_association.send_message(
response, request.get_affected_sop_class_uid());
}
diff --git a/src/odil/Reader.cpp b/src/odil/Reader.cpp
index dc3dfd4..988acd9 100644
--- a/src/odil/Reader.cpp
+++ b/src/odil/Reader.cpp
@@ -26,37 +26,6 @@
#include "odil/VR.h"
#include "odil/VRFinder.h"
-#define odil_read_binary(type, value, stream, byte_ordering, size) \
-type value; \
-{ \
- uint##size##_t raw; \
- stream.read(reinterpret_cast<char*>(&raw), sizeof(raw)); \
- if(!stream) \
- { \
- throw Exception("Could not read from stream"); \
- } \
- if(byte_ordering == ByteOrdering::LittleEndian) \
- { \
- raw = little_endian_to_host(raw); \
- } \
- else if(byte_ordering == ByteOrdering::BigEndian) \
- { \
- raw = big_endian_to_host(raw); \
- } \
- else \
- { \
- throw Exception("Unknown endianness"); \
- } \
- value = *reinterpret_cast<type*>(&raw);\
-}
-
-#define odil_ignore(stream, size) \
- stream.ignore(size); \
- if(!stream) \
- { \
- throw Exception("Could not read from stream"); \
- }
-
std::string read_string(std::istream & stream, unsigned int size)
{
if(size == 0)
@@ -75,6 +44,66 @@ std::string read_string(std::istream & stream, unsigned int size)
namespace odil
{
+Value::Binary
+Reader
+::read_encapsulated_pixel_data(
+ std::istream & stream, ByteOrdering byte_ordering,
+ std::string transfer_syntax, bool keep_group_length)
+{
+ Value::Binary value;
+
+ // PS 3.5, A.4
+ Reader const sequence_reader(stream, transfer_syntax, keep_group_length);
+
+ bool done = false;
+ while(!done)
+ {
+ auto const tag = sequence_reader.read_tag();
+ auto const item_length = Reader::read_binary<uint32_t>(
+ stream, byte_ordering);
+
+ if(tag == registry::Item)
+ {
+ Value::Binary::value_type item_data(item_length);
+
+ if(item_length > 0)
+ {
+ stream.read(
+ reinterpret_cast<char*>(&item_data[0]), item_length);
+ if(!stream)
+ {
+ throw Exception("Could not read from stream");
+ }
+ }
+
+ value.push_back(item_data);
+ }
+ else if(tag == registry::SequenceDelimitationItem)
+ {
+ // No value for Sequence Delimitation Item
+ done = true;
+ }
+ else
+ {
+ throw Exception(
+ "Expected SequenceDelimitationItem, got: "+std::string(tag));
+ }
+ }
+
+ return value;
+}
+
+void
+Reader
+::ignore(std::istream & stream, std::streamsize size)
+{
+ stream.ignore(size);
+ if(!stream)
+ {
+ throw Exception("Could not read from stream");
+ }
+}
+
Reader
::Reader(
std::istream & stream, std::string const & transfer_syntax,
@@ -126,8 +155,10 @@ Tag
Reader
::read_tag() const
{
- odil_read_binary(uint16_t, group, this->stream, this->byte_ordering, 16);
- odil_read_binary(uint16_t, element, this->stream, this->byte_ordering, 16);
+ auto const group = this->read_binary<uint16_t>(
+ this->stream, this->byte_ordering);
+ auto const element = this->read_binary<uint16_t>(
+ this->stream, this->byte_ordering);
return Tag(group, element);
}
@@ -169,7 +200,7 @@ Reader
{
value = Value(Value::DataSets());
}
- else if(vr == VR::OB || vr == VR::OF || vr == VR::OW || vr == VR::UN)
+ else if(is_binary(vr))
{
value = Value(Value::Binary());
}
@@ -284,26 +315,26 @@ Reader::Visitor
{
if(this->vr == VR::SL)
{
- odil_read_binary(
- int32_t, item, this->stream, this->byte_ordering, 32);
+ auto const item = Reader::read_binary<int32_t>(
+ this->stream, this->byte_ordering);
value[i] = item;
}
else if(this->vr == VR::SS)
{
- odil_read_binary(
- int16_t, item, this->stream, this->byte_ordering, 16);
+ auto const item = Reader::read_binary<int16_t>(
+ this->stream, this->byte_ordering);
value[i] = item;
}
else if(this->vr == VR::UL)
{
- odil_read_binary(
- uint32_t, item, this->stream, this->byte_ordering, 32);
+ auto const item = Reader::read_binary<uint32_t>(
+ this->stream, this->byte_ordering);
value[i] = item;
}
else if(this->vr == VR::AT || this->vr == VR::US)
{
- odil_read_binary(
- uint16_t, item, this->stream, this->byte_ordering, 16);
+ auto const item = Reader::read_binary<uint16_t>(
+ this->stream, this->byte_ordering);
value[i] = item;
}
}
@@ -353,14 +384,14 @@ Reader::Visitor
{
if(this->vr == VR::FD)
{
- odil_read_binary(
- double, item, this->stream, this->byte_ordering, 64);
+ auto const item = Reader::read_binary<double>(
+ this->stream, this->byte_ordering);
value[i] = item;
}
else if(this->vr == VR::FL)
{
- odil_read_binary(
- float, item, this->stream, this->byte_ordering, 32);
+ auto const item = Reader::read_binary<float>(
+ this->stream, this->byte_ordering);
value[i] = item;
}
}
@@ -460,7 +491,7 @@ Reader::Visitor
else if(tag == registry::SequenceDelimitationItem)
{
done = true;
- odil_ignore(this->stream, 4);
+ Reader::ignore(this->stream, 4);
}
else
{
@@ -481,7 +512,9 @@ Reader::Visitor
}
else if(vl == 0xffffffff)
{
- value = this->read_encapsulated_pixel_data(this->stream);
+ value = Reader::read_encapsulated_pixel_data(
+ this->stream, this->byte_ordering, this->transfer_syntax,
+ this->keep_group_length);
}
else
{
@@ -492,6 +525,21 @@ Reader::Visitor
this->stream.read(
reinterpret_cast<char*>(&value[0][0]), value[0].size());
}
+ else if(this->vr == VR::OD)
+ {
+ if(vl%8 != 0)
+ {
+ throw Exception("Cannot read OD for odd-sized array");
+ }
+
+ value[0].resize(vl);
+ for(unsigned int i=0; i<value[0].size(); i+=8)
+ {
+ auto const item = Reader::read_binary<double>(
+ this->stream, this->byte_ordering);
+ *reinterpret_cast<double*>(&value[0][i]) = item;
+ }
+ }
else if(this->vr == VR::OF)
{
if(vl%4 != 0)
@@ -502,11 +550,26 @@ Reader::Visitor
value[0].resize(vl);
for(unsigned int i=0; i<value[0].size(); i+=4)
{
- odil_read_binary(
- float, item, this->stream, this->byte_ordering, 32);
+ auto const item = Reader::read_binary<float>(
+ this->stream, this->byte_ordering);
*reinterpret_cast<float*>(&value[0][i]) = item;
}
}
+ else if(this->vr == VR::OL)
+ {
+ if(vl%4 != 0)
+ {
+ throw Exception("Cannot read OL for odd-sized array");
+ }
+
+ value[0].resize(vl);
+ for(unsigned int i=0; i<value[0].size(); i+=4)
+ {
+ auto const item = Reader::read_binary<uint32_t>(
+ this->stream, this->byte_ordering);
+ *reinterpret_cast<uint32_t*>(&value[0][i]) = item;
+ }
+ }
else if(this->vr == VR::OW)
{
if(vl%2 != 0)
@@ -517,8 +580,8 @@ Reader::Visitor
value[0].resize(vl);
for(unsigned int i=0; i<value[0].size(); i+=2)
{
- odil_read_binary(
- uint16_t, item, this->stream, this->byte_ordering, 16);
+ auto const item = Reader::read_binary<uint16_t>(
+ this->stream, this->byte_ordering);
*reinterpret_cast<uint16_t*>(&value[0][i]) = item;
}
}
@@ -536,25 +599,26 @@ Reader::Visitor
uint32_t length;
if(this->explicit_vr)
{
- if(vr == VR::OB || vr == VR::OW || vr == VR::OF || vr == VR::SQ ||
- vr == VR::UC || vr == VR::UR || vr == VR::UT || vr == VR::UN)
+ if(vr == VR::OB || vr == VR::OD || vr == VR::OF || vr == VR::OL ||
+ vr == VR::OW || vr == VR::OF || vr == VR::SQ || vr == VR::UC ||
+ vr == VR::UR || vr == VR::UT || vr == VR::UN)
{
- odil_ignore(this->stream, 2);
- odil_read_binary(
- uint32_t, vl, this->stream, this->byte_ordering, 32);
+ Reader::ignore(this->stream, 2);
+ auto const vl = Reader::read_binary<uint32_t>(
+ this->stream, this->byte_ordering);
length = vl;
}
else
{
- odil_read_binary(
- uint16_t, vl, this->stream, this->byte_ordering, 16);
+ auto const vl = Reader::read_binary<uint16_t>(
+ this->stream, this->byte_ordering);
length = vl;
}
}
else
{
- odil_read_binary(
- uint32_t, vl, this->stream, this->byte_ordering, 32);
+ auto const vl = Reader::read_binary<uint32_t>(
+ this->stream, this->byte_ordering);
length = vl;
}
@@ -590,8 +654,8 @@ DataSet
Reader::Visitor
::read_item(std::istream & specific_stream) const
{
- odil_read_binary(
- uint32_t, item_length, specific_stream, this->byte_ordering, 32);
+ auto const item_length = Reader::read_binary<uint32_t>(
+ specific_stream, this->byte_ordering);
DataSet item;
if(item_length != 0xffffffff)
@@ -616,60 +680,10 @@ Reader::Visitor
{
throw Exception("Unexpected tag: "+std::string(tag));
}
- odil_ignore(specific_stream, 4);
+ Reader::ignore(specific_stream, 4);
}
return item;
}
-Value::Binary
-Reader::Visitor
-::read_encapsulated_pixel_data(std::istream & specific_stream) const
-{
- Value::Binary value;
-
- // PS 3.5, A.4
- Reader const sequence_reader(
- specific_stream, this->transfer_syntax, this->keep_group_length);
- bool done = false;
- while(!done)
- {
- auto const tag = sequence_reader.read_tag();
- odil_read_binary(
- uint32_t, item_length, specific_stream, this->byte_ordering, 32);
-
- if(tag == registry::Item)
- {
- Value::Binary::value_type item_data(item_length);
-
- if(item_length > 0)
- {
- specific_stream.read(
- reinterpret_cast<char*>(&item_data[0]), item_length);
- if(!stream)
- {
- throw Exception("Could not read from stream");
- }
- }
-
- value.push_back(item_data);
- }
- else if(tag == registry::SequenceDelimitationItem)
- {
- // No value for Sequence Delimitation Item
- done = true;
- }
- else
- {
- throw Exception(
- "Expected SequenceDelimitationItem, got: "+std::string(tag));
- }
- }
-
- return value;
-}
-
}
-
-#undef odil_ignore
-#undef odil_read_binary
diff --git a/src/odil/Reader.h b/src/odil/Reader.h
index 00e1fb5..5ae85a5 100644
--- a/src/odil/Reader.h
+++ b/src/odil/Reader.h
@@ -44,6 +44,21 @@ public:
bool keep_group_length;
/**
+ * @brief Read binary data from an stream encoded with the given endianness,
+ * ensure stream is still good.
+ */
+ template<typename T>
+ static T read_binary(std::istream & stream, ByteOrdering byte_ordering);
+
+ /// @brief Read pixel data in encapsulated form.
+ static Value::Binary read_encapsulated_pixel_data(
+ std::istream & stream, ByteOrdering byte_ordering,
+ std::string transfer_syntax, bool keep_group_length=false);
+
+ /// @brief Ignore data from a stream, ensure stream is still good.
+ static void ignore(std::istream & stream, std::streamsize size);
+
+ /**
* @brief Build a reader, derive byte ordering and explicit-ness of VR
* from transfer syntax.
*/
@@ -107,4 +122,6 @@ private:
}
+#include "odil/Reader.txx"
+
#endif // _aa2965aa_e891_4713_9c90_e8eacd2944ea
diff --git a/src/odil/Reader.txx b/src/odil/Reader.txx
new file mode 100644
index 0000000..d1f7df1
--- /dev/null
+++ b/src/odil/Reader.txx
@@ -0,0 +1,59 @@
+/*************************************************************************
+ * odil - Copyright (C) Universite de Strasbourg
+ * Distributed under the terms of the CeCILL-B license, as published by
+ * the CEA-CNRS-INRIA. Refer to the LICENSE file or to
+ * http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+ * for details.
+ ************************************************************************/
+
+#ifndef _b5ac563c_c5fd_4dcc_815c_66868a4b9614
+#define _b5ac563c_c5fd_4dcc_815c_66868a4b9614
+
+#include "odil/Reader.h"
+
+#include <functional>
+#include <istream>
+#include <string>
+#include <utility>
+
+#include "odil/DataSet.h"
+#include "odil/Element.h"
+#include "odil/Exception.h"
+#include "odil/endian.h"
+#include "odil/Tag.h"
+#include "odil/Value.h"
+#include "odil/VR.h"
+
+namespace odil
+{
+
+template<typename T>
+T
+Reader
+::read_binary(std::istream & stream, ByteOrdering byte_ordering)
+{
+ T value;
+ stream.read(reinterpret_cast<char*>(&value), sizeof(value));
+ if(!stream)
+ {
+ throw Exception("Could not read from stream");
+ }
+ if(byte_ordering == ByteOrdering::LittleEndian)
+ {
+ value = little_endian_to_host(value);
+ }
+ else if(byte_ordering == ByteOrdering::BigEndian)
+ {
+ value = big_endian_to_host(value);
+ }
+ else
+ {
+ throw Exception("Unknown endianness");
+ }
+
+ return value;
+}
+
+}
+
+#endif // _b5ac563c_c5fd_4dcc_815c_66868a4b9614
diff --git a/src/odil/SCP.cpp b/src/odil/SCP.cpp
index 6132188..854c0b6 100644
--- a/src/odil/SCP.cpp
+++ b/src/odil/SCP.cpp
@@ -22,6 +22,21 @@ SCP::DataSetGenerator
// Nothing to do.
}
+SCP::Exception
+::Exception(
+ std::string const & message,
+ Value::Integer status, DataSet const & status_fields)
+: ::odil::Exception(message), status(status), status_fields(status_fields)
+{
+ // Nothing else.
+}
+
+SCP::Exception
+::~Exception() noexcept
+{
+ // Nothing to do.
+}
+
SCP
::SCP(Association & association)
: _association(association)
diff --git a/src/odil/SCP.h b/src/odil/SCP.h
index fa0dc09..4f55077 100644
--- a/src/odil/SCP.h
+++ b/src/odil/SCP.h
@@ -11,8 +11,10 @@
#include "odil/Association.h"
#include "odil/DataSet.h"
+#include "odil/Exception.h"
#include "odil/message/Message.h"
#include "odil/message/Request.h"
+#include "odil/Value.h"
namespace odil
{
@@ -21,7 +23,11 @@ namespace odil
class SCP
{
public:
- /// @brief Abstract base class for SCP returning multiple data sets.
+ /**
+ * @brief Abstract base class for SCP returning multiple data sets.
+ *
+ * initialize, done, next and get shall throw an SCP::Exception on error.
+ */
class DataSetGenerator
{
public:
@@ -41,6 +47,24 @@ public:
virtual DataSet get() const =0;
};
+ class Exception: public odil::Exception
+ {
+ public:
+ /// @brief Status to be sent back to user.
+ Value::Integer status;
+
+ /// @brief Status detail fields (e.g. offending element).
+ DataSet status_fields;
+
+ /// @brief Constructor.
+ Exception(
+ std::string const & message,
+ Value::Integer status, DataSet const & status_fields=DataSet());
+
+ /// @brief Destructor.
+ virtual ~Exception() noexcept;
+ };
+
/// @brief Create a Service Class Provider.
SCP(Association & association);
diff --git a/src/odil/StoreSCP.cpp b/src/odil/StoreSCP.cpp
index 2dd4b79..ae1a3a9 100644
--- a/src/odil/StoreSCP.cpp
+++ b/src/odil/StoreSCP.cpp
@@ -63,20 +63,24 @@ StoreSCP
message::CStoreRequest const request(message);
Value::Integer status=message::CStoreResponse::Success;
+ DataSet status_fields;
try
{
status = this->_callback(request);
}
- catch(Exception const & exception)
+ catch(SCP::Exception const & e)
+ {
+ status = e.status;
+ status_fields = e.status_fields;
+ }
+ catch(odil::Exception const &)
{
status = message::CStoreResponse::ProcessingFailure;
- // Error Comment
- // Error ID
- // Affected SOP Class UID
}
- message::CStoreResponse const response(request.get_message_id(), status);
+ message::CStoreResponse response(request.get_message_id(), status);
+ response.set_status_fields(status_fields);
this->_association.send_message(
response, request.get_affected_sop_class_uid());
}
diff --git a/src/odil/StoreSCP.h b/src/odil/StoreSCP.h
index f92b71f..55ac977 100644
--- a/src/odil/StoreSCP.h
+++ b/src/odil/StoreSCP.h
@@ -24,7 +24,10 @@ namespace odil
class StoreSCP: public SCP
{
public:
- /// @brief Callback called when a request is received.
+ /**
+ * @brief Callback called when a request is received, shall throw an
+ * SCP::Exception on error.
+ */
typedef std::function<Value::Integer(message::CStoreRequest const &)> Callback;
/// @brief Constructor.
diff --git a/src/odil/VR.cpp b/src/odil/VR.cpp
index 6d1e828..eb76674 100644
--- a/src/odil/VR.cpp
+++ b/src/odil/VR.cpp
@@ -141,7 +141,9 @@ bool is_string(VR vr)
bool is_binary(VR vr)
{
- return (vr == VR::OB || vr == VR::OF || vr == VR::OW || vr == VR::UN);
+ return (
+ vr == VR::OB || vr == VR::OD || vr == VR::OF || vr == VR::OL ||
+ vr == VR::OW || vr == VR::UN);
}
diff --git a/src/odil/Writer.cpp b/src/odil/Writer.cpp
index 9d98e2f..55186b2 100644
--- a/src/odil/Writer.cpp
+++ b/src/odil/Writer.cpp
@@ -26,31 +26,40 @@
#include "odil/VR.h"
#include "odil/write_ds.h"
-#define odil_write_binary(value, stream, byte_ordering, size) \
-{ \
- auto raw = value; \
- if(byte_ordering == ByteOrdering::LittleEndian) \
- { \
- raw = host_to_little_endian(raw); \
- } \
- else if(byte_ordering == ByteOrdering::BigEndian) \
- { \
- raw = host_to_big_endian(raw); \
- } \
- else \
- { \
- throw Exception("Unknown endianness"); \
- } \
- stream.write(reinterpret_cast<char const*>(&raw), sizeof(raw)); \
- if(!stream) \
- { \
- throw Exception("Could not write to stream"); \
- } \
-}
-
namespace odil
{
+void
+Writer
+::write_encapsulated_pixel_data(
+ Value::Binary const & value, std::ostream & stream,
+ ByteOrdering byte_ordering, bool explicit_vr)
+{
+ Writer writer(stream, byte_ordering, explicit_vr);
+ uint32_t length;
+ for(auto const & fragment: value)
+ {
+ writer.write_tag(registry::Item);
+ length = fragment.size();
+ Writer::write_binary(length, stream, byte_ordering);
+ if(length > 0)
+ {
+ stream.write(reinterpret_cast<char const*>(&fragment[0]), length);
+ if(!stream)
+ {
+ throw Exception("Could not write to stream");
+ }
+ }
+ }
+ writer.write_tag(registry::SequenceDelimitationItem);
+ length = 0;
+ Writer::write_binary(length, stream, byte_ordering);
+ if(!stream)
+ {
+ throw Exception("Could not write to stream");
+ }
+}
+
Writer
::Writer(
std::ostream & stream,
@@ -132,8 +141,8 @@ void
Writer
::write_tag(Tag const & tag) const
{
- odil_write_binary(tag.group, this->stream, this->byte_ordering, 16);
- odil_write_binary(tag.element, this->stream, this->byte_ordering, 16);
+ this->write_binary(tag.group, this->stream, this->byte_ordering);
+ this->write_binary(tag.element, this->stream, this->byte_ordering);
}
void
@@ -165,10 +174,11 @@ Writer
// Write VL
if(this->explicit_vr)
{
- if(vr == VR::OB || vr == VR::OW || vr == VR::OF || vr == VR::SQ ||
- vr == VR::UC || vr == VR::UR || vr == VR::UT || vr == VR::UN)
+ if(vr == VR::OB || vr == VR::OD || vr == VR::OF || vr == VR::OL ||
+ vr == VR::OW || vr == VR::OF || vr == VR::SQ || vr == VR::UC ||
+ vr == VR::UR || vr == VR::UT || vr == VR::UN)
{
- odil_write_binary(uint16_t(0), this->stream, this->byte_ordering, 16);
+ this->write_binary(uint16_t(0), this->stream, this->byte_ordering);
uint32_t vl;
if(vr == VR::SQ &&
@@ -176,7 +186,7 @@ Writer
{
vl = 0xffffffff;
}
- else if((vr == VR::OB || vr == VR::OW) && element.size() > 1)
+ else if(is_binary(vr) && element.size() > 1)
{
vl = 0xffffffff;
}
@@ -184,16 +194,18 @@ Writer
{
vl = value_stream.tellp();
}
- odil_write_binary(vl, this->stream, this->byte_ordering, 32);
+ this->write_binary(vl, this->stream, this->byte_ordering);
}
else
{
- odil_write_binary(uint16_t(value_stream.tellp()), this->stream, this->byte_ordering, 16);
+ this->write_binary(
+ uint16_t(value_stream.tellp()), this->stream, this->byte_ordering);
}
}
else
{
- odil_write_binary(uint32_t(value_stream.tellp()), this->stream, this->byte_ordering, 32);
+ this->write_binary(
+ uint32_t(value_stream.tellp()), this->stream, this->byte_ordering);
}
this->stream.write(value_stream.str().c_str(), value_stream.tellp());
@@ -314,32 +326,32 @@ Writer::Visitor
{
for(auto item: value)
{
- odil_write_binary(
- int32_t(item), this->stream, this->byte_ordering, 32);
+ Writer::write_binary(
+ int32_t(item), this->stream, this->byte_ordering);
}
}
else if(this->vr == VR::SS)
{
for(auto item: value)
{
- odil_write_binary(
- int16_t(item), this->stream, this->byte_ordering, 16);
+ Writer::write_binary(
+ int16_t(item), this->stream, this->byte_ordering);
}
}
else if(this->vr == VR::UL)
{
for(auto item: value)
{
- odil_write_binary(
- uint32_t(item), this->stream, this->byte_ordering, 32);
+ Writer::write_binary(
+ uint32_t(item), this->stream, this->byte_ordering);
}
}
else if(this->vr == VR::AT || this->vr == VR::US)
{
for(auto item: value)
{
- odil_write_binary(
- uint16_t(item), this->stream, this->byte_ordering, 16);
+ Writer::write_binary(
+ uint16_t(item), this->stream, this->byte_ordering);
}
}
else
@@ -364,9 +376,10 @@ Writer::Visitor
throw Exception("DS items must be finite");
}
- // Each item in the DS is at most 16 bytes.
- static char buffer[16];
- write_ds(item, buffer, 16);
+ // Each item in the DS is at most 16 bytes, account for NUL at end
+ static unsigned int const buffer_size=16+1;
+ static char buffer[buffer_size];
+ write_ds(item, buffer, buffer_size);
auto const length = strlen(buffer);
this->stream.write(buffer, length);
@@ -396,16 +409,16 @@ Writer::Visitor
{
for(auto const & item: value)
{
- odil_write_binary(
- double(item), this->stream, this->byte_ordering, 64);
+ Writer::write_binary(
+ double(item), this->stream, this->byte_ordering);
}
}
else if(this->vr == VR::FL)
{
for(auto const & item: value)
{
- odil_write_binary(
- float(item), this->stream, this->byte_ordering, 32);
+ Writer::write_binary(
+ float(item), this->stream, this->byte_ordering);
}
}
else
@@ -467,7 +480,7 @@ Writer::Visitor
{
item_length = 0xffffffff;
}
- odil_write_binary(item_length, sequence_stream, this->byte_ordering, 32);
+ Writer::write_binary(item_length, sequence_stream, this->byte_ordering);
// Data set
sequence_stream.write(item_stream.str().c_str(), item_stream.tellp());
@@ -480,7 +493,7 @@ Writer::Visitor
if(this->item_encoding == ItemEncoding::UndefinedLength)
{
sequence_writer.write_tag(registry::ItemDelimitationItem);
- odil_write_binary(uint32_t(0), sequence_stream, this->byte_ordering, 32);
+ Writer::write_binary(uint32_t(0), sequence_stream, this->byte_ordering);
}
}
@@ -488,7 +501,7 @@ Writer::Visitor
if(this->item_encoding == ItemEncoding::UndefinedLength)
{
sequence_writer.write_tag(registry::SequenceDelimitationItem);
- odil_write_binary(uint32_t(0), sequence_stream, this->byte_ordering, 32);
+ Writer::write_binary(uint32_t(0), sequence_stream, this->byte_ordering);
}
this->stream.write(sequence_stream.str().c_str(), sequence_stream.tellp());
@@ -508,7 +521,8 @@ Writer::Visitor
}
else if(value.size() > 1)
{
- this->write_encapsulated_pixel_data(value);
+ Writer::write_encapsulated_pixel_data(
+ value, this->stream, this->byte_ordering, this->explicit_vr);
}
else
{
@@ -526,7 +540,7 @@ Writer::Visitor
for(int i=0; i<value[0].size(); i+=2)
{
uint16_t item = *reinterpret_cast<uint16_t const *>(&value[0][i]);
- odil_write_binary(item, this->stream, this->byte_ordering, 16);
+ Writer::write_binary(item, this->stream, this->byte_ordering);
}
}
else if(this->vr == VR::OF)
@@ -538,7 +552,7 @@ Writer::Visitor
for(int i=0; i<value[0].size(); i+=4)
{
uint32_t item = *reinterpret_cast<uint32_t const *>(&value[0][i]);
- odil_write_binary(item, this->stream, this->byte_ordering, 32);
+ Writer::write_binary(item, this->stream, this->byte_ordering);
}
}
else
@@ -601,38 +615,4 @@ Writer::Visitor
}
}
-void
-Writer::Visitor
-::write_encapsulated_pixel_data(Value::Binary const & value) const
-{
- // FIXME: handle fragments
- Writer writer(this->stream, this->byte_ordering, this->explicit_vr);
- uint32_t length;
- for(auto const & fragment: value)
- {
- writer.write_tag(registry::Item);
- length = fragment.size();
- odil_write_binary(
- length, this->stream, this->byte_ordering, 8*sizeof(length));
- if(length > 0)
- {
- this->stream.write(reinterpret_cast<char const*>(&fragment[0]), length);
- if(!this->stream)
- {
- throw Exception("Could not write to stream");
- }
- }
- }
- writer.write_tag(registry::SequenceDelimitationItem);
- length = 0;
- odil_write_binary(
- length, this->stream, this->byte_ordering, 8*sizeof(length));
- if(!this->stream)
- {
- throw Exception("Could not write to stream");
- }
}
-
-}
-
-#undef odil_write_binary
diff --git a/src/odil/Writer.h b/src/odil/Writer.h
index 4998307..b7eb63f 100644
--- a/src/odil/Writer.h
+++ b/src/odil/Writer.h
@@ -46,6 +46,19 @@ public:
/// @brief Presence of group length elements.
bool use_group_length;
+ /**
+ * @brief Write binary data to an stream encoded with the given endianness,
+ * ensure stream is still good.
+ */
+ template<typename T>
+ static void write_binary(
+ T const & value, std::ostream & stream, ByteOrdering byte_ordering);
+
+ /// @brief Write pixel data in encapsulated form.
+ static void write_encapsulated_pixel_data(
+ Value::Binary const & value, std::ostream & stream,
+ ByteOrdering byte_ordering, bool explicit_vr);
+
/// @brief Build a writer.
Writer(
std::ostream & stream,
@@ -107,11 +120,11 @@ private:
template<typename T>
void write_strings(T const & sequence, char padding) const;
-
- void write_encapsulated_pixel_data(Value::Binary const & value) const;
};
};
}
+#include "odil/Writer.txx"
+
#endif // _ca5c06d2_04f9_4009_9e98_5607e1060379
diff --git a/src/odil/Writer.txx b/src/odil/Writer.txx
new file mode 100644
index 0000000..bd5f0c5
--- /dev/null
+++ b/src/odil/Writer.txx
@@ -0,0 +1,56 @@
+/*************************************************************************
+ * odil - Copyright (C) Universite de Strasbourg
+ * Distributed under the terms of the CeCILL-B license, as published by
+ * the CEA-CNRS-INRIA. Refer to the LICENSE file or to
+ * http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+ * for details.
+ ************************************************************************/
+
+#ifndef _2e568ec2_62fc_43e5_8342_b4511db705e3
+#define _2e568ec2_62fc_43e5_8342_b4511db705e3
+
+#include "odil/Writer.h"
+
+#include <ostream>
+#include <string>
+
+#include "odil/DataSet.h"
+#include "odil/Element.h"
+#include "odil/endian.h"
+#include "odil/registry.h"
+#include "odil/Tag.h"
+#include "odil/Value.h"
+#include "odil/VR.h"
+
+namespace odil
+{
+
+template<typename T>
+void
+Writer
+::write_binary(
+ T const & value, std::ostream & stream, ByteOrdering byte_ordering)
+{
+ auto raw = value;
+ if(byte_ordering == ByteOrdering::LittleEndian)
+ {
+ raw = host_to_little_endian(raw);
+ }
+ else if(byte_ordering == ByteOrdering::BigEndian)
+ {
+ raw = host_to_big_endian(raw);
+ }
+ else
+ {
+ throw Exception("Unknown endianness");
+ }
+ stream.write(reinterpret_cast<char const*>(&raw), sizeof(raw));
+ if(!stream)
+ {
+ throw Exception("Could not write to stream");
+ }
+}
+
+}
+
+#endif // _2e568ec2_62fc_43e5_8342_b4511db705e3
diff --git a/src/odil/dcmtk/Exception.cpp b/src/odil/dcmtk/Exception.cpp
index d918f27..d7eb13d 100644
--- a/src/odil/dcmtk/Exception.cpp
+++ b/src/odil/dcmtk/Exception.cpp
@@ -43,7 +43,7 @@ Exception
char const *
Exception
-::what() const throw()
+::what() const noexcept
{
if(this->_source == Source::Message)
{
diff --git a/src/odil/json_converter.cpp b/src/odil/json_converter.cpp
index dfd4f55..4ecd7d6 100644
--- a/src/odil/json_converter.cpp
+++ b/src/odil/json_converter.cpp
@@ -260,7 +260,7 @@ DataSet as_dataset(Json::Value const & json)
element.as_data_set().push_back(dicom_item);
}
}
- else if(vr == VR::OB || vr == VR::OF || vr == VR::OW || vr == VR::UN)
+ else if(is_binary(vr))
{
element = Element(Value::Binary(), vr);
diff --git a/src/odil/message/Response.cpp b/src/odil/message/Response.cpp
index fdeb491..85c2aa4 100644
--- a/src/odil/message/Response.cpp
+++ b/src/odil/message/Response.cpp
@@ -92,6 +92,23 @@ Response
return Response::is_failure(this->get_status());
}
+void
+Response
+::set_status_fields(DataSet const & status_fields)
+{
+ for(auto const & tag_and_element: status_fields)
+ {
+ if(this->_command_set.has(tag_and_element.first))
+ {
+ this->_command_set[tag_and_element.first] = tag_and_element.second;
+ }
+ else
+ {
+ this->_command_set.add(tag_and_element.first, tag_and_element.second);
+ }
+ }
+}
+
}
}
diff --git a/src/odil/message/Response.h b/src/odil/message/Response.h
index a1f44b6..fe1dd80 100644
--- a/src/odil/message/Response.h
+++ b/src/odil/message/Response.h
@@ -86,6 +86,17 @@ public:
message_id_being_responded_to, registry::MessageIDBeingRespondedTo)
ODIL_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(status, registry::Status)
+ ODIL_MESSAGE_OPTIONAL_FIELD_STRING_MACRO(
+ offending_element, registry::OffendingElement)
+ ODIL_MESSAGE_OPTIONAL_FIELD_STRING_MACRO(
+ error_comment, registry::ErrorComment)
+ ODIL_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(
+ error_id, registry::ErrorID)
+ ODIL_MESSAGE_OPTIONAL_FIELD_STRING_MACRO(
+ affected_sop_instance_uid, odil::registry::AffectedSOPInstanceUID)
+ ODIL_MESSAGE_OPTIONAL_FIELD_STRING_MACRO(
+ attribute_identifier_list, odil::registry::AttributeIdentifierList)
+
/// @brief Test whether the status class is pending.
bool is_pending() const;
@@ -94,6 +105,9 @@ public:
/// @brief Test whether the status class is failure.
bool is_failure() const;
+
+ /// @brief Set the status fields (cf. PS.37, C)
+ void set_status_fields(DataSet const & status_fields);
};
}
diff --git a/src/odil/xml_converter.cpp b/src/odil/xml_converter.cpp
index a97ce16..94a7944 100644
--- a/src/odil/xml_converter.cpp
+++ b/src/odil/xml_converter.cpp
@@ -545,7 +545,7 @@ DataSet as_dataset(boost::property_tree::ptree const & xml)
element.as_data_set().push_back(it->second);
}
}
- else if(vr == VR::OB || vr == VR::OF || vr == VR::OW || vr == VR::UN)
+ else if(is_binary(vr))
{
element = Element(Value::Binary(), vr);
diff --git a/tests/code/message/Response.cpp b/tests/code/message/Response.cpp
index 616772c..a8adab1 100644
--- a/tests/code/message/Response.cpp
+++ b/tests/code/message/Response.cpp
@@ -110,3 +110,17 @@ BOOST_AUTO_TEST_CASE(StatusFailure)
BOOST_REQUIRE(response.is_failure());
}
}
+
+BOOST_AUTO_TEST_CASE(StatusDetails)
+{
+ odil::message::Response response(
+ 1234, odil::message::Response::SOPClassNotSupported);
+ odil::DataSet status_details;
+ status_details.add(
+ odil::registry::ErrorComment, {"This is the error comment"});
+ response.set_status_fields(status_details);
+
+ BOOST_REQUIRE(response.has_error_comment());
+ BOOST_REQUIRE_EQUAL(
+ response.get_error_comment(), "This is the error comment");
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/odil.git
More information about the debian-med-commit
mailing list