[med-svn] [odil] 01/03: Imported Upstream version 0.7.0
Julien Lamy
lamy-guest at moszumanska.debian.org
Tue Jun 14 14:28:00 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 63a87252558431a075cb1a44cc3d4c1a29939510
Author: Julien Lamy <lamy at unistra.fr>
Date: Tue Jun 14 16:12:27 2016 +0200
Imported Upstream version 0.7.0
---
.travis.yml | 5 +-
CMakeLists.txt | 5 +-
FindLog4Cpp.cmake | 24 +++++
applications/find.py | 2 +-
applications/print_.py | 2 +-
registry.h.tmpl | 7 +-
src/CMakeLists.txt | 17 ++-
src/odil/Association.cpp | 58 ++++++++--
src/odil/AssociationAcceptor.cpp | 4 +-
src/odil/DataSet.cpp | 109 ++++++++++---------
src/odil/DataSet.h | 55 ++++++----
src/odil/MoveSCP.cpp | 5 +-
src/odil/Reader.cpp | 8 +-
src/odil/Reader.h | 3 +-
src/odil/StoreSCU.cpp | 9 +-
src/odil/StoreSCU.h | 7 +-
src/odil/VRFinder.cpp | 42 ++++----
src/odil/Writer.cpp | 41 +++++++-
src/odil/dul/StateMachine.cpp | 11 +-
src/odil/logging.cpp | 37 +++++++
src/odil/logging.h | 17 +++
src/odil/message/CStoreRequest.cpp | 9 +-
src/odil/message/CStoreRequest.h | 4 +-
src/odil/pdu/Item.cpp | 9 +-
src/odil/pdu/UserInformation.cpp | 38 ++++++-
src/odil/registry.cpp | 26 ++++-
src/odil/registry.h | 6 ++
src/odil/uid.cpp | 4 +-
src/odil/uid.h | 6 +-
src/odil/write_ds.cpp | 210 +++++++++++++++++++++++++++++++++++++
src/odil/write_ds.h | 25 +++++
tests/CMakeLists.txt | 18 +++-
tests/code/DataSet.cpp | 16 +++
tests/code/VRFinder.cpp | 35 +++----
tests/code/Writer.cpp | 2 +-
tests/code/unicode.cpp | 1 -
tests/run | 2 +-
tests/wrappers/test_data_set.py | 14 +++
wrappers/DataSet.cpp | 8 +-
wrappers/StoreSCU.cpp | 7 +-
wrappers/read.cpp | 29 +++--
41 files changed, 762 insertions(+), 175 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 9b19003..4f694f8 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -20,6 +20,7 @@ addons:
- libboost-python-dev
- libboost-regex-dev
- libboost-test-dev
+ - liblog4cpp5-dev
- dcmtk
- ninja-build
- cmake
@@ -30,7 +31,7 @@ before_install:
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew uninstall json-c; fi
# Boost is already installed with another version
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew unlink boost; brew install boost boost-python; fi
- - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew install dcmtk icu4c jsoncpp ninja; fi
+ - if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew install dcmtk icu4c jsoncpp log4cpp ninja; fi
- pip install --user cpp-coveralls nose
- export PATH=$(python -c 'import site; print(site.getuserbase())')/bin:${PATH}
before_script:
@@ -40,7 +41,7 @@ before_script:
- export BIN_DIR=$PWD
- CMAKE_CXX_FLAGS="-std=c++11"
- if [ "${CC}" = "gcc" ]; then CMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} --coverage"; fi
- - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export PKG_CONFIG_PATH=/usr/local/opt/icu4c/lib/pkgconfig; fi
+ - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:/usr/local/opt/icu4c/lib/pkgconfig; fi
- cmake -G Ninja -D CMAKE_CXX_FLAGS:STRING="${CMAKE_CXX_FLAGS}" -D CMAKE_BUILD_TYPE:STRING=Debug ../
script:
- ninja
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a8f8a62..c5a1276 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,15 +2,16 @@ cmake_minimum_required(VERSION 2.8)
project("odil")
set(odil_MAJOR_VERSION 0)
-set(odil_MINOR_VERSION 5)
+set(odil_MINOR_VERSION 7)
set(odil_PATCH_VERSION 0)
set(odil_VERSION
${odil_MAJOR_VERSION}.${odil_MINOR_VERSION}.${odil_PATCH_VERSION})
+option(BUILD_SHARED_LIBS "Build Odil with shared libraries." ON)
option(BUILD_EXAMPLES "Build the examples directory." ON)
option(BUILD_WRAPPERS "Build the Python Wrappers." ON)
-option(BUILD_SHARED_LIBS "Build Odil with shared libraries." ON)
+option(WITH_DCMTK "Build the DCMTK converter" ON)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}" ${CMAKE_MODULE_PATH})
include(CTest)
diff --git a/FindLog4Cpp.cmake b/FindLog4Cpp.cmake
new file mode 100644
index 0000000..7f83da4
--- /dev/null
+++ b/FindLog4Cpp.cmake
@@ -0,0 +1,24 @@
+# - Try to find Log4Cpp
+# Once done this will define
+# Log4Cpp_FOUND - System has Log4Cpp
+# Log4Cpp_INCLUDE_DIRS - The Log4Cpp include directories
+# Log4Cpp_LIBRARIES - The libraries needed to use Log4Cpp
+# Log4Cpp_DEFINITIONS - Compiler switches required for using Log4Cpp
+
+find_package(PkgConfig)
+pkg_check_modules(PC_Log4Cpp QUIET log4cpp)
+set(Log4Cpp_DEFINITIONS ${PC_Log4Cpp_CFLAGS_OTHER})
+
+find_path(Log4Cpp_INCLUDE_DIR "log4cpp/Category.hh" HINTS ${PC_Log4Cpp_INCLUDE_DIRS})
+find_library(Log4Cpp_LIBRARY NAMES log4cpp HINTS ${PC_Log4Cpp_LIBRARY_DIRS} )
+
+set(Log4Cpp_LIBRARIES ${Log4Cpp_LIBRARY} )
+set(Log4Cpp_INCLUDE_DIRS ${Log4Cpp_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+# handle the QUIETLY and REQUIRED arguments and set Log4Cpp_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(
+ Log4Cpp DEFAULT_MSG Log4Cpp_LIBRARY Log4Cpp_INCLUDE_DIR)
+
+mark_as_advanced(Log4Cpp_INCLUDE_DIR Log4Cpp_LIBRARY)
diff --git a/applications/find.py b/applications/find.py
index e522386..f3e256d 100644
--- a/applications/find.py
+++ b/applications/find.py
@@ -80,7 +80,7 @@ def find(host, port, calling_ae_title, called_ae_title, level, keys, decode_uids
for data_set in data_sets:
print_data_set(data_set, decode_uids, "", max_length)
- print
+ print()
association.release()
logging.info("Association released")
diff --git a/applications/print_.py b/applications/print_.py
index ce15da1..2a7be04 100644
--- a/applications/print_.py
+++ b/applications/print_.py
@@ -29,7 +29,7 @@ def print_(inputs, print_header, decode_uids):
if print_header:
print_data_set(header, decode_uids, "", max_length)
- print
+ print()
print_data_set(data_set, decode_uids, "", max_length)
def print_data_set(data_set, decode_uids, padding, max_length):
diff --git a/registry.h.tmpl b/registry.h.tmpl
index dc82d5c..a9c8728 100644
--- a/registry.h.tmpl
+++ b/registry.h.tmpl
@@ -12,6 +12,7 @@
#include <map>
#include <string>
+#include "odil/odil.h"
#include "odil/ElementsDictionary.h"
#include "odil/Tag.h"
#include "odil/UIDsDictionary.h"
@@ -33,9 +34,9 @@ Tag const {{ entry[2] }}({{ "0x%04x, 0x%04x"|format(*entry[0]) }});
std::string const {{ entry[2] }}("{{ entry[0] }}");
{% endfor %}
-extern ElementsDictionary public_dictionary;
-extern std::map<std::string, Tag> public_tags;
-extern UIDsDictionary uids_dictionary;
+extern ODIL_API ElementsDictionary public_dictionary;
+extern ODIL_API std::map<std::string, Tag> public_tags;
+extern ODIL_API UIDsDictionary uids_dictionary;
}
}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 47bea79..e17e565 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,19 +1,29 @@
find_package(Boost REQUIRED COMPONENTS filesystem system)
-find_package(DCMTK REQUIRED)
find_package(ICU REQUIRED)
find_package(JsonCpp REQUIRED)
+find_package(Log4Cpp REQUIRED)
+if(WITH_DCMTK)
+ find_package(DCMTK REQUIRED)
+endif()
file(GLOB_RECURSE Header_Files "*.h")
file(GLOB_RECURSE Source_Files "*.cpp")
file(GLOB_RECURSE templates "*.txx")
+if(NOT WITH_DCMTK)
+ set(pattern "${CMAKE_CURRENT_SOURCE_DIR}/odil/dcmtk/[^;]+[;$]")
+ string(REGEX REPLACE ${pattern} "" Header_Files "${Header_Files}")
+ string(REGEX REPLACE ${pattern} "" Source_Files "${Source_Files}")
+ string(REGEX REPLACE ${pattern} "" templates "${templates}")
+endif()
+
# Regroup files by folder
GroupFiles(Header_Files)
GroupFiles(Source_Files)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR} ${Boost_INCLUDE_DIRS} ${DCMTK_INCLUDE_DIRS}
- ${ICU_INCLUDE_DIRS} ${JsonCpp_INCLUDE_DIRS})
+${ICU_INCLUDE_DIRS} ${Log4Cpp_INCLUDE_DIRS} ${JsonCpp_INCLUDE_DIRS})
add_definitions(
${DCMTK_DEFINITIONS}
-D BOOST_ASIO_SEPARATE_COMPILATION
@@ -25,7 +35,8 @@ add_library(libodil ${Source_Files} ${Header_Files} ${templates})
set_target_properties(libodil PROPERTIES OUTPUT_NAME odil)
target_link_libraries(libodil
- ${Boost_LIBRARIES} ${DCMTK_LIBRARIES} ${ICU_LIBRARIES} ${JsonCpp_LIBRARIES})
+ ${Boost_LIBRARIES} ${DCMTK_LIBRARIES} ${ICU_LIBRARIES} ${JsonCpp_LIBRARIES}
+ ${Log4Cpp_LIBRARIES})
if(WIN32)
add_definitions(-DBUILDING_ODIL)
diff --git a/src/odil/Association.cpp b/src/odil/Association.cpp
index 2d0fb47..cbc8bf3 100644
--- a/src/odil/Association.cpp
+++ b/src/odil/Association.cpp
@@ -463,8 +463,8 @@ Association
command_stream, registry::ImplicitVRLittleEndian, // implicit vr for command
Writer::ItemEncoding::ExplicitLength, true); // true for Command
command_writer.write_data_set(message.get_command_set());
- pdv_items.push_back(
- pdu::PDataTF::PresentationDataValueItem(id, 3, command_stream.str()));
+ auto const command_buffer = command_stream.str();
+ pdv_items.emplace_back(id, 3, command_buffer);
if (message.has_data_set())
{
@@ -473,17 +473,55 @@ Association
data_stream, transfer_syntax,
Writer::ItemEncoding::ExplicitLength, false);
data_writer.write_data_set(message.get_data_set());
- pdv_items.push_back(
- pdu::PDataTF::PresentationDataValueItem(
- transfer_syntax_it->second.first, 2, data_stream.str()));
- }
+ auto const data_buffer = data_stream.str();
- auto pdu = std::make_shared<pdu::PDataTF>(pdv_items);
+ auto const max_length = this->_negotiated_parameters.get_maximum_length();
+ auto current_length = command_buffer.size() + 12; // 12 is the size of all that is added on top of the fragment
+ if (!max_length
+ || (current_length + data_buffer.size() + 6 < max_length))
+ { // Can send all the buffer in one go
+ pdv_items.emplace_back(transfer_syntax_it->second.first, 2, data_buffer);
- dul::EventData data;
- data.pdu = pdu;
+ dul::EventData data;
+ data.pdu = std::make_shared<pdu::PDataTF>(pdv_items);
+ this->_state_machine.send_pdu(data);
+ }
+ else // We have to fragment into multiple PDUs
+ {
+ auto available = max_length - 6 - current_length; // Need at least 6 bytes for the headers
+ int64_t remaining = data_buffer.size();
+ std::size_t offset = 0;
- this->_state_machine.send_pdu(data);
+ if (available > 0) // Send some data with the command set
+ {
+ remaining -= available;
+ pdv_items.emplace_back(transfer_syntax_it->second.first, (remaining > 0 ? 0 : 2), data_buffer.substr(0, available));
+ offset += available;
+ }
+
+ auto pdu = std::make_shared<pdu::PDataTF>(pdv_items);
+ dul::EventData data;
+ data.pdu = pdu;
+ this->_state_machine.send_pdu(data);
+
+ available = max_length - 6; // In case some software do not take into account the size of the header when allocating their buffer
+ while (remaining > 0)
+ {
+ remaining -= available;
+ pdv_items.clear();
+ pdv_items.emplace_back(transfer_syntax_it->second.first, (remaining > 0 ? 0 : 2), data_buffer.substr(offset, available));
+ offset += available;
+ pdu->set_pdv_items(pdv_items);
+ this->_state_machine.send_pdu(data);
+ }
+ }
+ }
+ else
+ {
+ dul::EventData data;
+ data.pdu = std::make_shared<pdu::PDataTF>(pdv_items);
+ this->_state_machine.send_pdu(data);
+ }
}
uint16_t
diff --git a/src/odil/AssociationAcceptor.cpp b/src/odil/AssociationAcceptor.cpp
index bcdd8b2..e5c625c 100644
--- a/src/odil/AssociationAcceptor.cpp
+++ b/src/odil/AssociationAcceptor.cpp
@@ -23,8 +23,8 @@ default_association_acceptor(AssociationParameters const & input)
{
AssociationParameters output;
- output.set_called_ae_title(input.get_calling_ae_title());
- output.set_calling_ae_title(input.get_called_ae_title());
+ output.set_called_ae_title(input.get_called_ae_title());
+ output.set_calling_ae_title(input.get_calling_ae_title());
std::vector<AssociationParameters::PresentationContext>
presentation_contexts = input.get_presentation_contexts();
diff --git a/src/odil/DataSet.cpp b/src/odil/DataSet.cpp
index cd60507..493c788 100644
--- a/src/odil/DataSet.cpp
+++ b/src/odil/DataSet.cpp
@@ -22,9 +22,10 @@ namespace odil
{
DataSet
-::DataSet()
+::DataSet(std::string const & transfer_syntax)
+: _transfer_syntax(transfer_syntax)
{
- // Nothing to do.
+ // Nothing else.
}
void
@@ -244,150 +245,150 @@ template<typename TContainer>
typename TContainer::value_type const & at_pos(
TContainer const & container, unsigned int position)
{
- if(container.size() <= position)
- {
- throw Exception("No such element");
- }
- return container[position];
+ if(container.size() <= position)
+ {
+ throw Exception("No such element");
+ }
+ return container[position];
}
bool
DataSet
-::is_int(Tag const & tag) const
-{
+::is_int(Tag const & tag) const
+{
return (*this)[tag].is_int();
}
Value::Integers const &
DataSet
-::as_int(Tag const & tag) const
-{
+::as_int(Tag const & tag) const
+{
return (*this)[tag].as_int();
}
Value::Integers &
DataSet
-::as_int(Tag const & tag)
-{
+::as_int(Tag const & tag)
+{
return (*this)[tag].as_int();
}
Value::Integer const &
DataSet
-::as_int(Tag const & tag, unsigned int position) const
-{
+::as_int(Tag const & tag, unsigned int position) const
+{
return at_pos(as_int(tag), position);
}
bool
DataSet
-::is_real(Tag const & tag) const
-{
+::is_real(Tag const & tag) const
+{
return (*this)[tag].is_real();
}
Value::Reals const &
DataSet
-::as_real(Tag const & tag) const
-{
+::as_real(Tag const & tag) const
+{
return (*this)[tag].as_real();
}
Value::Reals &
DataSet
-::as_real(Tag const & tag)
-{
+::as_real(Tag const & tag)
+{
return (*this)[tag].as_real();
}
Value::Real const &
DataSet
-::as_real(Tag const & tag, unsigned int position) const
-{
+::as_real(Tag const & tag, unsigned int position) const
+{
return at_pos(as_real(tag), position);
}
bool
DataSet
-::is_string(Tag const & tag) const
-{
+::is_string(Tag const & tag) const
+{
return (*this)[tag].is_string();
}
Value::Strings const &
DataSet
-::as_string(Tag const & tag) const
-{
+::as_string(Tag const & tag) const
+{
return (*this)[tag].as_string();
}
Value::Strings &
DataSet
-::as_string(Tag const & tag)
-{
+::as_string(Tag const & tag)
+{
return (*this)[tag].as_string();
}
Value::String const &
DataSet
-::as_string(Tag const & tag, unsigned int position) const
-{
+::as_string(Tag const & tag, unsigned int position) const
+{
return at_pos(as_string(tag), position);
}
bool
DataSet
-::is_data_set(Tag const & tag) const
-{
+::is_data_set(Tag const & tag) const
+{
return (*this)[tag].is_data_set();
}
Value::DataSets const &
DataSet
-::as_data_set(Tag const & tag) const
-{
+::as_data_set(Tag const & tag) const
+{
return (*this)[tag].as_data_set();
}
Value::DataSets &
DataSet
-::as_data_set(Tag const & tag)
-{
+::as_data_set(Tag const & tag)
+{
return (*this)[tag].as_data_set();
}
DataSet const &
DataSet
-::as_data_set(Tag const & tag, unsigned int position) const
-{
+::as_data_set(Tag const & tag, unsigned int position) const
+{
return at_pos(as_data_set(tag), position);
}
bool
DataSet
-::is_binary(Tag const & tag) const
-{
+::is_binary(Tag const & tag) const
+{
return (*this)[tag].is_binary();
}
Value::Binary const &
DataSet
-::as_binary(Tag const & tag) const
-{
+::as_binary(Tag const & tag) const
+{
return (*this)[tag].as_binary();
}
Value::Binary &
DataSet
-::as_binary(Tag const & tag)
-{
+::as_binary(Tag const & tag)
+{
return (*this)[tag].as_binary();
}
Value::Binary::value_type const &
DataSet
-::as_binary(Tag const & tag, unsigned int position) const
-{
+::as_binary(Tag const & tag, unsigned int position) const
+{
return at_pos(as_binary(tag), position);
}
@@ -451,4 +452,18 @@ DataSet
return !(*this == other);
}
+std::string const &
+DataSet
+::get_transfer_syntax() const
+{
+ return _transfer_syntax;
+}
+
+void
+DataSet
+::set_transfer_syntax(std::string const & transfer_syntax)
+{
+ this->_transfer_syntax = transfer_syntax;
+}
+
}
diff --git a/src/odil/DataSet.h b/src/odil/DataSet.h
index e8bf7ed..ff59454 100644
--- a/src/odil/DataSet.h
+++ b/src/odil/DataSet.h
@@ -29,7 +29,7 @@ class DataSet
{
public:
/// @brief Create an empty data set.
- DataSet();
+ explicit DataSet(std::string const & transfer_syntax="");
/// @brief Add an element to the dataset.
void add(Tag const & tag, Element const & element);
@@ -135,71 +135,71 @@ public:
/// @brief Test whether an existing element has integer type.
bool is_int(Tag const & tag) const;
-
+
/// @brief Return the integers contained in an existing element (read-only).
Value::Integers const & as_int(Tag const & tag) const;
-
+
/// @brief Return the integers contained in an existing element (read-write).
Value::Integers & as_int(Tag const & tag);
-
+
/// @brief Return an integer contained in an existing element (read-only).
Value::Integer const & as_int(Tag const & tag, unsigned int position) const;
-
+
/// @brief Test whether an existing element has real type.
bool is_real(Tag const & tag) const;
-
+
/// @brief Return the reals contained in an existing element (read-only).
Value::Reals const & as_real(Tag const & tag) const;
-
+
/// @brief Return the reals contained in an existing element (read-write).
Value::Reals & as_real(Tag const & tag);
-
+
/// @brief Return an real contained in an existing element (read-only).
Value::Real const & as_real(Tag const & tag, unsigned int position) const;
-
+
/// @brief Test whether an existing element has string type.
bool is_string(Tag const & tag) const;
-
+
/// @brief Return the strings contained in an existing element (read-only).
Value::Strings const & as_string(Tag const & tag) const;
-
+
/// @brief Return the strings contained in an existing element (read-write).
Value::Strings & as_string(Tag const & tag);
-
+
/// @brief Return a string contained in an existing element (read-only).
Value::String const & as_string(Tag const & tag, unsigned int position) const;
-
+
/// @brief Test whether an existing element has data set type.
bool is_data_set(Tag const & tag) const;
-
+
/// @brief Return the data sets contained in an existing element (read-only).
Value::DataSets const & as_data_set(Tag const & tag) const;
-
+
/// @brief Return the data sets contained in an existing element (read-write).
Value::DataSets & as_data_set(Tag const & tag);
-
+
/// @brief Return a data set contained in an existing element (read-only).
DataSet const & as_data_set(Tag const & tag, unsigned int position) const;
-
+
/// @brief Test whether an existing element has binary type.
bool is_binary(Tag const & tag) const;
-
+
/// @brief Return the binary items contained in an existing element (read-only).
Value::Binary const & as_binary(Tag const & tag) const;
-
+
/// @brief Return the binary items contained in an existing element (read-write).
Value::Binary & as_binary(Tag const & tag);
-
+
/// @brief Return a binary item contained in an existing element (read-only).
- Value::Binary::value_type const &
+ Value::Binary::value_type const &
as_binary(Tag const & tag, unsigned int position) const;
/// @brief Iterator to the elements.
typedef std::map<Tag, Element>::const_iterator const_iterator;
-
+
/// @brief Return an iterator to the start of the elements.
const_iterator begin() const { return this->_elements.begin(); }
-
+
/// @brief Return an iterator to the end of the elements.
const_iterator end() const { return this->_elements.end(); }
@@ -209,10 +209,19 @@ public:
/// @brief Difference test.
bool operator!=(DataSet const & other) const;
+ /// @brief Return the current transfer syntax.
+ std::string const & get_transfer_syntax() const;
+
+ /// @brief Set the current transfer syntax.
+ void set_transfer_syntax(std::string const & transfer_syntax);
+
private:
typedef std::map<Tag, Element> ElementMap;
ElementMap _elements;
+
+ /// @brief Current transfer syntax.
+ std::string _transfer_syntax;
};
}
diff --git a/src/odil/MoveSCP.cpp b/src/odil/MoveSCP.cpp
index 9b9b7bf..739925a 100644
--- a/src/odil/MoveSCP.cpp
+++ b/src/odil/MoveSCP.cpp
@@ -75,6 +75,9 @@ MoveSCP
unsigned int failed_sub_operations=0;
unsigned int warning_sub_operations=0;
+ auto const& move_originator_aet = this->_association.get_negotiated_parameters().get_calling_ae_title();
+ auto move_originator_message_id = request.get_message_id();
+
try
{
this->_generator->initialize(request);
@@ -99,7 +102,7 @@ MoveSCP
store_scu.set_affected_sop_class(data_set);
try
{
- store_scu.store(data_set);
+ store_scu.store(data_set, move_originator_aet, move_originator_message_id);
--remaining_sub_operations;
++completed_sub_operations;
diff --git a/src/odil/Reader.cpp b/src/odil/Reader.cpp
index 668c9a9..dc3dfd4 100644
--- a/src/odil/Reader.cpp
+++ b/src/odil/Reader.cpp
@@ -93,7 +93,7 @@ DataSet
Reader
::read_data_set(std::function<bool(Tag const &)> halt_condition) const
{
- DataSet data_set;
+ DataSet data_set(transfer_syntax);
bool done = (this->stream.peek() == EOF);
while(!done)
@@ -188,7 +188,9 @@ Reader
std::pair<DataSet, DataSet>
Reader
-::read_file(std::istream & stream, bool keep_group_length)
+::read_file(
+ std::istream & stream, bool keep_group_length,
+ std::function<bool(Tag const &)> halt_condition)
{
// File preamble
stream.ignore(128);
@@ -226,7 +228,7 @@ Reader
Reader data_set_reader(
stream, meta_information.as_string(registry::TransferSyntaxUID)[0],
keep_group_length);
- auto const data_set = data_set_reader.read_data_set();
+ auto const data_set = data_set_reader.read_data_set(halt_condition);
return std::pair<DataSet, DataSet>(meta_information, data_set);
}
diff --git a/src/odil/Reader.h b/src/odil/Reader.h
index 4081a31..00e1fb5 100644
--- a/src/odil/Reader.h
+++ b/src/odil/Reader.h
@@ -70,7 +70,8 @@ public:
/// @brief Return the meta-data header and data set stored in the stream.
static std::pair<DataSet, DataSet> read_file(
std::istream & stream,
- bool keep_group_length=false);
+ bool keep_group_length=false,
+ std::function<bool(Tag const &)> halt_condition = [](Tag const &) { return false;});
private:
struct Visitor
diff --git a/src/odil/StoreSCU.cpp b/src/odil/StoreSCU.cpp
index 33fa4bd..f4c7ae7 100644
--- a/src/odil/StoreSCU.cpp
+++ b/src/odil/StoreSCU.cpp
@@ -64,14 +64,19 @@ StoreSCU
void
StoreSCU
-::store(DataSet const & dataset) const
+::store(
+ DataSet const & dataset,
+ Value::String const & move_originator_ae_title,
+ Value::Integer move_originator_message_id) const
{
message::CStoreRequest const request(
this->_association.next_message_id(),
this->_affected_sop_class,
dataset.as_string(registry::SOPInstanceUID, 0),
message::Message::Priority::MEDIUM,
- dataset);
+ dataset, move_originator_ae_title,
+ move_originator_message_id);
+
this->_association.send_message(request, this->_affected_sop_class);
message::CStoreResponse const response = this->_association.receive_message();
diff --git a/src/odil/StoreSCU.h b/src/odil/StoreSCU.h
index d1f71bf..7500fa1 100644
--- a/src/odil/StoreSCU.h
+++ b/src/odil/StoreSCU.h
@@ -28,9 +28,14 @@ public:
/// @brief Set the affected SOP class based on the dataset.
void set_affected_sop_class(DataSet const & dataset);
+
+ using SCU::set_affected_sop_class;
/// @brief Perform the C-STORE.
- void store(DataSet const & dataset) const;
+ void store(
+ DataSet const & dataset,
+ Value::String const & move_originator_ae_title = "",
+ Value::Integer move_originator_message_id = -1) const;
};
}
diff --git a/src/odil/VRFinder.cpp b/src/odil/VRFinder.cpp
index 0277f90..a85b88c 100644
--- a/src/odil/VRFinder.cpp
+++ b/src/odil/VRFinder.cpp
@@ -46,7 +46,10 @@ VRFinder::operator()(
try
{
vr = finder(tag, data_set, transfer_syntax);
- break;
+ if (vr != VR::UNKNOWN)
+ {
+ break;
+ }
}
catch(Exception &)
{
@@ -61,7 +64,10 @@ VRFinder::operator()(
try
{
vr = finder(tag, data_set, transfer_syntax);
- break;
+ if (vr != VR::UNKNOWN)
+ {
+ break;
+ }
}
catch(Exception &)
{
@@ -83,7 +89,7 @@ VRFinder
::public_dictionary(
Tag const & tag, DataSet const &, std::string const &)
{
- VR vr = VR::INVALID;
+ VR vr = VR::UNKNOWN;
auto const iterator = find(registry::public_dictionary, tag);
if(iterator != registry::public_dictionary.end())
@@ -91,12 +97,6 @@ VRFinder
vr = as_vr(iterator->second.vr);
}
- if(vr == VR::INVALID)
- {
- throw Exception(
- "Element " + std::string(tag) + " is not in the public dictionary");
- }
-
return vr;
}
@@ -109,10 +109,8 @@ VRFinder
{
return VR::UL;
}
- else
- {
- throw Exception("Not a group length tag");
- }
+
+ return VR::UNKNOWN; // Not a group length tag
}
VR
@@ -124,10 +122,8 @@ VRFinder
{
return VR::UN;
}
- else
- {
- throw Exception("Not a private tag");
- }
+
+ return VR::UNKNOWN; // Not a private tag
}
VR
@@ -194,12 +190,14 @@ VRFinder
}
else
{
- throw Exception("Unknown tag");
+ // Unknown tag
+ return VR::UNKNOWN;
}
}
else
{
- throw Exception("Unknown transfer syntax");
+ // Unknown transfer syntax
+ return VR::UNKNOWN;
}
}
@@ -273,12 +271,14 @@ VRFinder
}
else
{
- throw Exception("Unknown tag");
+ // Unknown tag
+ return VR::UNKNOWN;
}
}
else
{
- throw Exception("Unknown transfer syntax");
+ // Unknown transfer syntax
+ return VR::UNKNOWN;
}
}
diff --git a/src/odil/Writer.cpp b/src/odil/Writer.cpp
index 96b8efd..9d98e2f 100644
--- a/src/odil/Writer.cpp
+++ b/src/odil/Writer.cpp
@@ -8,7 +8,9 @@
#include "odil/Writer.h"
+#include <cmath>
#include <cstdint>
+#include <cstring>
#include <map>
#include <ostream>
#include <sstream>
@@ -22,6 +24,7 @@
#include "odil/Tag.h"
#include "odil/uid.h"
#include "odil/VR.h"
+#include "odil/write_ds.h"
#define odil_write_binary(value, stream, byte_ordering, size) \
{ \
@@ -351,7 +354,43 @@ Writer::Visitor
{
if(this->vr == VR::DS)
{
- this->write_strings(value, ' ');
+ int written = 0;
+ for(int i=0; i<value.size(); ++i)
+ {
+ auto const & item = value[i];
+
+ if(!std::isfinite(item))
+ {
+ 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);
+ auto const length = strlen(buffer);
+
+ this->stream.write(buffer, length);
+ written += length;
+ if(!this->stream.good())
+ {
+ throw Exception("Could not write DS");
+ }
+
+ if(i<value.size()-1)
+ {
+ this->stream.put('\\');
+ written += 1;
+ if(!this->stream.good())
+ {
+ throw Exception("Could not write DS");
+ }
+ }
+ };
+ if(written % 2 == 1)
+ {
+ this->stream.put(' ');
+ }
+ //this->write_strings(value, ' ');
}
else if(this->vr == VR::FD)
{
diff --git a/src/odil/dul/StateMachine.cpp b/src/odil/dul/StateMachine.cpp
index 6f6dc5a..13d451b 100644
--- a/src/odil/dul/StateMachine.cpp
+++ b/src/odil/dul/StateMachine.cpp
@@ -697,8 +697,15 @@ void
StateMachine
::AA_1(EventData & data)
{
- data.pdu = std::make_shared<pdu::AAbort>(1, 2);
- this->send_pdu(data);
+ if(std::dynamic_pointer_cast<pdu::AAbort>(data.pdu))
+ {
+ this->_send_pdu(data, 0x07);
+ }
+ else
+ {
+ data.pdu = std::make_shared<pdu::AAbort>(1, 2);
+ this->send_pdu(data);
+ }
this->start_timer(data);
}
diff --git a/src/odil/logging.cpp b/src/odil/logging.cpp
new file mode 100644
index 0000000..52e9626
--- /dev/null
+++ b/src/odil/logging.cpp
@@ -0,0 +1,37 @@
+/*************************************************************************
+ * 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.
+ ************************************************************************/
+
+#include <iostream>
+
+#include <log4cpp/Category.hh>
+#include <log4cpp/OstreamAppender.hh>
+#include <log4cpp/Priority.hh>
+
+namespace odil
+{
+
+namespace logging
+{
+
+bool configure()
+{
+ auto * appender = new log4cpp::OstreamAppender("console", &std::cout);
+ appender->setLayout(new log4cpp::BasicLayout());
+
+ auto & root = log4cpp::Category::getRoot();
+ root.setPriority(log4cpp::Priority::WARN);
+ root.addAppender(appender);
+
+ return true;
+}
+
+static bool const configured = configure();
+
+}
+
+}
diff --git a/src/odil/logging.h b/src/odil/logging.h
new file mode 100644
index 0000000..21b2677
--- /dev/null
+++ b/src/odil/logging.h
@@ -0,0 +1,17 @@
+/*************************************************************************
+ * 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 _5382f5e0_e993_4966_9447_542844edb635
+#define _5382f5e0_e993_4966_9447_542844edb635
+
+#include <log4cpp/Category.hh>
+#include <log4cpp/Priority.hh>
+
+#define ODIL_LOG(level) log4cpp::Category::getRoot() << log4cpp::Priority::level
+
+#endif // _5382f5e0_e993_4966_9447_542844edb635
diff --git a/src/odil/message/CStoreRequest.cpp b/src/odil/message/CStoreRequest.cpp
index e6b1b56..5cd1f31 100644
--- a/src/odil/message/CStoreRequest.cpp
+++ b/src/odil/message/CStoreRequest.cpp
@@ -23,7 +23,9 @@ CStoreRequest
::CStoreRequest(
Value::Integer message_id, Value::String const & affected_sop_class_uid,
Value::String const & affected_sop_instance_uid,
- Value::Integer priority, DataSet const & dataset)
+ Value::Integer priority, DataSet const & dataset,
+ Value::String const & move_originator_ae_title,
+ Value::Integer move_originator_message_id)
: Request(message_id)
{
this->set_command_field(Command::C_STORE_RQ);
@@ -31,6 +33,11 @@ CStoreRequest
this->set_affected_sop_instance_uid(affected_sop_instance_uid);
this->set_priority(priority);
+ if(!move_originator_ae_title.empty())
+ this->set_move_originator_ae_title(move_originator_ae_title);
+ if(move_originator_message_id >= 0)
+ this->set_move_originator_message_id(move_originator_message_id);
+
if(dataset.empty())
{
throw Exception("Data set is required");
diff --git a/src/odil/message/CStoreRequest.h b/src/odil/message/CStoreRequest.h
index 2f20ac8..0c08e26 100644
--- a/src/odil/message/CStoreRequest.h
+++ b/src/odil/message/CStoreRequest.h
@@ -33,7 +33,9 @@ public:
CStoreRequest(
Value::Integer message_id, Value::String const & affected_sop_class_uid,
Value::String const & affected_sop_instance_uid,
- Value::Integer priority, DataSet const & dataset);
+ Value::Integer priority, DataSet const & dataset,
+ Value::String const & move_originator_ae_title = "",
+ Value::Integer move_originator_message_id = -1);
/**
* @brief Create a C-STORE-RQ from a generic Message.
diff --git a/src/odil/pdu/Item.cpp b/src/odil/pdu/Item.cpp
index 368a50a..dc78a50 100644
--- a/src/odil/pdu/Item.cpp
+++ b/src/odil/pdu/Item.cpp
@@ -391,10 +391,13 @@ Item
else if(type == Field::Type::string)
{
std::string value(size, '\0');
- stream.read(reinterpret_cast<char*>(&value[0]), value.size());
- if(!stream.good())
+ if(value.size() > 0)
{
- throw Exception("Could not read string field");
+ stream.read(reinterpret_cast<char*>(&value[0]), value.size());
+ if(!stream.good())
+ {
+ throw Exception("Could not read string field");
+ }
}
this->add(name, Field(value));
diff --git a/src/odil/pdu/UserInformation.cpp b/src/odil/pdu/UserInformation.cpp
index 32b4c52..e788519 100644
--- a/src/odil/pdu/UserInformation.cpp
+++ b/src/odil/pdu/UserInformation.cpp
@@ -12,7 +12,9 @@
#include <istream>
#include <vector>
+#include "odil/endian.h"
#include "odil/Exception.h"
+#include "odil/logging.h"
#include "odil/pdu/ImplementationClassUID.h"
#include "odil/pdu/ImplementationVersionName.h"
#include "odil/pdu/MaximumLength.h"
@@ -96,7 +98,41 @@ UserInformation
}
else
{
- throw Exception("Invalid sub-item type");
+ stream.ignore(2*sizeof(uint8_t)); // item type, reserved
+ if(!stream.good())
+ {
+ throw Exception("Could not skip sub-item header");
+ }
+
+ uint16_t sub_item_length;
+ stream.read(
+ reinterpret_cast<char*>(&sub_item_length),
+ sizeof(sub_item_length));
+ if(!stream.good())
+ {
+ throw Exception("Could not read length");
+ }
+ sub_item_length = big_endian_to_host(sub_item_length);
+
+ ODIL_LOG(WARN)
+ << "Skipping unknown item with type "
+ << std::hex << (unsigned int)type << std::dec << " "
+ << "(" << sub_item_length << " byte"
+ << (sub_item_length>1?"s":"") << ")";
+
+ if(sub_item_length > 0)
+ {
+ // CAUTION: using ignore could cause eofbit to be positioned and
+ // change semantics of later calls. Read the sub-item instead; this
+ // is sub-optimal but does not crash.
+ std::string sub_item(sub_item_length, '\0');
+ stream.read(reinterpret_cast<char*>(&sub_item[0]), sub_item.size());
+
+ if(!stream.good())
+ {
+ throw Exception("Could not skip sub-item");
+ }
+ }
}
}
diff --git a/src/odil/registry.cpp b/src/odil/registry.cpp
index 2121eeb..e8b39d7 100644
--- a/src/odil/registry.cpp
+++ b/src/odil/registry.cpp
@@ -296,6 +296,12 @@ ElementsDictionary create_public_dictionary()
"URN Code Value", "URNCodeValue", "UR", "1" },
{ Tag(0x0008, 0x0121),
"Equivalent Code Sequence", "EquivalentCodeSequence", "SQ", "1" },
+ { Tag(0x0008, 0x0122),
+ "Mapping Resource Name", "MappingResourceName", "LO", "1" },
+ { Tag(0x0008, 0x0123),
+ "Context Group Identification Sequence", "ContextGroupIdentificationSequence", "SQ", "1" },
+ { Tag(0x0008, 0x0124),
+ "Mapping Resource Identification Sequence", "MappingResourceIdentificationSequence", "SQ", "1" },
{ Tag(0x0008, 0x0201),
"Timezone Offset From UTC", "TimezoneOffsetFromUTC", "SH", "1" },
{ Tag(0x0008, 0x0300),
@@ -7719,10 +7725,16 @@ ElementsDictionary create_public_dictionary()
"Isocenter to Range Modulator Distance", "IsocenterToRangeModulatorDistance", "FL", "1" },
{ Tag(0x300a, 0x0390),
"Scan Spot Tune ID", "ScanSpotTuneID", "SH", "1" },
+ { Tag(0x300a, 0x0391),
+ "Scan Spot Prescribed Indices", "ScanSpotPrescribedIndices", "IS", "1-n" },
{ Tag(0x300a, 0x0392),
"Number of Scan Spot Positions", "NumberOfScanSpotPositions", "IS", "1" },
+ { Tag(0x300a, 0x0393),
+ "Scan Spot Reordered", "ScanSpotReordered", "CS", "1" },
{ Tag(0x300a, 0x0394),
"Scan Spot Position Map", "ScanSpotPositionMap", "FL", "1-n" },
+ { Tag(0x300a, 0x0395),
+ "Scan Spot Reordering Allowed", "ScanSpotReorderingAllowed", "CS", "1" },
{ Tag(0x300a, 0x0396),
"Scan Spot Meterset Weights", "ScanSpotMetersetWeights", "FL", "1-n" },
{ Tag(0x300a, 0x0398),
@@ -8534,7 +8546,10 @@ std::map<std::string, odil::Tag> create_public_tags()
"Mapping Resource UID", "MappingResourceUID", "UI", "1" }, { Tag(0x0008, 0x0119),
"Long Code Value", "LongCodeValue", "UC", "1" }, { Tag(0x0008, 0x0120),
"URN Code Value", "URNCodeValue", "UR", "1" }, { Tag(0x0008, 0x0121),
- "Equivalent Code Sequence", "EquivalentCodeSequence", "SQ", "1" }, { Tag(0x0008, 0x0201),
+ "Equivalent Code Sequence", "EquivalentCodeSequence", "SQ", "1" }, { Tag(0x0008, 0x0122),
+ "Mapping Resource Name", "MappingResourceName", "LO", "1" }, { Tag(0x0008, 0x0123),
+ "Context Group Identification Sequence", "ContextGroupIdentificationSequence", "SQ", "1" }, { Tag(0x0008, 0x0124),
+ "Mapping Resource Identification Sequence", "MappingResourceIdentificationSequence", "SQ", "1" }, { Tag(0x0008, 0x0201),
"Timezone Offset From UTC", "TimezoneOffsetFromUTC", "SH", "1" }, { Tag(0x0008, 0x0300),
"Private Data Element Characteristics Sequence", "PrivateDataElementCharacteristicsSequence", "SQ", "1" }, { Tag(0x0008, 0x0301),
"Private Group Reference", "PrivateGroupReference", "US", "1" }, { Tag(0x0008, 0x0302),
@@ -12220,9 +12235,12 @@ std::map<std::string, odil::Tag> create_public_tags()
"Range Modulator Gating Start Water Equivalent Thickness", "RangeModulatorGatingStartWaterEquivalentThickness", "FL", "1" }, { Tag(0x300a, 0x0388),
"Range Modulator Gating Stop Water Equivalent Thickness", "RangeModulatorGatingStopWaterEquivalentThickness", "FL", "1" }, { Tag(0x300a, 0x038a),
"Isocenter to Range Modulator Distance", "IsocenterToRangeModulatorDistance", "FL", "1" }, { Tag(0x300a, 0x0390),
- "Scan Spot Tune ID", "ScanSpotTuneID", "SH", "1" }, { Tag(0x300a, 0x0392),
- "Number of Scan Spot Positions", "NumberOfScanSpotPositions", "IS", "1" }, { Tag(0x300a, 0x0394),
- "Scan Spot Position Map", "ScanSpotPositionMap", "FL", "1-n" }, { Tag(0x300a, 0x0396),
+ "Scan Spot Tune ID", "ScanSpotTuneID", "SH", "1" }, { Tag(0x300a, 0x0391),
+ "Scan Spot Prescribed Indices", "ScanSpotPrescribedIndices", "IS", "1-n" }, { Tag(0x300a, 0x0392),
+ "Number of Scan Spot Positions", "NumberOfScanSpotPositions", "IS", "1" }, { Tag(0x300a, 0x0393),
+ "Scan Spot Reordered", "ScanSpotReordered", "CS", "1" }, { Tag(0x300a, 0x0394),
+ "Scan Spot Position Map", "ScanSpotPositionMap", "FL", "1-n" }, { Tag(0x300a, 0x0395),
+ "Scan Spot Reordering Allowed", "ScanSpotReorderingAllowed", "CS", "1" }, { Tag(0x300a, 0x0396),
"Scan Spot Meterset Weights", "ScanSpotMetersetWeights", "FL", "1-n" }, { Tag(0x300a, 0x0398),
"Scanning Spot Size", "ScanningSpotSize", "FL", "2" }, { Tag(0x300a, 0x039a),
"Number of Paintings", "NumberOfPaintings", "IS", "1" }, { Tag(0x300a, 0x03a0),
diff --git a/src/odil/registry.h b/src/odil/registry.h
index 56ba225..f19064a 100644
--- a/src/odil/registry.h
+++ b/src/odil/registry.h
@@ -152,6 +152,9 @@ Tag const MappingResourceUID(0x0008, 0x0118);
Tag const LongCodeValue(0x0008, 0x0119);
Tag const URNCodeValue(0x0008, 0x0120);
Tag const EquivalentCodeSequence(0x0008, 0x0121);
+Tag const MappingResourceName(0x0008, 0x0122);
+Tag const ContextGroupIdentificationSequence(0x0008, 0x0123);
+Tag const MappingResourceIdentificationSequence(0x0008, 0x0124);
Tag const TimezoneOffsetFromUTC(0x0008, 0x0201);
Tag const PrivateDataElementCharacteristicsSequence(0x0008, 0x0300);
Tag const PrivateGroupReference(0x0008, 0x0301);
@@ -3855,8 +3858,11 @@ Tag const RangeModulatorGatingStartWaterEquivalentThickness(0x300a, 0x0386);
Tag const RangeModulatorGatingStopWaterEquivalentThickness(0x300a, 0x0388);
Tag const IsocenterToRangeModulatorDistance(0x300a, 0x038a);
Tag const ScanSpotTuneID(0x300a, 0x0390);
+Tag const ScanSpotPrescribedIndices(0x300a, 0x0391);
Tag const NumberOfScanSpotPositions(0x300a, 0x0392);
+Tag const ScanSpotReordered(0x300a, 0x0393);
Tag const ScanSpotPositionMap(0x300a, 0x0394);
+Tag const ScanSpotReorderingAllowed(0x300a, 0x0395);
Tag const ScanSpotMetersetWeights(0x300a, 0x0396);
Tag const ScanningSpotSize(0x300a, 0x0398);
Tag const NumberOfPaintings(0x300a, 0x039a);
diff --git a/src/odil/uid.cpp b/src/odil/uid.cpp
index 8a8be2b..0db0d2c 100644
--- a/src/odil/uid.cpp
+++ b/src/odil/uid.cpp
@@ -20,8 +20,8 @@ namespace odil
{
#ifdef ODIL_MAJOR_VERSION
-std::string const implementation_class_uid=uid_prefix+"0." ODIL_STRINGIFY(ODIL_MAJOR_VERSION);
-std::string const implementation_version_name="Odil " ODIL_STRINGIFY(ODIL_MAJOR_VERSION);
+std::string implementation_class_uid=uid_prefix+"0." ODIL_STRINGIFY(ODIL_MAJOR_VERSION);
+std::string implementation_version_name="Odil " ODIL_STRINGIFY(ODIL_MAJOR_VERSION);
#else
#error ODIL_MAJOR_VERSION must be defined
#endif
diff --git a/src/odil/uid.h b/src/odil/uid.h
index b1fd3c2..e60d25c 100644
--- a/src/odil/uid.h
+++ b/src/odil/uid.h
@@ -11,6 +11,8 @@
#include <string>
+#include "odil/odil.h"
+
namespace odil
{
@@ -18,10 +20,10 @@ namespace odil
std::string const uid_prefix="1.2.826.0.1.3680043.9.5560";
/// @brief Implementation class UID of Odil.
-extern std::string const implementation_class_uid;
+extern ODIL_API std::string implementation_class_uid;
/// @brief Implementation version name of Odil.
-extern std::string const implementation_version_name;
+extern ODIL_API std::string implementation_version_name;
/// @brief Generate a UID under the UID prefix.
std::string generate_uid();
diff --git a/src/odil/write_ds.cpp b/src/odil/write_ds.cpp
new file mode 100644
index 0000000..46d9049
--- /dev/null
+++ b/src/odil/write_ds.cpp
@@ -0,0 +1,210 @@
+/*************************************************************************
+ * 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.
+ ************************************************************************/
+
+#include "odil/write_ds.h"
+
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+// Helper functions
+namespace
+{
+
+/// @brief Remove the trailing zeros of the mantissa.
+void clean(char * mantissa)
+{
+ char * it = mantissa + strlen(mantissa) - 1;
+ while(*it == '0' && it > mantissa)
+ {
+ *it = '\0';
+ --it;
+ }
+
+ if(*it == '.')
+ {
+ *it = '\0';
+ }
+}
+
+/**
+ * @brief Add 1 to the integer contained in (buffer[0], buffer[n]) (end included).
+ * @return 1 if a carry was used, 0 otherwise.
+ */
+int add1(char * buffer, int n)
+{
+ if(n < 0)
+ {
+ return 1;
+ }
+
+ if(buffer[n] == '9')
+ {
+ buffer[n] = '0';
+ return add1(buffer, n-1);
+ }
+ else
+ {
+ buffer[n] += 1;
+ }
+ return 0;
+}
+
+/**
+ * @brief Round the integer contained in (buffer[0], buffer[n]) (end included)
+ * to the nearest factor of 10.
+ * @return 1 if rounding was carried to next digit, 0 otherwise.
+ */
+int doround(char * buffer, unsigned int n)
+{
+ if(n >= strlen(buffer))
+ {
+ return 0;
+ }
+
+ char const c = buffer[n];
+ buffer[n] = 0;
+ if(c >= '5' && c <= '9')
+ {
+ return add1(buffer, n-1);
+ }
+
+ return 0;
+}
+
+int roundat(char * buffer, unsigned int i, int exponent)
+{
+ if(doround(buffer, i) != 0)
+ {
+ exponent += 1;
+ switch(exponent)
+ {
+ case -2:
+ strcpy(buffer, ".01");
+ break;
+ case -1:
+ strcpy(buffer, ".1");
+ break;
+ case 0:
+ strcpy(buffer, "1.");
+ break;
+ case 1:
+ strcpy(buffer, "10");
+ break;
+ case 2:
+ strcpy(buffer, "100");
+ break;
+ default:
+ sprintf(buffer, "1e%d", exponent);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+}
+
+namespace odil
+{
+
+void write_ds(double f, char * buffer, int size)
+{
+ // Negative number: add initial '-' to buffer and process as positive number
+ if(f < 0)
+ {
+ f = -f;
+ size -= 1;
+ *buffer = '-';
+ ++buffer;
+ }
+
+
+ char line[40];
+ sprintf(line, "%1.16e", f);
+
+ if(line[0] == '-')
+ {
+ // Happens in the case of -0, other negative numbers have been already
+ // handled
+ f = -f;
+ size -= 1;
+ *buffer = '-';
+ ++buffer;
+ sprintf(line, "%1.16e", f);
+ }
+
+ char * mantissa = line+1;
+ *mantissa = line[0];
+
+ auto const end_of_mantissa = strcspn(mantissa, "eE");
+ mantissa[end_of_mantissa] = '\0';
+ int const exponent = strtol(mantissa + end_of_mantissa + 1, NULL, 10);
+
+ if((exponent >= size) || (exponent < -3))
+ {
+ char exponent_buffer[6];
+ auto const exponent_length = sprintf(exponent_buffer, "e%d", exponent);
+
+ auto const i = roundat(mantissa, size - 1 - exponent_length, exponent);
+ if(i == 1)
+ {
+ strcpy(buffer, mantissa);
+ return;
+ }
+ buffer[0] = mantissa[0];
+ buffer[1] = '.';
+ strncpy(buffer + i + 2, mantissa + 1, size - 2 - exponent_length);
+ buffer[size-exponent_length] = 0;
+ clean(buffer);
+ strcat(buffer, exponent_buffer);
+ }
+ else if(exponent >= size - 2)
+ {
+ roundat(mantissa, exponent + 1, exponent);
+ strcpy(buffer, mantissa);
+ }
+ else if(exponent >= 0)
+ {
+ auto const i = roundat(mantissa, size - 1, exponent);
+ if(i == 1)
+ {
+ strcpy(buffer, mantissa);
+ return;
+ }
+ strncpy(buffer, mantissa, exponent + 1);
+ buffer[exponent + 1] = '.';
+ strncpy(buffer + exponent + 2, mantissa + exponent + 1, size - exponent - 1);
+ buffer[size] = 0;
+ clean(buffer);
+ }
+ else
+ {
+ int const i = roundat(mantissa, size + 1 + exponent, exponent);
+ if (i == 1)
+ {
+ strcpy(buffer, mantissa);
+ return;
+ }
+ buffer[0] = '.';
+ for(int j=0; j< -1 - exponent; j++)
+ {
+ buffer[j+1] = '0';
+ }
+ if((i == 1) && (exponent != -1))
+ {
+ buffer[-exponent] = '1';
+ ++buffer;
+ }
+ strncpy(buffer - exponent, mantissa, size + 1 + exponent);
+ buffer[size] = 0;
+ clean(buffer);
+ }
+}
+
+}
diff --git a/src/odil/write_ds.h b/src/odil/write_ds.h
new file mode 100644
index 0000000..87b5cac
--- /dev/null
+++ b/src/odil/write_ds.h
@@ -0,0 +1,25 @@
+/*************************************************************************
+ * 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 _1fe89041_9f3b_4536_a55a_81f045984a62
+#define _1fe89041_9f3b_4536_a55a_81f045984a62
+
+#include <cmath>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+
+namespace odil
+{
+
+/// @brief Write a double as a DS to the buffer.
+void write_ds(double f, char * buffer, int size=16);
+
+}
+
+#endif // _1fe89041_9f3b_4536_a55a_81f045984a62
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 64b23aa..2afa0eb 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -6,6 +6,19 @@ add_subdirectory(tools)
file(GLOB headers *.h)
file(GLOB_RECURSE tests code/*.cpp)
+if(NOT WITH_DCMTK)
+ list(
+ REMOVE_ITEM tests
+ ${CMAKE_CURRENT_SOURCE_DIR}/code/conversion.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/code/DcmtkException.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/code/ElementAccessor.cpp)
+ set(
+ extra_files
+ ${CMAKE_SOURCE_DIR}/src/odil/dcmtk/conversion.cpp
+ ${CMAKE_SOURCE_DIR}/src/odil/dcmtk/ElementAccessor.cpp
+ ${CMAKE_SOURCE_DIR}/src/odil/dcmtk/ElementTraits.cpp
+ ${CMAKE_SOURCE_DIR}/src/odil/dcmtk/Exception.cpp)
+endif()
include_directories(
${CMAKE_SOURCE_DIR}/src ${Boost_INCLUDE_DIRS} ${DCMTK_INCLUDE_DIRS}
@@ -22,8 +35,9 @@ link_directories(${Boost_LIBRARY_DIRS} ${DCMTK_LIBRARY_DIRS})
foreach(test_file ${tests})
get_filename_component(test ${test_file} NAME_WE)
- add_executable(test_${test} ${test_file} ${headers})
- target_link_libraries(test_${test} libodil ${Boost_LIBRARIES})
+ add_executable(test_${test} ${test_file} ${headers} ${extra_files})
+ target_link_libraries(
+ test_${test} libodil ${Boost_LIBRARIES} ${DCMTK_LIBRARIES})
set_target_properties(test_${test} PROPERTIES OUTPUT_NAME ${test} FOLDER "Tests")
file(READ ${test_file} content)
diff --git a/tests/code/DataSet.cpp b/tests/code/DataSet.cpp
index 608537b..891071d 100644
--- a/tests/code/DataSet.cpp
+++ b/tests/code/DataSet.cpp
@@ -14,6 +14,22 @@ BOOST_AUTO_TEST_CASE(Empty)
BOOST_CHECK(dataset.empty());
BOOST_CHECK_EQUAL(dataset.size(), 0);
BOOST_CHECK(!dataset.has(odil::Tag("PatientName")));
+ BOOST_CHECK(dataset.get_transfer_syntax().empty());
+}
+
+BOOST_AUTO_TEST_CASE(TransferSyntaxConstructor)
+{
+ odil::DataSet dataset(odil::registry::ExplicitVRLittleEndian);
+ BOOST_CHECK_EQUAL(
+ dataset.get_transfer_syntax(), odil::registry::ExplicitVRLittleEndian);
+}
+
+BOOST_AUTO_TEST_CASE(TransferSyntax)
+{
+ odil::DataSet dataset;
+ dataset.set_transfer_syntax(odil::registry::ExplicitVRLittleEndian);
+ BOOST_CHECK_EQUAL(
+ dataset.get_transfer_syntax(), odil::registry::ExplicitVRLittleEndian);
}
BOOST_AUTO_TEST_CASE(AddExplicitVR)
diff --git a/tests/code/VRFinder.cpp b/tests/code/VRFinder.cpp
index ddc26be..8dfa958 100644
--- a/tests/code/VRFinder.cpp
+++ b/tests/code/VRFinder.cpp
@@ -50,11 +50,10 @@ BOOST_AUTO_TEST_CASE(PublicDictionaryRepeatingGroup)
BOOST_AUTO_TEST_CASE(PublicDictionaryNotApplicable)
{
- BOOST_REQUIRE_THROW(
- odil::VRFinder::public_dictionary(
+ auto const vr = odil::VRFinder::public_dictionary(
odil::Tag(0x0011, 0x0011), odil::DataSet(),
- odil::registry::ImplicitVRLittleEndian),
- odil::Exception);
+ odil::registry::ImplicitVRLittleEndian);
+ BOOST_REQUIRE(vr == odil::VR::UNKNOWN);
}
BOOST_AUTO_TEST_CASE(GroupLength)
@@ -67,11 +66,10 @@ BOOST_AUTO_TEST_CASE(GroupLength)
BOOST_AUTO_TEST_CASE(GroupLengthNotApplicable)
{
- BOOST_REQUIRE_THROW(
- odil::VRFinder::group_length(
+ auto const vr = odil::VRFinder::group_length(
odil::Tag(0x0010, 0x0010), odil::DataSet(),
- odil::registry::ImplicitVRLittleEndian),
- odil::Exception);
+ odil::registry::ImplicitVRLittleEndian);
+ BOOST_REQUIRE(vr == odil::VR::UNKNOWN);
}
BOOST_AUTO_TEST_CASE(PrivateTag)
@@ -84,11 +82,10 @@ BOOST_AUTO_TEST_CASE(PrivateTag)
BOOST_AUTO_TEST_CASE(PrivateTagNotApplicable)
{
- BOOST_REQUIRE_THROW(
- odil::VRFinder::private_tag(
+ auto const vr = odil::VRFinder::private_tag(
odil::Tag(0x0010, 0x0010), odil::DataSet(),
- odil::registry::ImplicitVRLittleEndian),
- odil::Exception);
+ odil::registry::ImplicitVRLittleEndian);
+ BOOST_REQUIRE(vr == odil::VR::UNKNOWN);
}
BOOST_AUTO_TEST_CASE(ImplictiVRLittleEndian)
@@ -101,20 +98,18 @@ BOOST_AUTO_TEST_CASE(ImplictiVRLittleEndian)
BOOST_AUTO_TEST_CASE(ImplictiVRLittleEndianNotApplicableTag)
{
- BOOST_REQUIRE_THROW(
- odil::VRFinder::implicit_vr_little_endian(
+ auto const vr = odil::VRFinder::implicit_vr_little_endian(
odil::Tag(0x0010, 0x0010), odil::DataSet(),
- odil::registry::ImplicitVRLittleEndian),
- odil::Exception);
+ odil::registry::ImplicitVRLittleEndian);
+ BOOST_REQUIRE(vr == odil::VR::UNKNOWN);
}
BOOST_AUTO_TEST_CASE(ImplictiVRLittleEndianNotApplicableVR)
{
- BOOST_REQUIRE_THROW(
- odil::VRFinder::implicit_vr_little_endian(
+ auto const vr = odil::VRFinder::implicit_vr_little_endian(
odil::Tag(0x7fe0, 0x0010), odil::DataSet(),
- odil::registry::ExplicitVRLittleEndian),
- odil::Exception);
+ odil::registry::ExplicitVRLittleEndian);
+ BOOST_REQUIRE(vr == odil::VR::UNKNOWN);
}
BOOST_AUTO_TEST_CASE(PublicElement)
diff --git a/tests/code/Writer.cpp b/tests/code/Writer.cpp
index ee13d3e..15ec884 100644
--- a/tests/code/Writer.cpp
+++ b/tests/code/Writer.cpp
@@ -107,7 +107,7 @@ BOOST_AUTO_TEST_CASE(CS)
BOOST_AUTO_TEST_CASE(DS)
{
- odil::Element odil_element({1.23, -4.56}, odil::VR::DS);
+ odil::Element odil_element({24.5282145946261, -4.56}, odil::VR::DS);
odil::DataSet odil_data_set;
odil_data_set.add(odil::registry::SelectorDSValue, odil_element);
diff --git a/tests/code/unicode.cpp b/tests/code/unicode.cpp
index 5b540e2..8f7d16c 100644
--- a/tests/code/unicode.cpp
+++ b/tests/code/unicode.cpp
@@ -3,7 +3,6 @@
#include "odil/DataSet.h"
#include "odil/unicode.h"
-#include "odil/dcmtk/conversion.h"
BOOST_AUTO_TEST_CASE(SCSARAB)
{
diff --git a/tests/run b/tests/run
index c41e6d4..e426ca4 100755
--- a/tests/run
+++ b/tests/run
@@ -23,7 +23,7 @@ def main():
if arguments.no_network:
excluded_cpp = [
"Association", "Network", "ServiceRole", "SCP", "SCU", "Transport"]
- excluded_python = ["scu"]
+ excluded_python = ["scu", "scp"]
arguments.exclude_regex = "{}{}{}".format(
arguments.exclude_regex or "",
diff --git a/tests/wrappers/test_data_set.py b/tests/wrappers/test_data_set.py
index d0ca2ac..8e4a20e 100644
--- a/tests/wrappers/test_data_set.py
+++ b/tests/wrappers/test_data_set.py
@@ -8,6 +8,20 @@ class TestDataSet(unittest.TestCase):
self.assertTrue(data_set.empty())
self.assertEqual(data_set.size(), 0)
self.assertEqual(len(data_set), 0)
+ self.assertEqual(len(data_set.get_transfer_syntax()), 0)
+
+ def test_transfer_syntax_constructor(self):
+ data_set = odil.DataSet(odil.registry.ExplicitVRLittleEndian)
+ self.assertEqual(
+ data_set.get_transfer_syntax(),
+ odil.registry.ExplicitVRLittleEndian)
+
+ def test_transfer_syntax(self):
+ data_set = odil.DataSet()
+ data_set.set_transfer_syntax(odil.registry.ExplicitVRLittleEndian)
+ self.assertEqual(
+ data_set.get_transfer_syntax(),
+ odil.registry.ExplicitVRLittleEndian)
def test_empty_element_tag(self):
tag = odil.registry.PatientName
diff --git a/wrappers/DataSet.cpp b/wrappers/DataSet.cpp
index d84cdb3..e9cd25e 100644
--- a/wrappers/DataSet.cpp
+++ b/wrappers/DataSet.cpp
@@ -177,7 +177,9 @@ void wrap_DataSet()
using namespace odil;
- class_<DataSet>("DataSet", init<>())
+ class_<DataSet>("DataSet")
+ .def(init<>())
+ .def(init<std::string>())
.def(
"add",
static_cast<void (DataSet::*)(Tag const &, VR)>(&DataSet::add),
@@ -231,6 +233,10 @@ void wrap_DataSet()
static_cast<Value::Binary & (DataSet::*)(Tag const &)>(
&DataSet::as_binary),
return_value_policy<reference_existing_object>())
+ .def(
+ "get_transfer_syntax", &DataSet::get_transfer_syntax,
+ return_value_policy<copy_const_reference>())
+ .def("set_transfer_syntax", &DataSet::set_transfer_syntax)
.def("set", &set)
.def("keys", &keys)
.def("__iter__", range(&begin, &end))
diff --git a/wrappers/StoreSCU.cpp b/wrappers/StoreSCU.cpp
index 266d731..1481201 100644
--- a/wrappers/StoreSCU.cpp
+++ b/wrappers/StoreSCU.cpp
@@ -10,6 +10,8 @@
#include "odil/StoreSCU.h"
+BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(storeMethod, odil::StoreSCU::store, 1, 3)
+
void wrap_StoreSCU()
{
using namespace boost::python;
@@ -23,11 +25,12 @@ void wrap_StoreSCU()
)
.def(
"set_affected_sop_class",
- &StoreSCU::set_affected_sop_class
+ static_cast<void(StoreSCU::*)(DataSet const &)>(&StoreSCU::set_affected_sop_class)
)
.def(
"store",
- &StoreSCU::store
+ &StoreSCU::store,
+ storeMethod()
)
;
}
diff --git a/wrappers/read.cpp b/wrappers/read.cpp
index ba76e85..98d11f3 100644
--- a/wrappers/read.cpp
+++ b/wrappers/read.cpp
@@ -13,12 +13,15 @@
#include "odil/Exception.h"
#include "odil/Reader.h"
+#include "odil/Tag.h"
namespace
{
boost::python::tuple
-read(std::string const & path, bool keep_group_length=false)
+read(
+ std::string const & path, bool keep_group_length,
+ boost::python::object const & halt_condition)
{
std::ifstream stream(path);
if(!stream)
@@ -26,8 +29,19 @@ read(std::string const & path, bool keep_group_length=false)
throw odil::Exception("Could not open "+path);
}
- auto const header_and_data_set =
- odil::Reader::read_file(stream, keep_group_length);
+ std::function<bool(odil::Tag const &)> halt_condition_cpp =
+ [](odil::Tag const &) { return false;};
+ if(halt_condition)
+ {
+ halt_condition_cpp =
+ [halt_condition](odil::Tag const & tag)
+ {
+ return boost::python::call<bool>(halt_condition.ptr(), tag);
+ };
+ }
+
+ auto const header_and_data_set = odil::Reader::read_file(
+ stream, keep_group_length, halt_condition_cpp);
return boost::python::make_tuple(
header_and_data_set.first, header_and_data_set.second);
@@ -35,14 +49,15 @@ read(std::string const & path, bool keep_group_length=false)
}
-BOOST_PYTHON_FUNCTION_OVERLOADS(read_overloads, read, 1, 2)
-
void wrap_read()
{
using namespace boost::python;
def(
"read",
- static_cast<boost::python::tuple (*)(std::string const &, bool)>(read),
- read_overloads());
+ static_cast<boost::python::tuple (*)(std::string const &, bool, object const &)>(read),
+ (
+ arg("path"), arg("keep_group_length")=false,
+ arg("halt_condition")=object())
+ );
}
--
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