[med-svn] [dcmtkpp] 01/09: Imported Upstream version 0.4.0
Julien Lamy
lamy-guest at moszumanska.debian.org
Thu Jan 14 08:45:45 UTC 2016
This is an automated email from the git hooks/post-receive script.
lamy-guest pushed a commit to branch master
in repository dcmtkpp.
commit 40b3289c52a1cc216217df6cb54a7db6d7edebfe
Author: Julien Lamy <lamy at unistra.fr>
Date: Tue Jan 12 17:05:10 2016 +0100
Imported Upstream version 0.4.0
---
.travis.yml | 6 +-
CMakeLists.txt | 24 +-
Doxyfile | 4 +-
FindDCMTK.cmake | 32 +
FindICU.cmake | 26 +-
FindJsonCpp.cmake | 24 +
README.md | 26 +-
appveyor.full.yml | 52 +
appveyor.yml | 43 +
examples/CMakeLists.txt | 12 +-
examples/dicomdir.cpp | 14 +-
examples/dump.cpp | 18 +-
examples/find.cpp | 77 +-
examples/genericscp.cpp | 142 ++
examples/get.cpp | 136 +-
examples/move.cpp | 138 +-
examples/store.cpp | 76 +-
generate_registry | 4 +-
registry.cpp.tmpl | 18 +-
registry.h.tmpl | 10 +-
src/CMakeLists.txt | 43 +-
src/dcmtkpp/Association.cpp | 535 ----
src/dcmtkpp/Association.h | 184 --
src/dcmtkpp/GetSCU.cpp | 105 -
src/dcmtkpp/MoveSCU.cpp | 162 --
src/dcmtkpp/Network.cpp | 201 --
src/dcmtkpp/Network.h | 91 -
src/dcmtkpp/Response.cpp | 43 -
src/dcmtkpp/Response.h | 44 -
src/dcmtkpp/SCP.cpp | 44 -
src/dcmtkpp/SCP.h | 31 -
src/dcmtkpp/ServiceRole.cpp | 438 ----
src/dcmtkpp/ServiceRole.h | 113 -
src/dcmtkpp/ServiceRole.txx | 38 -
src/dcmtkpp/StoreSCP.cpp | 94 -
src/dcmtkpp/StoreSCP.h | 43 -
src/dcmtkpp/uid.cpp | 88 -
src/odil/Association.cpp | 496 ++++
src/odil/Association.h | 222 ++
src/odil/AssociationAcceptor.cpp | 98 +
src/odil/AssociationAcceptor.h | 64 +
src/odil/AssociationParameters.cpp | 457 ++++
src/odil/AssociationParameters.h | 156 ++
src/{dcmtkpp => odil}/BasicDirectoryCreator.cpp | 27 +-
src/{dcmtkpp => odil}/BasicDirectoryCreator.h | 21 +-
src/{dcmtkpp => odil}/DataSet.cpp | 22 +-
src/{dcmtkpp => odil}/DataSet.h | 36 +-
src/odil/EchoSCP.cpp | 82 +
src/odil/EchoSCP.h | 54 +
src/{dcmtkpp => odil}/Element.cpp | 10 +-
src/{dcmtkpp => odil}/Element.h | 32 +-
src/{dcmtkpp => odil}/Element.txx | 8 +-
src/{dcmtkpp => odil}/ElementsDictionary.cpp | 6 +-
src/{dcmtkpp => odil}/ElementsDictionary.h | 6 +-
.../UIDsDictionary.cpp => odil/Exception.cpp} | 29 +-
src/odil/Exception.h | 37 +
src/odil/FindSCP.cpp | 94 +
src/odil/FindSCP.h | 52 +
src/{dcmtkpp => odil}/FindSCU.cpp | 40 +-
src/{dcmtkpp => odil}/FindSCU.h | 11 +-
src/odil/GetSCP.cpp | 141 ++
src/odil/GetSCP.h | 59 +
src/odil/GetSCU.cpp | 115 +
src/{dcmtkpp => odil}/GetSCU.h | 28 +-
src/odil/MoveSCP.cpp | 147 ++
src/odil/MoveSCP.h | 65 +
src/odil/MoveSCU.cpp | 169 ++
src/{dcmtkpp => odil}/MoveSCU.h | 17 +-
src/{dcmtkpp => odil}/Reader.cpp | 118 +-
src/{dcmtkpp => odil}/Reader.h | 23 +-
src/odil/SCP.cpp | 46 +
src/odil/SCP.h | 55 +
src/odil/SCPDispatcher.cpp | 78 +
src/odil/SCPDispatcher.h | 47 +
src/{dcmtkpp => odil}/SCU.cpp | 35 +-
src/{dcmtkpp => odil}/SCU.h | 16 +-
src/odil/StoreSCP.cpp | 84 +
src/odil/StoreSCP.h | 54 +
src/{dcmtkpp => odil}/StoreSCU.cpp | 33 +-
src/{dcmtkpp => odil}/StoreSCU.h | 18 +-
src/{dcmtkpp => odil}/Tag.cpp | 10 +-
src/{dcmtkpp => odil}/Tag.h | 10 +-
src/{dcmtkpp => odil}/UIDsDictionary.cpp | 6 +-
src/{dcmtkpp => odil}/UIDsDictionary.h | 4 +-
src/{dcmtkpp => odil}/VR.cpp | 30 +-
src/{dcmtkpp => odil}/VR.h | 10 +-
src/{dcmtkpp => odil}/VRFinder.cpp | 18 +-
src/{dcmtkpp => odil}/VRFinder.h | 10 +-
src/{dcmtkpp => odil}/Value.cpp | 10 +-
src/{dcmtkpp => odil}/Value.h | 26 +-
src/{dcmtkpp => odil}/Value.txx | 8 +-
src/{dcmtkpp => odil}/Writer.cpp | 111 +-
src/{dcmtkpp => odil}/Writer.h | 29 +-
src/{dcmtkpp/UIDsDictionary.cpp => odil/asio.cpp} | 22 +-
src/odil/base64.cpp | 36 +
src/odil/base64.h | 42 +
src/odil/base64.txx | 131 +
src/{dcmtkpp => odil/dcmtk}/ElementAccessor.cpp | 11 +-
src/{dcmtkpp => odil/dcmtk}/ElementAccessor.h | 15 +-
src/{dcmtkpp => odil/dcmtk}/ElementAccessor.txx | 11 +-
src/{dcmtkpp => odil/dcmtk}/ElementTraits.cpp | 13 +-
src/{dcmtkpp => odil/dcmtk}/ElementTraits.h | 11 +-
src/{dcmtkpp => odil/dcmtk}/Exception.cpp | 27 +-
src/{dcmtkpp => odil/dcmtk}/Exception.h | 60 +-
src/{dcmtkpp => odil/dcmtk}/VRTraits.h | 16 +-
src/{dcmtkpp => odil/dcmtk}/conversion.cpp | 23 +-
src/{dcmtkpp => odil/dcmtk}/conversion.h | 35 +-
src/{dcmtkpp => odil/dcmtk}/conversion.txx | 17 +-
src/odil/dul/EventData.h | 39 +
src/odil/dul/StateMachine.cpp | 764 ++++++
src/odil/dul/StateMachine.h | 274 +++
src/odil/dul/Transport.cpp | 295 +++
src/odil/dul/Transport.h | 107 +
src/odil/endian.h | 87 +
src/{dcmtkpp => odil}/json_converter.cpp | 44 +-
src/{dcmtkpp => odil}/json_converter.h | 8 +-
src/{dcmtkpp => odil/message}/CEchoRequest.cpp | 15 +-
src/{dcmtkpp => odil/message}/CEchoRequest.h | 17 +-
src/{dcmtkpp => odil/message}/CEchoResponse.cpp | 17 +-
src/{dcmtkpp => odil/message}/CEchoResponse.h | 17 +-
src/{dcmtkpp => odil/message}/CFindRequest.cpp | 17 +-
src/{dcmtkpp => odil/message}/CFindRequest.h | 19 +-
src/{dcmtkpp => odil/message}/CFindResponse.cpp | 23 +-
src/{dcmtkpp => odil/message}/CFindResponse.h | 36 +-
src/{dcmtkpp => odil/message}/CGetRequest.cpp | 19 +-
src/{dcmtkpp => odil/message}/CGetRequest.h | 21 +-
src/{dcmtkpp => odil/message}/CGetResponse.cpp | 31 +-
src/{dcmtkpp => odil/message}/CGetResponse.h | 45 +-
src/{dcmtkpp => odil/message}/CMoveRequest.cpp | 19 +-
src/{dcmtkpp => odil/message}/CMoveRequest.h | 23 +-
src/{dcmtkpp => odil/message}/CMoveResponse.cpp | 31 +-
src/{dcmtkpp => odil/message}/CMoveResponse.h | 46 +-
src/{dcmtkpp => odil/message}/CStoreRequest.cpp | 18 +-
src/{dcmtkpp => odil/message}/CStoreRequest.h | 27 +-
src/{dcmtkpp => odil/message}/CStoreResponse.cpp | 23 +-
src/{dcmtkpp => odil/message}/CStoreResponse.h | 34 +-
src/{dcmtkpp => odil/message}/Cancellation.cpp | 21 +-
src/{dcmtkpp => odil/message}/Cancellation.h | 19 +-
src/{dcmtkpp => odil/message}/Message.cpp | 17 +-
src/{dcmtkpp => odil/message}/Message.h | 41 +-
src/{dcmtkpp => odil/message}/Request.cpp | 15 +-
src/{dcmtkpp => odil/message}/Request.h | 17 +-
src/odil/message/Response.cpp | 97 +
src/odil/message/Response.h | 103 +
src/odil/pdu/AAbort.cpp | 93 +
src/odil/pdu/AAbort.h | 49 +
src/odil/pdu/AAssociate.cpp | 272 ++
src/odil/pdu/AAssociate.h | 91 +
src/odil/pdu/AAssociateAC.cpp | 93 +
src/odil/pdu/AAssociateAC.h | 47 +
src/odil/pdu/AAssociateRJ.cpp | 112 +
src/odil/pdu/AAssociateRJ.h | 54 +
src/odil/pdu/AAssociateRQ.cpp | 93 +
src/odil/pdu/AAssociateRQ.h | 47 +
src/odil/pdu/AReleaseRP.cpp | 48 +
src/odil/pdu/AReleaseRP.h | 37 +
src/odil/pdu/AReleaseRQ.cpp | 48 +
src/odil/pdu/AReleaseRQ.h | 37 +
src/odil/pdu/ApplicationContext.cpp | 68 +
src/odil/pdu/ApplicationContext.h | 46 +
src/odil/pdu/ImplementationClassUID.cpp | 68 +
src/odil/pdu/ImplementationClassUID.h | 48 +
src/odil/pdu/ImplementationVersionName.cpp | 72 +
src/odil/pdu/ImplementationVersionName.h | 48 +
src/odil/pdu/Item.cpp | 454 ++++
src/odil/pdu/Item.h | 266 ++
src/odil/pdu/MaximumLength.cpp | 65 +
src/odil/pdu/MaximumLength.h | 47 +
src/odil/pdu/Object.cpp | 116 +
src/odil/pdu/Object.h | 55 +
src/odil/pdu/PDataTF.cpp | 186 ++
src/odil/pdu/PDataTF.h | 67 +
src/odil/pdu/PresentationContext.cpp | 218 ++
src/odil/pdu/PresentationContext.h | 70 +
src/odil/pdu/PresentationContextAC.cpp | 91 +
src/odil/pdu/PresentationContextAC.h | 55 +
src/odil/pdu/PresentationContextRQ.cpp | 101 +
src/odil/pdu/PresentationContextRQ.h | 58 +
src/odil/pdu/RoleSelection.cpp | 108 +
src/odil/pdu/RoleSelection.h | 65 +
src/odil/pdu/UserIdentityAC.cpp | 75 +
src/odil/pdu/UserIdentityAC.h | 47 +
src/odil/pdu/UserIdentityRQ.cpp | 169 ++
src/odil/pdu/UserIdentityRQ.h | 75 +
src/odil/pdu/UserInformation.cpp | 113 +
src/odil/pdu/UserInformation.h | 66 +
src/odil/pdu/UserInformation.txx | 157 ++
src/{dcmtkpp => odil}/registry.cpp | 18 +-
src/{dcmtkpp => odil}/registry.h | 10 +-
src/odil/uid.cpp | 43 +
src/{dcmtkpp => odil}/uid.h | 19 +-
src/{dcmtkpp => odil}/unicode.cpp | 11 +-
src/{dcmtkpp => odil}/unicode.h | 6 +-
src/{dcmtkpp => odil}/xml_converter.cpp | 40 +-
src/{dcmtkpp => odil}/xml_converter.h | 8 +-
tests/CMakeLists.txt | 33 +-
tests/MessageFixtureBase.h | 26 +-
tests/PeerFixtureBase.h | 71 +-
tests/code/Association.cpp | 268 +-
tests/code/AssociationAcceptor.cpp | 13 +
tests/code/AssociationParameters.cpp | 178 ++
tests/code/BasicDirectoryCreator.cpp | 159 ++
tests/code/CEchoRequest.cpp | 56 -
tests/code/CEchoResponse.cpp | 58 -
tests/code/CFindResponse.cpp | 72 -
tests/code/CGetResponse.cpp | 93 -
tests/code/CMoveResponse.cpp | 93 -
tests/code/CStoreResponse.cpp | 68 -
tests/code/Cancellation.cpp | 40 -
tests/code/DataSet.cpp | 295 ++-
tests/code/DcmtkException.cpp | 26 +
tests/code/EchoSCP.cpp | 122 +
tests/code/Element.cpp | 96 +-
tests/code/ElementAccessor.cpp | 44 +-
tests/code/Exception.cpp | 36 +-
tests/code/FindSCP.cpp | 166 ++
tests/code/FindSCU.cpp | 31 +-
tests/code/GetSCP.cpp | 201 ++
tests/code/GetSCU.cpp | 38 +-
tests/code/Message.cpp | 57 -
tests/code/MoveSCP.cpp | 237 ++
tests/code/MoveSCU.cpp | 45 +-
tests/code/Network.cpp | 156 --
tests/code/Reader.cpp | 250 +-
tests/code/Response.cpp | 31 -
tests/code/SCPDispatcher.cpp | 132 +
tests/code/SCU.cpp | 26 +-
tests/code/ServiceRole.cpp | 62 -
tests/code/StoreSCU.cpp | 66 +-
tests/code/Tag.cpp | 82 +-
tests/code/VR.cpp | 18 +-
tests/code/VRFinder.cpp | 177 ++
tests/code/Value.cpp | 224 +-
tests/code/Writer.cpp | 268 +-
tests/code/base64.cpp | 75 +
tests/code/conversion.cpp | 272 +-
tests/code/dul/StateMachine.cpp | 22 +
tests/code/dul/Transport.cpp | 45 +
tests/code/endian.cpp | 89 +
tests/code/json_converter.cpp | 126 +-
tests/code/message/CEchoRequest.cpp | 60 +
tests/code/message/CEchoResponse.cpp | 64 +
tests/code/{ => message}/CFindRequest.cpp | 46 +-
tests/code/message/CFindResponse.cpp | 107 +
tests/code/{ => message}/CGetRequest.cpp | 48 +-
tests/code/message/CGetResponse.cpp | 129 +
tests/code/{ => message}/CMoveRequest.cpp | 47 +-
tests/code/message/CMoveResponse.cpp | 130 +
tests/code/{ => message}/CStoreRequest.cpp | 44 +-
tests/code/message/CStoreResponse.cpp | 106 +
tests/code/message/Cancellation.cpp | 44 +
tests/code/message/Message.cpp | 63 +
tests/code/{ => message}/Request.cpp | 16 +-
tests/code/message/Response.cpp | 112 +
tests/code/pdu/AAbort.cpp | 51 +
tests/code/pdu/AAssociateAC.cpp | 279 +++
tests/code/pdu/AAssociateRJ.cpp | 59 +
tests/code/pdu/AAssociateRQ.cpp | 290 +++
tests/code/pdu/AReleaseRP.cpp | 34 +
tests/code/pdu/AReleaseRQ.cpp | 34 +
tests/code/pdu/ApplicationContext.cpp | 44 +
tests/code/pdu/ImplementationClassUID.cpp | 43 +
tests/code/pdu/ImplementationVersionName.cpp | 59 +
tests/code/pdu/Item.cpp | 128 +
tests/code/pdu/MaximumLength.cpp | 56 +
tests/code/pdu/PDataTF.cpp | 74 +
tests/code/pdu/PresentationContextAC.cpp | 62 +
tests/code/pdu/PresentationContextRQ.cpp | 82 +
tests/code/pdu/RoleSelection.cpp | 74 +
tests/code/pdu/UserIdentityAC.cpp | 58 +
tests/code/pdu/UserIdentityRQ.cpp | 124 +
tests/code/pdu/UserInformation.cpp | 160 ++
tests/code/registry.cpp | 16 +-
tests/code/uid.cpp | 4 +-
tests/code/unicode.cpp | 113 +-
tests/code/xml_converter.cpp | 219 +-
tests/run.sh | 12 +-
tests/tools/CMakeLists.txt | 11 +
tests/tools/dndefine.h | 80 +
tests/tools/getscu.cc | 657 +++++
tests/tools/scu.cc | 2590 ++++++++++++++++++++
tests/tools/scu.h | 1197 +++++++++
282 files changed, 20946 insertions(+), 5558 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 3741652..41f89da 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,8 +1,8 @@
-sudo: false
+sudo: required
+dist: trusty
language: cpp
compiler:
- gcc
- - clang
addons:
apt:
packages:
@@ -10,7 +10,6 @@ addons:
- libwrap0-dev
- libjsoncpp-dev
- libicu-dev
- - uuid-dev
- zlib1g-dev
- libboost-dev
- libboost-filesystem-dev
@@ -28,6 +27,7 @@ before_script:
- cmake -D CMAKE_CXX_FLAGS:STRING="${CMAKE_CXX_FLAGS}" -D CMAKE_BUILD_TYPE:STRING=Debug ../
script:
- make -j $(nproc)
+ - export PATH=$PWD/tests/tools:$PATH
- ../tests/run.sh
after_success:
- if [ "${CC}" = "gcc" ]; then ${HOME}/.local/bin/coveralls --exclude examples --exclude tests --exclude-pattern '.*CMake[^/]+\.c(?:pp)?' --exclude-pattern "/usr/.*" --root=${SRC_DIR} --build-root ${BIN_DIR} | grep -vP "^File '.*'$" | grep -vP ":creating '.*'$" | grep -vP "^Lines executed:.*" | sed '/^$/d'; fi
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0cc8bea..1fe85f3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,11 +1,11 @@
cmake_minimum_required(VERSION 2.8)
-project("dcmtkpp")
-set(dcmtkpp_MAJOR_VERSION 0)
-set(dcmtkpp_MINOR_VERSION 3)
-set(dcmtkpp_PATCH_VERSION 1)
-set(dcmtkpp_VERSION
- ${dcmtkpp_MAJOR_VERSION}.${dcmtkpp_MINOR_VERSION}.${dcmtkpp_PATCH_VERSION})
+project("odil")
+set(odil_MAJOR_VERSION 0)
+set(odil_MINOR_VERSION 4)
+set(odil_PATCH_VERSION 0)
+set(odil_VERSION
+ ${odil_MAJOR_VERSION}.${odil_MINOR_VERSION}.${odil_PATCH_VERSION})
option(BUILD_EXAMPLES "Build the examples directory." ON)
@@ -21,3 +21,15 @@ endif()
if(BUILD_TESTING)
add_subdirectory("tests")
endif()
+
+add_custom_target(
+ CIIntegration ${CMAKE_COMMAND} -E echo "CI Integration"
+ SOURCES appveyor.yml .travis.yml)
+
+add_custom_target(
+ Documentation ${CMAKE_COMMAND} -E echo "Documentation"
+ SOURCES Doxyfile LICENSE.txt README.md)
+
+add_custom_target(
+ Registry ${CMAKE_COMMAND} -E echo "Registry"
+ SOURCES generate_registry registry.cpp.tmpl registry.h.tmpl)
diff --git a/Doxyfile b/Doxyfile
index 885d4f2..9b2a083 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -26,7 +26,7 @@ DOXYFILE_ENCODING = UTF-8
# identify the project. Note that if you do not use Doxywizard you need
# to put quotes around the project name if it contains spaces.
-PROJECT_NAME = "dcmtkpp"
+PROJECT_NAME = "odil"
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
# This could be handy for archiving the generated documentation or
@@ -657,7 +657,7 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
-INPUT = ./src/dcmtkpp
+INPUT = ./src/odil
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
diff --git a/FindDCMTK.cmake b/FindDCMTK.cmake
new file mode 100644
index 0000000..22f205d
--- /dev/null
+++ b/FindDCMTK.cmake
@@ -0,0 +1,32 @@
+# - Try to find DCMTK
+# Once done this will define
+# DCMTK_FOUND - System has DCMTK
+# DCMTK_INCLUDE_DIRS - The DCMTK include directories
+# DCMTK_LIBRARY_DIRS - The DCMTK link directories
+# DCMTK_LIBRARIES - The libraries needed to use DCMTK
+# DCMTK_DEFINITIONS - Compiler switches required for using DCMTK
+
+set(DCMTK_DEFINITIONS "-D HAVE_CONFIG_H")
+
+find_path(DCMTK_INCLUDE_DIR "dcmtk/dcmdata/dctk.h")
+find_library(DCMTK_LIBRARY dcmdata)
+
+set(DCMTK_INCLUDE_DIRS ${DCMTK_INCLUDE_DIR})
+
+set(DCMTK_LIBRARIES ${DCMTK_LIBRARY} dcmnet dcmdata oflog ofstd)
+foreach(library iconv pthread wrap z)
+ find_library(${library}_LIBRARY ${library})
+ if(${library}_LIBRARY)
+ set(DCMTK_LIBRARIES ${DCMTK_LIBRARIES} ${${library}_LIBRARY})
+ endif()
+endforeach()
+
+get_filename_component(DCMTK_LIBRARY_DIRS "${DCMTK_LIBRARY}" PATH)
+
+include(FindPackageHandleStandardArgs)
+# handle the QUIETLY and REQUIRED arguments and set DCMTK_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(
+ DCMTK DEFAULT_MSG DCMTK_INCLUDE_DIR DCMTK_LIBRARY)
+
+mark_as_advanced(DCMTK_INCLUDE_DIR DCMTK_LIBRARY)
diff --git a/FindICU.cmake b/FindICU.cmake
index 57cf7ed..61e9d72 100644
--- a/FindICU.cmake
+++ b/FindICU.cmake
@@ -1,7 +1,23 @@
-find_path(ICU_INCLUDE_DIR "unicode/ucnv.h")
-# icucore is for OS X
-find_library(ICU_LIBRARIES NAMES icuuc icucore)
+# - Try to find ICU
+# Once done this will define
+# ICU_FOUND - System has ICU
+# ICU_INCLUDE_DIRS - The ICU include directories
+# ICU_LIBRARIES - The libraries needed to use ICU
+# ICU_DEFINITIONS - Compiler switches required for using ICU
+
+find_package(PkgConfig)
+pkg_check_modules(PC_ICU QUIET icu-uc)
+set(ICU_DEFINITIONS ${PC_ICU_CFLAGS_OTHER})
+
+find_path(ICU_INCLUDE_DIR "unicode/ucnv.h" HINTS ${PC_ICU_INCLUDE_DIRS})
+find_library(ICU_LIBRARY NAMES icuuc HINTS ${PC_ICU_LIBRARY_DIRS} )
+
+set(ICU_LIBRARIES ${ICU_LIBRARY} )
+set(ICU_INCLUDE_DIRS ${ICU_INCLUDE_DIR} )
include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(ICU DEFAULT_MSG
- ICU_INCLUDE_DIR ICU_LIBRARIES)
+# handle the QUIETLY and REQUIRED arguments and set ICU_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(ICU DEFAULT_MSG ICU_LIBRARY ICU_INCLUDE_DIR)
+
+mark_as_advanced(ICU_INCLUDE_DIR ICU_LIBRARY)
diff --git a/FindJsonCpp.cmake b/FindJsonCpp.cmake
new file mode 100644
index 0000000..5f6c57c
--- /dev/null
+++ b/FindJsonCpp.cmake
@@ -0,0 +1,24 @@
+# - Try to find JsonCpp
+# Once done this will define
+# JsonCpp_FOUND - System has JsonCpp
+# JsonCpp_INCLUDE_DIRS - The JsonCpp include directories
+# JsonCpp_LIBRARIES - The libraries needed to use JsonCpp
+# JsonCpp_DEFINITIONS - Compiler switches required for using JsonCpp
+
+find_package(PkgConfig)
+pkg_check_modules(PC_JsonCpp QUIET jsoncpp)
+set(JsonCpp_DEFINITIONS ${PC_JsonCpp_CFLAGS_OTHER})
+
+find_path(JsonCpp_INCLUDE_DIR "json/json.h" HINTS ${PC_JsonCpp_INCLUDE_DIRS})
+find_library(JsonCpp_LIBRARY NAMES jsoncpp HINTS ${PC_JsonCpp_LIBRARY_DIRS} )
+
+set(JsonCpp_LIBRARIES ${JsonCpp_LIBRARY} )
+set(JsonCpp_INCLUDE_DIRS ${JsonCpp_INCLUDE_DIR} )
+
+include(FindPackageHandleStandardArgs)
+# handle the QUIETLY and REQUIRED arguments and set JsonCpp_FOUND to TRUE
+# if all listed variables are TRUE
+find_package_handle_standard_args(
+ JsonCpp DEFAULT_MSG JsonCpp_LIBRARY JsonCpp_INCLUDE_DIR)
+
+mark_as_advanced(JsonCpp_INCLUDE_DIR JsonCpp_LIBRARY)
diff --git a/README.md b/README.md
index 086f9e0..dc3dc37 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,20 @@
-DCMTK++
+Odil
=======
-DCMTK++ is a C++ wrapper library for the [DCMTK](http://dicom.offis.de/dcmtk.php.en)
-DICOM toolkit.
+Odil is a C++11 library for the [DICOM](http://dicom.nema.org/) standard.
-DCMTK++ is a C++ wrapper library for [DCMTK](http://dicom.offis.de/dcmtk.php.en),
-a C++ toolkit handling the DICOM medical imaging standard. DCMTK++
-leverages C++ constructs to provide a more user-friendly API, notably
-for the networking part. Included in DCMTK++ are exception-based error
-handling, generic access to datasets elements, standard JSON
-representation of datasets, and generic implementation of messages,
+Odil leverages C++ constructs to provide a user-friendly API of the different
+parts of the DICOM standard. Included in Odil are exception-based error
+handling, generic access to datasets elements, standard JSON and XML
+representation of datasets, and generic implementation of messages,
clients and servers for the various DICOM protocols.
-[![Build Status](https://travis-ci.org/lamyj/dcmtkpp.svg?branch=master)](https://travis-ci.org/lamyj/dcmtkpp)
-[![Coverage Status](https://coveralls.io/repos/lamyj/dcmtkpp/badge.svg)](https://coveralls.io/r/lamyj/dcmtkpp)
+Odil also provides conversion to and from
+[DCMTK](http://dicom.offis.de/dcmtk.php.en) data structures.
+
+Odil builds and run on:
+* Linux (Debian 7, Debian 8, Ubuntu 12.04, Ubuntu 14.04, both 32 and 64 bits)
+* OS X
+
+[![Build Status](https://travis-ci.org/lamyj/odil.svg?branch=master)](https://travis-ci.org/lamyj/odil)
+[![Coverage Status](https://coveralls.io/repos/lamyj/odil/badge.svg)](https://coveralls.io/r/lamyj/odil)
diff --git a/appveyor.full.yml b/appveyor.full.yml
new file mode 100644
index 0000000..092d607
--- /dev/null
+++ b/appveyor.full.yml
@@ -0,0 +1,52 @@
+version: "{build}"
+
+os: Visual Studio 2015
+
+clone_folder: c:\projects\dcmtkpp
+
+environment:
+ BOOST_ROOT: C:/Libraries/boost_1_59_0
+ BOOST_LIBRARYDIR: C:/Libraries/boost_1_59_0/lib64-msvc-14.0
+ ICU_INCLUDE_DIR: C:/Libraries/icu/include
+ ICU_LIBRARY: C:/Libraries/icu/lib64/icuuc.lib
+ JsonCpp_INCLUDE_DIR: c:/Libraries/jsoncpp_0_10_5/include
+ JsonCpp_LIBRARY: c:/Libraries/jsoncpp_0_10_5/lib/jsoncpp.lib
+ DCMTK_INCLUDE_DIR: C:/Libraries/dcmtk-3.6.1_20150924/include
+ DCMTK_LIBRARY: C:/Libraries/dcmtk-3.6.1_20150924/lib/dcmdata.lib
+
+#init:
+#- ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
+
+install:
+ # ICU4C
+- ps: Start-FileDownload http://download.icu-project.org/files/icu4c/56.1/icu4c-56_1-Win64-msvc10.zip
+- 7z x -oC:\Libraries icu4c-56_1-Win64-msvc10.zip
+ # JsonCpp
+- ps: Start-FileDownload https://github.com/open-source-parsers/jsoncpp/archive/0.10.5.zip
+- 7z x -oC:\projects 0.10.5.zip
+- cd C:\projects\jsoncpp-0.10.5
+- mkdir build
+- cd build
+- cmake -D CMAKE_INSTALL_PREFIX=c:\Libraries\jsoncpp_0_10_5 ..
+- cmake --build . --config release --target install
+ # DCMTK
+- ps: Start-FileDownload http://dicom.offis.de/download/dcmtk/snapshot/dcmtk-3.6.1_20150924.tar.gz
+- 7z x -so dcmtk-3.6.1_20150924.tar.gz | 7z x -si -oC:\projects -ttar
+- cd C:\projects\dcmtk-3.6.1_20150924
+- mkdir build
+- cd build
+- cmake -D CMAKE_INSTALL_PREFIX=c:\Libraries\dcmtk-3.6.1_20150924 ..
+- cmake --build . --config release --target install
+
+before_build:
+ - cd c:\projects\dcmtkpp
+ - md build
+ - cd build
+ - set PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;%PATH%
+ - cmake -DBOOST_ROOT="%BOOST_ROOT%" -DBOOST_LIBRARYDIR="%BOOST_LIBRARYDIR%" -DICU_INCLUDE_DIR="%ICU_INCLUDE_DIR%" -DICU_LIBRARY="%ICU_LIBRARY%" -DJsonCpp_INCLUDE_DIR="%JsonCpp_INCLUDE_DIR%" -DJsonCpp_LIBRARY="%JsonCpp_LIBRARY%" -DDCMTK_INCLUDE_DIR="%DCMTK_INCLUDE_DIR%" -DDCMTK_LIBRARY="%DCMTK_LIBRARY%" ..
+
+build:
+ project: C:\projects\dcmtkpp\build\dcmtkpp.sln
+
+#on_finish:
+#- ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..f791cea
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,43 @@
+version: "{build}"
+
+os: Windows Server 2012 R2
+
+clone_folder: c:\projects\odil
+
+environment:
+ BOOST_ROOT: C:/Libraries/boost_1_59_0
+ BOOST_LIBRARYDIR: C:/Libraries/boost_1_59_0/lib64-msvc-14.0
+ ICU_INCLUDE_DIR: C:/Libraries/icu/include
+ ICU_LIBRARY: C:/Libraries/icu/lib64/icuuc.lib
+ JsonCpp_INCLUDE_DIR: c:/Libraries/jsoncpp_0_10_5/include
+ JsonCpp_LIBRARY: c:/Libraries/jsoncpp_0_10_5/lib/jsoncpp.lib
+ DCMTK_INCLUDE_DIR: C:/Libraries/dcmtk-3.6.1_20150924/include
+ DCMTK_LIBRARY: C:/Libraries/dcmtk-3.6.1_20150924/lib/dcmdata.lib
+
+configuration:
+ - Release
+
+install:
+ # ICU4C
+- ps: Start-FileDownload http://download.icu-project.org/files/icu4c/56.1/icu4c-56_1-Win64-msvc10.zip
+- 7z x -bd -oC:\Libraries icu4c-56_1-Win64-msvc10.zip
+ # JsonCpp
+- ps: Start-FileDownload https://github.com/lamyj/jsoncpp/releases/download/0.10.5/jsoncpp_0_10_5_Win64_msvc14.zip
+- 7z x -bd -oC:\Libraries jsoncpp_0_10_5_Win64_msvc14.zip
+ # DCMTK
+- ps: Start-FileDownload https://github.com/lamyj/dcmtk/releases/download/DCMTK-3.6.1_20150924/dcmtk-3.6.1_20150924_Win64_msvc14.zip
+- 7z x -bd -oC:\Libraries dcmtk-3.6.1_20150924_Win64_msvc14.zip
+
+before_build:
+ - cd c:\projects\odil
+ - md build
+ - cd build
+ - cmake
+ -DBOOST_ROOT="%BOOST_ROOT%" -DBOOST_LIBRARYDIR="%BOOST_LIBRARYDIR%"
+ -DICU_INCLUDE_DIR="%ICU_INCLUDE_DIR%" -DICU_LIBRARY="%ICU_LIBRARY%"
+ -DJsonCpp_INCLUDE_DIR="%JsonCpp_INCLUDE_DIR%" -DJsonCpp_LIBRARY="%JsonCpp_LIBRARY%"
+ -DDCMTK_INCLUDE_DIR="%DCMTK_INCLUDE_DIR%" -DDCMTK_LIBRARY="%DCMTK_LIBRARY%"
+ ..
+
+build:
+ project: C:\projects\odil\build\odil.sln
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index fa27216..0c5827e 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -1,11 +1,17 @@
-add_definitions("-D HAVE_CONFIG_H")
+find_package(DCMTK REQUIRED)
+find_package(JsonCpp REQUIRED)
-include_directories(${CMAKE_SOURCE_DIR}/src)
+include_directories(${CMAKE_SOURCE_DIR}/src ${JsonCpp_INCLUDE_DIRS})
+add_definitions(
+ ${DCMTK_DEFINITIONS}
+ -D BOOST_ASIO_DYN_LINK
+ -D ODIL_MAJOR_VERSION=${odil_MAJOR_VERSION}
+)
file(GLOB_RECURSE examples *.cpp)
foreach(example_file ${examples})
get_filename_component(example ${example_file} NAME_WE)
add_executable(${example} ${example_file})
- target_link_libraries(${example} dcmtkpp dcmdata)
+ target_link_libraries(${example} odil)
endforeach()
diff --git a/examples/dicomdir.cpp b/examples/dicomdir.cpp
index 426a488..5274ebd 100644
--- a/examples/dicomdir.cpp
+++ b/examples/dicomdir.cpp
@@ -1,20 +1,20 @@
#include <string>
#include <vector>
-#include <dcmtkpp/BasicDirectoryCreator.h>
-#include <dcmtkpp/registry.h>
+#include <odil/BasicDirectoryCreator.h>
+#include <odil/registry.h>
int main(int argc, char** argv)
{
std::string const root(argv[1]);
std::vector<std::string> const files(argv+2, argv+argc);
- dcmtkpp::BasicDirectoryCreator creator(root, files,
+ odil::BasicDirectoryCreator creator(root, files,
{
- {"PATIENT", { {dcmtkpp::registry::PatientBirthDate, 3} }},
- {"STUDY", { {dcmtkpp::registry::PatientAge, 3} }},
- {"SERIES", { {dcmtkpp::registry::SeriesDescription, 3} }},
- {"IMAGE", { {dcmtkpp::registry::ImageType, 3} }},
+ {"PATIENT", { {odil::registry::PatientBirthDate, 3} }},
+ {"STUDY", { {odil::registry::PatientAge, 3} }},
+ {"SERIES", { {odil::registry::SeriesDescription, 3} }},
+ {"IMAGE", { {odil::registry::ImageType, 3} }},
});
creator();
diff --git a/examples/dump.cpp b/examples/dump.cpp
index 86200ce..c7c987d 100644
--- a/examples/dump.cpp
+++ b/examples/dump.cpp
@@ -4,9 +4,9 @@
#include <string>
#include <utility>
-#include <dcmtkpp/DataSet.h>
-#include <dcmtkpp/Reader.h>
-#include <dcmtkpp/Value.h>
+#include <odil/DataSet.h>
+#include <odil/Reader.h>
+#include <odil/Value.h>
struct Printer
{
@@ -30,7 +30,7 @@ struct Printer
}
}
- void operator()(dcmtkpp::Value::DataSets const & value) const
+ void operator()(odil::Value::DataSets const & value) const
{
this->stream << "\n";
@@ -46,17 +46,17 @@ struct Printer
}
}
- void operator()(dcmtkpp::Value::Binary const & value) const
+ void operator()(odil::Value::Binary const & value) const
{
this->stream << this->indent << "(binary)";
}
- void operator()(dcmtkpp::DataSet const & data_set) const
+ void operator()(odil::DataSet const & data_set) const
{
for(auto const & item: data_set)
{
this->stream << this->indent << item.first << " " << as_string(item.second.vr) << " ";
- dcmtkpp::apply_visitor(*this, item.second.get_value());
+ odil::apply_visitor(*this, item.second.get_value());
this->stream << "\n";
}
}
@@ -68,10 +68,10 @@ int main(int argc, char** argv)
{
std::ifstream stream(argv[i], std::ios::in | std::ios::binary);
- std::pair<dcmtkpp::DataSet, dcmtkpp::DataSet> file;
+ std::pair<odil::DataSet, odil::DataSet> file;
try
{
- file = dcmtkpp::Reader::read_file(stream);
+ file = odil::Reader::read_file(stream);
}
catch(std::exception & e)
{
diff --git a/examples/find.cpp b/examples/find.cpp
index 9acb22a..4c5b1e0 100644
--- a/examples/find.cpp
+++ b/examples/find.cpp
@@ -1,73 +1,72 @@
#include <iostream>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/FindSCU.h"
-#include "dcmtkpp/registry.h"
+#include "odil/Association.h"
+#include "odil/DataSet.h"
+#include "odil/FindSCU.h"
+#include "odil/registry.h"
-void print_informations(dcmtkpp::DataSet const & response)
+void print_informations(odil::DataSet const & response)
{
- std::cout << response.as_string("PatientName", 0) << ": "
- << response.as_string("StudyDescription", 0)
- << " on " << response.as_string("StudyDate", 0) << ", "
- << response.as_int(dcmtkpp::registry::NumberOfStudyRelatedSeries, 0) << " series\n";
+ auto const name = response.has("PatientName")?
+ response.as_string("PatientName", 0):"(no name)";
+ auto const study = response.has("StudyDescription")?
+ response.as_string("StudyDescription", 0):"(no description)";
+ auto const date = response.has("StudyDate")?
+ response.as_string("StudyDate", 0):"(no date)";
+ std::cout
+ << "\"" << name << "\": \"" << study << "\" on " << date << "\n";
}
int main()
{
- dcmtkpp::Network network;
- network.initialize();
-
- dcmtkpp::Association association;
-
- association.set_own_ae_title("myself");
-
- association.set_peer_host_name("pacs.example.com");
+ odil::Association association;
+ association.set_peer_host("184.73.255.26");
association.set_peer_port(11112);
- association.set_peer_ae_title("pacs");
-
- association.add_presentation_context(
- dcmtkpp::registry::StudyRootQueryRetrieveInformationModelFIND,
- { dcmtkpp::registry::ImplicitVRLittleEndian });
-
- association.add_presentation_context(
- dcmtkpp::registry::VerificationSOPClass,
- { dcmtkpp::registry::ImplicitVRLittleEndian });
-
- association.associate(network);
+ association.update_parameters()
+ .set_calling_ae_title("myself")
+ .set_called_ae_title("AWSPIXELMEDPUB")
+ .set_presentation_contexts({
+ {
+ 1, odil::registry::StudyRootQueryRetrieveInformationModelFIND,
+ { odil::registry::ExplicitVRLittleEndian }, true, false
+ },
+ {
+ 3, odil::registry::VerificationSOPClass,
+ { odil::registry::ExplicitVRLittleEndian }, true, false
+ }
+ });
- dcmtkpp::FindSCU scu;
- scu.set_network(&network);
- scu.set_association(&association);
+ association.associate();
+ odil::FindSCU scu(association);
+
scu.echo();
-
- dcmtkpp::DataSet query;
- query.add("PatientName", { "DOE^John" });
+
+ odil::DataSet query;
+ query.add("PatientName", { "*" });
query.add("QueryRetrieveLevel", { "STUDY" });
query.add("StudyDescription");
- query.add(dcmtkpp::registry::NumberOfStudyRelatedSeries);
query.add("StudyDate");
- scu.set_affected_sop_class(dcmtkpp::registry::StudyRootQueryRetrieveInformationModelFIND);
+ scu.set_affected_sop_class(odil::registry::StudyRootQueryRetrieveInformationModelFIND);
std::cout << "--------\n";
std::cout << "Callback\n";
std::cout << "--------\n\n";
scu.find(query, print_informations);
-
+
std::cout << "\n";
std::cout << "------\n";
std::cout << "vector\n";
std::cout << "------\n\n";
- std::vector<dcmtkpp::DataSet> result = scu.find(query);
- for(auto dataset: result)
+ auto const result = scu.find(query);
+ for(auto const & dataset: result)
{
print_informations(dataset);
}
association.release();
- network.drop();
}
diff --git a/examples/genericscp.cpp b/examples/genericscp.cpp
new file mode 100644
index 0000000..3bd0a3c
--- /dev/null
+++ b/examples/genericscp.cpp
@@ -0,0 +1,142 @@
+#include <boost/asio.hpp>
+
+#include "odil/Association.h"
+#include "odil/DataSet.h"
+#include "odil/EchoSCP.h"
+#include "odil/FindSCP.h"
+#include "odil/StoreSCP.h"
+#include "odil/registry.h"
+#include "odil/SCPDispatcher.h"
+#include "odil/SCP.h"
+
+#include "odil/message/CEchoRequest.h"
+#include "odil/message/CFindRequest.h"
+#include "odil/message/CFindResponse.h"
+#include "odil/message/CStoreRequest.h"
+
+class FindGenerator: public odil::SCP::DataSetGenerator
+{
+public:
+ FindGenerator()
+ {
+ // Nothing to do
+ }
+
+ virtual ~FindGenerator()
+ {
+ // Nothing to do.
+ }
+
+ virtual void initialize(odil::message::Request const & )
+ {
+ odil::DataSet data_set_1;
+ data_set_1.add(odil::registry::PatientName, {"Hello^World"});
+ data_set_1.add(odil::registry::PatientID, {"1234"});
+ this->_responses.push_back(data_set_1);
+
+ odil::DataSet data_set_2;
+ data_set_2.add(odil::registry::PatientName, {"Doe^John"});
+ data_set_2.add(odil::registry::PatientID, {"5678"});
+ this->_responses.push_back(data_set_2);
+
+ this->_response_iterator = this->_responses.begin();
+ }
+
+ virtual bool done() const
+ {
+ return (this->_response_iterator == this->_responses.end());
+ }
+
+ virtual odil::DataSet get() const
+ {
+ return *this->_response_iterator;
+ }
+
+ virtual void next()
+ {
+ ++this->_response_iterator;
+ }
+
+
+private:
+ std::vector<odil::DataSet> _responses;
+ std::vector<odil::DataSet>::const_iterator _response_iterator;
+};
+
+odil::Value::Integer echo(odil::message::CEchoRequest const & request)
+{
+ std::cout << "Received echo\n";
+ std::cout << " ID: " << request.get_message_id() << "\n";
+ std::cout << " Affected SOP Class UID: " << request.get_affected_sop_class_uid() << "\n";
+ return odil::message::Response::Success;
+}
+
+odil::Value::Integer store(odil::message::CStoreRequest const & request)
+{
+ auto const patient_name =
+ request.get_data_set().as_string(odil::registry::PatientName)[0];
+ std::cout << "Storing " << patient_name << "\n";
+ return odil::message::Response::Success;
+}
+
+int main()
+{
+ odil::Association association;
+ association.receive_association(boost::asio::ip::tcp::v4(), 11112);
+
+ std::cout
+ << "Received association from "
+ << association.get_peer_host() << ":" << association.get_peer_port()
+ << "\n";
+
+ auto const & contexts =
+ association.get_negotiated_parameters().get_presentation_contexts();
+ std::cout << "Presentation contexts (" << contexts.size() << ")\n";
+ for(auto const & context: contexts)
+ {
+ std::cout
+ << " "
+ << odil::registry::uids_dictionary.at(context.abstract_syntax).name
+ << ": "
+ << odil::registry::uids_dictionary.at(context.transfer_syntaxes[0]).name
+ << ", "
+ << (context.scu_role_support?"SCU":"")
+ << ((context.scu_role_support & context.scp_role_support)?"/":"")
+ << (context.scp_role_support?"SCP":"")
+ << std::endl;
+ }
+
+ auto echo_scp = std::make_shared<odil::EchoSCP>(association, echo);
+ auto find_scp = std::make_shared<odil::FindSCP>(
+ association, std::make_shared<FindGenerator>());
+ auto store_scp = std::make_shared<odil::StoreSCP>(association, store);
+
+ odil::SCPDispatcher dispatcher(association);
+ dispatcher.set_scp(odil::message::Message::Command::C_ECHO_RQ, echo_scp);
+ dispatcher.set_scp(odil::message::Message::Command::C_FIND_RQ, find_scp);
+ dispatcher.set_scp(
+ odil::message::Message::Command::C_STORE_RQ, store_scp);
+
+ bool done = false;
+ while(!done)
+ {
+ try
+ {
+ dispatcher.dispatch();
+ }
+ catch(odil::AssociationReleased const &)
+ {
+ std::cout << "Peer released association" << std::endl;
+ done = true;
+ }
+ catch(odil::AssociationAborted const & e)
+ {
+ std::cout
+ << "Peer aborted association, "
+ << "source: " << int(e.source) << ", "
+ << "reason: " << int(e.reason)
+ << std::endl;
+ done = true;
+ }
+ }
+}
diff --git a/examples/get.cpp b/examples/get.cpp
index 19ea720..a082989 100644
--- a/examples/get.cpp
+++ b/examples/get.cpp
@@ -1,69 +1,100 @@
#include <iostream>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/GetSCU.h"
-#include "dcmtkpp/registry.h"
+#include "odil/Association.h"
+#include "odil/DataSet.h"
+#include "odil/FindSCU.h"
+#include "odil/GetSCU.h"
+#include "odil/registry.h"
-void print_informations(dcmtkpp::DataSet const & response)
+void print_informations(odil::DataSet const & response)
{
+ auto const name = response.has("PatientName")?
+ response.as_string("PatientName", 0):"(no name)";
+ auto const study = response.has("StudyDescription")?
+ response.as_string("StudyDescription", 0):"(no study description)";
+ auto const series = response.has("SeriesDescription")?
+ response.as_string("SeriesDescription", 0):"(no series description)";
+ auto const instance = response.has("InstanceNumber")?
+ response.as_int("InstanceNumber", 0):(-1);
std::cout
- << response.as_string("PatientName", 0)
- << ": "
- << response.as_string("StudyDescription", 0)
- << " / "
- << response.as_string("SeriesDescription", 0)
- << ": "
- << response.as_string("InstanceNumber", 0)
- << "\n";
+ << name << ": " << study << " / " << series << ": " << instance << "\n";
}
int main()
{
- dcmtkpp::Network network;
- network.initialize();
-
- dcmtkpp::Association association;
-
- association.set_own_ae_title("myself");
-
- association.set_peer_host_name("pacs.example.com");
+ odil::Association association;
+ association.set_peer_host("184.73.255.26");
association.set_peer_port(11112);
- association.set_peer_ae_title("pacs");
-
- association.add_presentation_context(
- dcmtkpp::registry::StudyRootQueryRetrieveInformationModelGET,
- { dcmtkpp::registry::ImplicitVRLittleEndian });
-
- association.add_presentation_context(
- dcmtkpp::registry::MRImageStorage,
- { dcmtkpp::registry::ImplicitVRLittleEndian },
- ASC_SC_ROLE_SCP);
-
- association.add_presentation_context(
- dcmtkpp::registry::VerificationSOPClass,
- { dcmtkpp::registry::ImplicitVRLittleEndian });
-
- association.associate(network);
-
- dcmtkpp::GetSCU scu;
- scu.set_network(&network);
- scu.set_association(&association);
-
- scu.echo();
+ association.update_parameters()
+ .set_calling_ae_title("myself")
+ .set_called_ae_title("AWSPIXELMEDPUB")
+ .set_presentation_contexts({
+ {
+ 1, odil::registry::StudyRootQueryRetrieveInformationModelFIND,
+ { odil::registry::ImplicitVRLittleEndian }, true, false
+ },
+ {
+ 3, odil::registry::StudyRootQueryRetrieveInformationModelGET,
+ { odil::registry::ImplicitVRLittleEndian }, true, false
+ },
+ {
+ 5, odil::registry::MRImageStorage,
+ { odil::registry::ImplicitVRLittleEndian }, false, true
+ },
+ {
+ 7, odil::registry::VerificationSOPClass,
+ { odil::registry::ImplicitVRLittleEndian }, true, false
+ }
+ });
+
+ association.associate();
+
+ odil::FindSCU find_scu(association);
+ find_scu.set_affected_sop_class(
+ odil::registry::StudyRootQueryRetrieveInformationModelFIND);
+
+ odil::DataSet query;
+ query.add("QueryRetrieveLevel", { "STUDY" });
+ query.add("StudyInstanceUID");
+
+ auto const studies = find_scu.find(query);
+ odil::DataSet series;
+ for(auto const & study: studies)
+ {
+ if(!study.has("StudyInstanceUID"))
+ {
+ continue;
+ }
+
+ query = odil::DataSet();
+ query.add("QueryRetrieveLevel", {"SERIES"});
+ query.add("Modality", {"MR"});
+ query.add("StudyInstanceUID", study.as_string("StudyInstanceUID"));
+ query.add("SeriesInstanceUID");
+
+ auto const study_series = find_scu.find(query);
+
+ if(!study_series.empty())
+ {
+ series = study_series[0];
+ break;
+ }
+ }
+
+ odil::GetSCU get_scu(association);
+ get_scu.set_affected_sop_class(
+ odil::registry::StudyRootQueryRetrieveInformationModelGET);
- dcmtkpp::DataSet query;
- query.add("PatientID",{ "1234" });
+ query = odil::DataSet();
query.add("QueryRetrieveLevel", { "SERIES" });
- query.add("StudyInstanceUID", { "1.2.3.4.5" });
- query.add("SeriesInstanceUID", { "1.2.3.4.5.1" });
-
- scu.set_affected_sop_class(dcmtkpp::registry::StudyRootQueryRetrieveInformationModelGET);
-
+ query.add("StudyInstanceUID", series["StudyInstanceUID"]);
+ query.add("SeriesInstanceUID", series["SeriesInstanceUID"]);
+
std::cout << "--------\n";
std::cout << "Callback\n";
std::cout << "--------\n\n";
- scu.get(query, print_informations);
+ get_scu.get(query, print_informations);
std::cout << "\n";
@@ -71,12 +102,11 @@ int main()
std::cout << "vector\n";
std::cout << "------\n\n";
- std::vector<dcmtkpp::DataSet> result = scu.get(query);
- for(auto dataset: result)
+ auto const result = get_scu.get(query);
+ for(auto const & dataset: result)
{
print_informations(dataset);
}
association.release();
- network.drop();
}
diff --git a/examples/move.cpp b/examples/move.cpp
index 589ce1d..b96e2c7 100644
--- a/examples/move.cpp
+++ b/examples/move.cpp
@@ -1,74 +1,103 @@
#include <iostream>
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmdata/dctk.h>
+#include "odil/Association.h"
+#include "odil/DataSet.h"
+#include "odil/FindSCU.h"
+#include "odil/MoveSCU.h"
+#include "odil/registry.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/MoveSCU.h"
-#include "dcmtkpp/registry.h"
-
-void print_informations(dcmtkpp::DataSet const & response)
+void print_informations(odil::DataSet const & response)
{
+ auto const name = response.has("PatientName")?
+ response.as_string("PatientName", 0):"(no name)";
+ auto const study = response.has("StudyDescription")?
+ response.as_string("StudyDescription", 0):"(no study description)";
+ auto const series = response.has("SeriesDescription")?
+ response.as_string("SeriesDescription", 0):"(no series description)";
+ auto const instance = response.has("InstanceNumber")?
+ response.as_int("InstanceNumber", 0):(-1);
std::cout
- << response.as_string("PatientName", 0)
- << ": "
- << response.as_string("StudyDescription", 0)
- << " / "
- << response.as_string("SeriesDescription", 0)
- << ": "
- << response.as_string("InstanceNumber", 0)
- << "\n";
+ << name << ": " << study << " / " << series << ": " << instance << "\n";
}
int main()
{
- dcmtkpp::Network network;
- network.set_role(NET_ACCEPTORREQUESTOR);
- network.set_port(11112);
- network.initialize();
-
- dcmtkpp::Association association;
- association.set_own_ae_title("myself");
-
- association.set_peer_host_name("pacs.example.com");
+ odil::Association association;
+ association.set_peer_host("184.73.255.26");
association.set_peer_port(11112);
- association.set_peer_ae_title("pacs");
-
- association.add_presentation_context(
- dcmtkpp::registry::StudyRootQueryRetrieveInformationModelMOVE,
- { dcmtkpp::registry::ImplicitVRLittleEndian });
-
- association.add_presentation_context(
- dcmtkpp::registry::MRImageStorage,
- { dcmtkpp::registry::ImplicitVRLittleEndian },
- ASC_SC_ROLE_SCP);
-
- association.add_presentation_context(
- dcmtkpp::registry::VerificationSOPClass,
- { dcmtkpp::registry::ImplicitVRLittleEndian });
-
- association.associate(network);
-
- dcmtkpp::MoveSCU scu;
- scu.set_network(&network);
- scu.set_association(&association);
- scu.set_move_destination(association.get_own_ae_title());
+ association.update_parameters()
+ .set_calling_ae_title("myself")
+ .set_called_ae_title("AWSPIXELMEDPUB")
+ .set_presentation_contexts({
+ {
+ 1, odil::registry::StudyRootQueryRetrieveInformationModelFIND,
+ { odil::registry::ImplicitVRLittleEndian }, true, false
+ },
+ {
+ 3, odil::registry::StudyRootQueryRetrieveInformationModelMOVE,
+ { odil::registry::ImplicitVRLittleEndian }, true, false
+ },
+ {
+ 5, odil::registry::MRImageStorage,
+ { odil::registry::ImplicitVRLittleEndian }, false, true
+ },
+ {
+ 7, odil::registry::VerificationSOPClass,
+ { odil::registry::ImplicitVRLittleEndian }, true, false
+ }
+ });
+
+ association.associate();
+
+ odil::FindSCU find_scu(association);
+ find_scu.set_affected_sop_class(
+ odil::registry::StudyRootQueryRetrieveInformationModelFIND);
+
+ odil::DataSet query;
+ query.add("QueryRetrieveLevel", { "STUDY" });
+ query.add("StudyInstanceUID");
+ query.add("NumberOfStudyRelatedSeries");
+
+ auto const studies = find_scu.find(query);
+ odil::DataSet series;
+ for(auto const & study: studies)
+ {
+ if(!study.has("StudyInstanceUID"))
+ {
+ continue;
+ }
+
+ query = odil::DataSet();
+ query.add("QueryRetrieveLevel", {"SERIES"});
+ query.add("Modality", {"MR"});
+ query.add("StudyInstanceUID", study.as_string("StudyInstanceUID"));
+ query.add("SeriesInstanceUID");
+
+ auto const study_series = find_scu.find(query);
+
+ if(!study_series.empty())
+ {
+ series = study_series[0];
+ break;
+ }
+ }
- scu.echo();
+ odil::MoveSCU move_scu(association);
+ move_scu.set_affected_sop_class(
+ odil::registry::StudyRootQueryRetrieveInformationModelMOVE);
+ move_scu.set_move_destination(
+ association.get_parameters().get_calling_ae_title());
- dcmtkpp::DataSet query;
- query.add("PatientID",{ "1234" });
+ query = odil::DataSet();
query.add("QueryRetrieveLevel", { "SERIES" });
- query.add("StudyInstanceUID", { "1.2.3.4.5" });
- query.add("SeriesInstanceUID", { "1.2.3.4.5.1" });
-
- scu.set_affected_sop_class(dcmtkpp::registry::StudyRootQueryRetrieveInformationModelMOVE);
+ query.add("StudyInstanceUID", series["StudyInstanceUID"]);
+ query.add("SeriesInstanceUID", series["SeriesInstanceUID"]);
std::cout << "--------\n";
std::cout << "Callback\n";
std::cout << "--------\n\n";
- scu.move(query, print_informations);
+ move_scu.move(query, print_informations);
std::cout << "\n";
@@ -76,12 +105,11 @@ int main()
std::cout << "vector\n";
std::cout << "------\n\n";
- std::vector<dcmtkpp::DataSet> result = scu.move(query);
+ std::vector<odil::DataSet> result = move_scu.move(query);
for(auto dataset: result)
{
print_informations(dataset);
}
association.release();
- network.drop();
}
diff --git a/examples/store.cpp b/examples/store.cpp
index 515821c..4e3d170 100644
--- a/examples/store.cpp
+++ b/examples/store.cpp
@@ -1,63 +1,41 @@
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmdata/dctk.h>
-
-#include "dcmtkpp/conversion.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/StoreSCU.h"
-
-void progress_callback(void * data, unsigned long bytes_count)
-{
- long file_size = *reinterpret_cast<long*>(data);
- std::cout << bytes_count << "/" << file_size << std::endl;
-}
+#include "odil/Association.h"
+#include "odil/DataSet.h"
+#include "odil/Reader.h"
+#include "odil/StoreSCU.h"
int main(int argc, char** argv)
{
- dcmtkpp::Network network;
- network.initialize();
-
- dcmtkpp::Association association;
-
- association.set_own_ae_title("myself");
-
- association.set_peer_host_name("pacs.example.com");
+ odil::Association association;
+ association.set_peer_host("184.73.255.26");
association.set_peer_port(11112);
- association.set_peer_ae_title("pacs");
-
- association.add_presentation_context(
- dcmtkpp::registry::MRImageStorage,
- { dcmtkpp::registry::ImplicitVRLittleEndian });
-
- association.add_presentation_context(
- dcmtkpp::registry::EnhancedMRImageStorage,
- { dcmtkpp::registry::ImplicitVRLittleEndian });
-
- association.add_presentation_context(
- dcmtkpp::registry::VerificationSOPClass,
- { dcmtkpp::registry::ImplicitVRLittleEndian });
-
- association.associate(network);
-
- dcmtkpp::StoreSCU scu;
- scu.set_network(&network);
- scu.set_association(&association);
+ association.update_parameters()
+ .set_calling_ae_title("myself")
+ .set_called_ae_title("AWSPIXELMEDPUB")
+ .set_presentation_contexts({
+ {
+ 1, odil::registry::MRImageStorage,
+ { odil::registry::ImplicitVRLittleEndian }, false, true
+ },
+ {
+ 3, odil::registry::VerificationSOPClass,
+ { odil::registry::ImplicitVRLittleEndian }, true, false
+ }
+ });
+
+ association.associate();
+
+ odil::StoreSCU scu(association);
scu.echo();
for(int i=1; i<argc; ++i)
{
- std::cout << "Storing " << argv[i] << std::endl;
-
- long file_size = OFStandard::getFileSize(argv[i]);
- DcmFileFormat file;
- file.loadFile(argv[i]);
-
- dcmtkpp::DataSet const data_set = dcmtkpp::convert(file.getDataset());
+ std::ifstream stream(argv[i], std::ios::binary);
+ auto const header_and_data_set = odil::Reader::read_file(stream);
- scu.set_affected_sop_class(data_set);
- scu.store(data_set, progress_callback, &file_size);
+ scu.set_affected_sop_class(header_and_data_set.first);
+ scu.store(header_and_data_set.first);
}
association.release();
- network.drop();
}
diff --git a/generate_registry b/generate_registry
index 3ed077f..53e836f 100755
--- a/generate_registry
+++ b/generate_registry
@@ -23,8 +23,8 @@ def main():
trim_blocks=True)
templates = {
- "src/dcmtkpp/registry.h": jinja_environment.get_template("registry.h.tmpl"),
- "src/dcmtkpp/registry.cpp": jinja_environment.get_template("registry.cpp.tmpl"),
+ "src/odil/registry.h": jinja_environment.get_template("registry.h.tmpl"),
+ "src/odil/registry.cpp": jinja_environment.get_template("registry.cpp.tmpl"),
}
sources = [
diff --git a/registry.cpp.tmpl b/registry.cpp.tmpl
index 0169112..5a78062 100644
--- a/registry.cpp.tmpl
+++ b/registry.cpp.tmpl
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -8,14 +8,14 @@
#include <stdint.h>
-#include "dcmtkpp/registry.h"
+#include "odil/registry.h"
-#include "dcmtkpp/ElementsDictionary.h"
-#include "dcmtkpp/Tag.h"
-#include "dcmtkpp/VR.h"
-#include "dcmtkpp/UIDsDictionary.h"
+#include "odil/ElementsDictionary.h"
+#include "odil/Tag.h"
+#include "odil/VR.h"
+#include "odil/UIDsDictionary.h"
-namespace dcmtkpp
+namespace odil
{
namespace registry
@@ -91,5 +91,5 @@ UIDsDictionary create_uids_dictionary()
}
-dcmtkpp::ElementsDictionary dcmtkpp::registry::public_dictionary=dcmtkpp::registry::create_public_dictionary();
-dcmtkpp::UIDsDictionary dcmtkpp::registry::uids_dictionary=dcmtkpp::registry::create_uids_dictionary();
+odil::ElementsDictionary odil::registry::public_dictionary=odil::registry::create_public_dictionary();
+odil::UIDsDictionary odil::registry::uids_dictionary=odil::registry::create_uids_dictionary();
diff --git a/registry.h.tmpl b/registry.h.tmpl
index 9a5a216..670e0d1 100644
--- a/registry.h.tmpl
+++ b/registry.h.tmpl
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -9,11 +9,11 @@
#ifndef _afc7b2d7_0869_4fea_9a9b_7fe6228baca9
#define _afc7b2d7_0869_4fea_9a9b_7fe6228baca9
-#include "dcmtkpp/ElementsDictionary.h"
-#include "dcmtkpp/Tag.h"
-#include "dcmtkpp/UIDsDictionary.h"
+#include "odil/ElementsDictionary.h"
+#include "odil/Tag.h"
+#include "odil/UIDsDictionary.h"
-namespace dcmtkpp
+namespace odil
{
namespace registry
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e3ae50c..417282b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,28 +1,35 @@
find_package(Boost REQUIRED COMPONENTS filesystem system)
+find_package(DCMTK REQUIRED)
find_package(ICU REQUIRED)
-
-set(DCMTK_LIBRARIES dcmnet dcmdata oflog ofstd pthread z)
-find_library(USE_WRAP wrap)
-if(USE_WRAP)
- set(DCMTK_LIBRARIES ${DCMTK_LIBRARIES} wrap)
-endif()
+find_package(JsonCpp REQUIRED)
file(GLOB_RECURSE headers "*.h")
file(GLOB_RECURSE templates "*.txx")
file(GLOB_RECURSE files "*.cpp")
+
include_directories(
- ${CMAKE_CURRENT_SOURCE_DIR} ${Boost_INCLUDE_DIRS} ${ICU_INCLUDE_DIR})
-add_definitions("-D HAVE_CONFIG_H")
-add_definitions("-D DCMTKPP_MAJOR_VERSION=${dcmtkpp_MAJOR_VERSION}")
+ ${CMAKE_CURRENT_SOURCE_DIR} ${Boost_INCLUDE_DIRS} ${DCMTK_INCLUDE_DIRS}
+ ${ICU_INCLUDE_DIRS} ${JsonCpp_INCLUDE_DIRS})
+add_definitions(
+ ${DCMTK_DEFINITIONS}
+ -D BOOST_ASIO_SEPARATE_COMPILATION
+ -D ODIL_MAJOR_VERSION=${odil_MAJOR_VERSION}
+)
+link_directories(${Boost_LIBRARY_DIRS} ${DCMTK_LIBRARY_DIRS})
+
+add_library(odil SHARED ${files} ${headers} ${templates})
+
+target_link_libraries(odil
+ ${Boost_LIBRARIES} ${DCMTK_LIBRARIES} ${ICU_LIBRARIES} ${JsonCpp_LIBRARIES})
+set_target_properties(odil PROPERTIES
+ VERSION ${odil_VERSION}
+ SOVERSION ${odil_MAJOR_VERSION})
-link_directories(${Boost_LIBRARY_DIRS})
-add_library(dcmtkpp SHARED ${files})
-install(TARGETS dcmtkpp LIBRARY DESTINATION lib)
-install(FILES ${headers} ${templates} DESTINATION include/dcmtkpp)
+install(FILES ${headers} ${templates} DESTINATION include/odil)
+install(
+ TARGETS odil
+ ARCHIVE DESTINATION lib
+ LIBRARY DESTINATION lib
+ RUNTIME DESTINATION bin)
-target_link_libraries(dcmtkpp
- ${Boost_LIBRARIES} ${DCMTK_LIBRARIES} jsoncpp ${ICU_LIBRARIES} uuid)
-set_target_properties(dcmtkpp PROPERTIES
- VERSION ${dcmtkpp_VERSION}
- SOVERSION ${dcmtkpp_MAJOR_VERSION})
diff --git a/src/dcmtkpp/Association.cpp b/src/dcmtkpp/Association.cpp
deleted file mode 100644
index d60dec2..0000000
--- a/src/dcmtkpp/Association.cpp
+++ /dev/null
@@ -1,535 +0,0 @@
-/*************************************************************************
- * dcmtkpp - 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 "Association.h"
-
-#include <sstream>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/assoc.h>
-#include <dcmtk/dcmnet/cond.h>
-
-#include "dcmtkpp/Exception.h"
-
-namespace dcmtkpp
-{
-
-Association
-::Association()
-: _own_ae_title(""),
- _peer_host_name(""), _peer_port(104), _peer_ae_title(""),
- _user_identity_type(UserIdentityType::None),
- _user_identity_primary_field(""), _user_identity_secondary_field(""),
- _association(NULL)
-{
- // Nothing else
-}
-
-Association
-::Association(Association const & other)
-: _own_ae_title(other.get_own_ae_title()),
- _peer_host_name(other.get_peer_host_name()), _peer_port(other.get_peer_port()),
- _peer_ae_title(other.get_peer_ae_title()),
- _user_identity_type(other.get_user_identity_type()),
- _user_identity_primary_field(other.get_user_identity_primary_field()),
- _user_identity_secondary_field(other.get_user_identity_secondary_field()),
- _association(NULL)
-{
-}
-
-Association
-::~Association()
-{
- if(this->is_associated())
- {
- this->release();
- }
-}
-
-Association &
-Association
-::operator=(Association const & other)
-{
- if(this != &other)
- {
- this->set_own_ae_title(other.get_own_ae_title());
- this->set_peer_host_name(other.get_peer_host_name());
- this->set_peer_port(other.get_peer_port());
- this->set_peer_ae_title(other.get_peer_ae_title());
- this->set_user_identity_type(other.get_user_identity_type());
- this->set_user_identity_primary_field(other.get_user_identity_primary_field());
- this->set_user_identity_secondary_field(other.get_user_identity_secondary_field());
- }
-
- return *this;
-}
-
-std::string const &
-Association
-::get_own_ae_title() const
-{
- return this->_own_ae_title;
-}
-
-void
-Association
-::set_own_ae_title(std::string const & ae_title)
-{
- if(this->is_associated())
- {
- throw Exception("Cannot set member while associated");
- }
-
- this->_own_ae_title = ae_title;
-}
-
-std::string const &
-Association
-::get_peer_host_name() const
-{
- return this->_peer_host_name;
-}
-
-void
-Association
-::set_peer_host_name(std::string const & host_name)
-{
- if(this->is_associated())
- {
- throw Exception("Cannot set member while associated");
- }
-
- this->_peer_host_name = host_name;
-}
-
-uint16_t
-Association
-::get_peer_port() const
-{
- return this->_peer_port;
-}
-
-void
-Association
-::set_peer_port(uint16_t port)
-{
- if(this->is_associated())
- {
- throw Exception("Cannot set member while associated");
- }
-
- this->_peer_port = port;
-}
-
-std::string const &
-Association
-::get_peer_ae_title() const
-{
- return this->_peer_ae_title;
-}
-
-void
-Association
-::set_peer_ae_title(std::string const & ae_title)
-{
- if(this->is_associated())
- {
- throw Exception("Cannot set member while associated");
- }
-
- this->_peer_ae_title = ae_title;
-}
-
-void
-Association
-::add_presentation_context(std::string const & abstract_syntax,
- std::vector<std::string> const & transfer_syntaxes, T_ASC_SC_ROLE role)
-{
- if(this->is_associated())
- {
- throw Exception("Cannot set member while associated");
- }
-
- this->_presentation_contexts.push_back(
- {abstract_syntax, transfer_syntaxes, role});
-}
-
-UserIdentityType
-Association
-::get_user_identity_type() const
-{
- return this->_user_identity_type;
-}
-
-void
-Association
-::set_user_identity_type(UserIdentityType type)
-{
- if(this->is_associated())
- {
- throw Exception("Cannot set member while associated");
- }
-
- this->_user_identity_type = type;
-}
-
-std::string const &
-Association
-::get_user_identity_primary_field() const
-{
- return this->_user_identity_primary_field;
-}
-
-void
-Association
-::set_user_identity_primary_field(std::string const & value)
-{
- if(this->is_associated())
- {
- throw Exception("Cannot set member while associated");
- }
-
- this->_user_identity_primary_field = value;
-}
-
-std::string const &
-Association
-::get_user_identity_secondary_field() const
-{
- return this->_user_identity_secondary_field;
-}
-
-void
-Association
-::set_user_identity_secondary_field(std::string const & value)
-{
- if(this->is_associated())
- {
- throw Exception("Cannot set member while associated");
- }
-
- this->_user_identity_secondary_field = value;
-}
-
-void
-Association
-::set_user_identity_to_none()
-{
- this->set_user_identity_type(UserIdentityType::None);
- this->set_user_identity_primary_field("");
- this->set_user_identity_secondary_field("");
-}
-
-void
-Association
-::set_user_identity_to_username(std::string const & username)
-{
- this->set_user_identity_type(UserIdentityType::Username);
- this->set_user_identity_primary_field(username);
- this->set_user_identity_secondary_field("");
-}
-
-void
-Association
-::set_user_identity_to_username_and_password(
- std::string const & username, std::string const & password)
-{
- this->set_user_identity_type(UserIdentityType::UsernameAndPassword);
- this->set_user_identity_primary_field(username);
- this->set_user_identity_secondary_field(password);
-}
-
-void
-Association
-::set_user_identity_to_kerberos(std::string const & ticket)
-{
- this->set_user_identity_type(UserIdentityType::Kerberos);
- this->set_user_identity_primary_field(ticket);
- this->set_user_identity_secondary_field("");
-}
-
-void
-Association
-::set_user_identity_to_saml(std::string const & assertion)
-{
- this->set_user_identity_type(UserIdentityType::SAML);
- this->set_user_identity_primary_field(assertion);
- this->set_user_identity_secondary_field("");
-}
-
-bool
-Association
-::is_associated() const
-{
- return (this->_association != NULL);
-}
-
-void
-Association
-::associate(Network & network)
-{
- if(!network.is_initialized())
- {
- throw Exception("Network is not initialized");
- }
-
- if(this->is_associated())
- {
- throw Exception("Already associated");
- }
-
- OFCondition condition;
-
- T_ASC_Parameters * params;
- condition = ASC_createAssociationParameters(¶ms, ASC_MAXIMUMPDUSIZE);
- if(condition.bad())
- {
- throw Exception(condition);
- }
-
- condition = ASC_setAPTitles(params,
- this->_own_ae_title.c_str(), this->_peer_ae_title.c_str(), NULL);
- if(condition.bad())
- {
- ASC_destroyAssociationParameters(¶ms);
- throw Exception(condition);
- }
-
- std::string localhost(128, '\0');
- gethostname(&localhost[0], localhost.size()-1);
-
- std::ostringstream peer;
- peer << this->_peer_host_name << ":" << this->_peer_port;
-
- condition = ASC_setPresentationAddresses(params,
- "localhost", peer.str().c_str());
- if(condition.bad())
- {
- ASC_destroyAssociationParameters(¶ms);
- throw Exception(condition);
- }
-
- unsigned int context_id = 1;
- for(auto const & context: this->_presentation_contexts)
- {
- char const ** transfer_syntaxes = new char const *[context.transfer_syntaxes.size()];
- for(std::size_t i = 0; i < context.transfer_syntaxes.size(); ++i)
- {
- transfer_syntaxes[i] = context.transfer_syntaxes[i].c_str();
- }
-
- condition = ASC_addPresentationContext(params,
- context_id, context.abstract_syntax.c_str(),
- transfer_syntaxes, context.transfer_syntaxes.size(), context.role);
- if(condition.bad())
- {
- ASC_destroyAssociationParameters(¶ms);
- throw Exception(condition);
- }
-
- context_id += 2;
- }
-
- if(this->_user_identity_type == UserIdentityType::None)
- {
- // Nothing to do.
- }
- else if(this->_user_identity_type == UserIdentityType::Username)
- {
- condition = ASC_setIdentRQUserOnly(params,
- this->_user_identity_primary_field.c_str());
- }
- else if(this->_user_identity_type == UserIdentityType::UsernameAndPassword)
- {
- condition = ASC_setIdentRQUserOnly(params,
- this->_user_identity_primary_field.c_str(),
- this->_user_identity_secondary_field.c_str());
- }
- else if(this->_user_identity_type == UserIdentityType::Kerberos)
- {
- condition = ASC_setIdentRQKerberos(params,
- this->_user_identity_primary_field.c_str(),
- this->_user_identity_primary_field.size());
- }
- else if(this->_user_identity_type == UserIdentityType::SAML)
- {
- condition = ASC_setIdentRQSaml(params,
- this->_user_identity_primary_field.c_str(),
- this->_user_identity_primary_field.size());
- }
- else
- {
- ASC_destroyAssociationParameters(¶ms);
- throw Exception("Unknown identity type");
- }
-
- if(condition.bad())
- {
- ASC_destroyAssociationParameters(¶ms);
- throw Exception(condition);
- }
-
- condition = ASC_requestAssociation(
- network.get_network(), params, &this->_association);
- if(condition.bad())
- {
- OFString empty;
-
- if(condition == DUL_ASSOCIATIONREJECTED)
- {
- T_ASC_RejectParameters rej;
- ASC_getRejectParameters(params, &rej);
-
- ASC_destroyAssociationParameters(¶ms);
- throw Exception(ASC_printRejectParameters(empty, &rej).c_str());
- }
- else
- {
- ASC_destroyAssociationParameters(¶ms);
- throw Exception(DimseCondition::dump(empty, condition).c_str());
- }
- }
-}
-
-void
-Association
-::receive(Network & network, bool accept_all)
-{
- if(!network.is_initialized())
- {
- throw Exception("Network is not initialized");
- }
-
- if(this->is_associated())
- {
- throw Exception("Already associated");
- }
-
- OFCondition condition;
-
- condition = ASC_receiveAssociation(
- network.get_network(), &this->_association, ASC_DEFAULTMAXPDU);
- if(condition.bad())
- {
- throw Exception(condition);
- }
-
- T_ASC_Parameters * const params = this->_association->params;
- DUL_ASSOCIATESERVICEPARAMETERS const dul = params->DULparams;
- // No peer port should be defined when receiving
- this->_peer_host_name = dul.callingPresentationAddress;
- this->_peer_port = 0;
- this->_peer_ae_title = dul.callingAPTitle;
-
- if(accept_all)
- {
- unsigned int const pc_count = ASC_countPresentationContexts(params);
- for(unsigned int pc_index=0; pc_index<pc_count; ++pc_index)
- {
- T_ASC_PresentationContext pc;
- memset(&pc, 0, sizeof(pc));
- ASC_getPresentationContext(params, pc_index, &pc);
-
- for(unsigned int ts_index=0; ts_index<pc.transferSyntaxCount; ++ts_index)
- {
- std::string const abstract_syntax = pc.abstractSyntax;
- char const * abstract_syntax_data = abstract_syntax.c_str();
-
- condition = ASC_acceptContextsWithTransferSyntax(
- this->_association->params,
- pc.proposedTransferSyntaxes[ts_index],
- 1, &abstract_syntax_data);
- if(condition.bad())
- {
- this->abort();
- throw Exception(condition);
- }
- }
- }
- }
- else
- {
- for(auto const & context: this->_presentation_contexts)
- {
- for(std::size_t i = 0; i < context.transfer_syntaxes.size(); ++i)
- {
- char const * abstract_syntax = context.abstract_syntax.c_str();
- char const * transfer_syntax = context.transfer_syntaxes[i].c_str();
- condition = ASC_acceptContextsWithTransferSyntax(
- this->_association->params, transfer_syntax,
- 1, &abstract_syntax);
- if(condition.bad())
- {
- this->abort();
- throw Exception(condition);
- }
- }
- }
- }
-
- condition = ASC_acknowledgeAssociation(this->_association);
- if(condition.bad())
- {
- throw Exception(condition);
- }
-}
-
-T_ASC_Association *
-Association
-::get_association()
-{
- return this->_association;
-}
-
-void
-Association
-::release()
-{
- if(!this->is_associated())
- {
- throw Exception("Not associated");
- }
-
- ASC_releaseAssociation(this->_association);
- ASC_destroyAssociation(&this->_association);
- this->_association = NULL;
-}
-
-void
-Association
-::abort()
-{
- if(!this->is_associated())
- {
- throw Exception("Not associated");
- }
-
- ASC_abortAssociation(this->_association);
- ASC_destroyAssociation(&this->_association);
- this->_association = NULL;
-}
-
-void
-Association
-::drop()
-{
- if(!this->is_associated())
- {
- throw Exception("Not associated");
- }
-
- ASC_acknowledgeRelease(this->_association);
- ASC_dropSCPAssociation(this->_association);
- ASC_destroyAssociation(&this->_association);
- this->_association = NULL;
-}
-
-}
diff --git a/src/dcmtkpp/Association.h b/src/dcmtkpp/Association.h
deleted file mode 100644
index 8b2fcb8..0000000
--- a/src/dcmtkpp/Association.h
+++ /dev/null
@@ -1,184 +0,0 @@
-/*************************************************************************
- * dcmtkpp - 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 _a52696bc_5c6e_402d_a343_6cb085eb0138
-#define _a52696bc_5c6e_402d_a343_6cb085eb0138
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/assoc.h>
-
-#include "dcmtkpp/Network.h"
-
-namespace dcmtkpp
-{
-
-/// @brief User identity types as defined by PS3.8 2013 D.3.3.7
-enum class UserIdentityType
-{
- None = 0,
- Username = 1,
- UsernameAndPassword = 2,
- Kerberos = 3,
- SAML = 4
-};
-
-/**
- * @brief Wrapper around the T_ASC_Association class.
- *
- * No member can be set while the object is associated.
- */
-class Association
-{
-public:
- /// @brief Create a default, un-associated, association.
- Association();
-
- /// @brief Create an un-associated association.
- Association(Association const & other);
-
- /// @brief Destroy the association, release it if necessary.
- ~Association();
-
- /// @brief Assing an un-associated association; it remains un-associated.
- Association & operator=(Association const & other);
-
- /// @brief Return the AE title of the caller. Defaults to "".
- std::string const & get_own_ae_title() const;
- /// @brief Set the AE title of the caller.
- void set_own_ae_title(std::string const & ae_title);
-
- /// @name Peer
- /// @{
-
- /// @brief Return the host name of the peer. Defaults to "".
- std::string const & get_peer_host_name() const;
- /// @brief Set the host name of the peer.
- void set_peer_host_name(std::string const & host_name);
-
- /// @brief Return the port of the peer. Defaults to 104.
- uint16_t get_peer_port() const;
- /// @brief Set the port of the peer.
- void set_peer_port(uint16_t port);
-
- /// @brief Return the AE title of the peer. Defaults to "".
- std::string const & get_peer_ae_title() const;
- /// @brief Set the AE title of the peer.
- void set_peer_ae_title(std::string const & ae_title);
-
- /// @}
-
- /// @name Presentation contexts
- /// @{
-
- void add_presentation_context(std::string const & abstract_syntax,
- std::vector<std::string> const & transfer_syntaxes,
- T_ASC_SC_ROLE role=ASC_SC_ROLE_DEFAULT);
-
- /// @}
-
- /// @name User identity
- /// @{
-
- /// @brief Return the user identity type. Defaults to None.
- UserIdentityType get_user_identity_type() const;
- /// @brief Set the user identity type.
- void set_user_identity_type(UserIdentityType type);
-
- /// @brief Return the user identity primary field. Defaults to "".
- std::string const & get_user_identity_primary_field() const;
- /// @brief Set the user identity primary field.
- void set_user_identity_primary_field(std::string const & value);
-
- /// @brief Return the user identity secondary field. Defaults to "".
- std::string const & get_user_identity_secondary_field() const;
- /// @brief Set the user identity secondary field.
- void set_user_identity_secondary_field(std::string const & value);
-
- /// @brief Do no authenticate user.
- void set_user_identity_to_none();
-
- /// @brief Authenticate user using only a username.
- void set_user_identity_to_username(std::string const & username);
-
- /// @brief Authenticate user using a username and a password.
- void set_user_identity_to_username_and_password(
- std::string const & username, std::string const & password);
-
- /// @brief Authenticate user using a Kerberos ticket.
- void set_user_identity_to_kerberos(std::string const & ticket);
-
- /// @brief Authenticate user using a SAML assertion.
- void set_user_identity_to_saml(std::string const & assertion);
-
- /// @}
-
- /// @name Association
- /// @{
-
- /// @brief Test whether the object is currently associated to its peer.
- bool is_associated() const;
-
- /**
- * @brief Request an association with the peer.
- * @param network network to use for the data transmission.
- *
- * Throws an exception if already associated.
- */
- void associate(Network & network);
-
- /**
- * @brief Receive an association for a peer.
- * @param network network to use for the data transmission.
- * @param accept_all if true, accept all presentations contexts proposed
- * by peer.
- */
- void receive(Network & network, bool accept_all=false);
-
- /// @brief Return the association object.
- T_ASC_Association * get_association();
-
- /// @brief Gracefully release the association. Throws an exception if not associated.
- void release();
- /// @brief Forcefully release the association. Throws an exception if not associated.
- void abort();
-
- /// @brief Drop an association that has been released by the peer.
- void drop();
-
- /// @}
-
-private:
- std::string _own_ae_title;
-
- std::string _peer_host_name;
- uint16_t _peer_port;
- std::string _peer_ae_title;
-
- struct PresentationContext
- {
- std::string abstract_syntax;
- std::vector<std::string> transfer_syntaxes;
- T_ASC_SC_ROLE role;
- };
-
- std::vector<PresentationContext> _presentation_contexts;
-
- UserIdentityType _user_identity_type;
- std::string _user_identity_primary_field;
- std::string _user_identity_secondary_field;
-
- T_ASC_Association * _association;
-};
-
-}
-
-#endif // _a52696bc_5c6e_402d_a343_6cb085eb0138
diff --git a/src/dcmtkpp/GetSCU.cpp b/src/dcmtkpp/GetSCU.cpp
deleted file mode 100644
index 52fa1fb..0000000
--- a/src/dcmtkpp/GetSCU.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-/*************************************************************************
- * dcmtkpp - 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 "dcmtkpp/GetSCU.h"
-
-#include <functional>
-#include <sstream>
-#include <vector>
-
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/dimse.h>
-
-#include "dcmtkpp/CGetRequest.h"
-#include "dcmtkpp/CGetResponse.h"
-#include "dcmtkpp/CStoreRequest.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Exception.h"
-
-namespace dcmtkpp
-{
-
-GetSCU
-::~GetSCU()
-{
- // Nothing to do
-}
-
-void
-GetSCU
-::get(DataSet const & query, Callback callback) const
-{
- // Send the request
- CGetRequest request(this->_association->get_association()->nextMsgID++,
- this->_affected_sop_class, Message::Priority::MEDIUM, query);
- this->_send(request, this->_affected_sop_class);
-
- // Receive the responses
- bool done = false;
- while(!done)
- {
- Message const message = this->_receive();
-
- if(message.get_command_field() == Message::Command::C_GET_RSP)
- {
- done = this->_get_response(CGetResponse(message));
- }
- else if(message.get_command_field() == Message::Command::C_STORE_RQ)
- {
- try
- {
- this->_store_request(CStoreRequest(message), callback);
- }
- catch(...)
- {
- // FIXME: logging
- done = true;
- }
- }
- else
- {
- std::ostringstream exception_message;
- exception_message << "DIMSE: Unexpected Response Command Field: 0x"
- << std::hex << message.get_command_field();
- throw Exception(exception_message.str());
- }
- }
-}
-
-std::vector<DataSet>
-GetSCU
-::get(DataSet const & query) const
-{
- std::vector<DataSet> result;
- auto callback = [&result](DataSet const & dataset) {
- result.push_back(dataset);
- };
- this->get(query, callback);
-
- return result;
-}
-
-bool
-GetSCU
-::_get_response(CGetResponse const & response) const
-{
- bool const done = (response.get_status() != STATUS_Pending);
- return done;
-}
-
-void
-GetSCU
-::_store_request(CStoreRequest const & request, Callback callback) const
-{
- StoreSCP scp;
- scp.set_network(this->get_network());
- scp.set_association(this->get_association());
- scp.store(request, callback);
-}
-
-}
diff --git a/src/dcmtkpp/MoveSCU.cpp b/src/dcmtkpp/MoveSCU.cpp
deleted file mode 100644
index 9181523..0000000
--- a/src/dcmtkpp/MoveSCU.cpp
+++ /dev/null
@@ -1,162 +0,0 @@
-/*************************************************************************
- * dcmtkpp - 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 "dcmtkpp/MoveSCU.h"
-
-#include <vector>
-
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/dimse.h>
-
-#include "dcmtkpp/CMoveRequest.h"
-#include "dcmtkpp/CMoveResponse.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/Message.h"
-#include "dcmtkpp/StoreSCP.h"
-
-namespace dcmtkpp
-{
-
-MoveSCU
-::MoveSCU()
-: SCU(), _move_destination("")
-{
- // Nothing else.
-}
-
-MoveSCU
-::~MoveSCU()
-{
- // Nothing to do
-}
-
-std::string const &
-MoveSCU
-::get_move_destination() const
-{
- return this->_move_destination;
-}
-
-void
-MoveSCU
-::set_move_destination(std::string const & move_destination)
-{
- this->_move_destination = move_destination;
-}
-
-void
-MoveSCU
-::move(DataSet const & query, Callback callback) const
-{
- // Send the request
- CMoveRequest const request(this->_association->get_association()->nextMsgID++,
- this->_affected_sop_class, Message::Priority::MEDIUM,
- this->_move_destination, query);
- this->_send(request, this->_affected_sop_class);
-
- // Receive the responses
- Association store_association;
- bool done = false;
- while(!done)
- {
- // Use a small timeout to avoid blocking for a long time.
- if(!store_association.is_associated() &&
- this->_network->is_association_waiting(1))
- {
- store_association.receive(*this->_network, true);
- }
-
- done = this->_dispatch(store_association, callback);
- }
-}
-
-std::vector<DataSet>
-MoveSCU
-::move(DataSet const & query) const
-{
- std::vector<DataSet> result;
- auto callback = [&result](DataSet const & dataset) {
- result.push_back(dataset);
- };
- this->move(query, callback);
-
- return result;
-}
-
-bool
-MoveSCU
-::_dispatch(Association & association, Callback callback) const
-{
- T_ASC_Association *associations[2];
- int size = 0;
-
- associations[0] = this->_association->get_association();
- size = 1;
- associations[1] = association.get_association();
- if(association.is_associated())
- {
- ++size;
- }
-
- // At this point, we should have a readable association.
- if(!ASC_selectReadableAssociation(associations, size, 1))
- {
- throw Exception("No readable association");
- }
-
- bool move_finished;
-
- if(associations[0] != NULL)
- {
- move_finished = this->_handle_main_association();
- }
- else if(associations[1] != NULL)
- {
- bool const release =
- this->_handle_store_association(association, callback);
- if(release)
- {
- association.drop();
- }
- }
-
- return move_finished;
-}
-
-bool
-MoveSCU
-::_handle_main_association() const
-{
- Message const message = this->_receive();
-
- if(message.get_command_field() != Message::Command::C_MOVE_RSP)
- {
- std::ostringstream exception_message;
- exception_message << "DIMSE: Unexpected Response Command Field: 0x"
- << std::hex << message.get_command_field();
- throw Exception(exception_message.str());
- }
-
- CMoveResponse const response(message);
- bool const done = (response.get_status() != STATUS_Pending);
-
- return done;
-}
-
-bool
-MoveSCU
-::_handle_store_association(Association & association, Callback callback) const
-{
- StoreSCP scp;
- scp.set_network(this->_network);
- scp.set_association(&association);
- return scp.store(callback);
-}
-
-}
diff --git a/src/dcmtkpp/Network.cpp b/src/dcmtkpp/Network.cpp
deleted file mode 100644
index 218b4c3..0000000
--- a/src/dcmtkpp/Network.cpp
+++ /dev/null
@@ -1,201 +0,0 @@
-/*************************************************************************
- * dcmtkpp - 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 "dcmtkpp/Network.h"
-
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/assoc.h>
-#include <dcmtk/dcmnet/cond.h>
-
-#include "dcmtkpp/Exception.h"
-
-namespace dcmtkpp
-{
-
-Network
-::Network()
-: _role(NET_REQUESTOR), _port(0), _timeout(30), _options(0), _network(NULL)
-{
- // Nothing else.
-}
-
-Network
-::Network(T_ASC_NetworkRole role, int port, int timeout, unsigned long options)
-: _role(role), _port(port), _timeout(timeout), _options(options), _network(NULL)
-{
- // Nothing else.
-}
-
-Network
-::Network(Network const & other)
-: _role(other.get_role()), _port(other.get_port()),
- _timeout(other.get_timeout()), _options(other.get_options()), _network(NULL)
-{
- // Nothing else
-}
-
-Network
-::~Network()
-{
- if(this->is_initialized())
- {
- this->drop();
- }
-}
-
-Network &
-Network
-::operator=(Network const & other)
-{
- if(this != &other)
- {
- this->set_role(other.get_role());
- this->set_port(other.get_port());
- this->set_timeout(other.get_timeout());
- this->set_options(other.get_options());
- }
-
- return *this;
-}
-
-T_ASC_NetworkRole
-Network
-::get_role() const
-{
- return this->_role;
-}
-
-void
-Network
-::set_role(T_ASC_NetworkRole role)
-{
- if(this->is_initialized())
- {
- throw Exception("Cannot set member while initialized");
- }
-
- this->_role = role;
-}
-
-int
-Network
-::get_port() const
-{
- return this->_port;
-}
-
-void
-Network
-::set_port(int port)
-{
- if(this->is_initialized())
- {
- throw Exception("Cannot set member while initialized");
- }
-
- this->_port = port;
-}
-
-int
-Network
-::get_timeout() const
-{
- return this->_timeout;
-}
-
-void
-Network
-::set_timeout(int timeout)
-{
- if(this->is_initialized())
- {
- throw Exception("Cannot set member while initialized");
- }
-
- this->_timeout = timeout;
-}
-
-unsigned long
-Network
-::get_options() const
-{
- return this->_options;
-}
-
-void
-Network
-::set_options(unsigned long options)
-{
- if(this->is_initialized())
- {
- throw Exception("Cannot set member while initialized");
- }
-
- this->_options = options;
-}
-
-void
-Network
-::initialize()
-{
- if(this->is_initialized())
- {
- throw Exception("Already initialized");
- }
-
- OFCondition const condition = ASC_initializeNetwork(
- this->_role, this->_port, this->_timeout, &this->_network,
- this->_options);
-
- if(condition.bad())
- {
- throw Exception(condition);
- }
-}
-
-T_ASC_Network *
-Network
-::get_network()
-{
- return this->_network;
-}
-
-bool
-Network
-::is_association_waiting(int const timeout)
-{
- return ASC_associationWaiting(this->_network, timeout);
-}
-
-void
-Network
-::drop()
-{
- if(!this->is_initialized())
- {
- throw Exception("Not initialized");
- }
-
- OFCondition const condition = ASC_dropNetwork(&this->_network);
-
- if(condition.bad())
- {
- throw Exception(condition);
- }
-
- this->_network = NULL;
-}
-
-bool
-Network
-::is_initialized() const
-{
- return (this->_network != NULL);
-}
-
-}
diff --git a/src/dcmtkpp/Network.h b/src/dcmtkpp/Network.h
deleted file mode 100644
index 7cf8acf..0000000
--- a/src/dcmtkpp/Network.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*************************************************************************
- * dcmtkpp - 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 _fad49120_3f5c_447d_b0e9_72719087640c
-#define _fad49120_3f5c_447d_b0e9_72719087640c
-
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/assoc.h>
-
-namespace dcmtkpp
-{
-
-/**
- * @brief Wrapper around the T_ASC_Network class.
- *
- * No member can be set while the network is initialized.
- */
-class Network
-{
-public:
- /// @brief Create a default, un-initialized, network.
- Network();
-
- /// @brief Create an un-initialized network.
- Network(T_ASC_NetworkRole role, int port, int timeout, unsigned long options=0);
-
- /// @brief Create an un-initialized network.
- Network(Network const & other);
-
- /// @brief Destroy the network, dropping it if necessary.
- ~Network();
-
- /// @brief Assing an un-initialized network; it remains un-initialized.
- Network & operator=(Network const & other);
-
- /// @brief Return the role of the network, defaults to NET_REQUESTOR.
- T_ASC_NetworkRole get_role() const;
-
- /// @brief Set the role of the network.
- void set_role(T_ASC_NetworkRole role);
-
- /// @brief Return the port for acceptor role, defaults to 0.
- int get_port() const;
-
- /// @brief Set the port for acceptor role.
- void set_port(int port);
-
- /// @brief Return the timeout in seconds, defaults to 30.
- int get_timeout() const;
-
- /// @brief Set the timeout in seconds.
- void set_timeout(int timeout);
-
- /// @brief Return the options, defaults to 0.
- unsigned long get_options() const;
-
- /// @brief the set options.
- void set_options(unsigned long options);
-
- /// @brief Initialize the network.
- void initialize();
-
- /// @brief Return the underlying DCMTK object.
- T_ASC_Network * get_network();
-
- /// @brief Test whether an association request is waiting.
- bool is_association_waiting(int const timeout);
-
- /// @brief Drop (i.e. un-initialize) the network.
- void drop();
-
- /// @brief Test whether the network is initialized.
- bool is_initialized() const;
-
-private:
- T_ASC_NetworkRole _role;
- int _port;
- int _timeout;
- unsigned long _options;
-
- T_ASC_Network * _network;
-};
-
-}
-
-#endif // _fad49120_3f5c_447d_b0e9_72719087640c
diff --git a/src/dcmtkpp/Response.cpp b/src/dcmtkpp/Response.cpp
deleted file mode 100644
index f3c2e20..0000000
--- a/src/dcmtkpp/Response.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/*************************************************************************
- * dcmtkpp - 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 "Response.h"
-
-#include "dcmtkpp/Message.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Value.h"
-
-namespace dcmtkpp
-{
-
-Response
-::Response(Value::Integer message_id_being_responded_to, Value::Integer status)
-: Message()
-{
- this->set_message_id_being_responded_to(message_id_being_responded_to);
- this->set_status(status);
-}
-
-Response
-::Response(Message const & message)
-: Message()
-{
- this->set_message_id_being_responded_to(
- message.get_command_set().as_int(
- registry::MessageIDBeingRespondedTo, 0));
-
- this->set_status(message.get_command_set().as_int(registry::Status, 0));
-}
-
-Response
-::~Response()
-{
- // Nothing to do.
-}
-
-}
diff --git a/src/dcmtkpp/Response.h b/src/dcmtkpp/Response.h
deleted file mode 100644
index a1a66c4..0000000
--- a/src/dcmtkpp/Response.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*************************************************************************
- * dcmtkpp - 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 _0dd2e31e_212a_494a_a8d3_93b235336658
-#define _0dd2e31e_212a_494a_a8d3_93b235336658
-
-#include "dcmtkpp/Message.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Value.h"
-
-namespace dcmtkpp
-{
-
-/// @brief Base class for all DIMSE response messages.
-class Response: public Message
-{
-public:
- /// @brief Create a response with given message id and status;
- Response(Value::Integer message_id_being_responded_to, Value::Integer status);
-
- /**
- * @brief Create a response from the Message ID Being Responded To and the
- * Status stored in the message command set.
- *
- * Raise an exception is either of those elements is missing.
- */
- Response(Message const & message);
-
- /// @brief Destructor.
- virtual ~Response();
-
- DCMTKPP_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(
- message_id_being_responded_to, registry::MessageIDBeingRespondedTo)
- DCMTKPP_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(status, registry::Status)
-};
-
-}
-
-#endif // _0dd2e31e_212a_494a_a8d3_93b235336658
diff --git a/src/dcmtkpp/SCP.cpp b/src/dcmtkpp/SCP.cpp
deleted file mode 100644
index d24e861..0000000
--- a/src/dcmtkpp/SCP.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-/*************************************************************************
- * dcmtkpp - 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 "SCP.h"
-
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/dimse.h>
-
-#include "dcmtkpp/CEchoRequest.h"
-#include "dcmtkpp/CEchoResponse.h"
-#include "dcmtkpp/ServiceRole.h"
-
-namespace dcmtkpp
-{
-
-SCP
-::SCP()
-: ServiceRole()
-{
- // Nothing else.
-}
-
-SCP
-::~SCP()
-{
- // Nothing to do.
-}
-
-void
-SCP
-::_send_echo_response(CEchoRequest const & request) const
-{
- CEchoResponse response(
- request.get_message_id(), STATUS_Success,
- request.get_affected_sop_class_uid());
- this->_send(response, request.get_affected_sop_class_uid());
-}
-
-}
diff --git a/src/dcmtkpp/SCP.h b/src/dcmtkpp/SCP.h
deleted file mode 100644
index d6130a3..0000000
--- a/src/dcmtkpp/SCP.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*************************************************************************
- * dcmtkpp - 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 _f4680d8c_18a8_4317_956d_3ae238cb39cc
-#define _f4680d8c_18a8_4317_956d_3ae238cb39cc
-
-#include "dcmtkpp/CEchoRequest.h"
-#include "dcmtkpp/ServiceRole.h"
-
-namespace dcmtkpp
-{
-
-/// @brief Base class for all Service Class Providers.
-class SCP: public ServiceRole
-{
-public:
- SCP();
- virtual ~SCP();
-protected:
- /// @brief Send a C-ECHO response.
- void _send_echo_response(CEchoRequest const & request) const;
-};
-
-}
-
-#endif // _f4680d8c_18a8_4317_956d_3ae238cb39cc
diff --git a/src/dcmtkpp/ServiceRole.cpp b/src/dcmtkpp/ServiceRole.cpp
deleted file mode 100644
index d2a076b..0000000
--- a/src/dcmtkpp/ServiceRole.cpp
+++ /dev/null
@@ -1,438 +0,0 @@
-/*************************************************************************
- * dcmtkpp - 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 "ServiceRole.h"
-
-#include <unistd.h>
-
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/assoc.h>
-#include <dcmtk/dcmnet/dimse.h>
-
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/Reader.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Writer.h"
-
-namespace dcmtkpp
-{
-
-ServiceRole
-::ServiceRole()
-: _association(NULL), _network(NULL)
-{
- // Nothing else
-}
-
-ServiceRole
-::ServiceRole(ServiceRole const & other)
-: _association(other._association), _network(other._network)
-{
- // Nothing else
-}
-
-ServiceRole const &
-ServiceRole
-::operator=(ServiceRole const & other)
-{
- if(this != &other)
- {
- this->set_association(other.get_association());
- this->set_network(other.get_network());
- }
-
- return *this;
-}
-
-ServiceRole
-::~ServiceRole()
-{
- // Nothing to do.
-}
-
-Network *
-ServiceRole
-::get_network() const
-{
- return this->_network;
-}
-
-void
-ServiceRole
-::set_network(Network * network)
-{
- this->_network = network;
-}
-
-Association *
-ServiceRole
-::get_association() const
-{
- return this->_association;
-}
-
-void
-ServiceRole
-::set_association(Association * association)
-{
- this->_association = association;
-}
-
-void
-ServiceRole
-::_progress_callback_wrapper(void * data, unsigned long bytes_count)
-{
- ProgressCallbackData * encapsulated =
- reinterpret_cast<ProgressCallbackData*>(data);
- encapsulated->callback(encapsulated->data, bytes_count);
-}
-
-T_ASC_PresentationContextID
-ServiceRole
-::_find_presentation_context(std::string const & abstract_syntax) const
-{
- T_ASC_PresentationContextID const presentation_id =
- ASC_findAcceptedPresentationContextID(
- this->_association->get_association(),
- abstract_syntax.c_str());
- if(presentation_id == 0)
- {
- throw Exception("No Presentation Context for Get Operation");
- }
-
- return presentation_id;
-}
-void
-ServiceRole
-::_send(
- Message const & message, std::string const & abstract_syntax,
- ProgressCallback callback, void* callback_data) const
-{
- T_ASC_PresentationContextID const presentation_context =
- this->_find_presentation_context(abstract_syntax);
-
- this->_send(
- message.get_command_set(),
- presentation_context, registry::ImplicitVRLittleEndian,
- DUL_COMMANDPDV, NULL, NULL);
- if(message.has_data_set() && !message.get_data_set().empty())
- {
- // FIXME: transfer syntax
- this->_send(
- message.get_data_set(),
- presentation_context, registry::ImplicitVRLittleEndian,
- DUL_DATASETPDV, callback, callback_data);
- }
-}
-
-Message
-ServiceRole
-::_receive(ProgressCallback callback, void* callback_data) const
-{
- // Receive command set
- auto const command = this->_receive_dataset(callback, callback_data);
- if(command.second != DUL_COMMANDPDV)
- {
- throw Exception("Did not receive command set");
- }
- DataSet const & command_set = command.first;
-
- // Receive potential data set
- DataSet data_set;
- bool has_data_set;
- if(command_set.as_int(registry::CommandDataSetType, 0) != DIMSE_DATASET_NULL)
- {
- auto const data = this->_receive_dataset(callback, callback_data);
- if(data.second != DUL_DATASETPDV)
- {
- throw Exception("Did not receive data set");
- }
- data_set = data.first;
- has_data_set = true;
- }
- else
- {
- has_data_set = false;
- }
-
- return has_data_set?Message(command_set, data_set):Message(command_set);
-}
-
-void
-ServiceRole
-::_send(
- DataSet const & obj, T_ASC_PresentationContextID presID,
- std::string const & transfer_syntax, DUL_DATAPDV pdvType,
- ProgressCallback callback, void *callbackContext) const
- /*
- * This function sends all information which is included in a DcmDataset object over
- * the network which is provided in assoc.
- *
- * Parameters:
- * obj - [in] Contains the information which shall be sent over the network.
- * presId - [in] The ID of the presentation context which shall be used
- * xferSyntax - [in] The transfer syntax which shall be used.
- * pdvType - [in] Specifies if the information in this DcmDataset object belongs to
- * a DIMSE command (as for example C-STORE) (DUL_COMMANDPDV) or if
- * the information is actual instance information (DUL_DATASETPDV).
- * callback - [in] Pointer to a function which shall be called to indicate progress.
- * callbackContext - []
- */
-{
- bool const use_group_length = (pdvType == DUL_COMMANDPDV);
- std::stringstream stream;
- Writer writer(
- stream, transfer_syntax, Writer::ItemEncoding::ExplicitLength,
- use_group_length);
- writer.write_data_set(obj);
- std::string const data = stream.str();
-
- /* we may wish to restrict output PDU size */
- /* max PDV size is max PDU size minus 12 bytes PDU/PDV header */
- unsigned long bufLen = this->_association->get_association()->sendPDVLength;
- if (bufLen + 12 > dcmMaxOutgoingPDUSize.get())
- {
- bufLen = dcmMaxOutgoingPDUSize.get() - 12;
- }
-
- bool done = false;
- unsigned long bytesTransmitted=0;
- while(!done)
- {
- std::string const buffer = data.substr(bytesTransmitted, bufLen);
-
- /* count the bytes and the amount of PDVs which were transmitted */
- bytesTransmitted += buffer.size();
- done = (bytesTransmitted>=data.size());
-
- /* initialize a DUL_PDV variable with the buffer's data */
- DUL_PDV pdv;
- pdv.fragmentLength = buffer.size();
- pdv.presentationContextID = presID;
- pdv.pdvType = pdvType;
- pdv.lastPDV = done;
- pdv.data = reinterpret_cast<void*>(const_cast<char*>(&buffer[0]));
-
- /* append this PDV to a PDV list structure, set the counter variable */
- /* to 1 since this structure contains only 1 element */
- DUL_PDVLIST pdvList;
- pdvList.count = 1;
- pdvList.pdv = &pdv;
-
- /* send information over the network to the other DICOM application */
- OFCondition const dulCond = DUL_WritePDVs(
- &this->_association->get_association()->DULassociation, &pdvList);
- if (dulCond.bad())
- {
- throw Exception(
- makeDcmnetSubCondition(
- DIMSEC_SENDFAILED, OF_error, "DIMSE Failed to send message",
- dulCond));
- }
-
- /* execute callback function to indicate progress */
- if(callback)
- {
- callback(callbackContext, bytesTransmitted);
- }
- }
-}
-
-
-std::pair<DataSet, DUL_DATAPDV>
-ServiceRole::_receive_dataset(
- ProgressCallback callback, void *callbackContext) const
-{
- DataSet data_set;
-
- std::stringstream stream;
-
- /* start a loop in which we want to read a DIMSE command from the incoming socket stream. */
- /* Since the command could stretch over more than one PDU, the use of a loop is mandatory. */
- DUL_DATAPDV type;
- bool last = false;
- DIC_UL pdvCount = 0;
- T_ASC_PresentationContextID pid = 0;
- while(!last)
- {
- DUL_PDV const pdv = this->_read_next_pdv();
-
- /* if this is the first loop iteration, get the presentation context ID
- * which is captured in the current PDV. If this is not the first loop
- * iteration, check if the presentation context IDs in the current PDV
- * and in the last PDV are identical. If they are not, return an error.
- */
- if (pdvCount == 0)
- {
- pid = pdv.presentationContextID;
- }
- else if (pdv.presentationContextID != pid)
- {
- std::ostringstream message;
- message << "Different presentation IDs inside Command Set: "
- << pid << " != " << pdv.presentationContextID;
- throw Exception(message.str());
- }
-
- /* check if the fragment length of the current PDV is odd. This should */
- /* never happen (see DICOM standard (year 2000) part 7, annex F) (or */
- /* the corresponding section in a later version of the standard.) */
- if ((pdv.fragmentLength % 2) != 0)
- {
- /* This should NEVER happen. See Part 7, Annex F. */
- std::ostringstream message;
- message << "Odd fragment length: " << pdv.fragmentLength;
- throw Exception(message.str());
- }
-
- /* if information is contained the PDVs fragment, we want to insert
- * this information into the buffer
- */
- stream.write(reinterpret_cast<char *>(pdv.data), pdv.fragmentLength);
-
- std::string transfer_syntax;
- bool keep_group_length;
- if(pdv.pdvType == DUL_COMMANDPDV)
- {
- /* DIMSE commands are always specified in the little endian implicit
- * transfer syntax. Additionally, we want to remove group length
- * tags.
- */
- transfer_syntax = registry::ImplicitVRLittleEndian;
- keep_group_length = false;
- }
- else if(pdv.pdvType == DUL_DATASETPDV)
- {
- /* figure out if is this a valid presentation context */
- T_ASC_PresentationContext pc;
- OFCondition const cond =
- ASC_findAcceptedPresentationContext(
- this->_association->get_association()->params, pid, &pc);
- if (cond.bad())
- {
- throw Exception("No valid presentation context");
- }
-
- /* determine the transfer syntax which is specified in the presentation context */
- transfer_syntax = pc.acceptedTransferSyntax;
- keep_group_length = true;
- }
- else
- {
- throw Exception("Unkown PDV type");
- }
-
- Reader reader(stream, transfer_syntax, keep_group_length);
- data_set = reader.read_data_set();
-
- /* update the following variables which will be evaluated at the beginning of each loop iteration. */
- last = pdv.lastPDV;
- type = pdv.pdvType;
-
- /* update the counter that counts how many PDVs were received on the incoming */
- /* socket stream. This variable will be used for determining the first */
- /* loop iteration and dumping general information. */
- pdvCount++;
- }
-
- return std::make_pair(data_set, type);
-}
-
-DUL_PDV
-ServiceRole
-::_read_next_pdv() const
- /*
- * This function returns the next PDV which was (earlier or just now) received on the incoming
- * socket stream. If there are no PDVs (which were already received earlier) waiting to be picked
- * up, this function will go ahead and read a new PDU (containing one or more new PDVs) from the
- * incoming socket stream.
- */
-{
- /* get the next PDV from the association, in case there are still some PDVs waiting to be picked up */
- DUL_PDV pdv;
- OFCondition cond = DUL_NextPDV(
- &this->_association->get_association()->DULassociation, &pdv);
-
- if (cond.bad())
- {
- /* in case DUL_NextPDV(...) did not return DUL_NORMAL, the association */
- /* did not contain any more PDVs that are waiting to be picked up. Hence, */
- /* we need to read new PDVs from the incoming socket stream. */
-
- /* if the blocking mode is DIMSE_NONBLOCKING and there is no data waiting after timeout seconds, report an error */
- if(!ASC_dataWaiting(
- this->_association->get_association(), this->_network->get_timeout()))
- {
- throw Exception(DIMSE_NODATAAVAILABLE);
- }
-
- /* try to receive new PDVs on the incoming socket stream (in detail, try to receive one PDU) */
- cond = DUL_ReadPDVs(
- &this->_association->get_association()->DULassociation, NULL,
- DUL_BLOCK, this->_network->get_timeout());
-
- /* check return value, if it is different from DUL_PDATAPDUARRIVED, an error occurred */
- if (cond != DUL_PDATAPDUARRIVED)
- {
- if (cond == DUL_NULLKEY || cond == DUL_ILLEGALKEY)
- {
- throw Exception(DIMSE_ILLEGALASSOCIATION);
- }
- else if (cond == DUL_PEERREQUESTEDRELEASE ||
- cond == DUL_PEERABORTEDASSOCIATION)
- {
- throw Exception(cond);
- }
- else
- {
- throw Exception(
- makeDcmnetSubCondition(
- DIMSEC_READPDVFAILED, OF_error,
- "DIMSE Read PDV failed", cond));
- }
- }
-
- /* get the next PDV, assign it to pdv */
- cond = DUL_NextPDV(
- &this->_association->get_association()->DULassociation, &pdv);
- if (cond.bad())
- {
- throw Exception(
- makeDcmnetSubCondition(
- DIMSEC_READPDVFAILED, OF_error,
- "DIMSE Read PDV failed", cond));
- }
- }
-
- return pdv;
-}
-
-void
-ServiceRole
-::_check_dimse_ready() const
-{
- if(this->_network == NULL)
- {
- throw Exception("No network");
- }
- else if(!this->_network->is_initialized())
- {
- throw Exception("Network is not initialized");
- }
- else if(this->_association == NULL)
- {
- throw Exception("No association");
- }
- else if(!this->_association->is_associated())
- {
- throw Exception("Not associated");
- }
-}
-
-}
diff --git a/src/dcmtkpp/ServiceRole.h b/src/dcmtkpp/ServiceRole.h
deleted file mode 100644
index 0c9b25b..0000000
--- a/src/dcmtkpp/ServiceRole.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/*************************************************************************
- * dcmtkpp - 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 _fa7d372a_dd27_4a1e_9b29_be9d5fbe602a
-#define _fa7d372a_dd27_4a1e_9b29_be9d5fbe602a
-
-#include <functional>
-#include <utility>
-
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/assoc.h>
-
-#include "dcmtkpp/Association.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Message.h"
-#include "dcmtkpp/Network.h"
-
-namespace dcmtkpp
-{
-
-/// @brief Base class for all Service Class Users and Providers.
-class ServiceRole
-{
-public:
- /// @brief Progress callback, following the semantics of DCMTK.
- typedef std::function<void(void *, unsigned long)> ProgressCallback;
-
- /// @brief Create a default Service Role with no network and no association.
- ServiceRole();
-
- /// @brief Copy the other service role.
- ServiceRole(ServiceRole const & other);
-
- /// @brief Copy the other service role.
- ServiceRole const & operator=(ServiceRole const & other);
-
- /// @brief Destructor, network and association are not modified.
- virtual ~ServiceRole();
-
- /// @brief Return the network used by the ServiceRole.
- Network * get_network() const;
- /// @brief Set the network used by the ServiceRole.
- void set_network(Network * network);
-
- /// @brief Return the association used by the ServiceRole.
- Association * get_association() const;
- /// @brief Set the association used by the ServiceRole.
- void set_association(Association * association);
-
-protected:
- /// @brief Wrapper class for DMCTK progress callbacks.
- struct ProgressCallbackData
- {
- /// @brief Callback function.
- ProgressCallback callback;
-
- /// @brief Callback data.
- void * data;
- };
-
- /// @brief Network used by the ServiceRole.
- Network * _network;
-
- /// @brief Association used by the ServiceRole.
- Association * _association;
-
- /// @brief Wrapper from ProgressCallback to DIMSE_ProgressCallback.
- static void _progress_callback_wrapper(void * data, unsigned long bytes_count);
-
- /// @brief Find an accepted presentation context.
- T_ASC_PresentationContextID _find_presentation_context(
- std::string const & abstract_syntax) const;
-
- /// @brief Send a DIMSE message.
- void _send(
- Message const & message, std::string const & abstract_syntax,
- ProgressCallback callback=NULL, void* callback_data=NULL) const;
-
- /// @brief Receive a generic DIMSE message.
- Message _receive(ProgressCallback callback=NULL, void* callback_data=NULL) const;
-
- /**
- * @brief Receive a DIMSE message of specific type.
- *
- * Throw an exception if the received message is not of the requested type.
- */
- template<typename TMessage>
- TMessage _receive(ProgressCallback callback=NULL, void* callback_data=NULL) const;
-
-private:
- void _send(
- DataSet const & obj, T_ASC_PresentationContextID presID,
- std::string const & transfer_syntax, DUL_DATAPDV pdvType,
- ProgressCallback callback, void *callbackContext) const;
-
- std::pair<DataSet, DUL_DATAPDV> _receive_dataset(
- ProgressCallback callback, void *callbackContext) const;
-
- DUL_PDV _read_next_pdv() const;
-
- void _check_dimse_ready() const;
-};
-
-}
-
-#include "ServiceRole.txx"
-
-#endif // _fa7d372a_dd27_4a1e_9b29_be9d5fbe602a
diff --git a/src/dcmtkpp/ServiceRole.txx b/src/dcmtkpp/ServiceRole.txx
deleted file mode 100644
index 32dc73f..0000000
--- a/src/dcmtkpp/ServiceRole.txx
+++ /dev/null
@@ -1,38 +0,0 @@
-/*************************************************************************
- * dcmtkpp - 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 _8ac39caa_b7b1_44a8_82fc_e8e3de18b2f8
-#define _8ac39caa_b7b1_44a8_82fc_e8e3de18b2f8
-
-#include "ServiceRole.h"
-
-#include <functional>
-#include <utility>
-
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/assoc.h>
-
-#include "dcmtkpp/Association.h"
-#include "dcmtkpp/Message.h"
-#include "dcmtkpp/Network.h"
-
-namespace dcmtkpp
-{
-
-template<typename TMessage>
-TMessage
-ServiceRole
-::_receive(ProgressCallback callback, void* callback_data) const
-{
- Message const message = this->_receive(callback, callback_data);
- return TMessage(message);
-}
-
-}
-
-#endif // _8ac39caa_b7b1_44a8_82fc_e8e3de18b2f8
diff --git a/src/dcmtkpp/StoreSCP.cpp b/src/dcmtkpp/StoreSCP.cpp
deleted file mode 100644
index c6d152b..0000000
--- a/src/dcmtkpp/StoreSCP.cpp
+++ /dev/null
@@ -1,94 +0,0 @@
-/*************************************************************************
- * dcmtkpp - 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 "dcmtkpp/StoreSCP.h"
-
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/dimse.h>
-
-#include "dcmtkpp/CStoreRequest.h"
-#include "dcmtkpp/CStoreResponse.h"
-#include "dcmtkpp/DataSet.h"
-
-namespace dcmtkpp
-{
-
-bool
-StoreSCP
-::store(Callback callback) const
-{
- Message request;
- try
- {
- request = this->_receive();
- }
- catch(Exception const & e)
- {
- if(e.get_source() != Exception::Source::Condition ||
- e.get_condition() != DUL_PEERREQUESTEDRELEASE)
- {
- throw;
- }
- else
- {
- return true;
- }
- }
-
- if(request.get_command_field() == Message::Command::C_ECHO_RQ)
- {
- this->_send_echo_response(CEchoRequest(request));
- }
- else if(request.get_command_field() == Message::Command::C_STORE_RQ)
- {
- this->store(CStoreRequest(request), callback);
- request.delete_data_set();
- }
- else
- {
- std::ostringstream message;
- message << "DIMSE: Unexpected Response Command Field: 0x"
- << std::hex << request.get_command_field();
- throw Exception(message.str());
- }
-
- return false;
-}
-
-void
-StoreSCP
-::store(CStoreRequest const & request, Callback callback) const
-{
- // Execute user callback
- Uint16 status = STATUS_Success;
- if(!request.has_data_set() || request.get_data_set().empty())
- {
- status = STATUS_STORE_Error_CannotUnderstand;
- }
- else
- {
- try
- {
- callback(request.get_data_set());
- }
- catch(...)
- {
- // FIXME: logging
- status = STATUS_STORE_Error_CannotUnderstand;
- }
- }
-
- // Send store response
- CStoreResponse response(request.get_message_id(), status);
- response.set_affected_sop_class_uid(request.get_affected_sop_class_uid());
- response.set_affected_sop_instance_uid(
- request.get_affected_sop_instance_uid());
- this->_send(response, request.get_affected_sop_class_uid());
-}
-
-}
diff --git a/src/dcmtkpp/StoreSCP.h b/src/dcmtkpp/StoreSCP.h
deleted file mode 100644
index 9d6c8ed..0000000
--- a/src/dcmtkpp/StoreSCP.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*************************************************************************
- * dcmtkpp - 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 _fdbf3f51_91f5_464a_b449_c3f994297210
-#define _fdbf3f51_91f5_464a_b449_c3f994297210
-
-#include <functional>
-
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/CStoreRequest.h"
-#include "dcmtkpp/SCP.h"
-
-namespace dcmtkpp
-{
-
-class StoreSCP: public SCP
-{
-public:
- /// @brief Callback called when a response is received.
- typedef std::function<void(DataSet const &)> Callback;
-
- /**
- * @brief Receive a store request and respond to it.
- * @param callback function called with the dataset to be stored.
- */
- bool store(Callback callback) const;
-
- /**
- * @brief Respond to a store request.
- * @param request
- * @param callback function called with the dataset to be stored.
- */
- void store(CStoreRequest const & request, Callback callback) const;
-};
-
-}
-
-#endif // _fdbf3f51_91f5_464a_b449_c3f994297210
diff --git a/src/dcmtkpp/uid.cpp b/src/dcmtkpp/uid.cpp
deleted file mode 100644
index e9013e5..0000000
--- a/src/dcmtkpp/uid.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-/*************************************************************************
- * dcmtkpp - 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 "dcmtkpp/uid.h"
-
-#include <string>
-
-#include <uuid/uuid.h>
-
-#include "dcmtkpp/Exception.h"
-
-namespace dcmtkpp
-{
-
-std::string generate_uid()
-{
- if(uid_prefix.size() > 26)
- {
- throw Exception("UID prefix is too long");
- }
-
- // Our prefix is 26 characters long. With the "." for separation, 37
- // characters remain. Since a random UUID has 122 random bits and since
- // 2^122-1 is 37 decimal digits long, the UID can be generated from a UUID.
- uuid_t uuid;
- uuid_generate(uuid);
-
- // Convert the UUID to two 61 bits integers
- uint64_t first=0;
- // time-high-and-version: 12 bits random, 4 bits version (2 bytes)
- first = (first + (uuid[7]>>4)) << 8;
- first = (first + ((uuid[7]&0xf)<<4)+(uuid[6]&0xf)) << 8;
- // time-mid: random (2 bytes)
- first = (first + uuid[5]) << 8;
- first = (first + uuid[4]) << 8;
- // time-low: random (4 bytes)
- first = (first + uuid[3]) << 8;
- first = (first + uuid[2]) << 8;
- first = (first + uuid[1]) << 8;
- first = (first + uuid[0]);
-
- uint64_t second=0;
- // node: 48 bits random (6 bytes)
- second = (second + uuid[15]) << 8;
- second = (second + uuid[14]) << 8;
- second = (second + uuid[13]) << 8;
- second = (second + uuid[12]) << 8;
- second = (second + uuid[11]) << 8;
- second = (second + uuid[10]) << 8;
- // clock_seq_low: 8 bits random (1 byte)
- second = (second + uuid[9]) << 8;
- // clock_seq_hi_and_reserved: 4 bits random, 4 bits variant (1 byte)
- second = (second + ((uuid[8]&0xf) << 4)) >> 4;
-
- std::string suffix;
- int remainder = 0;
- while(first != 0)
- {
- int digit = first%10;
- first = (first-digit)/10;
- if(first != 0)
- {
- suffix = std::to_string(digit)+suffix;
- }
- else
- {
- remainder = digit;
- }
- }
-
- second += remainder;
- while(second != 0)
- {
- int digit = second%10;
- second = (second-digit)/10;
-
- suffix = std::to_string(digit)+suffix;
- }
-
- return uid_prefix+"."+suffix;
-}
-
-}
diff --git a/src/odil/Association.cpp b/src/odil/Association.cpp
new file mode 100644
index 0000000..2d0fb47
--- /dev/null
+++ b/src/odil/Association.cpp
@@ -0,0 +1,496 @@
+/*************************************************************************
+ * 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 "Association.h"
+
+#include <algorithm>
+#include <functional>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "odil/AssociationParameters.h"
+#include "odil/DataSet.h"
+#include "odil/Exception.h"
+#include "odil/uid.h"
+#include "odil/dul/StateMachine.h"
+#include "odil/message/Message.h"
+#include "odil/pdu/AAbort.h"
+#include "odil/pdu/AAssociate.h"
+#include "odil/pdu/AAssociateRJ.h"
+#include "odil/pdu/AReleaseRP.h"
+#include "odil/pdu/AReleaseRQ.h"
+#include "odil/pdu/ImplementationClassUID.h"
+#include "odil/pdu/ImplementationVersionName.h"
+#include "odil/pdu/PDataTF.h"
+#include "odil/pdu/PresentationContextAC.h"
+#include "odil/pdu/PresentationContextRQ.h"
+#include "odil/pdu/RoleSelection.h"
+#include "odil/pdu/UserIdentityRQ.h"
+#include "odil/pdu/UserInformation.h"
+#include "odil/Reader.h"
+#include "odil/Writer.h"
+
+namespace odil
+{
+
+Association
+::Association()
+: _state_machine(), _peer_host(""), _peer_port(104), _association_parameters(),
+ _transfer_syntaxes_by_abstract_syntax(), _transfer_syntaxes_by_id(),
+ _next_message_id(1)
+{
+ this->set_tcp_timeout(boost::posix_time::pos_infin);
+ this->set_message_timeout(boost::posix_time::seconds(30));
+}
+
+Association
+::Association(Association const & other)
+: _state_machine(), _peer_host(other._peer_host), _peer_port(other._peer_port),
+ _association_parameters(other._association_parameters),
+ _transfer_syntaxes_by_abstract_syntax(), _transfer_syntaxes_by_id(),
+ _next_message_id(other._next_message_id)
+{
+ this->set_tcp_timeout(other.get_tcp_timeout());
+ this->set_message_timeout(other.get_message_timeout());
+}
+
+Association
+::~Association()
+{
+ // Nothing to do, everything is taken care of by the StateMachine
+}
+
+dul::Transport &
+Association
+::get_transport()
+{
+ return this->_state_machine.get_transport();
+}
+
+Association &
+Association
+::operator=(Association const & other)
+{
+ if(this != &other)
+ {
+ this->set_peer_host(other.get_peer_host());
+ this->set_peer_port(other.get_peer_port());
+ this->set_parameters(other.get_parameters());
+ }
+
+ return *this;
+}
+
+std::string const &
+Association
+::get_peer_host() const
+{
+ return this->_peer_host;
+}
+
+void
+Association
+::set_peer_host(std::string const & host)
+{
+ if(this->is_associated())
+ {
+ throw Exception("Cannot set member while associated");
+ }
+
+ this->_peer_host = host;
+}
+
+uint16_t
+Association
+::get_peer_port() const
+{
+ return this->_peer_port;
+}
+
+void
+Association
+::set_peer_port(uint16_t port)
+{
+ if(this->is_associated())
+ {
+ throw Exception("Cannot set member while associated");
+ }
+
+ this->_peer_port = port;
+}
+
+AssociationParameters const &
+Association
+::get_parameters() const
+{
+ return this->_association_parameters;
+}
+
+AssociationParameters &
+Association
+::update_parameters()
+{
+ if(this->is_associated())
+ {
+ throw Exception("Cannot set member while associated");
+ }
+
+ return this->_association_parameters;
+}
+
+void
+Association
+::set_parameters(AssociationParameters const & value)
+{
+ if(this->is_associated())
+ {
+ throw Exception("Cannot set member while associated");
+ }
+
+ this->_association_parameters = value;
+}
+
+AssociationParameters const &
+Association
+::get_negotiated_parameters() const
+{
+ return this->_negotiated_parameters;
+}
+
+Association::duration_type
+Association
+::get_tcp_timeout() const
+{
+ return this->_state_machine.get_transport().get_timeout();
+}
+
+void
+Association
+::set_tcp_timeout(duration_type const & duration)
+{
+ this->_state_machine.get_transport().set_timeout(duration);
+}
+
+Association::duration_type
+Association
+::get_message_timeout() const
+{
+ return this->_state_machine.get_timeout();
+}
+
+void
+Association
+::set_message_timeout(duration_type const & duration)
+{
+ this->_state_machine.set_timeout(duration);
+}
+
+bool
+Association
+::is_associated() const
+{
+ return this->_state_machine.get_transport().is_open();
+}
+
+void
+Association
+::associate()
+{
+ boost::asio::ip::tcp::resolver resolver(
+ this->_state_machine.get_transport().get_service());
+ boost::asio::ip::tcp::resolver::query const query(this->_peer_host, "");
+ auto const endpoint_it = resolver.resolve(query);
+
+ dul::EventData data;
+ data.peer_endpoint = *endpoint_it;
+ data.peer_endpoint.port(this->_peer_port);
+
+ auto const request =
+ std::make_shared<pdu::AAssociateRQ>(
+ this->_association_parameters.as_a_associate_rq());
+
+ data.pdu = request;
+
+ this->_state_machine.send_pdu(data);
+ this->_state_machine.receive_pdu(data);
+
+ if(data.pdu == nullptr)
+ {
+ throw Exception("No response received");
+ }
+ else
+ {
+ auto const acceptation = std::dynamic_pointer_cast<pdu::AAssociateAC>(data.pdu);
+ auto const rejection = std::dynamic_pointer_cast<pdu::AAssociateRJ>(data.pdu);
+ if(acceptation != nullptr)
+ {
+ this->_negotiated_parameters = AssociationParameters(
+ *acceptation, this->_association_parameters);
+
+ this->_transfer_syntaxes_by_abstract_syntax.clear();
+ this->_transfer_syntaxes_by_id.clear();
+
+ for(auto const & pc: this->_negotiated_parameters.get_presentation_contexts())
+ {
+ if(pc.result != AssociationParameters::PresentationContext::Result::Acceptance)
+ {
+ continue;
+ }
+
+ this->_transfer_syntaxes_by_id[pc.id] = pc.transfer_syntaxes[0];
+ this->_transfer_syntaxes_by_abstract_syntax[pc.abstract_syntax] =
+ {pc.id, pc.transfer_syntaxes[0]};
+ }
+ }
+ else if(rejection != nullptr)
+ {
+ throw Exception("Association rejected");
+ }
+ else
+ {
+ throw Exception("Invalid response");
+ }
+ }
+}
+
+void
+Association
+::receive_association(
+ boost::asio::ip::tcp const & protocol, unsigned short port,
+ AssociationAcceptor acceptor)
+{
+ dul::EventData data;
+ data.peer_endpoint = dul::Transport::Socket::endpoint_type(protocol, port);
+
+ this->_state_machine.set_association_acceptor(acceptor);
+
+ this->_state_machine.receive(data);
+ this->_state_machine.receive_pdu(data);
+
+ if(data.pdu == NULL)
+ {
+ // We have rejected the request
+ //return false;
+ }
+ else
+ {
+ auto const & request = std::dynamic_pointer_cast<pdu::AAssociateRQ>(data.pdu);
+ if(request == nullptr)
+ {
+ throw Exception("Invalid response");
+ }
+
+ auto const endpoint =
+ this->_state_machine.get_transport().get_socket()->remote_endpoint();
+ this->_peer_host = endpoint.address().to_string();
+ this->_peer_port = endpoint.port();
+
+ this->_negotiated_parameters = data.association_parameters;
+
+ this->_transfer_syntaxes_by_abstract_syntax.clear();
+ this->_transfer_syntaxes_by_id.clear();
+
+ for(auto const & pc: this->_negotiated_parameters.get_presentation_contexts())
+ {
+ if(pc.result != AssociationParameters::PresentationContext::Result::Acceptance)
+ {
+ continue;
+ }
+
+ this->_transfer_syntaxes_by_id[pc.id] = pc.transfer_syntaxes[0];
+ this->_transfer_syntaxes_by_abstract_syntax[pc.abstract_syntax] =
+ {pc.id, pc.transfer_syntaxes[0]};
+ }
+
+ data.pdu = std::make_shared<pdu::AAssociateAC>(
+ this->_negotiated_parameters.as_a_associate_ac());
+ this->_state_machine.send_pdu(data);
+ }
+}
+
+void
+Association
+::release()
+{
+ if(!this->is_associated())
+ {
+ throw Exception("Not associated");
+ }
+
+ auto pdu = std::make_shared<pdu::AReleaseRQ>();
+ dul::EventData data;
+ data.pdu = pdu;
+ this->_state_machine.send_pdu(data);
+ this->_state_machine.receive_pdu(data);
+
+ auto const reply = std::dynamic_pointer_cast<pdu::AReleaseRP>(data.pdu);
+ if(reply == nullptr)
+ {
+ // Invalid response, accept it nevertheless.
+ }
+}
+
+void
+Association
+::abort(int source, int reason)
+{
+ if(!this->is_associated())
+ {
+ throw Exception("Not associated");
+ }
+
+ auto pdu = std::make_shared<pdu::AAbort>(source, reason);
+ dul::EventData data;
+ data.pdu = pdu;
+ this->_state_machine.send_pdu(data);
+}
+
+message::Message
+Association
+::receive_message()
+{
+ bool done = false;
+ int presentation_context_id;
+ bool command_set_received=false;
+ bool has_data_set=true;
+ bool data_set_received=false;
+
+ DataSet command_set;
+ std::stringstream command_stream, data_stream;
+
+ while(!done)
+ {
+ dul::EventData data;
+ data.pdu = nullptr;
+ this->_state_machine.receive_pdu(data);
+
+ auto const a_release_rq = std::dynamic_pointer_cast<pdu::AReleaseRQ>(data.pdu);
+ if(a_release_rq != nullptr)
+ {
+ data.pdu = std::make_shared<pdu::AReleaseRP>();
+ this->_state_machine.send_pdu(data);
+ throw AssociationReleased();
+ }
+
+ auto const a_abort = std::dynamic_pointer_cast<pdu::AAbort>(data.pdu);
+ if(a_abort != nullptr)
+ {
+ throw AssociationAborted(a_abort->get_source(), a_abort->get_reason());
+ }
+
+ auto const p_data_tf = std::dynamic_pointer_cast<pdu::PDataTF>(data.pdu);
+
+ if(p_data_tf == nullptr)
+ {
+ throw Exception("Invalid PDU received");
+ }
+
+ for(auto const & pdv: p_data_tf->get_pdv_items())
+ {
+ presentation_context_id = pdv.get_presentation_context_id();
+ bool & received =
+ pdv.is_command()?command_set_received:data_set_received;
+ received |= pdv.is_last_fragment();
+
+ auto const & fragment_data = pdv.get_fragment();
+
+ std::stringstream & stream =
+ pdv.is_command()?command_stream:data_stream;
+ stream.write(&fragment_data[0], fragment_data.size());
+
+ if(command_set_received && command_set.empty())
+ {
+ Reader reader(command_stream, registry::ImplicitVRLittleEndian);
+ command_set = reader.read_data_set();
+ auto const value =
+ command_set.as_int(registry::CommandDataSetType, 0);
+
+ if(value == message::Message::DataSetType::ABSENT)
+ {
+ has_data_set = false;
+ }
+ }
+ }
+
+ done = command_set_received && (!has_data_set || data_set_received);
+ }
+
+ if(has_data_set)
+ {
+ auto const transfer_syntax_it =
+ this->_transfer_syntaxes_by_id.find(presentation_context_id);
+ if(transfer_syntax_it == this->_transfer_syntaxes_by_id.end())
+ {
+ throw Exception("No such Presentation Context ID");
+ }
+
+ Reader reader(data_stream, transfer_syntax_it->second);
+ auto const data_set = reader.read_data_set();
+
+ return message::Message(command_set, data_set);
+ }
+ else
+ {
+ return message::Message(command_set);
+ }
+}
+
+void
+Association
+::send_message(
+ message::Message const & message, std::string const & abstract_syntax)
+{
+ auto const transfer_syntax_it =
+ this->_transfer_syntaxes_by_abstract_syntax.find(abstract_syntax);
+ if(transfer_syntax_it == this->_transfer_syntaxes_by_abstract_syntax.end())
+ {
+ throw Exception("No transfer syntax for "+abstract_syntax);
+ }
+
+ auto const & transfer_syntax = transfer_syntax_it->second.second;
+ auto const & id = transfer_syntax_it->second.first;
+
+ std::vector<pdu::PDataTF::PresentationDataValueItem> pdv_items;
+
+ std::ostringstream command_stream;
+ Writer command_writer(
+ 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()));
+
+ if (message.has_data_set())
+ {
+ std::ostringstream data_stream;
+ Writer data_writer(
+ 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 pdu = std::make_shared<pdu::PDataTF>(pdv_items);
+
+ dul::EventData data;
+ data.pdu = pdu;
+
+ this->_state_machine.send_pdu(data);
+}
+
+uint16_t
+Association
+::next_message_id()
+{
+ return ++this->_next_message_id;
+}
+
+}
diff --git a/src/odil/Association.h b/src/odil/Association.h
new file mode 100644
index 0000000..6210f36
--- /dev/null
+++ b/src/odil/Association.h
@@ -0,0 +1,222 @@
+/*************************************************************************
+ * 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 _a52696bc_5c6e_402d_a343_6cb085eb0138
+#define _a52696bc_5c6e_402d_a343_6cb085eb0138
+
+#include <cstdint>
+#include <functional>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "odil/AssociationAcceptor.h"
+#include "odil/AssociationParameters.h"
+#include "odil/dul/StateMachine.h"
+#include "odil/message/Message.h"
+
+namespace odil
+{
+
+/**
+ * @brief Association.
+ */
+class Association
+{
+public:
+ /// @brief Association result (ITU-T X.227, PS 3.8, 7.1.1.7 and PS 3.8, 9.3.4).
+ enum Result
+ {
+ Accepted=0,
+ RejectedPermanent=1,
+ RejectedTransient=2,
+ };
+
+ /// @brief Source of association result (PS 3.8, 7.1.1.8 and PS 3.8, 9.3.4).
+ enum ResultSource
+ {
+ ULServiceUser=1,
+ ULServiceProviderACSERelatedFunction=2,
+ ULServiceProvderPresentationRelatedFunction=3,
+ };
+
+ // PS 3.8, 7.1.1.9, and PS 3.8, 9.3.4, and ITU-T X.227 (UL service-user,
+ // UL service-provider ACSE-related function), ITU-T X.226 (UL
+ // service-provider presentation-related function)
+ /// @brief Diagnostic of association result
+ enum Diagnostic
+ {
+ // UL service-user
+ NoReasonGiven=1,
+ ApplicationContextNameNotSupported=2,
+ CallingAETitleNotRecognized=3,
+ CallingAPInvocationIdentifierNotRecognized=4,
+ CallingAEQualifierNotRecognized=5,
+ CallingAEInvocationIdentifierNotRecognized=6,
+ CalledAETitleNotRecognized=7,
+ CalledAPInvocationIdentifierNotRecognized=8,
+ CalledAEQualifierNotRecognized=9,
+ CalledAEInvocationIdentifierNotRecognized=10,
+
+ // UL service-provider, ACSE-related function
+ NoCommonULVersion=2,
+
+ // UL service-provider, presentation-related function
+ TemporaryCongestion=1,
+ LocalLimitExceeded=2,
+ CalledPresentationAddressUnknown=3,
+ PresentationProtocolVersionNotSupported=4,
+ NoPresentationServiceAccessPointAvailable=7,
+ };
+
+ typedef dul::StateMachine::duration_type duration_type;
+
+ /// @brief Create a default, un-associated, association.
+ Association();
+
+ /// @brief Create an un-associated association.
+ Association(Association const & other);
+
+ /// @brief Destroy the association, release it if necessary.
+ ~Association();
+
+ dul::Transport & get_transport();
+
+ /// @brief Assing an un-associated association; it remains un-associated.
+ Association & operator=(Association const & other);
+
+ /// @name Peer
+ /// @{
+
+ /// @brief Return the host name of the peer. Defaults to "".
+ std::string const & get_peer_host() const;
+ /// @brief Set the host name of the peer.
+ void set_peer_host(std::string const & host);
+
+ /// @brief Return the port of the peer. Defaults to 104.
+ uint16_t get_peer_port() const;
+ /// @brief Set the port of the peer.
+ void set_peer_port(uint16_t port);
+
+ /// @}
+
+ /// @brief Return the association parameters.
+ AssociationParameters const & get_parameters() const;
+
+ /// @brief Return the association parameters.
+ AssociationParameters & update_parameters();
+
+ /// @brief Set the association parameters, throw an exception when associated.
+ void set_parameters(AssociationParameters const & value);
+
+ /// @brief Return the negotiated association parameters.
+ AssociationParameters const & get_negotiated_parameters() const;
+
+ /// @name Timeouts
+ /// @{
+
+ /// @brief Return the TCP timeout, default to infinity.
+ duration_type get_tcp_timeout() const;
+
+ /// @brief Set the timeout.
+ void set_tcp_timeout(duration_type const & duration);
+
+ /// @brief Return the DIMSE timeout, default to 30s.
+ duration_type get_message_timeout() const;
+
+ /// @brief Set the DIMSE timeout.
+ void set_message_timeout(duration_type const & duration);
+
+ /// @}
+
+ /// @name Association
+ /// @{
+
+ /// @brief Test whether the object is currently associated to its peer.
+ bool is_associated() const;
+
+ /// @brief Request an association with the peer.
+ void associate();
+
+ /// @brief Receive an association from a peer.
+ void receive_association(
+ boost::asio::ip::tcp const & protocol, unsigned short port,
+ AssociationAcceptor acceptor=default_association_acceptor);
+
+ /// @brief Reject the received association request.
+ void reject(Result result, ResultSource result_source, Diagnostic diagnostic);
+
+ /// @brief Gracefully release the association. Throws an exception if not associated.
+ void release();
+ /// @brief Forcefully release the association. Throws an exception if not associated.
+ void abort(int source, int reason);
+
+ /// @}
+
+ /// @name DIMSE messages sending and reception.
+ /// @{
+
+ /**
+ * @brief Receive a generic DIMSE message.
+ *
+ * Throw an AssociationReleased or AssociationAborted if the peer released
+ * or aborted the association.
+ */
+ message::Message receive_message();
+
+ /// @brief Send a DIMSE message.
+ void send_message(
+ message::Message const & message, std::string const & abstract_syntax);
+
+ /// @brief Return the next available message id.
+ uint16_t next_message_id();
+
+ /// @}
+
+private:
+ dul::StateMachine _state_machine;
+
+ std::string _peer_host;
+ uint16_t _peer_port;
+
+ AssociationParameters _association_parameters;
+ AssociationParameters _negotiated_parameters;
+
+ std::map<std::string, std::pair<uint8_t, std::string>>
+ _transfer_syntaxes_by_abstract_syntax;
+ std::map<uint8_t, std::string> _transfer_syntaxes_by_id;
+
+ uint16_t _next_message_id;
+};
+
+class AssociationReleased: public Exception
+{
+public:
+ AssociationReleased()
+ : Exception("Association released")
+ {
+ // Nothing else.
+ }
+};
+
+class AssociationAborted: public Exception
+{
+public:
+ uint8_t source;
+ uint8_t reason;
+
+ AssociationAborted(unsigned char source, unsigned char reason)
+ : Exception("Association aborted"), source(source), reason(reason)
+ {
+ // Nothing else.
+ }
+};
+
+}
+
+#endif // _a52696bc_5c6e_402d_a343_6cb085eb0138
diff --git a/src/odil/AssociationAcceptor.cpp b/src/odil/AssociationAcceptor.cpp
new file mode 100644
index 0000000..bcdd8b2
--- /dev/null
+++ b/src/odil/AssociationAcceptor.cpp
@@ -0,0 +1,98 @@
+/*************************************************************************
+ * 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/AssociationAcceptor.h"
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#include "odil/AssociationParameters.h"
+#include "odil/Exception.h"
+
+namespace odil
+{
+
+AssociationParameters
+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());
+
+ std::vector<AssociationParameters::PresentationContext>
+ presentation_contexts = input.get_presentation_contexts();
+ for(auto & presentation_context: presentation_contexts)
+ {
+ presentation_context.transfer_syntaxes =
+ { presentation_context.transfer_syntaxes[0] };
+ presentation_context.result =
+ AssociationParameters::PresentationContext::Result::Acceptance;
+ }
+ output.set_presentation_contexts(presentation_contexts);
+
+ output.set_maximum_length(input.get_maximum_length());
+
+ return output;
+}
+
+AssociationRejected
+::AssociationRejected(
+ unsigned char result, unsigned char source, unsigned char reason,
+ std::string const & message)
+: Exception(message)
+{
+ this->set_result(result);
+ this->set_source(source);
+ this->set_reason(reason);
+}
+
+unsigned char
+AssociationRejected
+::get_result() const
+{
+ return this->_result;
+}
+
+void
+AssociationRejected
+::set_result(unsigned char value)
+{
+ this->_result = value;
+}
+
+unsigned char
+AssociationRejected
+::get_source() const
+{
+ return this->_source;
+}
+
+void
+AssociationRejected
+::set_source(unsigned char value)
+{
+ this->_source = value;
+}
+
+unsigned char
+AssociationRejected
+::get_reason() const
+{
+ return this->_reason;
+}
+
+void
+AssociationRejected
+::set_reason(unsigned char value)
+{
+ this->_reason = value;
+}
+
+}
diff --git a/src/odil/AssociationAcceptor.h b/src/odil/AssociationAcceptor.h
new file mode 100644
index 0000000..0603d92
--- /dev/null
+++ b/src/odil/AssociationAcceptor.h
@@ -0,0 +1,64 @@
+/*************************************************************************
+ * 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 _b21e4f11_9337_4612_8152_b59dedc16f18
+#define _b21e4f11_9337_4612_8152_b59dedc16f18
+
+#include <functional>
+#include <string>
+
+#include "odil/AssociationParameters.h"
+#include "odil/Exception.h"
+
+namespace odil
+{
+
+/**
+ * @brief Callback to check whether the association request is acceptable.
+ *
+ * The callback shall return the parameters for the acceptation or throw an
+ * AssociationRejected exception if the association is not acceptable.
+ */
+typedef
+ std::function<AssociationParameters(AssociationParameters const &)>
+ AssociationAcceptor;
+
+/**
+ * @brief Default association acceptor.
+ *
+ * Reverse the calling and called AE titles, accept the first transfer syntax
+ * and the roles of each presentation context, do not check identity,
+ * keep maximum length.
+ */
+AssociationParameters
+default_association_acceptor(AssociationParameters const & input);
+
+struct AssociationRejected: public Exception
+{
+public:
+ AssociationRejected(
+ unsigned char result, unsigned char source, unsigned char reason,
+ std::string const & message="Association rejected");
+
+ unsigned char get_result() const;
+ void set_result(unsigned char value);
+
+ unsigned char get_source() const;
+ void set_source(unsigned char value);
+
+ unsigned char get_reason() const;
+ void set_reason(unsigned char value);
+private:
+ unsigned char _result;
+ unsigned char _source;
+ unsigned char _reason;
+};
+
+}
+
+#endif // _b21e4f11_9337_4612_8152_b59dedc16f18
diff --git a/src/odil/AssociationParameters.cpp b/src/odil/AssociationParameters.cpp
new file mode 100644
index 0000000..ccae6ed
--- /dev/null
+++ b/src/odil/AssociationParameters.cpp
@@ -0,0 +1,457 @@
+/*************************************************************************
+ * 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/AssociationParameters.h"
+
+#include <cstdint>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "odil/pdu/AAssociateAC.h"
+#include "odil/pdu/AAssociateRQ.h"
+#include "odil/Exception.h"
+#include "odil/uid.h"
+#include "odil/pdu/ImplementationClassUID.h"
+#include "odil/pdu/ImplementationVersionName.h"
+#include "odil/pdu/PresentationContextAC.h"
+#include "odil/pdu/PresentationContextRQ.h"
+#include "odil/pdu/RoleSelection.h"
+
+namespace odil
+{
+
+AssociationParameters
+::AssociationParameters()
+: _called_ae_title(""), _calling_ae_title(""), _presentation_contexts(),
+ _user_identity({UserIdentity::Type::None, "", ""}), _maximum_length(16384)
+{
+ // Nothing else.
+}
+
+AssociationParameters
+::AssociationParameters(pdu::AAssociateRQ const & pdu)
+{
+ this->set_called_ae_title(pdu.get_called_ae_title());
+ this->set_calling_ae_title(pdu.get_calling_ae_title());
+
+ auto const user_information = pdu.get_user_information();
+
+ // Presentation contexts
+ auto const & pcs_pdu = pdu.get_presentation_contexts();
+
+ std::map<std::string, std::pair<bool, bool>> roles_map;
+ auto const roles = user_information.get_sub_items<pdu::RoleSelection>();
+ for(auto const & role: roles)
+ {
+ roles_map[role.get_sop_class_uid()] =
+ std::make_pair(
+ role.get_scu_role_support(),
+ role.get_scp_role_support());
+ }
+
+ std::vector<AssociationParameters::PresentationContext> pcs_parameters;
+ pcs_parameters.reserve(pcs_pdu.size());
+ for(auto const & pc_pdu: pcs_pdu)
+ {
+ AssociationParameters::PresentationContext pc_parameters;
+
+ pc_parameters.id = pc_pdu.get_id();
+ pc_parameters.abstract_syntax = pc_pdu.get_abstract_syntax();
+ pc_parameters.transfer_syntaxes = pc_pdu.get_transfer_syntaxes();
+
+ auto const it = roles_map.find(pc_pdu.get_abstract_syntax());
+ pc_parameters.scu_role_support =
+ (it!=roles_map.end())?it->second.first:true;
+ pc_parameters.scp_role_support =
+ (it!=roles_map.end())?it->second.second:false;
+
+ pcs_parameters.push_back(pc_parameters);
+ }
+ this->set_presentation_contexts(pcs_parameters);
+
+ // User identity
+ auto const user_identity =
+ user_information.get_sub_items<pdu::UserIdentityRQ>();
+ if(!user_identity.empty())
+ {
+ if(user_identity[0].get_type() == 1)
+ {
+ this->set_user_identity_to_username(
+ user_identity[0].get_primary_field());
+ }
+ else if(user_identity[0].get_type() == 2)
+ {
+ this->set_user_identity_to_username_and_password(
+ user_identity[0].get_primary_field(),
+ user_identity[0].get_secondary_field());
+ }
+ else if(user_identity[0].get_type() == 3)
+ {
+ this->set_user_identity_to_kerberos(
+ user_identity[0].get_primary_field());
+ }
+ else if(user_identity[0].get_type() == 4)
+ {
+ this->set_user_identity_to_saml(
+ user_identity[0].get_primary_field());
+ }
+ }
+
+ // Maximum length
+ auto const maximum_length =
+ user_information.get_sub_items<pdu::MaximumLength>();
+ if(!maximum_length.empty())
+ {
+ this->set_maximum_length(maximum_length[0].get_maximum_length());
+ }
+}
+
+AssociationParameters
+::AssociationParameters(
+ pdu::AAssociateAC const & pdu, AssociationParameters const & request)
+{
+ // Calling and Called AE titles are not meaningful in A-ASSOCIATE-AC
+ this->set_called_ae_title(request.get_called_ae_title());
+ this->set_calling_ae_title(request.get_calling_ae_title());
+
+ auto const user_information = pdu.get_user_information();
+
+ // Presentation contexts
+ auto const & pcs_request = request.get_presentation_contexts();
+ std::map<uint8_t, PresentationContext> pcs_request_map;
+ for(auto const & pc: pcs_request)
+ {
+ pcs_request_map[pc.id] = pc;
+ }
+
+ auto const & pcs_pdu = pdu.get_presentation_contexts();
+
+ std::map<std::string, std::pair<bool, bool>> roles_map;
+ auto const roles = user_information.get_sub_items<pdu::RoleSelection>();
+ for(auto const & role: roles)
+ {
+ roles_map[role.get_sop_class_uid()] =
+ std::make_pair(
+ role.get_scu_role_support(),
+ role.get_scp_role_support());
+ }
+
+ std::vector<AssociationParameters::PresentationContext> pcs_parameters;
+ pcs_parameters.reserve(pcs_pdu.size());
+ for(auto const & pc_pdu: pcs_pdu)
+ {
+ AssociationParameters::PresentationContext pc_parameters;
+ auto const & pc_request = pcs_request_map.at(pc_pdu.get_id());
+
+ pc_parameters.id = pc_pdu.get_id();
+ pc_parameters.abstract_syntax = pc_request.abstract_syntax;
+ pc_parameters.transfer_syntaxes = { pc_pdu.get_transfer_syntax() };
+
+ auto const it = roles_map.find(pc_request.abstract_syntax);
+ pc_parameters.scu_role_support =
+ (it!=roles_map.end())?it->second.first:pc_request.scu_role_support;
+ pc_parameters.scp_role_support =
+ (it!=roles_map.end())?it->second.second:pc_request.scp_role_support;
+
+ pc_parameters.result =
+ static_cast<PresentationContext::Result>(pc_pdu.get_result_reason());
+
+ pcs_parameters.push_back(pc_parameters);
+ }
+ this->set_presentation_contexts(pcs_parameters);
+
+ // User identity
+ auto const user_identity =
+ user_information.get_sub_items<pdu::UserIdentityAC>();
+ if(!user_identity.empty())
+ {
+ auto const type = request.get_user_identity().type;
+ if(type == UserIdentity::Type::Kerberos)
+ {
+ this->set_user_identity_to_kerberos(
+ user_identity[0].get_server_response());
+ }
+ else if(type == UserIdentity::Type::SAML)
+ {
+ this->set_user_identity_to_saml(
+ user_identity[0].get_server_response());
+ }
+ }
+
+ // Maximum length
+ auto const maximum_length =
+ user_information.get_sub_items<pdu::MaximumLength>();
+ if(!maximum_length.empty())
+ {
+ this->set_maximum_length(maximum_length[0].get_maximum_length());
+ }
+}
+
+std::string const &
+AssociationParameters
+::get_called_ae_title() const
+{
+ return this->_called_ae_title;
+}
+
+AssociationParameters &
+AssociationParameters
+::set_called_ae_title(std::string const & value)
+{
+ if(value.size() > 16)
+ {
+ throw Exception("AE Title must be less than 16 characters");
+ }
+
+ this->_called_ae_title = value;
+ return *this;
+}
+
+std::string const &
+AssociationParameters
+::get_calling_ae_title() const
+{
+ return this->_calling_ae_title;
+}
+
+AssociationParameters &
+AssociationParameters
+::set_calling_ae_title(std::string const & value)
+{
+ if(value.size() > 16)
+ {
+ throw Exception("AE Title must be less than 16 characters");
+ }
+
+ this->_calling_ae_title = value;
+ return *this;
+}
+
+std::vector<AssociationParameters::PresentationContext> const &
+AssociationParameters
+::get_presentation_contexts() const
+{
+ return this->_presentation_contexts;
+}
+
+AssociationParameters &
+AssociationParameters
+::set_presentation_contexts(std::vector<PresentationContext> const & value)
+{
+ std::set<uint8_t> ids;
+ for(auto const context: value)
+ {
+ auto const id = context.id;
+ if(id%2 == 0)
+ {
+ throw Exception("Presentation Context ID must be odd");
+ }
+ ids.insert(id);
+ }
+ if(ids.size() != value.size())
+ {
+ throw Exception("All Presentation Context IDs must be unique");
+ }
+
+ this->_presentation_contexts = value;
+ return *this;
+}
+
+AssociationParameters::UserIdentity const &
+AssociationParameters
+::get_user_identity() const
+{
+ return this->_user_identity;
+}
+
+AssociationParameters &
+AssociationParameters
+::set_user_identity_to_none()
+{
+ return this->_set_user_identity({UserIdentity::Type::None, "", ""});
+}
+
+AssociationParameters &
+AssociationParameters
+::set_user_identity_to_username(std::string const & username)
+{
+ return this->_set_user_identity(
+ {UserIdentity::Type::Username, username, ""});
+}
+
+AssociationParameters &
+AssociationParameters
+::set_user_identity_to_username_and_password(
+ std::string const & username, std::string const & password)
+{
+ return this->_set_user_identity(
+ {UserIdentity::Type::UsernameAndPassword, username, password});
+}
+
+AssociationParameters &
+AssociationParameters
+::set_user_identity_to_kerberos(std::string const & ticket)
+{
+ return this->_set_user_identity({UserIdentity::Type::Kerberos, ticket, ""});
+}
+
+AssociationParameters &
+AssociationParameters
+::set_user_identity_to_saml(std::string const & assertion)
+{
+ return this->_set_user_identity({UserIdentity::Type::SAML, assertion, ""});
+}
+
+uint32_t
+AssociationParameters
+::get_maximum_length() const
+{
+ return this->_maximum_length;
+}
+
+AssociationParameters &
+AssociationParameters
+::set_maximum_length(uint32_t value)
+{
+ this->_maximum_length = value;
+ return *this;
+}
+
+pdu::AAssociateRQ
+AssociationParameters
+::as_a_associate_rq() const
+{
+ pdu::AAssociateRQ pdu;
+ pdu.set_protocol_version(1);
+ pdu.set_application_context(std::string("1.2.840.10008.3.1.1.1"));
+ pdu.set_called_ae_title(this->get_called_ae_title());
+ pdu.set_calling_ae_title(this->get_calling_ae_title());
+
+ // Presentation contexts
+ {
+ auto const & source = this->get_presentation_contexts();
+
+ std::vector<pdu::PresentationContextRQ> destination;
+ destination.reserve(source.size());
+
+ for(auto const & source_pc: source)
+ {
+ pdu::PresentationContextRQ const pc(
+ source_pc.id, source_pc.abstract_syntax,
+ source_pc.transfer_syntaxes);
+ destination.push_back(pc);
+ }
+
+ pdu.set_presentation_contexts(destination);
+ }
+
+ pdu::UserInformation user_information;
+
+ user_information.set_sub_items<pdu::MaximumLength>(
+ {this->get_maximum_length()});
+
+ user_information.set_sub_items<pdu::ImplementationClassUID>(
+ {implementation_class_uid});
+ user_information.set_sub_items<pdu::ImplementationVersionName>(
+ {implementation_version_name});
+
+ std::vector<pdu::RoleSelection> roles;
+ for(auto const & presentation_context: this->get_presentation_contexts())
+ {
+ pdu::RoleSelection const role(
+ presentation_context.abstract_syntax,
+ presentation_context.scu_role_support,
+ presentation_context.scp_role_support);
+ roles.push_back(role);
+ }
+ user_information.set_sub_items(roles);
+
+ auto const & user_identity = this->get_user_identity();
+ if(user_identity.type != AssociationParameters::UserIdentity::Type::None)
+ {
+ pdu::UserIdentityRQ sub_item;
+ sub_item.set_type(static_cast<int>(user_identity.type));
+ sub_item.set_primary_field(user_identity.primary_field);
+ sub_item.set_secondary_field(user_identity.secondary_field);
+
+ // TODO
+ sub_item.set_positive_response_requested(true);
+
+ user_information.set_sub_items<pdu::UserIdentityRQ>({sub_item});
+ }
+
+ pdu.set_user_information(user_information);
+
+ return pdu;
+}
+
+pdu::AAssociateAC
+AssociationParameters
+::as_a_associate_ac() const
+{
+ pdu::AAssociateAC pdu;
+ pdu.set_protocol_version(1);
+ pdu.set_application_context(std::string("1.2.840.10008.3.1.1.1"));
+ pdu.set_called_ae_title(this->get_called_ae_title());
+ pdu.set_calling_ae_title(this->get_calling_ae_title());
+
+ // Presentation contexts
+ {
+ auto const & source = this->get_presentation_contexts();
+
+ std::vector<pdu::PresentationContextAC> destination;
+ destination.reserve(source.size());
+
+ for(auto const & source_pc: source)
+ {
+ pdu::PresentationContextAC const pc(
+ source_pc.id, source_pc.transfer_syntaxes[0],
+ static_cast<uint8_t>(source_pc.result));
+ destination.push_back(pc);
+ }
+
+ pdu.set_presentation_contexts(destination);
+ }
+
+ pdu::UserInformation user_information;
+
+ user_information.set_sub_items<pdu::MaximumLength>(
+ {this->get_maximum_length()});
+
+ user_information.set_sub_items<pdu::ImplementationClassUID>(
+ {implementation_class_uid});
+ user_information.set_sub_items<pdu::ImplementationVersionName>(
+ {implementation_version_name});
+
+ std::vector<pdu::RoleSelection> roles;
+ for(auto const & presentation_context: this->get_presentation_contexts())
+ {
+ pdu::RoleSelection const role(
+ presentation_context.abstract_syntax,
+ presentation_context.scu_role_support,
+ presentation_context.scp_role_support);
+ roles.push_back(role);
+ }
+ user_information.set_sub_items(roles);
+
+ pdu.set_user_information(user_information);
+
+ return pdu;
+}
+
+AssociationParameters &
+AssociationParameters
+::_set_user_identity(UserIdentity const & value)
+{
+ this->_user_identity = value;
+ return *this;
+}
+
+}
diff --git a/src/odil/AssociationParameters.h b/src/odil/AssociationParameters.h
new file mode 100644
index 0000000..c251834
--- /dev/null
+++ b/src/odil/AssociationParameters.h
@@ -0,0 +1,156 @@
+/*************************************************************************
+ * 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 _061fafd4_982e_4a7e_9eb0_29e06443ebf3
+#define _061fafd4_982e_4a7e_9eb0_29e06443ebf3
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include "odil/pdu/AAssociateAC.h"
+#include "odil/pdu/AAssociateRQ.h"
+
+namespace odil
+{
+
+/// @brief Encapsulate association parameters
+class AssociationParameters
+{
+public:
+ /**
+ * @brief Presentation Context, cf. PS 3.8, 9.3.2.2, PS 3.8, 9.3.3.2,
+ * PS 3.7, D.3.3.4.1 and PS 3.7 D.3.3.4.2.
+ */
+ struct PresentationContext
+ {
+ enum class Result
+ {
+ Acceptance = 0,
+ UserRejection = 1,
+ NoReason = 2,
+ AbstractSyntaxNotSupported = 3,
+ TransferSyntaxesNotSupported = 4,
+ };
+
+ uint8_t id;
+ std::string abstract_syntax;
+ std::vector<std::string> transfer_syntaxes;
+ bool scu_role_support;
+ bool scp_role_support;
+ Result result;
+ };
+
+ /// @brief User Identity, cf. PS3.8 D.3.3.7
+ struct UserIdentity
+ {
+ enum class Type
+ {
+ None = 0,
+ Username = 1,
+ UsernameAndPassword = 2,
+ Kerberos = 3,
+ SAML = 4
+ };
+
+ Type type;
+ std::string primary_field;
+ std::string secondary_field;
+ };
+
+ /// @brief Constructor.
+ AssociationParameters();
+
+ /// @brief Constructor from an A-ASSOCIATE-RQ PDU.
+ AssociationParameters(pdu::AAssociateRQ const & pdu);
+
+ /// @brief Constructor from an A-ASSOCIATE-RQ PDU.
+ AssociationParameters(
+ pdu::AAssociateAC const & pdu, AssociationParameters const & request);
+
+ /// @brief Return the called AE title, default to empty.
+ std::string const & get_called_ae_title() const;
+
+ /**
+ * @brief Set the called AE title.
+ *
+ * An exception is raised if the value is empty or if it is longer than
+ * 16 characters.
+ */
+ AssociationParameters & set_called_ae_title(std::string const & value);
+
+ /// @brief Return the calling AE title, default to empty.
+ std::string const & get_calling_ae_title() const;
+
+ /**
+ * @brief Set the calling AE title.
+ *
+ * An exception is raised if the value is empty or if it is longer than
+ * 16 characters.
+ */
+ AssociationParameters & set_calling_ae_title(std::string const & value);
+
+ /// @brief Return the presentation contexts, default to empty.
+ std::vector<PresentationContext> const & get_presentation_contexts() const;
+
+ /// @brief Set the presentation contexts. All ids must be odd and unique.
+ AssociationParameters &
+ set_presentation_contexts(std::vector<PresentationContext> const & value);
+
+ /// @brief Return the user identity, default to None.
+ UserIdentity const & get_user_identity() const;
+
+ /// @brief Do no authenticate user.
+ AssociationParameters & set_user_identity_to_none();
+
+ /// @brief Authenticate user using only a username.
+ AssociationParameters &
+ set_user_identity_to_username(std::string const & username);
+
+ /// @brief Authenticate user using a username and a password.
+ AssociationParameters &
+ set_user_identity_to_username_and_password(
+ std::string const & username, std::string const & password);
+
+ /// @brief Authenticate user using a Kerberos ticket.
+ AssociationParameters &
+ set_user_identity_to_kerberos(std::string const & ticket);
+
+ /// @brief Authenticate user using a SAML assertion.
+ AssociationParameters &
+ set_user_identity_to_saml(std::string const & assertion);
+
+ /// @brief Return the maximum length of a PDU, default to 16384.
+ uint32_t get_maximum_length() const;
+
+ /**
+ * @brief Set the maximum length of a PDU, the value 0 meaning
+ * no maximum length.
+ */
+ AssociationParameters & set_maximum_length(uint32_t value);
+
+ /// @brief Create an A-ASSOCIATE-RQ PDU.
+ pdu::AAssociateRQ as_a_associate_rq() const;
+
+ /// @brief Create an A-ASSOCIATE-AC PDU.
+ pdu::AAssociateAC as_a_associate_ac() const;
+
+private:
+ std::string _called_ae_title;
+ std::string _calling_ae_title;
+ std::vector<PresentationContext> _presentation_contexts;
+ UserIdentity _user_identity;
+ uint32_t _maximum_length;
+
+ /// @brief Set the user identity.
+ AssociationParameters & _set_user_identity(UserIdentity const & value);
+};
+
+}
+
+#endif // _061fafd4_982e_4a7e_9eb0_29e06443ebf3
diff --git a/src/dcmtkpp/BasicDirectoryCreator.cpp b/src/odil/BasicDirectoryCreator.cpp
similarity index 96%
rename from src/dcmtkpp/BasicDirectoryCreator.cpp
rename to src/odil/BasicDirectoryCreator.cpp
index 3730b81..b3c5aa8 100644
--- a/src/dcmtkpp/BasicDirectoryCreator.cpp
+++ b/src/odil/BasicDirectoryCreator.cpp
@@ -1,18 +1,19 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 "dcmtkpp/BasicDirectoryCreator.h"
+#include "odil/BasicDirectoryCreator.h"
#include <algorithm>
#include <fstream>
#include <iterator>
#include <map>
#include <memory>
+#include <sstream>
#include <stack>
#include <string>
#include <utility>
@@ -21,14 +22,14 @@
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/Reader.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Tag.h"
-#include "dcmtkpp/uid.h"
+#include "odil/DataSet.h"
+#include "odil/Exception.h"
+#include "odil/Reader.h"
+#include "odil/registry.h"
+#include "odil/Tag.h"
+#include "odil/uid.h"
-namespace dcmtkpp
+namespace odil
{
std::map<std::string, std::vector<BasicDirectoryCreator::RecordKey>> const
@@ -59,8 +60,7 @@ BasicDirectoryCreator
BasicDirectoryCreator
::BasicDirectoryCreator(
std::string const & root, std::vector<std::string> const & files,
- std::map<std::string, std::vector<RecordKey>> const & extra_record_keys,
- Writer::ItemEncoding item_encoding)
+ RecordKeyMap const & extra_record_keys, Writer::ItemEncoding item_encoding)
: root(root), files(files), extra_record_keys(extra_record_keys),
item_encoding(item_encoding)
{
@@ -227,7 +227,7 @@ BasicDirectoryCreator
}
}
- registry::RecordInUseFlag;
+ // TODO: registry::RecordInUseFlag;
}
BasicDirectoryCreator::LinearizedTree
@@ -368,8 +368,7 @@ BasicDirectoryCreator
meta_information.add(
registry::ImplementationClassUID, { implementation_class_uid });
meta_information.add(
- registry::ImplementationVersionName,
- { "DCMTK++ " DCMTKPP_STRINGIFY(DCMTKPP_MAJOR_VERSION) });
+ registry::ImplementationVersionName, { implementation_version_name });
DataSet basic_directory;
diff --git a/src/dcmtkpp/BasicDirectoryCreator.h b/src/odil/BasicDirectoryCreator.h
similarity index 87%
rename from src/dcmtkpp/BasicDirectoryCreator.h
rename to src/odil/BasicDirectoryCreator.h
index c441665..bd745f0 100644
--- a/src/dcmtkpp/BasicDirectoryCreator.h
+++ b/src/odil/BasicDirectoryCreator.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -15,21 +15,25 @@
#include <utility>
#include <vector>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Tag.h"
-#include "dcmtkpp/Writer.h"
+#include "odil/DataSet.h"
+#include "odil/Tag.h"
+#include "odil/Writer.h"
-namespace dcmtkpp
+namespace odil
{
/// @brief Write a Basic Directory (i.e. DICOMDIR) object to the disk.
class BasicDirectoryCreator
{
public:
+ /// @brief The tag and its associated type in the record.
typedef std::pair<Tag, int> RecordKey;
+ /// @brief Map from a record type to the extra keys.
+ typedef std::map<std::string, std::vector<RecordKey>> RecordKeyMap;
+
/// @brief Default record keys, classified by record type.
- static std::map<std::string, std::vector<RecordKey>> const default_record_keys;
+ static RecordKeyMap const default_record_keys;
/**
* @brief Root of the DICOM files and location of the output DICOMDIR file.
@@ -47,7 +51,7 @@ public:
std::vector<std::string> files;
/// @brief User-defined record keys, classified by record type.
- std::map<std::string, std::vector<RecordKey>> extra_record_keys;
+ RecordKeyMap extra_record_keys;
/// @brief Encoding of sequence items, defaults to Writer::ItemEncoding::ExplicitLength.
Writer::ItemEncoding item_encoding;
@@ -55,8 +59,7 @@ public:
BasicDirectoryCreator(
std::string const & root="",
std::vector<std::string> const & files=std::vector<std::string>(),
- std::map<std::string, std::vector<RecordKey>> const & extra_record_keys=
- std::map<std::string, std::vector<RecordKey>>(),
+ RecordKeyMap const & extra_record_keys=RecordKeyMap(),
Writer::ItemEncoding item_encoding=Writer::ItemEncoding::ExplicitLength
);
diff --git a/src/dcmtkpp/DataSet.cpp b/src/odil/DataSet.cpp
similarity index 92%
rename from src/dcmtkpp/DataSet.cpp
rename to src/odil/DataSet.cpp
index 82f70f8..4a3cbd4 100644
--- a/src/dcmtkpp/DataSet.cpp
+++ b/src/odil/DataSet.cpp
@@ -1,12 +1,12 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 "dcmtkpp/DataSet.h"
+#include "odil/DataSet.h"
#include <cstddef>
#include <cstdint>
@@ -14,11 +14,11 @@
#include <string>
#include <vector>
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/Tag.h"
-#include "dcmtkpp/VR.h"
+#include "odil/Exception.h"
+#include "odil/Tag.h"
+#include "odil/VR.h"
-namespace dcmtkpp
+namespace odil
{
DataSet
@@ -46,19 +46,19 @@ DataSet
vr = as_vr(tag);
}
- if(::dcmtkpp::is_int(vr))
+ if(::odil::is_int(vr))
{
value = Value::Integers();
}
- else if(::dcmtkpp::is_real(vr))
+ else if(::odil::is_real(vr))
{
value = Value::Reals();
}
- else if(::dcmtkpp::is_string(vr))
+ else if(::odil::is_string(vr))
{
value = Value::Strings();
}
- else if(::dcmtkpp::is_binary(vr))
+ else if(::odil::is_binary(vr))
{
value = Value::Binary();
}
@@ -68,7 +68,7 @@ DataSet
}
else
{
- throw Exception("Unknown VR: "+::dcmtkpp::as_string(vr));
+ throw Exception("Unknown VR: "+::odil::as_string(vr));
}
this->add(tag, Element(value, vr));
diff --git a/src/dcmtkpp/DataSet.h b/src/odil/DataSet.h
similarity index 83%
rename from src/dcmtkpp/DataSet.h
rename to src/odil/DataSet.h
index 2114f86..9924185 100644
--- a/src/dcmtkpp/DataSet.h
+++ b/src/odil/DataSet.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -16,13 +16,13 @@
#include <string>
#include <vector>
-#include "dcmtkpp/Element.h"
-#include "dcmtkpp/Value.h"
+#include "odil/Element.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
{
-#define dcmtkppElementTypeMacro(name, Type) \
+#define odilElementTypeMacro(name, Type) \
bool is_##name(Tag const & tag) const \
{ \
auto const it = this->_elements.find(tag); \
@@ -41,7 +41,7 @@ Value::Type const & as_##name(Tag const & tag) const \
} \
return it->second.as_##name(); \
} \
-Value::Type::value_type const & as_##name(Tag const & tag, int position) const \
+Value::Type::value_type const & as_##name(Tag const & tag, unsigned int position) const \
{ \
auto const & data = this->as_##name(tag); \
if(data.size() <= position) \
@@ -123,7 +123,7 @@ public:
/**
* @brief Remove an element from the data set.
*
- * If the element is not in the data set, a dcmtkpp::Exception is raised.
+ * If the element is not in the data set, a odil::Exception is raised.
*/
void remove(Tag const & tag);
@@ -139,50 +139,48 @@ public:
/**
* @brief Return the VR of an element in the data set.
*
- * If the element is not in the data set, a dcmtkpp::Exception is raised.
+ * If the element is not in the data set, a odil::Exception is raised.
*/
VR get_vr(Tag const & tag) const;
/**
* @brief Test whether an element of the data set is empty.
*
- * If the element is not in the data set, a dcmtkpp::Exception is raised.
+ * If the element is not in the data set, a odil::Exception is raised.
*/
bool empty(Tag const & tag) const;
/**
* @brief Return the number of values in an element of the data set.
*
- * If the element is not in the data set, a dcmtkpp::Exception is raised.
+ * If the element is not in the data set, a odil::Exception is raised.
*/
std::size_t size(Tag const & tag) const;
/**
* @brief Access the given element.
*
- * If the element is not in the data set, a dcmtkpp::Exception is raised.
+ * If the element is not in the data set, a odil::Exception is raised.
*/
Element const & operator[](Tag const & tag) const;
/**
* @brief Access the given element.
*
- * If the element is not in the data set, a dcmtkpp::Exception is raised.
+ * If the element is not in the data set, a odil::Exception is raised.
*/
Element & operator[](Tag const & tag);
- dcmtkppElementTypeMacro(int, Integers);
- dcmtkppElementTypeMacro(real, Reals);
- dcmtkppElementTypeMacro(string, Strings);
- dcmtkppElementTypeMacro(data_set, DataSets);
- dcmtkppElementTypeMacro(binary, Binary);
+ odilElementTypeMacro(int, Integers);
+ odilElementTypeMacro(real, Reals);
+ odilElementTypeMacro(string, Strings);
+ odilElementTypeMacro(data_set, DataSets);
+ odilElementTypeMacro(binary, Binary);
typedef std::map<Tag, Element>::const_iterator const_iterator;
const_iterator begin() const { return this->_elements.begin(); }
const_iterator end() const { return this->_elements.end(); }
- // FIXME: AT, binary
-
/// @brief Equality test
bool operator==(DataSet const & other) const;
diff --git a/src/odil/EchoSCP.cpp b/src/odil/EchoSCP.cpp
new file mode 100644
index 0000000..f663c77
--- /dev/null
+++ b/src/odil/EchoSCP.cpp
@@ -0,0 +1,82 @@
+/*************************************************************************
+ * 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/EchoSCP.h"
+
+#include <functional>
+
+#include "odil/Association.h"
+#include "odil/SCP.h"
+#include "odil/Value.h"
+#include "odil/message/CEchoRequest.h"
+#include "odil/message/CEchoResponse.h"
+#include "odil/message/Message.h"
+
+namespace odil
+{
+
+EchoSCP
+::EchoSCP(Association & association)
+: SCP(association), _callback()
+{
+ // Nothing else.
+}
+
+EchoSCP
+::EchoSCP(Association & association, Callback const & callback)
+: SCP(association), _callback()
+{
+ this->set_callback(callback);
+}
+
+EchoSCP
+::~EchoSCP()
+{
+ // Nothing to do.
+}
+
+EchoSCP::Callback const &
+EchoSCP::get_callback() const
+{
+ return this->_callback;
+}
+
+void
+EchoSCP
+::set_callback(Callback const & callback)
+{
+ this->_callback = callback;
+}
+
+void
+EchoSCP
+::operator()(message::Message const & message)
+{
+ message::CEchoRequest const request(message);
+
+ Value::Integer status=message::CEchoResponse::Success;
+
+ try
+ {
+ status = this->_callback(request);
+ }
+ catch(Exception const &)
+ {
+ status = message::CEchoResponse::ProcessingFailure;
+ // Error Comment
+ // Error ID
+ // Affected SOP Class UID
+ }
+
+ message::CEchoResponse const response(
+ request.get_message_id(), status, request.get_affected_sop_class_uid());
+ this->_association.send_message(
+ response, request.get_affected_sop_class_uid());
+}
+
+}
diff --git a/src/odil/EchoSCP.h b/src/odil/EchoSCP.h
new file mode 100644
index 0000000..ef9680b
--- /dev/null
+++ b/src/odil/EchoSCP.h
@@ -0,0 +1,54 @@
+/*************************************************************************
+ * 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 _1a61d976_ba12_4dba_af34_67f064d38506
+#define _1a61d976_ba12_4dba_af34_67f064d38506
+
+#include <functional>
+
+#include "odil/Association.h"
+#include "odil/SCP.h"
+#include "odil/Value.h"
+#include "odil/message/CEchoRequest.h"
+#include "odil/message/Message.h"
+
+namespace odil
+{
+
+/// @brief SCP for C-Echo services.
+class EchoSCP: public SCP
+{
+public:
+ /// @brief Callback called when a request is received.
+ typedef std::function<Value::Integer(message::CEchoRequest const &)> Callback;
+
+ /// @brief Constructor.
+ EchoSCP(Association & association);
+
+ /// @brief Constructor.
+ EchoSCP(Association & association, Callback const & callback);
+
+ /// @brief Destructor.
+ virtual ~EchoSCP();
+
+ /// @brief Return the callback.
+ Callback const & get_callback() const;
+
+ /// @brief Set the callback.
+ void set_callback(Callback const & callback);
+
+ /// @brief Process a C-Echo request.
+ virtual void operator()(message::Message const & message);
+
+private:
+ Callback _callback;
+};
+
+}
+
+#endif // _1a61d976_ba12_4dba_af34_67f064d38506
diff --git a/src/dcmtkpp/Element.cpp b/src/odil/Element.cpp
similarity index 95%
rename from src/dcmtkpp/Element.cpp
rename to src/odil/Element.cpp
index ab58fd7..4ac0d7d 100644
--- a/src/dcmtkpp/Element.cpp
+++ b/src/odil/Element.cpp
@@ -1,17 +1,17 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 "dcmtkpp/Element.h"
+#include "odil/Element.h"
-#include "dcmtkpp/Value.h"
-#include "dcmtkpp/DataSet.h"
+#include "odil/Value.h"
+#include "odil/DataSet.h"
-namespace dcmtkpp
+namespace odil
{
Element
diff --git a/src/dcmtkpp/Element.h b/src/odil/Element.h
similarity index 83%
rename from src/dcmtkpp/Element.h
rename to src/odil/Element.h
index 9959c3b..25774da 100644
--- a/src/dcmtkpp/Element.h
+++ b/src/odil/Element.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -12,11 +12,11 @@
#include <cstddef>
#include <initializer_list>
-#include "dcmtkpp/Tag.h"
-#include "dcmtkpp/Value.h"
-#include "dcmtkpp/VR.h"
+#include "odil/Tag.h"
+#include "odil/Value.h"
+#include "odil/VR.h"
-namespace dcmtkpp
+namespace odil
{
/**
@@ -85,14 +85,14 @@ public:
/**
* @brief Return the integers contained in the element.
*
- * If the element does not contain integers, a dcmtkpp::Exception is raised.
+ * If the element does not contain integers, a odil::Exception is raised.
*/
Value::Integers const & as_int() const;
/**
* @brief Return the integers contained in the element.
*
- * If the element does not contain integers, a dcmtkpp::Exception is raised.
+ * If the element does not contain integers, a odil::Exception is raised.
*/
Value::Integers & as_int();
@@ -102,14 +102,14 @@ public:
/**
* @brief Return the reals contained in the element.
*
- * If the element does not contain reals, a dcmtkpp::Exception is raised.
+ * If the element does not contain reals, a odil::Exception is raised.
*/
Value::Reals const & as_real() const;
/**
* @brief Return the reals contained in the element.
*
- * If the element does not contain reals, a dcmtkpp::Exception is raised.
+ * If the element does not contain reals, a odil::Exception is raised.
*/
Value::Reals & as_real();
@@ -119,14 +119,14 @@ public:
/**
* @brief Return the strings contained in the element.
*
- * If the element does not contain strings, a dcmtkpp::Exception is raised.
+ * If the element does not contain strings, a odil::Exception is raised.
*/
Value::Strings const & as_string() const;
/**
* @brief Return the strings contained in the element.
*
- * If the element does not contain strings, a dcmtkpp::Exception is raised.
+ * If the element does not contain strings, a odil::Exception is raised.
*/
Value::Strings & as_string();
@@ -136,14 +136,14 @@ public:
/**
* @brief Return the data sets contained in the element.
*
- * If the element does not contain data sets, a dcmtkpp::Exception is raised.
+ * If the element does not contain data sets, a odil::Exception is raised.
*/
Value::DataSets const & as_data_set() const;
/**
* @brief Return the data sets contained in the element.
*
- * If the element does not contain data sets, a dcmtkpp::Exception is raised.
+ * If the element does not contain data sets, a odil::Exception is raised.
*/
Value::DataSets & as_data_set();
@@ -153,14 +153,14 @@ public:
/**
* @brief Return the binary data contained in the element.
*
- * If the element does not contain binary data, a dcmtkpp::Exception is raised.
+ * If the element does not contain binary data, a odil::Exception is raised.
*/
Value::Binary const & as_binary() const;
/**
* @brief Return the binary data contained in the element.
*
- * If the element does not contain binary data, a dcmtkpp::Exception is raised.
+ * If the element does not contain binary data, a odil::Exception is raised.
*/
Value::Binary & as_binary();
@@ -207,6 +207,6 @@ apply_visitor(TVisitor const & visitor, Element const & element);
}
-#include "dcmtkpp/Element.txx"
+#include "odil/Element.txx"
#endif // _9c3d8f32_0310_4e3a_b5d2_6d69f229a2cf
diff --git a/src/dcmtkpp/Element.txx b/src/odil/Element.txx
similarity index 91%
rename from src/dcmtkpp/Element.txx
rename to src/odil/Element.txx
index 2ec2f54..9580c00 100644
--- a/src/dcmtkpp/Element.txx
+++ b/src/odil/Element.txx
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -9,13 +9,13 @@
#ifndef _feb2071a_de26_4adb_9305_59d0dd64c970
#define _feb2071a_de26_4adb_9305_59d0dd64c970
-#include "dcmtkpp/Element.h"
+#include "odil/Element.h"
#include <vector>
-#include "dcmtkpp/Exception.h"
+#include "odil/Exception.h"
-namespace dcmtkpp
+namespace odil
{
template<typename TVisitor>
diff --git a/src/dcmtkpp/ElementsDictionary.cpp b/src/odil/ElementsDictionary.cpp
similarity index 84%
rename from src/dcmtkpp/ElementsDictionary.cpp
rename to src/odil/ElementsDictionary.cpp
index 6ee89c5..c570967 100644
--- a/src/dcmtkpp/ElementsDictionary.cpp
+++ b/src/odil/ElementsDictionary.cpp
@@ -1,16 +1,16 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 "dcmtkpp/ElementsDictionary.h"
+#include "odil/ElementsDictionary.h"
#include <string>
-namespace dcmtkpp
+namespace odil
{
ElementsDictionaryEntry
diff --git a/src/dcmtkpp/ElementsDictionary.h b/src/odil/ElementsDictionary.h
similarity index 90%
rename from src/dcmtkpp/ElementsDictionary.h
rename to src/odil/ElementsDictionary.h
index 0589f14..c2336df 100644
--- a/src/dcmtkpp/ElementsDictionary.h
+++ b/src/odil/ElementsDictionary.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -12,9 +12,9 @@
#include <map>
#include <string>
-#include <dcmtkpp/Tag.h>
+#include <odil/Tag.h>
-namespace dcmtkpp
+namespace odil
{
/**
diff --git a/src/dcmtkpp/UIDsDictionary.cpp b/src/odil/Exception.cpp
similarity index 55%
copy from src/dcmtkpp/UIDsDictionary.cpp
copy to src/odil/Exception.cpp
index 35f4559..ce8ae19 100644
--- a/src/dcmtkpp/UIDsDictionary.cpp
+++ b/src/odil/Exception.cpp
@@ -1,26 +1,37 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 "dcmtkpp/UIDsDictionary.h"
+#include "odil/Exception.h"
-#include <map>
+#include <stdexcept>
#include <string>
-namespace dcmtkpp
+namespace odil
{
-UIDsDictionaryEntry
-::UIDsDictionaryEntry(
- std::string const & name, std::string const & keyword,
- std::string const & type)
-: name(name), keyword(keyword), type(type)
+Exception
+::Exception(std::string const & message)
+: _message(message)
{
// Nothing else.
}
+Exception
+::~Exception() throw()
+{
+ // Nothing to do.
+}
+
+char const *
+Exception
+::what() const throw()
+{
+ return this->_message.c_str();
+}
+
}
diff --git a/src/odil/Exception.h b/src/odil/Exception.h
new file mode 100644
index 0000000..8c4b14e
--- /dev/null
+++ b/src/odil/Exception.h
@@ -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.
+ ************************************************************************/
+
+#ifndef _b9607695_cb3b_4188_8caa_bc8bb051ef28
+#define _b9607695_cb3b_4188_8caa_bc8bb051ef28
+
+#include <exception>
+#include <string>
+
+namespace odil
+{
+
+/// @brief Base class for odil exceptions.
+class Exception: public std::exception
+{
+public:
+ /// @brief Message string constructor.
+ Exception(std::string const & message="");
+
+ /// @brief Destructor.
+ virtual ~Exception() throw();
+
+ /// @brief Return the reason for the exception.
+ virtual const char* what() const throw();
+
+protected:
+ std::string _message;
+};
+
+}
+
+#endif // _b9607695_cb3b_4188_8caa_bc8bb051ef28
diff --git a/src/odil/FindSCP.cpp b/src/odil/FindSCP.cpp
new file mode 100644
index 0000000..bc7badb
--- /dev/null
+++ b/src/odil/FindSCP.cpp
@@ -0,0 +1,94 @@
+/*************************************************************************
+ * 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/FindSCP.h"
+
+#include <memory>
+
+#include "odil/Association.h"
+#include "odil/Exception.h"
+#include "odil/SCP.h"
+#include "odil/message/CFindRequest.h"
+#include "odil/message/CFindResponse.h"
+#include "odil/message/Response.h"
+
+namespace odil
+{
+
+FindSCP
+::FindSCP(Association & association)
+: SCP(association), _generator(nullptr)
+{
+ // Nothing else.
+}
+
+FindSCP
+::FindSCP(
+ Association & association,
+ std::shared_ptr<DataSetGenerator> const & generator)
+: SCP(association), _generator(nullptr)
+{
+ this->set_generator(generator);
+}
+
+FindSCP
+::~FindSCP()
+{
+ // Nothing to do.
+}
+
+SCP::DataSetGenerator const &
+FindSCP
+::get_generator() const
+{
+ return *this->_generator;
+}
+
+void
+FindSCP
+::set_generator(std::shared_ptr<DataSetGenerator> const & generator)
+{
+ this->_generator = generator;
+}
+
+void
+FindSCP
+::operator()(message::Message const & message)
+{
+ message::CFindRequest const request(message);
+
+ try
+ {
+ this->_generator->initialize(request);
+ while(!this->_generator->done())
+ {
+ auto const data_set = this->_generator->get();
+ message::CFindResponse const response(
+ request.get_message_id(), message::CFindResponse::Pending,
+ data_set);
+ this->_association.send_message(
+ response, request.get_affected_sop_class_uid());
+ this->_generator->next();
+ }
+ }
+ catch(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;
+ }
+
+ message::CFindResponse response(
+ request.get_message_id(), message::CFindResponse::Success);
+ this->_association.send_message(
+ response, request.get_affected_sop_class_uid());
+}
+
+}
diff --git a/src/odil/FindSCP.h b/src/odil/FindSCP.h
new file mode 100644
index 0000000..eb61994
--- /dev/null
+++ b/src/odil/FindSCP.h
@@ -0,0 +1,52 @@
+/*************************************************************************
+ * 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 _d54d5d3d_791c_43e5_b13a_397954053963
+#define _d54d5d3d_791c_43e5_b13a_397954053963
+
+#include <memory>
+
+#include "odil/Association.h"
+#include "odil/SCP.h"
+#include "odil/message/Message.h"
+
+namespace odil
+{
+
+/// @brief SCP for C-Find services.
+class FindSCP: public SCP
+{
+public:
+
+ /// @brief Constructor.
+ FindSCP(Association & association);
+
+ /// @brief Constructor.
+ FindSCP(
+ Association & association,
+ std::shared_ptr<DataSetGenerator> const & generator);
+
+ /// @brief Destructor.
+ virtual ~FindSCP();
+
+ /// @brief Return the generator.
+ DataSetGenerator const & get_generator() const;
+
+ /// @brief Set the generator.
+ void set_generator(std::shared_ptr<DataSetGenerator> const & generator);
+
+ /// @brief Process a C-Find request.
+ virtual void operator()(message::Message const & message);
+
+private:
+ std::shared_ptr<DataSetGenerator> _generator;
+};
+
+}
+
+#endif // _d54d5d3d_791c_43e5_b13a_397954053963
diff --git a/src/dcmtkpp/FindSCU.cpp b/src/odil/FindSCU.cpp
similarity index 72%
rename from src/dcmtkpp/FindSCU.cpp
rename to src/odil/FindSCU.cpp
index e5650f0..1c928ae 100644
--- a/src/dcmtkpp/FindSCU.cpp
+++ b/src/odil/FindSCU.cpp
@@ -1,27 +1,32 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 "dcmtkpp/FindSCU.h"
+#include "odil/FindSCU.h"
#include <functional>
#include <sstream>
#include <vector>
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/dimse.h>
+#include "odil/Association.h"
+#include "odil/DataSet.h"
+#include "odil/Exception.h"
+#include "odil/message/CFindRequest.h"
+#include "odil/message/CFindResponse.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/CFindRequest.h"
-#include "dcmtkpp/CFindResponse.h"
+namespace odil
+{
-namespace dcmtkpp
+FindSCU
+::FindSCU(Association & asociation)
+: SCU(asociation)
{
+ // Nothing else.
+}
FindSCU
::~FindSCU()
@@ -29,21 +34,22 @@ FindSCU
// Nothing to do
}
-void
+void
FindSCU
::find(DataSet const & query, Callback callback) const
{
- CFindRequest request(
- this->_association->get_association()->nextMsgID++,
- this->_affected_sop_class, Message::Priority::MEDIUM, query);
- this->_send(request, this->_affected_sop_class);
+ message::CFindRequest request(
+ this->_association.next_message_id(),
+ this->_affected_sop_class, message::Message::Priority::MEDIUM, query);
+ this->_association.send_message(request, this->_affected_sop_class);
// Receive the responses
bool done = false;
while(!done)
{
// FIXME: include progress callback
- auto response = this->_receive<CFindResponse>();
+ message::CFindResponse const response =
+ this->_association.receive_message();
if(response.get_message_id_being_responded_to() != request.get_message_id())
{
@@ -63,7 +69,7 @@ FindSCU
throw Exception(message.str());
}
- done = !DICOM_PENDING_STATUS(response.get_status());
+ done = !response.is_pending();
if(!done)
{
callback(response.get_data_set());
@@ -80,7 +86,7 @@ FindSCU
result.push_back(dataset);
};
this->find(query, callback);
-
+
return result;
}
diff --git a/src/dcmtkpp/FindSCU.h b/src/odil/FindSCU.h
similarity index 85%
rename from src/dcmtkpp/FindSCU.h
rename to src/odil/FindSCU.h
index f1fa84e..4cd30ea 100644
--- a/src/dcmtkpp/FindSCU.h
+++ b/src/odil/FindSCU.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -11,10 +11,11 @@
#include <vector>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/SCU.h"
+#include "odil/Association.h"
+#include "odil/DataSet.h"
+#include "odil/SCU.h"
-namespace dcmtkpp
+namespace odil
{
/// @brief SCU for C-FIND services.
@@ -23,6 +24,8 @@ class FindSCU: public SCU
public:
/// @brief Callback called when a response is received.
typedef std::function<void(DataSet const &)> Callback;
+
+ FindSCU(Association & association);
/// @brief Destructor.
virtual ~FindSCU();
diff --git a/src/odil/GetSCP.cpp b/src/odil/GetSCP.cpp
new file mode 100644
index 0000000..a72262c
--- /dev/null
+++ b/src/odil/GetSCP.cpp
@@ -0,0 +1,141 @@
+/*************************************************************************
+ * 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/GetSCP.h"
+
+#include <memory>
+
+#include "odil/Association.h"
+#include "odil/Exception.h"
+#include "odil/SCP.h"
+#include "odil/StoreSCU.h"
+#include "odil/message/CGetRequest.h"
+#include "odil/message/CGetResponse.h"
+#include "odil/message/Response.h"
+
+namespace odil
+{
+
+GetSCP
+::GetSCP(Association & association)
+: SCP(association), _generator(nullptr)
+{
+ // Nothing else.
+}
+
+GetSCP
+::GetSCP(
+ Association & association,
+ std::shared_ptr<DataSetGenerator> const & generator)
+: SCP(association), _generator(nullptr)
+{
+ this->set_generator(generator);
+}
+
+GetSCP
+::~GetSCP()
+{
+ // Nothing to do.
+}
+
+GetSCP::DataSetGenerator const &
+GetSCP
+::get_generator() const
+{
+ return *this->_generator;
+}
+
+void
+GetSCP
+::set_generator(std::shared_ptr<DataSetGenerator> const & generator)
+{
+ this->_generator = generator;
+}
+
+void
+GetSCP
+::operator()(message::Message const & message)
+{
+ message::CGetRequest const request(message);
+
+ StoreSCU store_scu(this->_association);
+
+ unsigned int remaining_sub_operations = 0;
+ unsigned int completed_sub_operations=0;
+ unsigned int failed_sub_operations=0;
+ unsigned int warning_sub_operations=0;
+
+ try
+ {
+ this->_generator->initialize(request);
+ remaining_sub_operations = this->_generator->count();
+
+ while(!this->_generator->done())
+ {
+ message::CGetResponse response(
+ request.get_message_id(), message::CGetResponse::Pending);
+ 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());
+
+ auto const data_set = this->_generator->get();
+ store_scu.set_affected_sop_class(data_set);
+ try
+ {
+ store_scu.store(data_set);
+
+ --remaining_sub_operations;
+ ++completed_sub_operations;
+ }
+ catch(Exception const &)
+ {
+ ++failed_sub_operations;
+ }
+
+ this->_generator->next();
+ }
+ }
+ catch(Exception const &)
+ {
+ 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;
+ }
+
+ 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);
+ this->_association.send_message(
+ response, request.get_affected_sop_class_uid());
+}
+
+}
diff --git a/src/odil/GetSCP.h b/src/odil/GetSCP.h
new file mode 100644
index 0000000..8702338
--- /dev/null
+++ b/src/odil/GetSCP.h
@@ -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 _2f0ad1fd_8779_4ab3_b7e8_6d37fdc0c018
+#define _2f0ad1fd_8779_4ab3_b7e8_6d37fdc0c018
+
+#include <memory>
+
+#include "odil/Association.h"
+#include "odil/SCP.h"
+#include "odil/message/Message.h"
+
+namespace odil
+{
+
+/// @brief SCP for C-Get services.
+class GetSCP: public SCP
+{
+public:
+
+ class DataSetGenerator: public SCP::DataSetGenerator
+ {
+ public:
+ virtual unsigned int count() const =0;
+ };
+
+ /// @brief Constructor.
+ GetSCP(Association & association);
+
+ /// @brief Constructor.
+ GetSCP(
+ Association & association,
+ std::shared_ptr<DataSetGenerator> const & generator);
+
+ /// @brief Destructor.
+ virtual ~GetSCP();
+
+ /// @brief Return the generator.
+ DataSetGenerator const & get_generator() const;
+
+ /// @brief Set the generator.
+ void set_generator(std::shared_ptr<DataSetGenerator> const & generator);
+
+ /// @brief Process a C-Get request.
+ virtual void operator()(message::Message const & message);
+
+private:
+ std::shared_ptr<DataSetGenerator> _generator;
+};
+
+}
+
+#endif // _2f0ad1fd_8779_4ab3_b7e8_6d37fdc0c018
+
diff --git a/src/odil/GetSCU.cpp b/src/odil/GetSCU.cpp
new file mode 100644
index 0000000..ca73ffe
--- /dev/null
+++ b/src/odil/GetSCU.cpp
@@ -0,0 +1,115 @@
+/*************************************************************************
+ * 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/GetSCU.h"
+
+#include <functional>
+#include <sstream>
+#include <vector>
+
+#include "odil/Association.h"
+#include "odil/DataSet.h"
+#include "odil/Exception.h"
+#include "odil/StoreSCP.h"
+#include "odil/message/CGetRequest.h"
+#include "odil/message/CGetResponse.h"
+#include "odil/message/CStoreRequest.h"
+
+namespace odil
+{
+
+GetSCU
+::GetSCU(Association & association)
+: SCU(association)
+{
+ // Nothing else
+}
+
+GetSCU
+::~GetSCU()
+{
+ // Nothing to do
+}
+
+void
+GetSCU
+::get(DataSet const & query, Callback callback) const
+{
+ // Send the request
+ message::CGetRequest request(
+ this->_association.next_message_id(),
+ this->_affected_sop_class, message::Message::Priority::MEDIUM, query);
+ this->_association.send_message(request, this->_affected_sop_class);
+
+ // Receive the responses
+ bool done = false;
+ while(!done)
+ {
+ message::Message const message = this->_association.receive_message();
+
+ if(message.get_command_field() == message::Message::Command::C_GET_RSP)
+ {
+ done = this->_handle_get_response(message::CGetResponse(message));
+ }
+ else if(message.get_command_field() == message::Message::Command::C_STORE_RQ)
+ {
+ try
+ {
+ this->_handle_store_request(
+ message::CStoreRequest(message), callback);
+ }
+ catch(...)
+ {
+ // FIXME: logging
+ done = true;
+ }
+ }
+ else
+ {
+ std::ostringstream exception_message;
+ exception_message << "DIMSE: Unexpected Response Command Field: 0x"
+ << std::hex << message.get_command_field();
+ throw Exception(exception_message.str());
+ }
+ }
+}
+
+std::vector<DataSet>
+GetSCU
+::get(DataSet const & query) const
+{
+ std::vector<DataSet> result;
+ auto callback = [&result](DataSet const & data_set) {
+ result.push_back(data_set);
+ };
+ this->get(query, callback);
+
+ return result;
+}
+
+bool
+GetSCU
+::_handle_get_response(message::CGetResponse const & response) const
+{
+ return !response.is_pending();
+}
+
+void
+GetSCU
+::_handle_store_request(
+ message::CStoreRequest const & request, Callback callback) const
+{
+ auto const store_callback = [&callback](message::CStoreRequest const & request) {
+ callback(request.get_data_set());
+ return message::Response::Success;
+ };
+ StoreSCP scp(this->_association, store_callback);
+ scp(request);
+}
+
+}
diff --git a/src/dcmtkpp/GetSCU.h b/src/odil/GetSCU.h
similarity index 64%
rename from src/dcmtkpp/GetSCU.h
rename to src/odil/GetSCU.h
index acc72bf..b44b914 100644
--- a/src/dcmtkpp/GetSCU.h
+++ b/src/odil/GetSCU.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -9,16 +9,16 @@
#ifndef _f82a15e2_fd13_44b5_af7d_c6983494c9c6
#define _f82a15e2_fd13_44b5_af7d_c6983494c9c6
-#include "SCU.h"
-
+#include <functional>
#include <vector>
-#include "dcmtkpp/CGetResponse.h"
-#include "dcmtkpp/CStoreRequest.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/StoreSCP.h"
+#include "odil/Association.h"
+#include "odil/DataSet.h"
+#include "odil/SCU.h"
+#include "odil/message/CGetResponse.h"
+#include "odil/message/CStoreRequest.h"
-namespace dcmtkpp
+namespace odil
{
/// @brief SCU for C-GET services.
@@ -26,8 +26,11 @@ class GetSCU: public SCU
{
public:
/// @brief Callback called when a response is received.
- typedef StoreSCP::Callback Callback;
-
+ typedef std::function<void(DataSet const &)> Callback;
+
+ /// @brief Constructor.
+ GetSCU(Association & association);
+
/// @brief Destructor.
virtual ~GetSCU();
@@ -40,8 +43,9 @@ public:
std::vector<DataSet> get(DataSet const & query) const;
private:
- bool _get_response(CGetResponse const & response) const;
- void _store_request(CStoreRequest const & request, Callback callback) const;
+ bool _handle_get_response(message::CGetResponse const & response) const;
+ void _handle_store_request(
+ message::CStoreRequest const & request, Callback callback) const;
};
}
diff --git a/src/odil/MoveSCP.cpp b/src/odil/MoveSCP.cpp
new file mode 100644
index 0000000..9b9b7bf
--- /dev/null
+++ b/src/odil/MoveSCP.cpp
@@ -0,0 +1,147 @@
+/*************************************************************************
+ * 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/MoveSCP.h"
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "odil/Association.h"
+#include "odil/Exception.h"
+#include "odil/SCP.h"
+#include "odil/StoreSCU.h"
+#include "odil/message/CMoveRequest.h"
+#include "odil/message/CMoveResponse.h"
+#include "odil/message/Response.h"
+
+namespace odil
+{
+
+MoveSCP
+::MoveSCP(Association & association)
+: SCP(association), _generator(nullptr)
+{
+ // Nothing else.
+}
+
+MoveSCP
+::MoveSCP(
+ Association & association,
+ std::shared_ptr<DataSetGenerator> const & generator)
+: SCP(association), _generator(nullptr)
+{
+ this->set_generator(generator);
+}
+
+MoveSCP
+::~MoveSCP()
+{
+ // Nothing to do.
+}
+
+MoveSCP::DataSetGenerator const &
+MoveSCP
+::get_generator() const
+{
+ return *this->_generator;
+}
+
+void
+MoveSCP
+::set_generator(std::shared_ptr<DataSetGenerator> const & generator)
+{
+ this->_generator = generator;
+}
+
+void
+MoveSCP
+::operator()(message::Message const & message)
+{
+ message::CMoveRequest const request(message);
+
+ auto move_association = this->_generator->get_association(request);
+ move_association.associate();
+ StoreSCU store_scu(move_association);
+
+ unsigned int remaining_sub_operations = 0;
+ unsigned int completed_sub_operations=0;
+ unsigned int failed_sub_operations=0;
+ unsigned int warning_sub_operations=0;
+
+ try
+ {
+ this->_generator->initialize(request);
+ remaining_sub_operations = this->_generator->count();
+
+ while(!this->_generator->done())
+ {
+ message::CMoveResponse response(
+ request.get_message_id(), message::CMoveResponse::Pending);
+ 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());
+
+ auto const data_set = this->_generator->get();
+ store_scu.set_affected_sop_class(data_set);
+ try
+ {
+ store_scu.store(data_set);
+
+ --remaining_sub_operations;
+ ++completed_sub_operations;
+ }
+ catch(Exception const &)
+ {
+ --remaining_sub_operations;
+ ++failed_sub_operations;
+ }
+
+ this->_generator->next();
+ }
+ }
+ catch(Exception const &)
+ {
+ 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;
+ }
+
+ 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);
+ this->_association.send_message(
+ response, request.get_affected_sop_class_uid());
+}
+
+}
diff --git a/src/odil/MoveSCP.h b/src/odil/MoveSCP.h
new file mode 100644
index 0000000..77a3e6f
--- /dev/null
+++ b/src/odil/MoveSCP.h
@@ -0,0 +1,65 @@
+/*************************************************************************
+ * 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 _7e899e10_2a21_45b8_a2d6_af1d13cbfd29
+#define _7e899e10_2a21_45b8_a2d6_af1d13cbfd29
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "odil/Association.h"
+#include "odil/SCP.h"
+#include "odil/message/CMoveRequest.h"
+#include "odil/message/Message.h"
+
+namespace odil
+{
+
+/// @brief SCP for C-Move services.
+class MoveSCP: public SCP
+{
+public:
+
+ class DataSetGenerator: public SCP::DataSetGenerator
+ {
+ public:
+ virtual unsigned int count() const =0;
+ virtual Association get_association(message::CMoveRequest const &) const =0;
+ };
+
+ /// @brief Constructor.
+ MoveSCP(Association & association);
+
+ /// @brief Constructor.
+ MoveSCP(
+ Association & association,
+ std::shared_ptr<DataSetGenerator> const & generator);
+
+ /// @brief Destructor.
+ virtual ~MoveSCP();
+
+ /// @brief Return the generator.
+ DataSetGenerator const & get_generator() const;
+
+ /// @brief Set the generator.
+ void set_generator(std::shared_ptr<DataSetGenerator> const & generator);
+
+ /// @brief Process a C-Get request.
+ virtual void operator()(message::Message const & message);
+
+private:
+ std::shared_ptr<DataSetGenerator> _generator;
+};
+
+}
+
+
+#endif // _7e899e10_2a21_45b8_a2d6_af1d13cbfd29
+
diff --git a/src/odil/MoveSCU.cpp b/src/odil/MoveSCU.cpp
new file mode 100644
index 0000000..ae24730
--- /dev/null
+++ b/src/odil/MoveSCU.cpp
@@ -0,0 +1,169 @@
+/*************************************************************************
+ * 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/MoveSCU.h"
+
+#include <chrono>
+#include <functional>
+#include <thread>
+#include <vector>
+
+#include "odil/Association.h"
+#include "odil/DataSet.h"
+#include "odil/Exception.h"
+#include "odil/StoreSCP.h"
+#include "odil/message/CMoveRequest.h"
+#include "odil/message/CMoveResponse.h"
+#include "odil/message/Message.h"
+
+namespace odil
+{
+
+MoveSCU
+::MoveSCU(Association & association)
+: SCU(association), _move_destination("")
+{
+ // Nothing else.
+}
+
+MoveSCU
+::~MoveSCU()
+{
+ // Nothing to do
+}
+
+std::string const &
+MoveSCU
+::get_move_destination() const
+{
+ return this->_move_destination;
+}
+
+void
+MoveSCU
+::set_move_destination(std::string const & move_destination)
+{
+ this->_move_destination = move_destination;
+}
+
+void
+MoveSCU
+::move(DataSet const & query, Callback callback) const
+{
+ // Send the request
+ message::CMoveRequest const request(
+ this->_association.next_message_id(),
+ this->_affected_sop_class, message::Message::Priority::MEDIUM,
+ this->_move_destination, query);
+ this->_association.send_message(request, this->_affected_sop_class);
+
+ // Receive the responses
+ Association store_association;
+ bool done = false;
+ while(!done)
+ {
+ // Use a small timeout to avoid blocking for a long time.
+ boost::posix_time::milliseconds const timeout(1000);
+ store_association.set_tcp_timeout(timeout);
+ store_association.set_message_timeout(timeout);
+
+ if(!store_association.is_associated())
+ {
+ try
+ {
+ store_association.receive_association(boost::asio::ip::tcp::v4(), 11113);
+ store_association.set_tcp_timeout(
+ this->_association.get_tcp_timeout());
+ store_association.set_message_timeout(
+ this->_association.get_message_timeout());
+ }
+ catch(Exception const & e)
+ {
+ // Ignore
+ }
+
+ if(store_association.is_associated())
+ {
+ done = true;
+ }
+ }
+ }
+
+ this->_dispatch(store_association, callback);
+}
+
+std::vector<DataSet>
+MoveSCU
+::move(DataSet const & query) const
+{
+ std::vector<DataSet> result;
+ auto callback = [&result](DataSet const & data_set) {
+ result.push_back(data_set);
+ };
+ this->move(query, callback);
+
+ return result;
+}
+
+void
+MoveSCU
+::_dispatch(Association & store_association, Callback callback) const
+{
+ bool store_done = false;
+ bool main_done = false;
+ while(!(store_done && main_done))
+ {
+ if(store_association.get_transport().get_socket()->available() > 0)
+ {
+ store_done =
+ this->_handle_store_association(store_association, callback);
+ }
+ if(this->_association.get_transport().get_socket()->available() > 0)
+ {
+ main_done = this->_handle_main_association();
+ }
+
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
+ }
+}
+
+bool
+MoveSCU
+::_handle_main_association() const
+{
+ message::CMoveResponse const response = this->_association.receive_message();
+ return !response.is_pending();
+}
+
+bool
+MoveSCU
+::_handle_store_association(Association & association, Callback callback) const
+{
+ bool result = false;
+ try
+ {
+ auto const store_callback = [&callback](message::CStoreRequest const & request) {
+ callback(request.get_data_set());
+ return message::Response::Success;
+ };
+ StoreSCP scp(association, store_callback);
+ scp.receive_and_process();
+ }
+ catch(odil::AssociationReleased const &)
+ {
+ result = true;
+ }
+ catch(odil::AssociationAborted const & e)
+ {
+ result = true;
+ }
+
+ return result;
+}
+
+}
diff --git a/src/dcmtkpp/MoveSCU.h b/src/odil/MoveSCU.h
similarity index 81%
rename from src/dcmtkpp/MoveSCU.h
rename to src/odil/MoveSCU.h
index 9189309..9d62d58 100644
--- a/src/dcmtkpp/MoveSCU.h
+++ b/src/odil/MoveSCU.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -9,14 +9,15 @@
#ifndef _5ff4d940_4db7_4d85_9d3a_230b944b31fe
#define _5ff4d940_4db7_4d85_9d3a_230b944b31fe
+#include <functional>
#include <string>
#include <vector>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/SCU.h"
-#include "dcmtkpp/StoreSCP.h"
+#include "odil/Association.h"
+#include "odil/DataSet.h"
+#include "odil/SCU.h"
-namespace dcmtkpp
+namespace odil
{
/// @brief SCU for C-MOVE services.
@@ -24,10 +25,10 @@ class MoveSCU: public SCU
{
public:
/// @brief Callback called when a response is received.
- typedef StoreSCP::Callback Callback;
+ typedef std::function<void(DataSet const &)> Callback;
/// @brief Constructor.
- MoveSCU();
+ MoveSCU(Association & association);
/// @brief Destructor.
virtual ~MoveSCU();
@@ -48,7 +49,7 @@ public:
private:
std::string _move_destination;
- bool _dispatch(Association & association, Callback callback) const;
+ void _dispatch(Association & store_association, Callback callback) const;
bool _handle_main_association() const;
bool _handle_store_association(Association & association, Callback callback) const;
diff --git a/src/dcmtkpp/Reader.cpp b/src/odil/Reader.cpp
similarity index 87%
rename from src/dcmtkpp/Reader.cpp
rename to src/odil/Reader.cpp
index 836c928..9e69061 100644
--- a/src/dcmtkpp/Reader.cpp
+++ b/src/odil/Reader.cpp
@@ -1,32 +1,32 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 "dcmtkpp/Reader.h"
+#include "odil/Reader.h"
#include <algorithm>
#include <cstdint>
-#include <endian.h>
#include <functional>
#include <istream>
#include <sstream>
#include <string>
#include <utility>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Element.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Tag.h"
-#include "dcmtkpp/Value.h"
-#include "dcmtkpp/VR.h"
-#include "dcmtkpp/VRFinder.h"
-
-#define dcmtkpp_read_binary(type, value, stream, byte_ordering, size) \
+#include "odil/DataSet.h"
+#include "odil/Element.h"
+#include "odil/endian.h"
+#include "odil/Exception.h"
+#include "odil/registry.h"
+#include "odil/Tag.h"
+#include "odil/Value.h"
+#include "odil/VR.h"
+#include "odil/VRFinder.h"
+
+#define odil_read_binary(type, value, stream, byte_ordering, size) \
type value; \
{ \
uint##size##_t raw; \
@@ -35,13 +35,13 @@ type value; \
{ \
throw Exception("Could not read from stream"); \
} \
- if(byte_ordering == LITTLE_ENDIAN) \
+ if(byte_ordering == ByteOrdering::LittleEndian) \
{ \
- raw = le##size##toh(raw); \
+ raw = little_endian_to_host(raw); \
} \
- else if(byte_ordering == BIG_ENDIAN) \
+ else if(byte_ordering == ByteOrdering::BigEndian) \
{ \
- raw = be##size##toh(raw); \
+ raw = big_endian_to_host(raw); \
} \
else \
{ \
@@ -50,18 +50,25 @@ type value; \
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)
{
std::string value(size, '\0');
stream.read(&value[0], value.size());
if(!stream)
{
- throw dcmtkpp::Exception("Cannot read string");
+ throw odil::Exception("Cannot read string");
}
return value;
}
-namespace dcmtkpp
+namespace odil
{
Reader
@@ -71,7 +78,7 @@ Reader
: stream(stream), transfer_syntax(transfer_syntax),
byte_ordering(
(transfer_syntax==registry::ExplicitVRBigEndian_Retired)?
- BIG_ENDIAN:LITTLE_ENDIAN),
+ ByteOrdering::BigEndian:ByteOrdering::LittleEndian),
explicit_vr(transfer_syntax!=registry::ImplicitVRLittleEndian),
keep_group_length(keep_group_length)
{
@@ -115,8 +122,8 @@ Tag
Reader
::read_tag() const
{
- dcmtkpp_read_binary(uint16_t, group, this->stream, this->byte_ordering, 16);
- dcmtkpp_read_binary(uint16_t, element, this->stream, this->byte_ordering, 16);
+ odil_read_binary(uint16_t, group, this->stream, this->byte_ordering, 16);
+ odil_read_binary(uint16_t, element, this->stream, this->byte_ordering, 16);
return Tag(group, element);
}
@@ -223,7 +230,7 @@ Reader
Reader::Visitor
::Visitor(
std::istream & stream, VR vr, std::string const & transfer_syntax,
- int byte_ordering, bool explicit_vr, bool keep_group_length)
+ ByteOrdering byte_ordering, bool explicit_vr, bool keep_group_length)
: stream(stream), vr(vr), transfer_syntax(transfer_syntax),
byte_ordering(byte_ordering), explicit_vr(explicit_vr),
keep_group_length(keep_group_length)
@@ -271,25 +278,25 @@ Reader::Visitor
{
if(this->vr == VR::SL)
{
- dcmtkpp_read_binary(
+ odil_read_binary(
int32_t, item, this->stream, this->byte_ordering, 32);
value[i] = item;
}
else if(this->vr == VR::SS)
{
- dcmtkpp_read_binary(
+ odil_read_binary(
int16_t, item, this->stream, this->byte_ordering, 16);
value[i] = item;
}
else if(this->vr == VR::UL)
{
- dcmtkpp_read_binary(
+ odil_read_binary(
uint32_t, item, this->stream, this->byte_ordering, 32);
value[i] = item;
}
else if(this->vr == VR::AT || this->vr == VR::US)
{
- dcmtkpp_read_binary(
+ odil_read_binary(
uint16_t, item, this->stream, this->byte_ordering, 16);
value[i] = item;
}
@@ -336,13 +343,13 @@ Reader::Visitor
{
if(this->vr == VR::FD)
{
- dcmtkpp_read_binary(
+ odil_read_binary(
double, item, this->stream, this->byte_ordering, 64);
value[i] = item;
}
else if(this->vr == VR::FL)
{
- dcmtkpp_read_binary(
+ odil_read_binary(
float, item, this->stream, this->byte_ordering, 32);
value[i] = item;
}
@@ -381,6 +388,18 @@ Reader::Visitor
{
value = this->split_strings(string);
}
+
+ // Remove padding
+ static std::string const padding={ '\0', ' ' };
+
+ for(auto & item: value)
+ {
+ auto const last_char = item.find_last_not_of(padding);
+ if(last_char != std::string::npos)
+ {
+ item = item.substr(0, last_char+1);
+ }
+ }
}
}
@@ -431,8 +450,7 @@ Reader::Visitor
else if(tag == registry::SequenceDelimitationItem)
{
done = true;
- dcmtkpp_read_binary(
- uint32_t, unused, this->stream, this->byte_ordering, 32);
+ odil_ignore(this->stream, 4);
}
else
{
@@ -463,7 +481,7 @@ Reader::Visitor
value.resize(vl);
for(unsigned int i=0; i<value.size(); i+=4)
{
- dcmtkpp_read_binary(
+ odil_read_binary(
float, item, this->stream, this->byte_ordering, 32);
*reinterpret_cast<float*>(&value[i]) = item;
}
@@ -478,11 +496,16 @@ Reader::Visitor
value.resize(vl);
for(unsigned int i=0; i<value.size(); i+=2)
{
- dcmtkpp_read_binary(
+ odil_read_binary(
uint16_t, item, this->stream, this->byte_ordering, 16);
*reinterpret_cast<uint16_t*>(&value[i]) = item;
}
}
+ else if(this->vr == VR::UN)
+ {
+ value.resize(vl);
+ this->stream.read(reinterpret_cast<char*>(&value[0]), value.size());
+ }
else
{
throw Exception("Cannot read "+as_string(this->vr)+" as binary");
@@ -499,22 +522,21 @@ Reader::Visitor
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)
{
- dcmtkpp_read_binary(
- uint16_t, reserved, this->stream, this->byte_ordering, 16);
- dcmtkpp_read_binary(
+ odil_ignore(this->stream, 2);
+ odil_read_binary(
uint32_t, vl, this->stream, this->byte_ordering, 32);
length = vl;
}
else
{
- dcmtkpp_read_binary(
+ odil_read_binary(
uint16_t, vl, this->stream, this->byte_ordering, 16);
length = vl;
}
}
else
{
- dcmtkpp_read_binary(
+ odil_read_binary(
uint32_t, vl, this->stream, this->byte_ordering, 32);
length = vl;
}
@@ -544,18 +566,6 @@ Reader::Visitor
begin = (end==std::string::npos)?string.size():(end+1);
}
- if(!value.empty())
- {
- // Remove padding
- static std::string const padding={ '\0', ' ' };
- auto & last_item = value[value.size()-1];
- auto const last_char = last_item.find_last_not_of(padding);
- if(last_char != std::string::npos)
- {
- last_item = last_item.substr(0, last_char+1);
- }
- }
-
return value;
}
@@ -563,7 +573,7 @@ DataSet
Reader::Visitor
::read_item(std::istream & specific_stream) const
{
- dcmtkpp_read_binary(
+ odil_read_binary(
uint32_t, item_length, specific_stream, this->byte_ordering, 32);
DataSet item;
@@ -589,8 +599,7 @@ Reader::Visitor
{
throw Exception("Unexpected tag: "+std::string(tag));
}
- dcmtkpp_read_binary(
- uint32_t, unused, specific_stream, this->byte_ordering, 32);
+ odil_ignore(specific_stream, 4);
}
return item;
@@ -598,4 +607,5 @@ Reader::Visitor
}
-#undef dcmtkpp_read_binary
+#undef odil_ignore
+#undef odil_read_binary
diff --git a/src/dcmtkpp/Reader.h b/src/odil/Reader.h
similarity index 87%
rename from src/dcmtkpp/Reader.h
rename to src/odil/Reader.h
index 7a47275..aa3528b 100644
--- a/src/dcmtkpp/Reader.h
+++ b/src/odil/Reader.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -14,13 +14,14 @@
#include <string>
#include <utility>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Element.h"
-#include "dcmtkpp/Tag.h"
-#include "dcmtkpp/Value.h"
-#include "dcmtkpp/VR.h"
+#include "odil/DataSet.h"
+#include "odil/Element.h"
+#include "odil/endian.h"
+#include "odil/Tag.h"
+#include "odil/Value.h"
+#include "odil/VR.h"
-namespace dcmtkpp
+namespace odil
{
/// @brief Read DICOM objects from a stream.
@@ -33,8 +34,8 @@ public:
/// @brief Transfer syntax used to read the file.
std::string transfer_syntax;
- /// @brief Endianness (LITTLE_ENDIAN or BIG_ENDIAN).
- int byte_ordering;
+ /// @brief Endianness.
+ ByteOrdering byte_ordering;
/// @brief Explicit-ness of the Value Representations.
bool explicit_vr;
@@ -79,13 +80,13 @@ private:
VR vr;
std::string transfer_syntax;
- int byte_ordering;
+ ByteOrdering byte_ordering;
bool explicit_vr;
bool keep_group_length;
Visitor(
std::istream & stream, VR vr, std::string const & transfer_syntax,
- int byte_ordering, bool explicit_vr, bool keep_group_length);
+ ByteOrdering byte_ordering, bool explicit_vr, bool keep_group_length);
result_type operator()(Value::Integers & value) const;
result_type operator()(Value::Reals & value) const;
diff --git a/src/odil/SCP.cpp b/src/odil/SCP.cpp
new file mode 100644
index 0000000..6132188
--- /dev/null
+++ b/src/odil/SCP.cpp
@@ -0,0 +1,46 @@
+/*************************************************************************
+ * 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 "SCP.h"
+
+#include "odil/Association.h"
+#include "odil/DataSet.h"
+#include "odil/message/Message.h"
+#include "odil/message/Request.h"
+
+namespace odil
+{
+
+SCP::DataSetGenerator
+::~DataSetGenerator()
+{
+ // Nothing to do.
+}
+
+SCP
+::SCP(Association & association)
+: _association(association)
+{
+ // Nothing else.
+}
+
+SCP
+::~SCP()
+{
+ // Nothing to do.
+}
+
+void
+SCP
+::receive_and_process()
+{
+ auto const message = this->_association.receive_message();
+ (*this)(message);
+}
+
+}
diff --git a/src/odil/SCP.h b/src/odil/SCP.h
new file mode 100644
index 0000000..9f1b2f7
--- /dev/null
+++ b/src/odil/SCP.h
@@ -0,0 +1,55 @@
+/*************************************************************************
+ * 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 _f4680d8c_18a8_4317_956d_3ae238cb39cc
+#define _f4680d8c_18a8_4317_956d_3ae238cb39cc
+
+#include "odil/Association.h"
+#include "odil/DataSet.h"
+#include "odil/message/Message.h"
+#include "odil/message/Request.h"
+
+namespace odil
+{
+
+/// @brief Base class for all Service Class Providers.
+class SCP
+{
+public:
+ /// @brief Abstract base class for SCP returning multiple data sets.
+ class DataSetGenerator
+ {
+ public:
+ virtual ~DataSetGenerator() =0;
+
+ virtual void initialize(message::Request const & request) =0;
+
+ virtual bool done() const =0;
+ virtual void next() =0;
+
+ virtual DataSet get() const =0;
+ };
+
+ /// @brief Create a Service Class Provider.
+ SCP(Association & association);
+
+ /// @brief Destructor
+ virtual ~SCP();
+
+ /// @brief Receive and process a message.
+ void receive_and_process();
+
+ /// @brief Process a message.
+ virtual void operator()(message::Message const & message) =0;
+protected:
+ Association & _association;
+};
+
+}
+
+#endif // _f4680d8c_18a8_4317_956d_3ae238cb39cc
diff --git a/src/odil/SCPDispatcher.cpp b/src/odil/SCPDispatcher.cpp
new file mode 100644
index 0000000..6ddcbdc
--- /dev/null
+++ b/src/odil/SCPDispatcher.cpp
@@ -0,0 +1,78 @@
+/*************************************************************************
+ * 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/SCPDispatcher.h"
+
+#include <map>
+#include <memory>
+
+#include "odil/Association.h"
+#include "odil/Exception.h"
+#include "odil/SCP.h"
+#include "odil/Value.h"
+
+namespace odil
+{
+
+SCPDispatcher
+::SCPDispatcher(Association & association)
+: _association(association)
+{
+ // Nothing else.
+}
+
+SCPDispatcher
+::~SCPDispatcher()
+{
+ // Nothing to do.
+}
+
+bool
+SCPDispatcher
+::has_scp(Value::Integer command) const
+{
+ auto const it = this->_providers.find(command);
+ return (it != this->_providers.end());
+}
+
+std::shared_ptr<SCP> const &
+SCPDispatcher
+::get_scp(Value::Integer command) const
+{
+ auto const it = this->_providers.find(command);
+ if(it == this->_providers.end())
+ {
+ throw Exception("No such provider");
+ }
+ return it->second;
+}
+
+void
+SCPDispatcher
+::set_scp(Value::Integer command, std::shared_ptr<SCP> const & scp)
+{
+ this->_providers[command] = scp;
+}
+
+void
+SCPDispatcher
+::dispatch()
+{
+ auto const message = this->_association.receive_message();
+
+ auto const it = this->_providers.find(message.get_command_field());
+ if(it == this->_providers.end())
+ {
+ throw Exception("No such provider");
+ }
+
+ auto & scp = *(it->second);
+ scp(message);
+}
+
+}
diff --git a/src/odil/SCPDispatcher.h b/src/odil/SCPDispatcher.h
new file mode 100644
index 0000000..bc7e002
--- /dev/null
+++ b/src/odil/SCPDispatcher.h
@@ -0,0 +1,47 @@
+/*************************************************************************
+ * 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 _f0b69ac6_7d52_401d_a2f3_d5d3f7d69376
+#define _f0b69ac6_7d52_401d_a2f3_d5d3f7d69376
+
+#include <map>
+#include <memory>
+
+#include "odil/Association.h"
+#include "odil/SCP.h"
+#include "odil/Value.h"
+
+namespace odil
+{
+
+class SCPDispatcher
+{
+public:
+ /// @brief Create a dispatcher with network and association.
+ SCPDispatcher(Association & association);
+
+ /// @brief Destructor.
+ ~SCPDispatcher();
+
+ bool has_scp(Value::Integer command) const;
+
+ std::shared_ptr<SCP> const & get_scp(Value::Integer command) const;
+
+ void set_scp(Value::Integer command, std::shared_ptr<SCP> const & scp);
+
+ void dispatch();
+private:
+ typedef std::shared_ptr<SCP> SCPPointer;
+
+ Association & _association;
+ std::map<Value::Integer, std::shared_ptr<SCP>> _providers;
+};
+
+}
+
+#endif // _f0b69ac6_7d52_401d_a2f3_d5d3f7d69376
diff --git a/src/dcmtkpp/SCU.cpp b/src/odil/SCU.cpp
similarity index 58%
rename from src/dcmtkpp/SCU.cpp
rename to src/odil/SCU.cpp
index 9110c2e..0952b9d 100644
--- a/src/dcmtkpp/SCU.cpp
+++ b/src/odil/SCU.cpp
@@ -1,31 +1,30 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 "dcmtkpp/SCU.h"
+#include "odil/SCU.h"
#include <string>
-#include <unistd.h>
+#include "odil/Association.h"
+#include "odil/Exception.h"
+#include "odil/message/CEchoRequest.h"
+#include "odil/message/CEchoResponse.h"
+#include "odil/message/Message.h"
+#include "odil/registry.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/CEchoRequest.h"
-#include "dcmtkpp/CEchoResponse.h"
-#include "dcmtkpp/Message.h"
-#include "dcmtkpp/registry.h"
-
-namespace dcmtkpp
+namespace odil
{
SCU
-::SCU()
-: ServiceRole(), _affected_sop_class("")
+::SCU(Association & association)
+: _association(association), _affected_sop_class("")
{
- // Nothing else
+ // Nothing else.
}
SCU
@@ -52,12 +51,14 @@ void
SCU
::echo() const
{
- Uint16 const message_id = this->_association->get_association()->nextMsgID++;
+ auto const message_id = this->_association.next_message_id();
- CEchoRequest const request(message_id, registry::VerificationSOPClass);
- this->_send(request, request.get_affected_sop_class_uid());
+ message::CEchoRequest const request(
+ message_id, registry::VerificationSOPClass);
+ this->_association.send_message(
+ request, request.get_affected_sop_class_uid());
- CEchoResponse const response = this->_receive<CEchoResponse>();
+ message::CEchoResponse const response = this->_association.receive_message();
if(response.get_message_id_being_responded_to() != message_id)
{
std::ostringstream message;
diff --git a/src/dcmtkpp/SCU.h b/src/odil/SCU.h
similarity index 77%
rename from src/dcmtkpp/SCU.h
rename to src/odil/SCU.h
index efdd52c..1c51249 100644
--- a/src/dcmtkpp/SCU.h
+++ b/src/odil/SCU.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -11,17 +11,20 @@
#include <string>
-#include "dcmtkpp/ServiceRole.h"
+#include "odil/Association.h"
-namespace dcmtkpp
+namespace odil
{
/// @brief Base class for all Service Class Users.
-class SCU: public ServiceRole
+class SCU
{
public:
- SCU();
- ~SCU();
+ /// @brief Create a default Service Class User.
+ SCU(Association & association);
+
+ /// @brief Destructor.
+ virtual ~SCU();
/// @brief Return the affected SOP class. Defaults to "".
std::string const & get_affected_sop_class() const;
@@ -32,6 +35,7 @@ public:
void echo() const;
protected:
+ Association & _association;
/// @brief Affected SOP class.
std::string _affected_sop_class;
};
diff --git a/src/odil/StoreSCP.cpp b/src/odil/StoreSCP.cpp
new file mode 100644
index 0000000..2dd4b79
--- /dev/null
+++ b/src/odil/StoreSCP.cpp
@@ -0,0 +1,84 @@
+/*************************************************************************
+ * 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/StoreSCP.h"
+
+#include <functional>
+
+#include "odil/Association.h"
+#include "odil/Exception.h"
+#include "odil/SCP.h"
+#include "odil/Value.h"
+#include "odil/message/CStoreRequest.h"
+#include "odil/message/CStoreResponse.h"
+#include "odil/message/CStoreRequest.h"
+#include "odil/message/CStoreResponse.h"
+
+namespace odil
+{
+
+StoreSCP
+::StoreSCP(Association & association)
+: SCP(association), _callback()
+{
+ // Nothing else.
+}
+
+StoreSCP
+::StoreSCP(Association & association, Callback const & callback)
+: SCP(association), _callback()
+{
+ this->set_callback(callback);
+}
+
+StoreSCP
+::~StoreSCP()
+{
+ // Nothing to do.
+}
+
+StoreSCP::Callback const &
+StoreSCP
+::get_callback() const
+{
+ return this->_callback;
+}
+
+void
+StoreSCP
+::set_callback(Callback const & callback)
+{
+ this->_callback = callback;
+}
+
+void
+StoreSCP
+::operator()(message::Message const & message)
+{
+ message::CStoreRequest const request(message);
+
+ Value::Integer status=message::CStoreResponse::Success;
+
+ try
+ {
+ status = this->_callback(request);
+ }
+ catch(Exception const & exception)
+ {
+ status = message::CStoreResponse::ProcessingFailure;
+ // Error Comment
+ // Error ID
+ // Affected SOP Class UID
+ }
+
+ message::CStoreResponse const response(request.get_message_id(), status);
+ this->_association.send_message(
+ response, request.get_affected_sop_class_uid());
+}
+
+}
diff --git a/src/odil/StoreSCP.h b/src/odil/StoreSCP.h
new file mode 100644
index 0000000..f92b71f
--- /dev/null
+++ b/src/odil/StoreSCP.h
@@ -0,0 +1,54 @@
+/*************************************************************************
+ * 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 _fdbf3f51_91f5_464a_b449_c3f994297210
+#define _fdbf3f51_91f5_464a_b449_c3f994297210
+
+#include <functional>
+
+#include "odil/Association.h"
+#include "odil/SCP.h"
+#include "odil/Value.h"
+#include "odil/message/CStoreRequest.h"
+#include "odil/message/Message.h"
+
+namespace odil
+{
+
+/// @brief SCP for C-Store services.
+class StoreSCP: public SCP
+{
+public:
+ /// @brief Callback called when a request is received.
+ typedef std::function<Value::Integer(message::CStoreRequest const &)> Callback;
+
+ /// @brief Constructor.
+ StoreSCP(Association & association);
+
+ /// @brief Constructor.
+ StoreSCP(Association & association, Callback const & callback);
+
+ /// @brief Destructor.
+ virtual ~StoreSCP();
+
+ /// @brief Return the callback.
+ Callback const & get_callback() const;
+
+ /// @brief Set the callback.
+ void set_callback(Callback const & callback);
+
+ /// @brief Process a C-Store request.
+ virtual void operator()(message::Message const & message);
+
+private:
+ Callback _callback;
+};
+
+}
+
+#endif // _fdbf3f51_91f5_464a_b449_c3f994297210
diff --git a/src/dcmtkpp/StoreSCU.cpp b/src/odil/StoreSCU.cpp
similarity index 79%
rename from src/dcmtkpp/StoreSCU.cpp
rename to src/odil/StoreSCU.cpp
index b086831..33fa4bd 100644
--- a/src/dcmtkpp/StoreSCU.cpp
+++ b/src/odil/StoreSCU.cpp
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -13,15 +13,24 @@
#include <string>
#include <vector>
-#include "dcmtkpp/CStoreRequest.h"
-#include "dcmtkpp/CStoreResponse.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/registry.h"
+#include "odil/message/CStoreRequest.h"
+#include "odil/message/CStoreResponse.h"
+#include "odil/DataSet.h"
+#include "odil/Exception.h"
+#include "odil/registry.h"
+#include "odil/SCU.h"
-namespace dcmtkpp
+namespace odil
{
StoreSCU
+::StoreSCU(Association & association)
+: SCU(association)
+{
+ // Nothing else.
+}
+
+StoreSCU
::~StoreSCU()
{
// Nothing to do.
@@ -55,17 +64,17 @@ StoreSCU
void
StoreSCU
-::store(DataSet const & dataset, ProgressCallback callback, void * data) const
+::store(DataSet const & dataset) const
{
- CStoreRequest const request(
- this->_association->get_association()->nextMsgID++,
+ message::CStoreRequest const request(
+ this->_association.next_message_id(),
this->_affected_sop_class,
dataset.as_string(registry::SOPInstanceUID, 0),
- Message::Priority::MEDIUM,
+ message::Message::Priority::MEDIUM,
dataset);
- this->_send(request, this->_affected_sop_class, callback, data);
+ this->_association.send_message(request, this->_affected_sop_class);
- CStoreResponse const response = this->_receive<CStoreResponse>();
+ message::CStoreResponse const response = this->_association.receive_message();
if(response.get_message_id_being_responded_to() != request.get_message_id())
{
diff --git a/src/dcmtkpp/StoreSCU.h b/src/odil/StoreSCU.h
similarity index 72%
rename from src/dcmtkpp/StoreSCU.h
rename to src/odil/StoreSCU.h
index d805a73..d1f71bf 100644
--- a/src/dcmtkpp/StoreSCU.h
+++ b/src/odil/StoreSCU.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -9,26 +9,28 @@
#ifndef _1b2f876e_1ad2_464d_9423_28181320aed0
#define _1b2f876e_1ad2_464d_9423_28181320aed0
-#include "SCU.h"
+#include "odil/Association.h"
+#include "odil/DataSet.h"
+#include "odil/SCU.h"
-#include "dcmtkpp/DataSet.h"
-
-namespace dcmtkpp
+namespace odil
{
/// @brief SCU for C-Store services.
class StoreSCU: public SCU
{
public:
+ /// @brief Constructor.
+ StoreSCU(Association & association);
+
/// @brief Destructor.
virtual ~StoreSCU();
/// @brief Set the affected SOP class based on the dataset.
void set_affected_sop_class(DataSet const & dataset);
- /// @brief Perform the C-STORE using an optional callback.
- void store(DataSet const & dataset,
- ProgressCallback callback=NULL, void * data=NULL) const;
+ /// @brief Perform the C-STORE.
+ void store(DataSet const & dataset) const;
};
}
diff --git a/src/dcmtkpp/Tag.cpp b/src/odil/Tag.cpp
similarity index 96%
rename from src/dcmtkpp/Tag.cpp
rename to src/odil/Tag.cpp
index 8211fb5..e6db3da 100644
--- a/src/dcmtkpp/Tag.cpp
+++ b/src/odil/Tag.cpp
@@ -1,22 +1,22 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 "dcmtkpp/Tag.h"
+#include "odil/Tag.h"
#include <iomanip>
#include <ostream>
#include <sstream>
#include <string>
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/registry.h"
+#include "odil/Exception.h"
+#include "odil/registry.h"
-namespace dcmtkpp
+namespace odil
{
Tag
diff --git a/src/dcmtkpp/Tag.h b/src/odil/Tag.h
similarity index 89%
rename from src/dcmtkpp/Tag.h
rename to src/odil/Tag.h
index 12ec1d6..43bc170 100644
--- a/src/dcmtkpp/Tag.h
+++ b/src/odil/Tag.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -13,7 +13,7 @@
#include <ostream>
#include <string>
-namespace dcmtkpp
+namespace odil
{
/**
@@ -33,7 +33,7 @@ public:
* numeric value.
*
* If the name cannot be found in the public data dictionary, or if the
- * string is not the representation of a numeric value, a dcmtkpp::Exception
+ * string is not the representation of a numeric value, a odil::Exception
* is raised.
*/
Tag(std::string const & name);
@@ -43,7 +43,7 @@ public:
* numeric value.
*
* If the name cannot be found in the public data dictionary, or if the
- * string is not the representation of a numeric value, a dcmtkpp::Exception
+ * string is not the representation of a numeric value, a odil::Exception
* is raised.
*/
Tag(char const * name);
@@ -60,7 +60,7 @@ public:
* @brief Return the name of the tag.
*
* If the tag cannot be found in the public data dictionary,
- * a dcmtkpp::Exception is raised.
+ * a odil::Exception is raised.
*/
std::string get_name() const;
diff --git a/src/dcmtkpp/UIDsDictionary.cpp b/src/odil/UIDsDictionary.cpp
similarity index 85%
copy from src/dcmtkpp/UIDsDictionary.cpp
copy to src/odil/UIDsDictionary.cpp
index 35f4559..0ea0fbd 100644
--- a/src/dcmtkpp/UIDsDictionary.cpp
+++ b/src/odil/UIDsDictionary.cpp
@@ -1,17 +1,17 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 "dcmtkpp/UIDsDictionary.h"
+#include "odil/UIDsDictionary.h"
#include <map>
#include <string>
-namespace dcmtkpp
+namespace odil
{
UIDsDictionaryEntry
diff --git a/src/dcmtkpp/UIDsDictionary.h b/src/odil/UIDsDictionary.h
similarity index 92%
rename from src/dcmtkpp/UIDsDictionary.h
rename to src/odil/UIDsDictionary.h
index 2ffe188..a82d131 100644
--- a/src/dcmtkpp/UIDsDictionary.h
+++ b/src/odil/UIDsDictionary.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -12,7 +12,7 @@
#include <map>
#include <string>
-namespace dcmtkpp
+namespace odil
{
/**
diff --git a/src/dcmtkpp/VR.cpp b/src/odil/VR.cpp
similarity index 79%
rename from src/dcmtkpp/VR.cpp
rename to src/odil/VR.cpp
index 69bd5dd..73c5fef 100644
--- a/src/dcmtkpp/VR.cpp
+++ b/src/odil/VR.cpp
@@ -1,23 +1,23 @@
-#include <dcmtkpp/VR.h>
+#include <odil/VR.h>
#include <map>
#include <stdexcept>
#include <string>
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Tag.h"
+#include "odil/Exception.h"
+#include "odil/registry.h"
+#include "odil/Tag.h"
// Anonymous namespace, should not be publicly accessed
namespace
{
-#define ADD_TO_MAP(map, vr) map[dcmtkpp::VR::vr] = #vr
+#define ADD_TO_MAP(map, vr) map[odil::VR::vr] = #vr
-std::map<dcmtkpp::VR, std::string>
+std::map<odil::VR, std::string>
_build_enum_to_name()
{
- std::map<dcmtkpp::VR, std::string> result;
+ std::map<odil::VR, std::string> result;
ADD_TO_MAP(result, AE);
ADD_TO_MAP(result, AS);
ADD_TO_MAP(result, AT);
@@ -53,13 +53,13 @@ _build_enum_to_name()
#undef ADD_TO_MAP
-std::map<std::string, dcmtkpp::VR>
+std::map<std::string, odil::VR>
_build_name_to_enum()
{
- std::map<dcmtkpp::VR, std::string> const enum_to_name = _build_enum_to_name();
+ std::map<odil::VR, std::string> const enum_to_name = _build_enum_to_name();
- std::map<std::string, dcmtkpp::VR> result;
- for(std::map<dcmtkpp::VR, std::string>::const_iterator it = enum_to_name.begin();
+ std::map<std::string, odil::VR> result;
+ for(std::map<odil::VR, std::string>::const_iterator it = enum_to_name.begin();
it != enum_to_name.end(); ++it)
{
result[it->second] = it->first;
@@ -68,15 +68,15 @@ _build_name_to_enum()
return result;
}
-std::map<dcmtkpp::VR, std::string> const
+std::map<odil::VR, std::string> const
_enum_to_name = _build_enum_to_name();
-std::map<std::string, dcmtkpp::VR> const
+std::map<std::string, odil::VR> const
_name_to_enum = _build_name_to_enum();
}
-namespace dcmtkpp
+namespace odil
{
std::string as_string(VR vr)
@@ -91,7 +91,7 @@ std::string as_string(VR vr)
}
}
-VR as_vr(std::string const vr)
+VR as_vr(std::string const & vr)
{
try
{
diff --git a/src/dcmtkpp/VR.h b/src/odil/VR.h
similarity index 83%
rename from src/dcmtkpp/VR.h
rename to src/odil/VR.h
index f60b4b0..1f945e9 100644
--- a/src/dcmtkpp/VR.h
+++ b/src/odil/VR.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -11,7 +11,7 @@
#include <string>
-namespace dcmtkpp
+namespace odil
{
class Tag;
@@ -31,14 +31,14 @@ std::string as_string(VR vr);
/**
* @brief Convert a string to its VR.
*
- * If the string does not represent a VR, a dcmtkpp::Exception is raised.
+ * If the string does not represent a VR, a odil::Exception is raised.
*/
-VR as_vr(std::string const vr);
+VR as_vr(std::string const & vr);
/**
* @brief Guess a VR from a tag.
*
- * If the VR cannot be guessed, a dcmtkpp::Exception is raised.
+ * If the VR cannot be guessed, a odil::Exception is raised.
*/
VR as_vr(Tag const & tag);
diff --git a/src/dcmtkpp/VRFinder.cpp b/src/odil/VRFinder.cpp
similarity index 93%
rename from src/dcmtkpp/VRFinder.cpp
rename to src/odil/VRFinder.cpp
index 060a7fa..6401932 100644
--- a/src/dcmtkpp/VRFinder.cpp
+++ b/src/odil/VRFinder.cpp
@@ -1,24 +1,24 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 "dcmtkpp/VRFinder.h"
+#include "odil/VRFinder.h"
#include <functional>
#include <string>
#include <vector>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Tag.h"
-#include "dcmtkpp/VR.h"
+#include "odil/DataSet.h"
+#include "odil/Exception.h"
+#include "odil/registry.h"
+#include "odil/Tag.h"
+#include "odil/VR.h"
-namespace dcmtkpp
+namespace odil
{
std::vector<VRFinder::Finder> const
@@ -132,7 +132,7 @@ VRFinder
{
return VR::OW;
}
- else if((tag.group<<8) == 0x60 and tag.element == 0x3000)
+ else if((tag.group<<8) == 0x60 && tag.element == 0x3000)
{
return VR::OW;
}
diff --git a/src/dcmtkpp/VRFinder.h b/src/odil/VRFinder.h
similarity index 93%
rename from src/dcmtkpp/VRFinder.h
rename to src/odil/VRFinder.h
index ebaa3d7..4ed1ffa 100644
--- a/src/dcmtkpp/VRFinder.h
+++ b/src/odil/VRFinder.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -13,11 +13,11 @@
#include <string>
#include <vector>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Tag.h"
-#include "dcmtkpp/VR.h"
+#include "odil/DataSet.h"
+#include "odil/Tag.h"
+#include "odil/VR.h"
-namespace dcmtkpp
+namespace odil
{
/// @brief Find the VR of elements in an implicit VR data set.
diff --git a/src/dcmtkpp/Value.cpp b/src/odil/Value.cpp
similarity index 95%
rename from src/dcmtkpp/Value.cpp
rename to src/odil/Value.cpp
index 725b219..af2f77b 100644
--- a/src/dcmtkpp/Value.cpp
+++ b/src/odil/Value.cpp
@@ -1,22 +1,22 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 "dcmtkpp/Value.h"
+#include "odil/Value.h"
#include <cstdint>
#include <initializer_list>
#include <string>
#include <vector>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Exception.h"
+#include "odil/DataSet.h"
+#include "odil/Exception.h"
-namespace dcmtkpp
+namespace odil
{
Value
diff --git a/src/dcmtkpp/Value.h b/src/odil/Value.h
similarity index 83%
rename from src/dcmtkpp/Value.h
rename to src/odil/Value.h
index 40964cc..231ad74 100644
--- a/src/dcmtkpp/Value.h
+++ b/src/odil/Value.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -14,7 +14,7 @@
#include <string>
#include <vector>
-namespace dcmtkpp
+namespace odil
{
class DataSet;
@@ -99,70 +99,70 @@ public:
/**
* @brief Return the integers contained in the value.
*
- * If the value does not contain integers, a dcmtkpp::Exception is raised.
+ * If the value does not contain integers, a odil::Exception is raised.
*/
Integers const & as_integers() const;
/**
* @brief Return the integers contained in the value.
*
- * If the value does not contain integers, a dcmtkpp::Exception is raised.
+ * If the value does not contain integers, a odil::Exception is raised.
*/
Integers & as_integers();
/**
* @brief Return the reals contained in the value.
*
- * If the value does not contain reals, a dcmtkpp::Exception is raised.
+ * If the value does not contain reals, a odil::Exception is raised.
*/
Reals const & as_reals() const;
/**
* @brief Return the reals contained in the value.
*
- * If the value does not contain reals, a dcmtkpp::Exception is raised.
+ * If the value does not contain reals, a odil::Exception is raised.
*/
Reals & as_reals();
/**
* @brief Return the strings contained in the value.
*
- * If the value does not contain strings, a dcmtkpp::Exception is raised.
+ * If the value does not contain strings, a odil::Exception is raised.
*/
Strings const & as_strings() const;
/**
* @brief Return the strings contained in the value.
*
- * If the value does not contain strings, a dcmtkpp::Exception is raised.
+ * If the value does not contain strings, a odil::Exception is raised.
*/
Strings & as_strings();
/**
* @brief Return the data sets contained in the value.
*
- * If the value does not contain data sets, a dcmtkpp::Exception is raised.
+ * If the value does not contain data sets, a odil::Exception is raised.
*/
DataSets const & as_data_sets() const;
/**
* @brief Return the data sets contained in the value.
*
- * If the value does not contain data sets, a dcmtkpp::Exception is raised.
+ * If the value does not contain data sets, a odil::Exception is raised.
*/
DataSets & as_data_sets();
/**
* @brief Return the binary data contained in the value.
*
- * If the value does not contain binary data, a dcmtkpp::Exception is raised.
+ * If the value does not contain binary data, a odil::Exception is raised.
*/
Binary const & as_binary() const;
/**
* @brief Return the binary data contained in the value.
*
- * If the value does not contain binary data, a dcmtkpp::Exception is raised.
+ * If the value does not contain binary data, a odil::Exception is raised.
*/
Binary & as_binary();
@@ -198,6 +198,6 @@ apply_visitor(TVisitor const & visitor, Value & value);
}
-#include "dcmtkpp/Value.txx"
+#include "odil/Value.txx"
#endif // _dca5b15b_b8df_4925_a446_d42efe06c923
diff --git a/src/dcmtkpp/Value.txx b/src/odil/Value.txx
similarity index 94%
rename from src/dcmtkpp/Value.txx
rename to src/odil/Value.txx
index c1a7bcd..765676f 100644
--- a/src/dcmtkpp/Value.txx
+++ b/src/odil/Value.txx
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -9,11 +9,11 @@
#ifndef _de64b8e1_4116_41f8_a085_dabfdc6c63c3
#define _de64b8e1_4116_41f8_a085_dabfdc6c63c3
-#include "dcmtkpp/Value.h"
+#include "odil/Value.h"
-#include "dcmtkpp/Exception.h"
+#include "odil/Exception.h"
-namespace dcmtkpp
+namespace odil
{
template<typename TVisitor>
typename TVisitor::result_type
diff --git a/src/dcmtkpp/Writer.cpp b/src/odil/Writer.cpp
similarity index 82%
rename from src/dcmtkpp/Writer.cpp
rename to src/odil/Writer.cpp
index 9e70ad6..4998a20 100644
--- a/src/dcmtkpp/Writer.cpp
+++ b/src/odil/Writer.cpp
@@ -1,39 +1,38 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 "dcmtkpp/Writer.h"
+#include "odil/Writer.h"
#include <cstdint>
-#include <endian.h>
#include <map>
#include <ostream>
#include <sstream>
#include <string>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Element.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Tag.h"
-#include "dcmtkpp/uid.h"
-#include "dcmtkpp/VR.h"
+#include "odil/DataSet.h"
+#include "odil/endian.h"
+#include "odil/Element.h"
+#include "odil/Exception.h"
+#include "odil/registry.h"
+#include "odil/Tag.h"
+#include "odil/uid.h"
+#include "odil/VR.h"
-#define dcmtkpp_write_binary(value, stream, byte_ordering, size) \
+#define odil_write_binary(value, stream, byte_ordering, size) \
{ \
- auto const copy = value; \
- uint##size##_t raw = *reinterpret_cast<uint##size##_t const *>(©); \
- if(byte_ordering == LITTLE_ENDIAN) \
+ auto raw = value; \
+ if(byte_ordering == ByteOrdering::LittleEndian) \
{ \
- raw = htole##size(raw); \
+ raw = host_to_little_endian(raw); \
} \
- else if(byte_ordering == BIG_ENDIAN) \
+ else if(byte_ordering == ByteOrdering::BigEndian) \
{ \
- raw = htobe##size(raw); \
+ raw = host_to_big_endian(raw); \
} \
else \
{ \
@@ -46,13 +45,13 @@
} \
}
-namespace dcmtkpp
+namespace odil
{
Writer
::Writer(
std::ostream & stream,
- int byte_ordering, bool explicit_vr, ItemEncoding item_encoding,
+ ByteOrdering byte_ordering, bool explicit_vr, ItemEncoding item_encoding,
bool use_group_length)
: stream(stream), byte_ordering(byte_ordering), explicit_vr(explicit_vr),
item_encoding(item_encoding), use_group_length(use_group_length)
@@ -68,7 +67,7 @@ Writer
: stream(stream),
byte_ordering(
(transfer_syntax==registry::ExplicitVRBigEndian_Retired)?
- BIG_ENDIAN:LITTLE_ENDIAN),
+ ByteOrdering::BigEndian:ByteOrdering::LittleEndian),
explicit_vr(transfer_syntax!=registry::ImplicitVRLittleEndian),
item_encoding(item_encoding), use_group_length(use_group_length)
{
@@ -130,8 +129,8 @@ void
Writer
::write_tag(Tag const & tag) const
{
- dcmtkpp_write_binary(tag.group, this->stream, this->byte_ordering, 16);
- dcmtkpp_write_binary(tag.element, this->stream, this->byte_ordering, 16);
+ odil_write_binary(tag.group, this->stream, this->byte_ordering, 16);
+ odil_write_binary(tag.element, this->stream, this->byte_ordering, 16);
}
void
@@ -166,7 +165,7 @@ Writer
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)
{
- dcmtkpp_write_binary(uint16_t(0), this->stream, this->byte_ordering, 16);
+ odil_write_binary(uint16_t(0), this->stream, this->byte_ordering, 16);
uint32_t vl;
if(vr == VR::SQ &&
@@ -178,16 +177,16 @@ Writer
{
vl = value_stream.tellp();
}
- dcmtkpp_write_binary(vl, this->stream, this->byte_ordering, 32);
+ odil_write_binary(vl, this->stream, this->byte_ordering, 32);
}
else
{
- dcmtkpp_write_binary(uint16_t(value_stream.tellp()), this->stream, this->byte_ordering, 16);
+ odil_write_binary(uint16_t(value_stream.tellp()), this->stream, this->byte_ordering, 16);
}
}
else
{
- dcmtkpp_write_binary(uint32_t(value_stream.tellp()), this->stream, this->byte_ordering, 32);
+ odil_write_binary(uint32_t(value_stream.tellp()), this->stream, this->byte_ordering, 32);
}
this->stream.write(value_stream.str().c_str(), value_stream.tellp());
@@ -199,14 +198,14 @@ Writer
void
Writer
-::write_file(
- DataSet const &data_set , std::ostream & stream,
- std::string const & transfer_syntax, ItemEncoding item_encoding,
- bool use_group_length)
+::write_file(DataSet const &data_set , std::ostream & stream,
+ odil::DataSet const & meta_information,
+ std::string const & transfer_syntax, ItemEncoding item_encoding,
+ bool use_group_length)
{
// Build File Meta Information, PS3.10, 7.1
- DataSet meta_information;
- meta_information.add(
+ DataSet meta_info = meta_information;
+ meta_info.add(
registry::FileMetaInformationVersion, Value::Binary({0x00, 0x01}));
if(!data_set.has(registry::SOPClassUID))
@@ -222,7 +221,7 @@ Writer
throw Exception("Empty SOP Class UID");
}
- meta_information.add(
+ meta_info.add(
registry::MediaStorageSOPClassUID,
data_set.as_string(registry::SOPClassUID));
@@ -239,19 +238,19 @@ Writer
throw Exception("Empty SOP Instance UID");
}
- meta_information.add(registry::MediaStorageSOPInstanceUID,
+ meta_info.add(registry::MediaStorageSOPInstanceUID,
data_set.as_string(registry::SOPInstanceUID));
- meta_information.add(registry::TransferSyntaxUID, {transfer_syntax});
- meta_information.add(
+ meta_info.add(registry::TransferSyntaxUID, {transfer_syntax});
+ meta_info.add(
registry::ImplementationClassUID, {implementation_class_uid});
- meta_information.add(
- registry::ImplementationVersionName,
- { "DCMTK++ " DCMTKPP_STRINGIFY(DCMTKPP_MAJOR_VERSION) });
+ meta_info.add(
+ registry::ImplementationVersionName, { implementation_version_name });
- // SourceApplicationEntityTitle
- // SendingApplicationEntityTitle
- // ReceivingApplicationEntityTitle
+ // Information set by input attribut 'meta_information':
+ // - SourceApplicationEntityTitle
+ // - SendingApplicationEntityTitle
+ // - ReceivingApplicationEntityTitle
// File preamble
for(unsigned int i=0; i<128; ++i)
@@ -277,7 +276,7 @@ Writer
// Little Endian Transfer Syntax
Writer meta_information_writer(
stream, registry::ExplicitVRLittleEndian, item_encoding, use_group_length);
- meta_information_writer.write_data_set(meta_information);
+ meta_information_writer.write_data_set(meta_info);
// Data Set
Writer data_set_writer(
@@ -288,7 +287,7 @@ Writer
Writer::Visitor
::Visitor(
std::ostream & stream, VR vr,
- int byte_ordering, bool explicit_vr, Writer::ItemEncoding item_encoding,
+ ByteOrdering byte_ordering, bool explicit_vr, Writer::ItemEncoding item_encoding,
bool use_group_length)
: stream(stream), vr(vr), byte_ordering(byte_ordering), explicit_vr(explicit_vr),
item_encoding(item_encoding), use_group_length(use_group_length)
@@ -308,7 +307,7 @@ Writer::Visitor
{
for(auto item: value)
{
- dcmtkpp_write_binary(
+ odil_write_binary(
int32_t(item), this->stream, this->byte_ordering, 32);
}
}
@@ -316,7 +315,7 @@ Writer::Visitor
{
for(auto item: value)
{
- dcmtkpp_write_binary(
+ odil_write_binary(
int16_t(item), this->stream, this->byte_ordering, 16);
}
}
@@ -324,7 +323,7 @@ Writer::Visitor
{
for(auto item: value)
{
- dcmtkpp_write_binary(
+ odil_write_binary(
uint32_t(item), this->stream, this->byte_ordering, 32);
}
}
@@ -332,7 +331,7 @@ Writer::Visitor
{
for(auto item: value)
{
- dcmtkpp_write_binary(
+ odil_write_binary(
uint16_t(item), this->stream, this->byte_ordering, 16);
}
}
@@ -354,7 +353,7 @@ Writer::Visitor
{
for(auto const & item: value)
{
- dcmtkpp_write_binary(
+ odil_write_binary(
double(item), this->stream, this->byte_ordering, 64);
}
}
@@ -362,7 +361,7 @@ Writer::Visitor
{
for(auto const & item: value)
{
- dcmtkpp_write_binary(
+ odil_write_binary(
float(item), this->stream, this->byte_ordering, 32);
}
}
@@ -425,7 +424,7 @@ Writer::Visitor
{
item_length = 0xffffffff;
}
- dcmtkpp_write_binary(item_length, sequence_stream, this->byte_ordering, 32);
+ odil_write_binary(item_length, sequence_stream, this->byte_ordering, 32);
// Data set
sequence_stream.write(item_stream.str().c_str(), item_stream.tellp());
@@ -438,7 +437,7 @@ Writer::Visitor
if(this->item_encoding == ItemEncoding::UndefinedLength)
{
sequence_writer.write_tag(registry::ItemDelimitationItem);
- dcmtkpp_write_binary(uint32_t(0), sequence_stream, this->byte_ordering, 32);
+ odil_write_binary(uint32_t(0), sequence_stream, this->byte_ordering, 32);
}
}
@@ -446,7 +445,7 @@ Writer::Visitor
if(this->item_encoding == ItemEncoding::UndefinedLength)
{
sequence_writer.write_tag(registry::SequenceDelimitationItem);
- dcmtkpp_write_binary(uint32_t(0), sequence_stream, this->byte_ordering, 32);
+ odil_write_binary(uint32_t(0), sequence_stream, this->byte_ordering, 32);
}
this->stream.write(sequence_stream.str().c_str(), sequence_stream.tellp());
@@ -473,7 +472,7 @@ Writer::Visitor
for(int i=0; i<value.size(); i+=2)
{
uint16_t item = *reinterpret_cast<uint16_t const *>(&value[i]);
- dcmtkpp_write_binary(item, this->stream, this->byte_ordering, 16);
+ odil_write_binary(item, this->stream, this->byte_ordering, 16);
}
}
else if(this->vr == VR::OF)
@@ -485,7 +484,7 @@ Writer::Visitor
for(int i=0; i<value.size(); i+=4)
{
uint32_t item = *reinterpret_cast<uint32_t const *>(&value[i]);
- dcmtkpp_write_binary(item, this->stream, this->byte_ordering, 32);
+ odil_write_binary(item, this->stream, this->byte_ordering, 32);
}
}
else
@@ -544,4 +543,4 @@ Writer::Visitor
}
-#undef dcmtkpp_write_binary
+#undef odil_write_binary
diff --git a/src/dcmtkpp/Writer.h b/src/odil/Writer.h
similarity index 82%
rename from src/dcmtkpp/Writer.h
rename to src/odil/Writer.h
index 09fee4f..81a3c1e 100644
--- a/src/dcmtkpp/Writer.h
+++ b/src/odil/Writer.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -12,13 +12,15 @@
#include <ostream>
#include <string>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Element.h"
-#include "dcmtkpp/Tag.h"
-#include "dcmtkpp/Value.h"
-#include "dcmtkpp/VR.h"
+#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 dcmtkpp
+namespace odil
{
/// @brief Write DICOM objects to a stream.
@@ -35,8 +37,8 @@ public:
/// @brief Output stream.
std::ostream & stream;
- /// @brief Endianness (LITTLE_ENDIAN or BIG_ENDIAN).
- int byte_ordering;
+ /// @brief Endianness.
+ ByteOrdering byte_ordering;
/// @brief Explicit-ness of the Value Representations.
bool explicit_vr;
/// @brief Encoding of sequence items.
@@ -47,7 +49,7 @@ public:
/// @brief Build a writer.
Writer(
std::ostream & stream,
- int byte_ordering, bool explicit_vr,
+ ByteOrdering byte_ordering, bool explicit_vr,
ItemEncoding item_encoding=ItemEncoding::ExplicitLength,
bool use_group_length=false);
@@ -73,7 +75,8 @@ public:
/// @brief Write a file (meta-information and data set).
static void write_file(
DataSet const &data_set, std::ostream & stream,
- std::string const & transfer_syntax,
+ DataSet const & meta_information = DataSet(),
+ std::string const & transfer_syntax = registry::ExplicitVRLittleEndian,
ItemEncoding item_encoding=ItemEncoding::ExplicitLength,
bool use_group_length=false);
@@ -86,14 +89,14 @@ private:
std::ostream & stream;
VR vr;
- int byte_ordering;
+ ByteOrdering byte_ordering;
bool explicit_vr;
ItemEncoding item_encoding;
bool use_group_length;
Visitor(
std::ostream & stream, VR vr,
- int byte_ordering, bool explicit_vr, ItemEncoding item_encoding,
+ ByteOrdering byte_ordering, bool explicit_vr, ItemEncoding item_encoding,
bool use_group_length);
result_type operator()(Value::Integers const & value) const;
diff --git a/src/dcmtkpp/UIDsDictionary.cpp b/src/odil/asio.cpp
similarity index 50%
rename from src/dcmtkpp/UIDsDictionary.cpp
rename to src/odil/asio.cpp
index 35f4559..33401af 100644
--- a/src/dcmtkpp/UIDsDictionary.cpp
+++ b/src/odil/asio.cpp
@@ -1,26 +1,10 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 "dcmtkpp/UIDsDictionary.h"
-
-#include <map>
-#include <string>
-
-namespace dcmtkpp
-{
-
-UIDsDictionaryEntry
-::UIDsDictionaryEntry(
- std::string const & name, std::string const & keyword,
- std::string const & type)
-: name(name), keyword(keyword), type(type)
-{
- // Nothing else.
-}
-
-}
+// Build Boost.asio in Odil
+#include <boost/asio/impl/src.hpp>
diff --git a/src/odil/base64.cpp b/src/odil/base64.cpp
new file mode 100644
index 0000000..f431e45
--- /dev/null
+++ b/src/odil/base64.cpp
@@ -0,0 +1,36 @@
+/*************************************************************************
+ * 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/base64.h"
+
+#include <string>
+
+namespace odil
+{
+
+namespace base64
+{
+
+std::string const symbols =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+// Assume ASCII encoding
+std::string const reversed_symbols(
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" // Not used: 00-0F
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" // Not used: 10-1F
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" // Not used: 20-2F
+ "\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x00\x00\x00\x00\x00\x00" // 0-9
+ "\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e" // A-O
+ "\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x00\x00\x00\x00\x00" // P-Z
+ "\x00\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28" // a-o
+ "\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x00\x00\x00\x00\x00" // p-z
+ , 0x80);
+
+}
+
+}
diff --git a/src/odil/base64.h b/src/odil/base64.h
new file mode 100644
index 0000000..3619886
--- /dev/null
+++ b/src/odil/base64.h
@@ -0,0 +1,42 @@
+/*************************************************************************
+ * 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 _203e7be8_beaa_4d97_94b2_6a0070f158a1
+#define _203e7be8_beaa_4d97_94b2_6a0070f158a1
+
+#include <string>
+
+namespace odil
+{
+
+namespace base64
+{
+
+/// @brief Dictionary of symbols for Base64.
+extern std::string const symbols;
+
+/// @brief Mapping of ASCII characters to values of Base64 symbols.
+extern std::string const reversed_symbols;
+
+/// @brief Encode a sequence of 8 bits data to Base64.
+template<typename TInputIterator, typename TOutputIterator>
+void encode(
+ TInputIterator begin, TInputIterator end, TOutputIterator destination);
+
+/// @brief Decode a sequence of 8 bits data from Base64.
+template<typename TInputIterator, typename TOutputIterator>
+void decode(
+ TInputIterator begin, TInputIterator end, TOutputIterator destination);
+
+}
+
+}
+
+#include "odil/base64.txx"
+
+#endif // _203e7be8_beaa_4d97_94b2_6a0070f158a1
diff --git a/src/odil/base64.txx b/src/odil/base64.txx
new file mode 100644
index 0000000..652dc1b
--- /dev/null
+++ b/src/odil/base64.txx
@@ -0,0 +1,131 @@
+/*************************************************************************
+ * 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/base64.h"
+
+#include <iostream>
+#include <string>
+
+#ifndef _90eec83b_0ab6_4669_8454_e5a7e0a11a26
+#define _90eec83b_0ab6_4669_8454_e5a7e0a11a26
+
+namespace odil
+{
+
+namespace base64
+{
+
+template<typename TInputIterator, typename TOutputIterator>
+void encode(
+ TInputIterator begin, TInputIterator end, TOutputIterator destination)
+{
+ auto iterator = begin;
+ unsigned char carry=0;
+ unsigned int position=0;
+ while(iterator != end)
+ {
+ unsigned char const input = static_cast<unsigned char>(*iterator);
+
+ unsigned char symbol_index;
+ if(position%3 == 0)
+ {
+ symbol_index = input >> 2;
+ carry = input & 3;
+
+ *destination = symbols[symbol_index];
+ }
+ else if(position%3 == 1)
+ {
+ symbol_index = (carry << 4) + (input >> 4);
+ carry = input & 15;
+
+ *destination = symbols[symbol_index];
+ }
+ else // position%3 == 2
+ {
+ symbol_index = (carry << 2) + (input >> 6);
+ carry = input & 63;
+
+ *destination = symbols[symbol_index];
+ ++destination;
+ *destination = symbols[carry];
+ }
+
+ ++iterator;
+ ++destination;
+ ++position;
+ }
+
+ if(position%3 == 1)
+ {
+ *destination = symbols[carry<<4];
+ ++destination;
+ *destination = '=';
+ ++destination;
+ *destination = '=';
+ }
+ else if(position%3 == 2)
+ {
+ *destination = symbols[carry<<2];
+ ++destination;
+ *destination = '=';
+ }
+ // Otherwise (position%3 == 0), nothing to do.
+}
+
+template<typename TInputIterator, typename TOutputIterator>
+void decode(
+ TInputIterator begin, TInputIterator end, TOutputIterator destination)
+{
+ auto iterator = begin;
+ unsigned char current=0;
+ unsigned int position=0;
+
+ while(iterator != end)
+ {
+ unsigned char input = static_cast<unsigned char>(*iterator);
+ if(input != '=')
+ {
+ input = reversed_symbols[input];
+
+ if(position%4 == 0)
+ {
+ current = input;
+ }
+ else if(position%4 == 1)
+ {
+ current = (current << 2) + (input >> 4);
+ *destination = current;
+ ++destination;
+ current = input & 15;
+ }
+ else if(position%4 == 2)
+ {
+ current = (current << 4) + (input >> 2);
+ *destination = current;
+ ++destination;
+ current = input & 3;
+ }
+ else // position%4 == 3
+ {
+ current = (current << 6) + input;
+ *destination = current;
+ ++destination;
+ }
+ }
+
+ ++iterator;
+ ++position;
+ }
+}
+
+}
+
+}
+
+#endif // _90eec83b_0ab6_4669_8454_e5a7e0a11a26
diff --git a/src/dcmtkpp/ElementAccessor.cpp b/src/odil/dcmtk/ElementAccessor.cpp
similarity index 90%
rename from src/dcmtkpp/ElementAccessor.cpp
rename to src/odil/dcmtk/ElementAccessor.cpp
index fa9e2c5..ed345e6 100644
--- a/src/dcmtkpp/ElementAccessor.cpp
+++ b/src/odil/dcmtk/ElementAccessor.cpp
@@ -1,17 +1,20 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 "dcmtkpp/ElementAccessor.h"
+#include "odil/dcmtk/ElementAccessor.h"
#include <string>
#include <vector>
-namespace dcmtkpp
+namespace odil
+{
+
+namespace dcmtk
{
#define DEFINE_ELEMENT_ACCESSOR(TValueType, getter, setter) \
@@ -37,3 +40,5 @@ DEFINE_ELEMENT_ACCESSOR(Uint32, get_default, set_default)
#undef DEFINE_ELEMENT_ACCESSOR
}
+
+}
diff --git a/src/dcmtkpp/ElementAccessor.h b/src/odil/dcmtk/ElementAccessor.h
similarity index 89%
rename from src/dcmtkpp/ElementAccessor.h
rename to src/odil/dcmtk/ElementAccessor.h
index 784614c..cbaaf7f 100644
--- a/src/dcmtkpp/ElementAccessor.h
+++ b/src/odil/dcmtk/ElementAccessor.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -15,10 +15,13 @@
#include <dcmtk/dcmdata/dcelem.h>
#include <dcmtk/dcmdata/dcdatset.h>
-#include "dcmtkpp/ElementTraits.h"
-#include "dcmtkpp/Exception.h"
+#include "odil/dcmtk/ElementTraits.h"
+#include "odil/dcmtk/Exception.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace dcmtk
{
/// @brief Generic access to values in DcmElement.
@@ -58,6 +61,8 @@ struct ElementAccessor
}
-#include "ElementAccessor.txx"
+}
+
+#include "odil/dcmtk/ElementAccessor.txx"
#endif // _dfa4858b_1c9d_4ce9_b220_a1c15d873602
diff --git a/src/dcmtkpp/ElementAccessor.txx b/src/odil/dcmtk/ElementAccessor.txx
similarity index 97%
rename from src/dcmtkpp/ElementAccessor.txx
rename to src/odil/dcmtk/ElementAccessor.txx
index 586280a..94ba22a 100644
--- a/src/dcmtkpp/ElementAccessor.txx
+++ b/src/odil/dcmtk/ElementAccessor.txx
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -9,7 +9,7 @@
#ifndef _f9af3e63_3597_4513_8c10_b55058f5370b
#define _f9af3e63_3597_4513_8c10_b55058f5370b
-#include "ElementAccessor.h"
+#include "odil/dcmtk/ElementAccessor.h"
#include <dcmtk/config/osconfig.h>
#include <dcmtk/dcmdata/dcdatset.h>
@@ -17,7 +17,10 @@
#include <dcmtk/dcmdata/dcerror.h>
#include <dcmtk/ofstd/ofstring.h>
-namespace dcmtkpp
+namespace odil
+{
+
+namespace dcmtk
{
template<typename TValueType>
@@ -204,4 +207,6 @@ set_binary(
}
+}
+
#endif // _f9af3e63_3597_4513_8c10_b55058f5370b
diff --git a/src/dcmtkpp/ElementTraits.cpp b/src/odil/dcmtk/ElementTraits.cpp
similarity index 93%
rename from src/dcmtkpp/ElementTraits.cpp
rename to src/odil/dcmtk/ElementTraits.cpp
index 37492fd..d81ef7b 100644
--- a/src/dcmtkpp/ElementTraits.cpp
+++ b/src/odil/dcmtk/ElementTraits.cpp
@@ -1,12 +1,12 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 "dcmtkpp/ElementTraits.h"
+#include "odil/dcmtk/ElementTraits.h"
#include <functional>
#include <string>
@@ -15,9 +15,12 @@
#include <dcmtk/dcmdata/dcelem.h>
#include <dcmtk/dcmdata/dcvr.h>
-#include "dcmtkpp/Exception.h"
+#include "odil/Exception.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace dcmtk
{
OFCondition
@@ -75,3 +78,5 @@ DEFINE_ELEMENT_TRAITS(Uint32, &DcmElement::getUint32, &DcmElement::putUint32)
#undef DEFINE_ELEMENT_TRAITS
}
+
+}
diff --git a/src/dcmtkpp/ElementTraits.h b/src/odil/dcmtk/ElementTraits.h
similarity index 93%
rename from src/dcmtkpp/ElementTraits.h
rename to src/odil/dcmtk/ElementTraits.h
index 60c4904..03ebb77 100644
--- a/src/dcmtkpp/ElementTraits.h
+++ b/src/odil/dcmtk/ElementTraits.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -16,11 +16,14 @@
#include <dcmtk/dcmdata/dcelem.h>
#include <dcmtk/dcmdata/dcvr.h>
-namespace dcmtkpp
+namespace odil
+{
+
+namespace dcmtk
{
/**
- * @class dcmtkpp::ElementTraits
+ * @class odil::ElementTraits
* @brief Traits for generic data access to values of DcmElement.
*
* The members are:
@@ -44,4 +47,6 @@ struct ElementTraits
}
+}
+
#endif // _3ae28d18_6f01_4e10_98e2_1c0d21fdcab7
diff --git a/src/dcmtkpp/Exception.cpp b/src/odil/dcmtk/Exception.cpp
similarity index 72%
rename from src/dcmtkpp/Exception.cpp
rename to src/odil/dcmtk/Exception.cpp
index cfe9123..d918f27 100644
--- a/src/dcmtkpp/Exception.cpp
+++ b/src/odil/dcmtk/Exception.cpp
@@ -1,42 +1,47 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 "dcmtkpp/Exception.h"
+#include "odil/dcmtk/Exception.h"
-#include <string>
+#include <stdexcept>
#include <dcmtk/config/osconfig.h>
#include <dcmtk/ofstd/ofcond.h>
-namespace dcmtkpp
+#include "odil/Exception.h"
+
+namespace odil
+{
+
+namespace dcmtk
{
Exception
::Exception(std::string const & message)
-: _source(Source::Message), _message(message), _condition()
+: ::odil::Exception(message), _source(Source::Message), _condition()
{
// Nothing else.
}
Exception
::Exception(OFCondition const & condition)
-: _source(Source::Condition), _message(), _condition(condition)
+: ::odil::Exception(), _source(Source::Condition), _condition(condition)
{
// Nothing else.
}
Exception
-::~Exception() throw()
+::~Exception() noexcept
{
// Nothing to do.
}
-char const *
+char const *
Exception
::what() const throw()
{
@@ -48,6 +53,10 @@ Exception
{
return this->_condition.text();
}
+ else
+ {
+ throw std::runtime_error("Invalid source");
+ }
}
Exception::Source
@@ -69,3 +78,5 @@ Exception
}
}
+
+}
diff --git a/src/dcmtkpp/Exception.h b/src/odil/dcmtk/Exception.h
similarity index 73%
rename from src/dcmtkpp/Exception.h
rename to src/odil/dcmtk/Exception.h
index a4c9bc0..38a6b30 100644
--- a/src/dcmtkpp/Exception.h
+++ b/src/odil/dcmtk/Exception.h
@@ -1,66 +1,68 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 _b9607695_cb3b_4188_8caa_bc8bb051ef28
-#define _b9607695_cb3b_4188_8caa_bc8bb051ef28
-
-#include <exception>
-#include <string>
+#ifndef _9e106372_aca6_4551_a591_95695eb00e4c
+#define _9e106372_aca6_4551_a591_95695eb00e4c
#include <dcmtk/config/osconfig.h>
#include <dcmtk/ofstd/ofcond.h>
-namespace dcmtkpp
+#include "odil/Exception.h"
+
+namespace odil
+{
+
+namespace dcmtk
{
-/// @brief Base class for dcmtkpp exceptions.
-class Exception: public std::exception
+class Exception: public ::odil::Exception
{
-public:
- /**
- * @brief Source of the Exception: either a message string or an
+public:
+ /**
+ * @brief Source of the Exception: either a message string or an
* OFCondition.
*/
enum class Source { Message, Condition };
-
+
/// @brief Message string constructor, set the source to Source::Message.
Exception(std::string const & message);
-
+
/// @brief Condition constructor, set the source to Source::Condition.
Exception(OFCondition const & condition);
-
+
/// @brief Destructor.
- virtual ~Exception() throw();
-
- /**
- * @brief Return the reason for the exception.
- *
- * The reason for the exception is set to the message (for Source::Message) or
- * to the text of the condition (for Source::Condition).
- */
- virtual const char* what() const throw();
-
+ virtual ~Exception() noexcept;
+
/// @brief Return the exception source.
Source get_source() const;
-
+
/**
* @brief Return the condition that was used to create this exception.
- *
+ *
* If the source is not Source::Condition, throw an exception.
*/
OFCondition const & get_condition() const;
+ /**
+ * @brief Return the reason for the exception.
+ *
+ * The reason for the exception is set to the message (for Source::Message) or
+ * to the text of the condition (for Source::Condition).
+ */
+ virtual const char* what() const noexcept;
private:
Source _source;
- std::string _message;
OFCondition _condition;
};
}
-#endif // _b9607695_cb3b_4188_8caa_bc8bb051ef28
+}
+
+#endif // _9e106372_aca6_4551_a591_95695eb00e4c
+
diff --git a/src/dcmtkpp/VRTraits.h b/src/odil/dcmtk/VRTraits.h
similarity index 90%
rename from src/dcmtkpp/VRTraits.h
rename to src/odil/dcmtk/VRTraits.h
index feb3491..eb7b335 100644
--- a/src/dcmtkpp/VRTraits.h
+++ b/src/odil/dcmtk/VRTraits.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -9,11 +9,19 @@
#ifndef _4f556093_02e3_4659_9026_2b16bc2c8a51
#define _4f556093_02e3_4659_9026_2b16bc2c8a51
-namespace dcmtkpp
+#include <vector>
+
+#include <dcmtk/config/osconfig.h>
+#include <dcmtk/dcmdata/dcvr.h>
+
+namespace odil
+{
+
+namespace dcmtk
{
/**
- * @class dcmtkpp::VRTraits
+ * @class odil::VRTraits
* @brief Information related to VR.
*
* The members are:
@@ -63,4 +71,6 @@ DECLARE_VR_TRAITS(EVR_UT, std::string)
}
+}
+
#endif // _4f556093_02e3_4659_9026_2b16bc2c8a51
diff --git a/src/dcmtkpp/conversion.cpp b/src/odil/dcmtk/conversion.cpp
similarity index 98%
rename from src/dcmtkpp/conversion.cpp
rename to src/odil/dcmtk/conversion.cpp
index bd57e4e..ebb57a5 100644
--- a/src/dcmtkpp/conversion.cpp
+++ b/src/odil/dcmtk/conversion.cpp
@@ -1,23 +1,26 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 "dcmtkpp/conversion.h"
+#include "odil/dcmtk/conversion.h"
#include <dcmtk/config/osconfig.h>
#include <dcmtk/dcmdata/dctk.h>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Element.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/Tag.h"
-#include "dcmtkpp/VR.h"
+#include "odil/DataSet.h"
+#include "odil/Element.h"
+#include "odil/Tag.h"
+#include "odil/VR.h"
+#include "odil/dcmtk/Exception.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace dcmtk
{
DcmEVR convert(VR vr)
@@ -331,7 +334,7 @@ void convert<std::vector<uint8_t>, Value::Binary>(
{
auto & destination_values = (destination.*getter)();
destination_values =
- dcmtkpp::ElementAccessor<std::vector<uint8_t>>::element_get(*source, 0);
+ ElementAccessor<std::vector<uint8_t>>::element_get(*source, 0);
}
Element convert(DcmElement * source)
@@ -519,3 +522,5 @@ DataSet convert(DcmItem * source)
}
}
+
+}
diff --git a/src/dcmtkpp/conversion.h b/src/odil/dcmtk/conversion.h
similarity index 73%
rename from src/dcmtkpp/conversion.h
rename to src/odil/dcmtk/conversion.h
index e10e485..fddc6fc 100644
--- a/src/dcmtkpp/conversion.h
+++ b/src/odil/dcmtk/conversion.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -12,27 +12,30 @@
#include <dcmtk/config/osconfig.h>
#include <dcmtk/dcmdata/dctk.h>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Element.h"
-#include "dcmtkpp/Tag.h"
-#include "dcmtkpp/VR.h"
+#include "odil/DataSet.h"
+#include "odil/Element.h"
+#include "odil/Tag.h"
+#include "odil/VR.h"
-namespace dcmtkpp
+namespace odil
{
-/// @brief Convert a dcmtkpp::VR to a DcmVR.
+namespace dcmtk
+{
+
+/// @brief Convert a odil::VR to a DcmVR.
DcmEVR convert(VR vr);
-/// @brief Convert a DcmVR to a dcmtkpp::VR.
+/// @brief Convert a DcmVR to a odil::VR.
VR convert(DcmEVR evr);
-/// @brief Convert a dcmtkpp::Tag to a DcmTagKey.
+/// @brief Convert a odil::Tag to a DcmTagKey.
DcmTagKey convert(Tag const & tag);
-/// @brief Convert a DcmTagKey to a dcmtkpp::Tag.
+/// @brief Convert a DcmTagKey to a odil::Tag.
Tag convert(DcmTagKey const & tag);
-/// @brief Convert a dcmtkpp::Element to a DcmElement.
+/// @brief Convert a odil::Element to a DcmElement.
DcmElement * convert(Tag const & tag, Element const & source);
/// @brief Low-level element converter.
@@ -41,7 +44,7 @@ void convert(
Element const & source, DcmElement * destination,
TSourceType const & (Element::*getter)() const);
-/// @brief Convert a DcmElement to a dcmtkpp::Element.
+/// @brief Convert a DcmElement to a odil::Element.
Element convert(DcmElement * source);
/// @brief Low-level element converter.
@@ -62,14 +65,16 @@ void convert(
DcmElement * source, Element & destination,
TDestinationType & (Element::*getter)());
-/// @brief Convert a dcmtkpp::DataSet to a DcmDataset or a DcmItem.
+/// @brief Convert a odil::DataSet to a DcmDataset or a DcmItem.
DcmItem * convert(DataSet const & source, bool as_data_set=true);
-/// @brief Convert a DcmDataset to a dcmtkpp::DataSet.
+/// @brief Convert a DcmDataset to a odil::DataSet.
DataSet convert(DcmItem * source);
}
-#include <dcmtkpp/conversion.txx>
+}
+
+#include "odil/dcmtk/conversion.txx"
#endif // _d5ecacb8_04ff_48b0_8026_570c9b2ae360
diff --git a/src/dcmtkpp/conversion.txx b/src/odil/dcmtk/conversion.txx
similarity index 89%
rename from src/dcmtkpp/conversion.txx
rename to src/odil/dcmtk/conversion.txx
index 0c296eb..07ddb47 100644
--- a/src/dcmtkpp/conversion.txx
+++ b/src/odil/dcmtk/conversion.txx
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -9,14 +9,17 @@
#ifndef _665b5269_140c_4320_94f2_ad8a7c052e9b
#define _665b5269_140c_4320_94f2_ad8a7c052e9b
-#include "dcmtkpp/conversion.h"
+#include "odil/dcmtk/conversion.h"
#include <sstream>
-#include "dcmtkpp/Element.h"
-#include "dcmtkpp/ElementAccessor.h"
+#include "odil/Element.h"
+#include "odil/dcmtk/ElementAccessor.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace dcmtk
{
template<typename TSourceType, typename TDestinationType>
@@ -73,10 +76,12 @@ void convert(
for(auto i = 0; i<source->getVM(); ++i)
{
destination_values.push_back(
- dcmtkpp::ElementAccessor<TSourceType>::element_get(*source, i));
+ ElementAccessor<TSourceType>::element_get(*source, i));
}
}
}
+}
+
#endif // _665b5269_140c_4320_94f2_ad8a7c052e9b
diff --git a/src/odil/dul/EventData.h b/src/odil/dul/EventData.h
new file mode 100644
index 0000000..9402309
--- /dev/null
+++ b/src/odil/dul/EventData.h
@@ -0,0 +1,39 @@
+/*************************************************************************
+ * 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 _350775b8_701f_4069_ab1e_c974a209389c
+#define _350775b8_701f_4069_ab1e_c974a209389c
+
+#include <memory>
+
+#include "odil/AssociationAcceptor.h"
+#include "odil/AssociationParameters.h"
+#include "odil/dul/Transport.h"
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace dul
+{
+
+/// @brief Data related to events of the DUL state machine.
+class EventData
+{
+public:
+ Transport::Socket::endpoint_type peer_endpoint;
+ std::shared_ptr<pdu::Object> pdu;
+ AssociationParameters association_parameters;
+ std::shared_ptr<AssociationRejected> reject;
+};
+
+}
+
+}
+
+#endif // _350775b8_701f_4069_ab1e_c974a209389c
diff --git a/src/odil/dul/StateMachine.cpp b/src/odil/dul/StateMachine.cpp
new file mode 100644
index 0000000..6f6dc5a
--- /dev/null
+++ b/src/odil/dul/StateMachine.cpp
@@ -0,0 +1,764 @@
+/*************************************************************************
+ * 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/dul/StateMachine.h"
+
+#include <cstdint>
+#include <functional>
+#include <map>
+#include <sstream>
+#include <tuple>
+#include <utility>
+
+#include <boost/asio.hpp>
+#include <boost/system/system_error.hpp>
+
+#include "odil/AssociationParameters.h"
+#include "odil/endian.h"
+#include "odil/Exception.h"
+#include "odil/dul/EventData.h"
+#include "odil/dul/Transport.h"
+#include "odil/pdu/AAbort.h"
+#include "odil/pdu/AAssociate.h"
+#include "odil/pdu/AAssociateRJ.h"
+#include "odil/pdu/AReleaseRP.h"
+#include "odil/pdu/AReleaseRQ.h"
+#include "odil/pdu/PDataTF.h"
+
+namespace odil
+{
+
+namespace dul
+{
+
+StateMachine
+::StateMachine()
+: _state(State::Sta1), _timeout(boost::posix_time::pos_infin),
+ _artim_timer(_transport.get_service()),
+ _association_acceptor(default_association_acceptor)
+{
+ // Nothing else.
+}
+
+StateMachine
+::~StateMachine()
+{
+ // Nothing to to, transport is closed by ~Transport.
+}
+
+void
+StateMachine
+::transition(Event const & event, EventData & data)
+{
+ auto const guard_iterator = StateMachine::_guards.find(
+ {this->_state, event});
+ auto const guard_value =
+ (guard_iterator != StateMachine::_guards.end())?
+ guard_iterator->second(*this, data):true;
+
+ auto const transition_iterator = StateMachine::_transitions.find(
+ std::make_tuple(this->_state, event, guard_value));
+ if(transition_iterator == StateMachine::_transitions.end())
+ {
+ throw Exception("No such transition");
+ }
+
+ auto const & action = transition_iterator->second.first;
+ auto const & next_state = transition_iterator->second.second;
+
+ // Do action
+ if(action == Action::AE_1) { this->AE_1(data); }
+ else if(action == Action::AE_2) { this->AE_2(data); }
+ else if(action == Action::AE_3) { this->AE_3(data); }
+ else if(action == Action::AE_4) { this->AE_4(data); }
+ else if(action == Action::AE_5) { this->AE_5(data); }
+ else if(action == Action::AE_6) { this->AE_6(data); }
+ else if(action == Action::AE_7) { this->AE_7(data); }
+ else if(action == Action::AE_8) { this->AE_8(data); }
+ else if(action == Action::DT_1) { this->DT_1(data); }
+ else if(action == Action::DT_2) { this->DT_2(data); }
+ else if(action == Action::AR_1) { this->AR_1(data); }
+ else if(action == Action::AR_2) { this->AR_2(data); }
+ else if(action == Action::AR_3) { this->AR_3(data); }
+ else if(action == Action::AR_4) { this->AR_4(data); }
+ else if(action == Action::AR_5) { this->AR_5(data); }
+ else if(action == Action::AR_6) { this->AR_6(data); }
+ else if(action == Action::AR_7) { this->AR_7(data); }
+ else if(action == Action::AR_8) { this->AR_8(data); }
+ else if(action == Action::AR_9) { this->AR_9(data); }
+ else if(action == Action::AR_10) { this->AR_10(data); }
+ else if(action == Action::AA_1) { this->AA_1(data); }
+ else if(action == Action::AA_2) { this->AA_2(data); }
+ else if(action == Action::AA_3) { this->AA_3(data); }
+ else if(action == Action::AA_4) { this->AA_4(data); }
+ else if(action == Action::AA_5) { this->AA_5(data); }
+ else if(action == Action::AA_6) { this->AA_6(data); }
+ else if(action == Action::AA_7) { this->AA_7(data); }
+ else if(action == Action::AA_8) { this->AA_8(data); }
+ else
+ {
+ throw Exception("Unknown action");
+ }
+
+ this->_state = next_state;
+}
+
+StateMachine::State
+StateMachine
+::get_state() const
+{
+ return this->_state;
+}
+
+Transport const &
+StateMachine
+::get_transport() const
+{
+ return this->_transport;
+}
+
+Transport &
+StateMachine
+::get_transport()
+{
+ return this->_transport;
+}
+
+StateMachine::duration_type
+StateMachine
+::get_timeout() const
+{
+ return this->_timeout;
+}
+
+void
+StateMachine
+::set_timeout(duration_type timeout)
+{
+ this->_timeout = timeout;
+}
+
+void
+StateMachine
+::receive(EventData & data)
+{
+ this->_transport.receive(data.peer_endpoint);
+ this->transition(Event::TransportConnectionIndication, data);
+}
+
+void
+StateMachine
+::send_pdu(EventData & data)
+{
+ if(data.pdu == nullptr)
+ {
+ throw Exception("No PDU");
+ }
+ auto const & item = data.pdu->get_item();
+ auto const type = item.as_unsigned_int_8("PDU-type");
+
+ if(type == 0x01)
+ {
+ this->transition(Event::AAssociateRQLocal, data);
+ this->transition(Event::TransportConnectionConfirmation, data);
+ }
+ else if(type == 0x02)
+ {
+ this->transition(Event::AAssociateACLocal, data);
+ }
+ else if(type == 0x03)
+ {
+ this->transition(Event::AAssociateRJLocal, data);
+ }
+ else if(type == 0x04)
+ {
+ this->transition(Event::PDataTFLocal, data);
+ }
+ else if(type == 0x05)
+ {
+ this->transition(Event::AReleaseRQLocal, data);
+ }
+ else if(type == 0x06)
+ {
+ this->transition(Event::AReleaseRPLocal, data);
+ }
+ else if(type == 0x07)
+ {
+ this->transition(Event::AAbortLocal, data);
+ }
+ else
+ {
+ this->transition(Event::InvalidPDU, data);
+ }
+}
+
+void
+StateMachine
+::receive_pdu(EventData & data)
+{
+ auto const header = this->_transport.read(6);
+
+ uint8_t const type = header[0];
+ uint32_t const length = big_endian_to_host(
+ *reinterpret_cast<uint32_t const *>(&header[0]+2));
+ auto const pdu_data = this->_transport.read(length);
+
+ std::stringstream stream;
+ stream.write(&header[0], header.size());
+ stream.write(&pdu_data[0], pdu_data.size());
+
+ data.pdu=nullptr;
+ Event event = Event::None;
+ if(type == 0x01)
+ {
+ data.pdu = std::make_shared<pdu::AAssociateRQ>(stream);
+ event = Event::AAssociateRQRemote;
+ }
+ else if(type == 0x02)
+ {
+ data.pdu = std::make_shared<pdu::AAssociateAC>(stream);
+ event = Event::AAssociateACRemote;
+ }
+ else if(type == 0x03)
+ {
+ data.pdu = std::make_shared<pdu::AAssociateRJ>(stream);
+ event = Event::AAssociateRJRemote;
+ }
+ else if(type == 0x04)
+ {
+ data.pdu = std::make_shared<pdu::PDataTF>(stream);
+ event = Event::PDataTFRemote;
+ }
+ else if(type == 0x05)
+ {
+ data.pdu = std::make_shared<pdu::AReleaseRQ>(stream);
+ event = Event::AReleaseRQRemote;
+ }
+ else if(type == 0x06)
+ {
+ data.pdu = std::make_shared<pdu::AReleaseRP>(stream);
+ event = Event::AReleaseRPRemote;
+ }
+ else if(type == 0x07)
+ {
+ data.pdu = std::make_shared<pdu::AAbort>(stream);
+ event = Event::AAbortRemote;
+ }
+ else
+ {
+ event = Event::InvalidPDU;
+ }
+
+ this->transition(event, data);
+
+}
+
+void
+StateMachine
+::start_timer(EventData & data)
+{
+ return;
+
+
+ auto const canceled = this->_artim_timer.expires_from_now(this->_timeout);
+ if(canceled != 0)
+ {
+ throw Exception("ARTIM timer started with pending operations");
+ }
+
+ this->_artim_timer.async_wait(
+ [this,&data](boost::system::error_code const & e)
+ {
+ //source = Source::TIMER;
+ //error = e;
+
+ if(!e)
+ {
+ this->transition(Event::ARTIMTimerExpired, data);
+ }
+ else if(e == boost::asio::error::operation_aborted)
+ {
+ // Do nothing
+ }
+ else
+ {
+ throw boost::system::system_error(e);
+ }
+ }
+ );
+}
+
+void
+StateMachine
+::stop_timer()
+{
+ return;
+
+
+ this->_artim_timer.expires_at(boost::posix_time::pos_infin);
+ this->_transport.get_service().poll();
+ // FIXME: check that the timer was aborted
+ /*
+ if(source != Source::TIMER)
+ {
+ throw Exception("Unknown event");
+ }
+ else if(error != boost::asio::error::operation_aborted)
+ {
+ throw Exception("TCP timer error: "+error.message());
+ }
+ */
+ this->_transport.get_service().reset();
+}
+
+AssociationAcceptor const &
+StateMachine
+::get_association_acceptor() const
+{
+ return this->_association_acceptor;
+}
+
+void
+StateMachine
+::set_association_acceptor(AssociationAcceptor const & acceptor)
+{
+ this->_association_acceptor = acceptor;
+}
+
+#define transition_full(start, event, guard, action, end) { \
+ std::make_tuple(StateMachine::State::start, StateMachine::Event::event, guard), \
+ { StateMachine::Action::action, StateMachine::State::end } }
+
+#define transition(start, event, action, end) \
+ transition_full(start, event, true, action, end)
+
+StateMachine::TransitionMap const
+StateMachine
+::_transitions = {
+ transition(Sta1, AAssociateRQLocal, AE_1, Sta4),
+ transition(Sta1, TransportConnectionIndication, AE_5, Sta2),
+
+ transition(Sta2, AAssociateACRemote, AA_1, Sta13),
+ transition(Sta2, AAssociateRJRemote, AA_1, Sta13),
+ transition_full(Sta2, AAssociateRQRemote, true, AE_6, Sta3),
+ transition_full(Sta2, AAssociateRQRemote, false, AE_6, Sta13),
+ transition(Sta2, PDataTFRemote, AA_1, Sta13),
+ transition(Sta2, AReleaseRQRemote, AA_1, Sta13),
+ transition(Sta2, AReleaseRPRemote, AA_1, Sta13),
+ transition(Sta2, AAbortRemote, AA_2, Sta1),
+ transition(Sta2, TransportConnectionClosedIndication, AA_5, Sta1),
+ transition(Sta2, ARTIMTimerExpired, AA_2, Sta1),
+ transition(Sta2, InvalidPDU, AA_1, Sta13),
+
+ transition(Sta3, AAssociateACRemote, AA_8, Sta13),
+ transition(Sta3, AAssociateRJRemote, AA_8, Sta13),
+ transition(Sta3, AAssociateRQRemote, AA_8, Sta13),
+ transition(Sta3, AAssociateACLocal, AE_7, Sta6),
+ transition(Sta3, AAssociateRJLocal, AE_8, Sta13),
+ transition(Sta3, PDataTFRemote, AA_8, Sta13),
+ transition(Sta3, AReleaseRQRemote, AA_8, Sta13),
+ transition(Sta3, AReleaseRPRemote, AA_8, Sta13),
+ transition(Sta3, AAbortLocal, AA_1, Sta13),
+ transition(Sta3, AAbortRemote, AA_3, Sta1),
+ transition(Sta3, TransportConnectionClosedIndication, AA_4, Sta1),
+ transition(Sta3, InvalidPDU, AA_8, Sta13),
+
+ transition(Sta4, TransportConnectionConfirmation, AE_2, Sta5),
+ transition(Sta4, AAbortLocal, AA_2, Sta1),
+ transition(Sta4, TransportConnectionClosedIndication, AA_4, Sta1),
+
+ transition(Sta5, AAssociateACRemote, AE_3, Sta6),
+ transition(Sta5, AAssociateRJRemote, AE_4, Sta1),
+ transition(Sta5, AAssociateRQRemote, AA_8, Sta13),
+ transition(Sta5, PDataTFRemote, AA_8, Sta13),
+ transition(Sta5, AReleaseRQRemote, AA_8, Sta13),
+ transition(Sta5, AReleaseRPRemote, AA_8, Sta13),
+ transition(Sta5, AAbortLocal, AA_1, Sta13),
+ transition(Sta5, AAbortRemote, AA_3, Sta1),
+ transition(Sta5, TransportConnectionClosedIndication, AA_4, Sta1),
+ transition(Sta5, InvalidPDU, AA_8, Sta13),
+
+ transition(Sta6, AAssociateACRemote, AA_8, Sta13),
+ transition(Sta6, AAssociateRJRemote, AA_8, Sta13),
+ transition(Sta6, AAssociateRQRemote, AA_8, Sta13),
+ transition(Sta6, PDataTFLocal, DT_1, Sta6),
+ transition(Sta6, PDataTFRemote, DT_2, Sta6),
+ transition(Sta6, AReleaseRQLocal, AR_1, Sta7),
+ transition(Sta6, AReleaseRQRemote, AR_2, Sta8),
+ transition(Sta6, AReleaseRPRemote, AA_8, Sta13),
+ transition(Sta6, AAbortLocal, AA_1, Sta13),
+ transition(Sta6, AAbortRemote, AA_3, Sta1),
+ transition(Sta6, TransportConnectionClosedIndication, AA_4, Sta1),
+ transition(Sta6, InvalidPDU, AA_8, Sta13),
+
+ transition(Sta7, AAssociateACRemote, AA_8, Sta13),
+ transition(Sta7, AAssociateRJRemote, AA_8, Sta13),
+ transition(Sta7, AAssociateRQRemote, AA_8, Sta13),
+ transition(Sta7, PDataTFRemote, AR_6, Sta7),
+ //transition(Sta7, AReleaseRQRemote, AR_8, Sta9Or10),
+ transition(Sta7, AReleaseRPRemote, AR_3, Sta1),
+ transition(Sta7, AAbortLocal, AA_1, Sta13),
+ transition(Sta7, AAbortRemote, AA_3, Sta1),
+ transition(Sta7, TransportConnectionClosedIndication, AA_4, Sta1),
+ transition(Sta7, InvalidPDU, AA_8, Sta13),
+
+ transition(Sta8, AAssociateACRemote, AA_8, Sta13),
+ transition(Sta8, AAssociateRJRemote, AA_8, Sta13),
+ transition(Sta8, AAssociateRQRemote, AA_8, Sta13),
+ transition(Sta8, PDataTFLocal, AR_7, Sta8),
+ transition(Sta8, PDataTFRemote, AA_8, Sta13),
+ transition(Sta8, AReleaseRQRemote, AA_8, Sta13),
+ transition(Sta8, AReleaseRPRemote, AA_8, Sta13),
+ transition(Sta8, AReleaseRPLocal, AR_4, Sta13),
+ transition(Sta8, AAbortLocal, AA_1, Sta13),
+ transition(Sta8, AAbortRemote, AA_3, Sta1),
+ transition(Sta8, TransportConnectionClosedIndication, AA_4, Sta1),
+ transition(Sta8, InvalidPDU, AA_8, Sta13),
+
+ transition(Sta9, AAssociateACRemote, AA_8, Sta13),
+ transition(Sta9, AAssociateRJRemote, AA_8, Sta13),
+ transition(Sta9, AAssociateRQRemote, AA_8, Sta13),
+ transition(Sta9, PDataTFRemote, AA_8, Sta13),
+ transition(Sta9, AReleaseRQRemote, AA_8, Sta13),
+ transition(Sta9, AReleaseRPRemote, AA_8, Sta13),
+ transition(Sta9, AReleaseRPLocal, AR_9, Sta11),
+ transition(Sta9, AAbortLocal, AA_1, Sta13),
+ transition(Sta9, AAbortRemote, AA_3, Sta1),
+ transition(Sta9, TransportConnectionClosedIndication, AA_4, Sta1),
+ transition(Sta9, InvalidPDU, AA_8, Sta13),
+
+ transition(Sta10, AAssociateACRemote, AA_8, Sta13),
+ transition(Sta10, AAssociateRJRemote, AA_8, Sta13),
+ transition(Sta10, AAssociateRQRemote, AA_8, Sta13),
+ transition(Sta10, PDataTFRemote, AA_8, Sta13),
+ transition(Sta10, AReleaseRQRemote, AA_8, Sta13),
+ transition(Sta10, AReleaseRPRemote, AR_10, Sta12),
+ transition(Sta10, AAbortLocal, AA_1, Sta13),
+ transition(Sta10, AAbortRemote, AA_3, Sta1),
+ transition(Sta10, TransportConnectionClosedIndication, AA_4, Sta1),
+ transition(Sta10, InvalidPDU, AA_8, Sta13),
+
+ transition(Sta11, AAssociateACRemote, AA_8, Sta13),
+ transition(Sta11, AAssociateRJRemote, AA_8, Sta13),
+ transition(Sta11, AAssociateRQRemote, AA_8, Sta13),
+ transition(Sta11, PDataTFRemote, AA_8, Sta13),
+ transition(Sta11, AReleaseRQRemote, AA_8, Sta13),
+ transition(Sta11, AReleaseRPRemote, AR_3, Sta1),
+ transition(Sta11, AAbortLocal, AA_1, Sta13),
+ transition(Sta11, AAbortRemote, AA_3, Sta1),
+ transition(Sta11, TransportConnectionClosedIndication, AA_4, Sta1),
+ transition(Sta11, InvalidPDU, AA_8, Sta13),
+
+ transition(Sta12, AAssociateACRemote, AA_8, Sta13),
+ transition(Sta12, AAssociateRJRemote, AA_8, Sta13),
+ transition(Sta12, AAssociateRQRemote, AA_8, Sta13),
+ transition(Sta12, PDataTFRemote, AA_8, Sta13),
+ transition(Sta12, AReleaseRQRemote, AA_8, Sta13),
+ transition(Sta12, AReleaseRPRemote, AA_8, Sta13),
+ transition(Sta12, AReleaseRPLocal, AR_4, Sta13),
+ transition(Sta12, AAbortLocal, AA_1, Sta13),
+ transition(Sta12, AAbortRemote, AA_3, Sta1),
+ transition(Sta12, TransportConnectionClosedIndication, AA_4, Sta1),
+ transition(Sta12, InvalidPDU, AA_8, Sta13),
+
+ transition(Sta13, AAssociateACRemote, AA_6, Sta13),
+ transition(Sta13, AAssociateRJRemote, AA_6, Sta13),
+ transition(Sta13, AAssociateRQRemote, AA_7, Sta13),
+ transition(Sta13, PDataTFRemote, AA_6, Sta13),
+ transition(Sta13, AReleaseRQRemote, AA_6, Sta13),
+ transition(Sta13, AReleaseRPRemote, AA_6, Sta13),
+ transition(Sta13, AAbortRemote, AA_2, Sta1),
+ transition(Sta13, TransportConnectionClosedIndication, AR_5, Sta1),
+ transition(Sta13, ARTIMTimerExpired, AA_2, Sta1),
+ transition(Sta13, InvalidPDU, AA_7, Sta13),
+};
+
+StateMachine::GuardMap const
+StateMachine
+::_guards = {
+ {
+ {StateMachine::State::Sta2, StateMachine::Event::AAssociateRQRemote},
+ [](StateMachine const & state_machine, EventData & data)
+ {
+ try
+ {
+ AssociationParameters const input_parameters(
+ *std::dynamic_pointer_cast<pdu::AAssociateRQ>(data.pdu));
+ data.association_parameters =
+ state_machine.get_association_acceptor()(input_parameters);
+ }
+ catch(AssociationRejected const & reject)
+ {
+ data.reject = std::make_shared<AssociationRejected>(reject);
+ return false;
+ }
+ return true;
+ }
+ },
+};
+
+#undef transition
+#undef transition_full
+
+void
+StateMachine
+::_send_pdu(EventData & data, uint8_t pdu_type)
+{
+ if(data.pdu == nullptr)
+ {
+ throw Exception("No PDU");
+ }
+ auto const & item = data.pdu->get_item();
+
+ if(item.as_unsigned_int_8("PDU-type") != pdu_type)
+ {
+ throw Exception("Invalid PDU");
+ }
+
+ std::ostringstream stream;
+ stream << item;
+ this->_transport.write(stream.str());
+}
+
+void
+StateMachine
+::AE_1(EventData & data)
+{
+ this->_transport.connect(data.peer_endpoint);
+}
+
+void
+StateMachine
+::AE_2(EventData & data)
+{
+ this->_send_pdu(data, 0x01);
+}
+
+void
+StateMachine
+::AE_3(EventData & )
+{
+ // Do nothing: notification is implicit since this function is only called
+ // by receive_pdu
+}
+
+void
+StateMachine
+::AE_4(EventData & )
+{
+ // Notification is implicit since this function is only called by
+ // receive_pdu
+ this->_transport.close();
+}
+
+void
+StateMachine
+::AE_5(EventData & data)
+{
+ // Connection response has already been sent.
+ this->start_timer(data);
+}
+
+void
+StateMachine
+::AE_6(EventData & data)
+{
+ this->stop_timer();
+
+ if(data.reject)
+ {
+ data.pdu = std::make_shared<pdu::AAssociateRJ>(
+ data.reject->get_result(), data.reject->get_source(),
+ data.reject->get_reason());
+ this->_send_pdu(data, 0x03);
+ data.pdu = NULL;
+ }
+ else
+ {
+ // Issue A-ASSOCIATE indication
+ // Do nothing: notification is implicit since this function is only
+ // called by receive_pdu
+ }
+}
+
+void
+StateMachine
+::AE_7(EventData & data)
+{
+ this->_send_pdu(data, 0x02);
+}
+
+void
+StateMachine
+::AE_8(EventData & data)
+{
+ this->_send_pdu(data, 0x03);
+ this->start_timer(data);
+}
+
+void
+StateMachine
+::DT_1(EventData & data)
+{
+ this->_send_pdu(data, 0x04);
+}
+
+void
+StateMachine
+::DT_2(EventData & )
+{
+ // Do nothing: notification is implicit since this function is only called
+ // by receive_pdu
+}
+
+void
+StateMachine
+::AR_1(EventData & data)
+{
+ this->_send_pdu(data, 0x05);
+}
+
+void
+StateMachine
+::AR_2(EventData & )
+{
+ // Do nothing: notification is implicit since this function is only called
+ // by receive_pdu
+}
+
+void
+StateMachine
+::AR_3(EventData & )
+{
+ // Notification is implicit since this function is only called by
+ // receive_pdu
+ this->_transport.close();
+}
+
+void
+StateMachine
+::AR_4(EventData & data)
+{
+ this->_send_pdu(data, 0x06);
+ this->start_timer(data);
+}
+
+void
+StateMachine
+::AR_5(EventData & )
+{
+ this->stop_timer();
+}
+
+void
+StateMachine
+::AR_6(EventData & )
+{
+ // Do nothing: notification is implicit since this function is only called
+ // by receive_pdu
+}
+
+void
+StateMachine
+::AR_7(EventData & data)
+{
+ this->_send_pdu(data, 0x04);
+}
+
+void
+StateMachine
+::AR_8(EventData & )
+{
+ // Do nothing: notification is implicit since this function is only called
+ // by receive_pdu
+}
+
+void
+StateMachine
+::AR_9(EventData & data)
+{
+ this->_send_pdu(data, 0x06);
+}
+
+void
+StateMachine
+::AR_10(EventData & )
+{
+ // Do nothing: notification is implicit since this function is only called
+ // by receive_pdu
+}
+
+void
+StateMachine
+::AA_1(EventData & data)
+{
+ data.pdu = std::make_shared<pdu::AAbort>(1, 2);
+ this->send_pdu(data);
+
+ this->start_timer(data);
+}
+
+void
+StateMachine
+::AA_2(EventData & )
+{
+ this->stop_timer();
+ this->_transport.close();
+}
+
+void
+StateMachine
+::AA_3(EventData & )
+{
+ // Notification is implicit since this function is only called
+ // by receive_pdu
+ this->_transport.close();
+}
+
+void
+StateMachine
+::AA_4(EventData & )
+{
+ // Do nothing: notification is implicit since this function is only called
+ // by receive_pdu
+}
+
+void
+StateMachine
+::AA_5(EventData & )
+{
+ this->stop_timer();
+}
+
+void
+StateMachine
+::AA_6(EventData & )
+{
+ // Nothing to do.
+}
+
+void
+StateMachine
+::AA_7(EventData & data)
+{
+ this->_send_pdu(data, 0x07);
+}
+
+void
+StateMachine
+::AA_8(EventData & data)
+{
+ data.pdu = std::make_shared<pdu::AAbort>(2, 2);
+ this->_send_pdu(data, 0x07);
+ // Notification is implicit
+ this->start_timer(data);
+}
+
+}
+
+}
diff --git a/src/odil/dul/StateMachine.h b/src/odil/dul/StateMachine.h
new file mode 100644
index 0000000..a0c9703
--- /dev/null
+++ b/src/odil/dul/StateMachine.h
@@ -0,0 +1,274 @@
+/*************************************************************************
+ * 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 _981c80db_b2ac_4f25_af6c_febf5563d178
+#define _981c80db_b2ac_4f25_af6c_febf5563d178
+
+#include <functional>
+#include <map>
+#include <tuple>
+#include <utility>
+
+#include <boost/asio.hpp>
+
+#include "odil/AssociationAcceptor.h"
+#include "odil/dul/EventData.h"
+#include "odil/dul/Transport.h"
+
+namespace odil
+{
+
+namespace dul
+{
+
+/// @brief State machine for the DICOM upper layer.
+class StateMachine
+{
+public:
+
+ /// @brief States of the state machine.
+ enum class State
+ {
+ Sta1,
+ Sta2, Sta3, Sta4, Sta5,
+ Sta6,
+ Sta7, Sta8, Sta9, Sta10, Sta11, Sta12,
+ Sta13
+ };
+
+ /// @brief Event causing the transitions.
+ enum class Event
+ {
+ None, // dummy event to allow easier initialization
+ TransportConnectionIndication, TransportConnectionConfirmation,
+ TransportConnectionClosedIndication,
+
+ AAssociateRQLocal, AAssociateRQRemote,
+ AAssociateACLocal, AAssociateACRemote,
+ AAssociateRJLocal, AAssociateRJRemote,
+
+ AReleaseRQLocal, AReleaseRQRemote,
+ AReleaseRPLocal, AReleaseRPRemote,
+
+ PDataTFLocal, PDataTFRemote,
+
+ AAbortLocal, AAbortRemote,
+
+ ARTIMTimerExpired, InvalidPDU,
+ };
+
+ /// @brief Duration of the timeout.
+ typedef boost::asio::deadline_timer::duration_type duration_type;
+
+ /// @brief Constructor, initializing to Sta1.
+ StateMachine();
+
+ /// @brief Destructor, closing the transport.
+ ~StateMachine();
+
+ /**
+ * @brief Perform the transition related to the event and current state.
+ * Raise an exception if no such transition exists.
+ */
+ void transition(Event const & event, EventData & data);
+
+ /// @brief Return the current state.
+ State get_state() const;
+
+ /// @brief Return the TCP transport.
+ Transport const & get_transport() const;
+
+ /// @brief Return the TCP transport.
+ Transport & get_transport();
+
+ /// @brief Return the timeout, default to infinity.
+ duration_type get_timeout() const;
+
+ /// @brief Set the timeout.
+ void set_timeout(duration_type timeout);
+
+ /**
+ * @brief Receive a connection on the TCP transport, perform the
+ * corresponding transition.
+ */
+ void receive(EventData & data);
+
+ /// @brief Send a PDU to the transport, perform the corresponding transition.
+ void send_pdu(EventData & data);
+
+ /// @brief Receive a PDU on the transport, perform the corresponding transition.
+ void receive_pdu(EventData & data);
+
+ /// @brief Start (or re-start if already started) the ARTIM timer.
+ void start_timer(EventData & data);
+
+ /// @brief Stop the ARTIM timer.
+ void stop_timer();
+
+ /**
+ * @brief Return the callback checking whether the association request is
+ * acceptable.
+ *
+ * By default, all association requests are accepted.
+ */
+ AssociationAcceptor const & get_association_acceptor() const;
+
+ /**
+ * @brief Set the callback checking whether the association request is
+ * acceptable.
+ */
+ void set_association_acceptor(AssociationAcceptor const & acceptor);
+
+private:
+
+ enum class Action
+ {
+ AE_1, AE_2, AE_3, AE_4, AE_5, AE_6, AE_7, AE_8,
+ DT_1, DT_2,
+ AR_1, AR_2, AR_3, AR_4, AR_5, AR_6, AR_7, AR_8, AR_9, AR_10,
+ AA_1, AA_2, AA_3, AA_4, AA_5, AA_6, AA_7, AA_8
+ };
+
+ typedef std::map<
+ std::tuple<State, Event, bool>,
+ std::pair<Action, State>> TransitionMap;
+
+ typedef std::map<
+ std::pair<State, Event>,
+ std::function<bool(StateMachine const &, EventData &)>> GuardMap;
+
+ static TransitionMap const _transitions;
+ static GuardMap const _guards;
+
+ /// @brief Current state.
+ State _state;
+
+ /// @brief TCP transport.
+ Transport _transport;
+
+ /// @brief Timeout of the ARTIM timer.
+ duration_type _timeout;
+
+ /// @brief Association Request/Reject/Release Timer.
+ boost::asio::deadline_timer _artim_timer;
+
+ /// @brief Callback checking whether an association request is acceptable.
+ AssociationAcceptor _association_acceptor;
+
+ /// @brief Check the PDU type in data and send it.
+ void _send_pdu(EventData & data, uint8_t pdu_type);
+
+ /**
+ * @brief Issue TRANSPORT CONNECT request primitive to local transport
+ * service.
+ */
+ void AE_1(EventData & data);
+
+ /// @brief Send A-ASSOCIATE-RQ-PDU.
+ void AE_2(EventData & data);
+
+ /// @brief Issue A-ASSOCIATE confirmation (accept) primitive.
+ void AE_3(EventData & data);
+
+ /**
+ * @brief Issue A-ASSOCIATE confirmation (reject) primitive and close
+ * transport connection.
+ */
+ void AE_4(EventData & data);
+
+ /// @brief Issue Transport connection response primitive; start ARTIM timer.
+ void AE_5(EventData & data);
+
+ /// @brief Stop ARTIM timer and accept or reject connection.
+ void AE_6(EventData & data);
+
+ /// @brief Send A-ASSOCIATE-AC PDU.
+ void AE_7(EventData & data);
+
+ /// @brief Send A-ASSOCIATE-RJ PDU and start ARTIM timer.
+ void AE_8(EventData & data);
+
+ /// @brief Send P-DATA-TF PDU.
+ void DT_1(EventData & data);
+
+ /// @brief Send P-DATA indication primitive.
+ void DT_2(EventData & data);
+
+ /// @brief Send A-RELEASE-RQ PDU.
+ void AR_1(EventData & data);
+
+ /// @brief Issue A-RELEASE indication primitive.
+ void AR_2(EventData & data);
+
+ /**
+ * @brief Issue A-RELEASE confirmation primitive, and close transport.
+ * connection.
+ */
+ void AR_3(EventData & data);
+
+ /// @brief Issue A-RELEASE-RP PDU and start ARTIM timer.
+ void AR_4(EventData & data);
+
+ /// @brief Stop ARTIM timer.
+ void AR_5(EventData & data);
+
+ /// @brief Issue P-DATA indication.
+ void AR_6(EventData & data);
+
+ /// @brief Issue P-DATA-TF PDU.
+ void AR_7(EventData & data);
+
+ /// @brief Issue A-RELEASE indication (release collision).
+ void AR_8(EventData & data);
+
+ /// @brief Send A-RELEASE-RP PDU.
+ void AR_9(EventData & data);
+
+ /// @brief Issue A-RELEASE confirmation primitive.
+ void AR_10(EventData & data);
+
+ /**
+ * @brief Send A-ABORT PDU (service-user source) and start (or restart if
+ * already started) ARTIM timer.
+ */
+ void AA_1(EventData & data);
+
+ /// @brief Stop ARTIM timer if running. Close transport connection.
+ void AA_2(EventData & data);
+
+ /**
+ * @brief If (service-user inititated abort): issue A-ABORT indication and
+ * close transport connection ; otherwise (service-provider inititated
+ * abort): issue A-P-ABORT indication and close transport connection.
+ */
+ void AA_3(EventData & data);
+
+ /// @brief Issue A-P-ABORT indication primitive.
+ void AA_4(EventData & data);
+
+ /// @brief Stop ARTIM timer.
+ void AA_5(EventData & data);
+
+ /// @brief Ignore PDU.
+ void AA_6(EventData & data);
+
+ /// @brief Send A-ABORT PDU.
+ void AA_7(EventData & data);
+
+ /**
+ * @brief Send A-ABORT PDU (service-provider source-), issue an A-P-ABORT
+ * indication, and start ARTIM timer.
+ */
+ void AA_8(EventData & data);
+};
+
+}
+
+}
+
+#endif // _981c80db_b2ac_4f25_af6c_febf5563d178
diff --git a/src/odil/dul/Transport.cpp b/src/odil/dul/Transport.cpp
new file mode 100644
index 0000000..bd813c7
--- /dev/null
+++ b/src/odil/dul/Transport.cpp
@@ -0,0 +1,295 @@
+/*************************************************************************
+ * 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/dul/Transport.h"
+
+#include <memory>
+#include <string>
+
+#include <boost/asio.hpp>
+#include <boost/date_time.hpp>
+
+#include "odil/Exception.h"
+
+namespace odil
+{
+
+namespace dul
+{
+
+Transport
+::Transport()
+: _service(), _socket(nullptr), _timeout(boost::posix_time::pos_infin),
+ _deadline(_service)
+{
+ // Nothing else
+}
+
+Transport
+::~Transport()
+{
+ if(this->is_open())
+ {
+ this->close();
+ }
+}
+
+boost::asio::io_service const &
+Transport
+::get_service() const
+{
+ return this->_service;
+}
+
+boost::asio::io_service &
+Transport
+::get_service()
+{
+ return this->_service;
+}
+
+std::shared_ptr<Transport::Socket const>
+Transport
+::get_socket() const
+{
+ return this->_socket;
+}
+
+std::shared_ptr<Transport::Socket>
+Transport
+::get_socket()
+{
+ return this->_socket;
+}
+
+Transport::duration_type
+Transport
+::get_timeout() const
+{
+ return this->_timeout;
+}
+
+void
+Transport
+::set_timeout(duration_type timeout)
+{
+ this->_timeout = timeout;
+}
+
+bool
+Transport
+::is_open() const
+{
+ return (this->_socket != nullptr && this->_socket->is_open());
+}
+
+void
+Transport
+::connect(Socket::endpoint_type const & peer_endpoint)
+{
+ if(this->is_open())
+ {
+ throw Exception("Already connected");
+ }
+
+ auto source = Source::NONE;
+ boost::system::error_code error;
+ this->_start_deadline(source, error);
+
+ this->_socket = std::make_shared<Socket>(this->_service);
+ this->_socket->async_connect(
+ peer_endpoint,
+ [&source,&error](boost::system::error_code const & e)
+ {
+ source = Source::OPERATION;
+ error = e;
+ }
+ );
+
+ this->_run(source, error);
+}
+
+void
+Transport
+::receive(Socket::endpoint_type const & endpoint)
+{
+ if(this->is_open())
+ {
+ throw Exception("Already connected");
+ }
+
+ auto source = Source::NONE;
+ boost::system::error_code error;
+ this->_start_deadline(source, error);
+
+ this->_socket = std::make_shared<Socket>(this->_service);
+ boost::asio::ip::tcp::acceptor acceptor(this->_service, endpoint);
+ acceptor.async_accept(
+ *this->_socket,
+ [&source,&error](boost::system::error_code const & e)
+ {
+ source = Source::OPERATION;
+ error = e;
+ }
+ );
+
+ this->_run(source, error);
+}
+
+void
+Transport
+::close()
+{
+ if(!this->is_open())
+ {
+ throw Exception("Not connected");
+ }
+
+ this->_socket->close();
+ this->_socket = nullptr;
+}
+
+std::string
+Transport
+::read(std::size_t length)
+{
+ if(!this->is_open())
+ {
+ throw Exception("Not connected");
+ }
+
+ std::string data(length, 'a');
+
+ auto source = Source::NONE;
+ boost::system::error_code error;
+ this->_start_deadline(source, error);
+
+ boost::asio::async_read(
+ *this->_socket,
+ boost::asio::buffer(&data[0], data.size()),
+ [&source,&error](boost::system::error_code const & e, std::size_t)
+ {
+ source = Source::OPERATION;
+ error = e;
+ }
+ );
+
+ this->_run(source, error);
+
+ return data;
+}
+
+void
+Transport
+::write(std::string const & data)
+{
+ if(!this->is_open())
+ {
+ throw Exception("Not connected");
+ }
+
+ auto source = Source::NONE;
+ boost::system::error_code error;
+ this->_start_deadline(source, error);
+
+ boost::asio::async_write(
+ *this->_socket, boost::asio::buffer(data),
+ [&source,&error](boost::system::error_code const & e, std::size_t)
+ {
+ source = Source::OPERATION;
+ error = e;
+ }
+ );
+
+ this->_run(source, error);
+}
+
+void
+Transport
+::_start_deadline(Source & source, boost::system::error_code & error)
+{
+ auto const canceled = this->_deadline.expires_from_now(this->_timeout);
+ if(canceled != 0)
+ {
+ throw Exception("TCP timer started with pending operations");
+ }
+
+ this->_deadline.async_wait(
+ [&source,&error](boost::system::error_code const & e)
+ {
+ source = Source::TIMER;
+ error = e;
+ }
+ );
+}
+
+void
+Transport
+::_stop_deadline()
+{
+ this->_deadline.expires_at(boost::posix_time::pos_infin);
+}
+
+void
+Transport
+::_run(Source & source, boost::system::error_code & error)
+{
+ // WARNING: it seems that run_one runs a *simple* operation, not a
+ // *composed* operation, as is done by async_read/async_write
+ while(source == Source::NONE)
+ {
+ auto const ran = this->_service.run_one();
+ if(ran == 0)
+ {
+ throw Exception("No operations ran");
+ }
+ this->_service.reset();
+ }
+
+ if(source == Source::OPERATION)
+ {
+ if(error)
+ {
+ throw Exception("Operation error: "+error.message());
+ }
+
+ source = Source::NONE;
+ this->_stop_deadline();
+
+ while(source == Source::NONE)
+ {
+ auto const polled = this->_service.poll_one();
+ if(polled == 0)
+ {
+ throw Exception("No operations polled");
+ }
+ this->_service.reset();
+ }
+
+ if(source != Source::TIMER)
+ {
+ throw Exception("Unknown event");
+ }
+ else if(error != boost::asio::error::operation_aborted)
+ {
+ throw Exception("TCP timer error: "+error.message());
+ }
+ }
+ else if(source == Source::TIMER)
+ {
+ throw Exception("TCP time out");
+ }
+ else
+ {
+ throw Exception("Unknown source");
+ }
+}
+
+}
+
+}
diff --git a/src/odil/dul/Transport.h b/src/odil/dul/Transport.h
new file mode 100644
index 0000000..726ef87
--- /dev/null
+++ b/src/odil/dul/Transport.h
@@ -0,0 +1,107 @@
+/*************************************************************************
+ * 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 _1619bae8_acba_4bf8_8205_aa8dd0085c66
+#define _1619bae8_acba_4bf8_8205_aa8dd0085c66
+
+#include <memory>
+#include <string>
+
+#include <boost/asio.hpp>
+#include <boost/date_time.hpp>
+
+namespace odil
+{
+
+namespace dul
+{
+
+/**
+ * @brief TCP transport for the DICOM Upper Layer.
+ *
+ * The behavior of connect, receive, read and write is governed by the timeout
+ * value: if the timeout expires before the operation is completed, an exception
+ * will be raised.
+ */
+struct Transport
+{
+ /// @brief Socket type.
+ typedef boost::asio::ip::tcp::socket Socket;
+
+ /// @brief Duration of the timeout.
+ typedef boost::asio::deadline_timer::duration_type duration_type;
+
+ /// @brief Constructor.
+ Transport();
+
+ /// @brief Destructor.
+ ~Transport();
+
+ /// @brief Return the io_service.
+ boost::asio::io_service const & get_service() const;
+
+ /// @brief Return the io_service.
+ boost::asio::io_service & get_service();
+
+ /// @brief Return the socket.
+ std::shared_ptr<Socket const> get_socket() const;
+
+ /// @brief Return the socket.
+ std::shared_ptr<Socket> get_socket();
+
+ /// @brief Return the timeout, default to infinity.
+ duration_type get_timeout() const;
+
+ /// @brief Set the timeout.
+ void set_timeout(duration_type timeout);
+
+ /// @brief Test whether the transport is open.
+ bool is_open() const;
+
+ /// @brief Connect to the specified endpoint, raise an exception upon error.
+ void connect(Socket::endpoint_type const & peer_endpoint);
+
+ /**
+ * @brief Receive a connection on the specified endpoint, raise an
+ * exception upon error.
+ */
+ void receive(Socket::endpoint_type const & endpoint);
+
+ /// @brief Close the connection.
+ void close();
+
+ /// @brief Read data, raise an exception on error.
+ std::string read(std::size_t length);
+
+ /// @brief Write data, raise an exception on error.
+ void write(std::string const & data);
+
+private:
+ boost::asio::io_service _service;
+ std::shared_ptr<Socket> _socket;
+ duration_type _timeout;
+ boost::asio::deadline_timer _deadline;
+
+ enum class Source
+ {
+ NONE,
+ TIMER,
+ OPERATION,
+ };
+
+ void _start_deadline(Source & source, boost::system::error_code & error);
+ void _stop_deadline();
+
+ void _run(Source & source, boost::system::error_code & error);
+};
+
+}
+
+}
+
+#endif // _1619bae8_acba_4bf8_8205_aa8dd0085c66
diff --git a/src/odil/endian.h b/src/odil/endian.h
new file mode 100644
index 0000000..141fdaa
--- /dev/null
+++ b/src/odil/endian.h
@@ -0,0 +1,87 @@
+/*************************************************************************
+ * 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 _05d00816_25d0_41d1_9768_afd39f0503da
+#define _05d00816_25d0_41d1_9768_afd39f0503da
+
+#include <boost/detail/endian.hpp>
+
+#define ODIL_SWAP \
+ auto source = reinterpret_cast<char const *>(&value); \
+ auto const end = source + sizeof(value); \
+ T result; \
+ auto destination = reinterpret_cast<char *>(&result) + sizeof(result) - 1; \
+ while(source != end) \
+ { \
+ *destination = *source; \
+ ++source; \
+ --destination; \
+ }
+
+namespace odil
+{
+
+enum class ByteOrdering
+{
+ LittleEndian,
+ BigEndian
+};
+
+template<typename T>
+T host_to_big_endian(T const & value)
+{
+#ifdef BOOST_LITTLE_ENDIAN
+ ODIL_SWAP
+
+ return result;
+#else
+ return value;
+#endif
+}
+
+template<typename T>
+T host_to_little_endian(T const & value)
+{
+#ifdef BOOST_BIG_ENDIAN
+ ODIL_SWAP
+
+ return result;
+#else
+ return value;
+#endif
+}
+
+template<typename T>
+T big_endian_to_host(T const & value)
+{
+#ifdef BOOST_LITTLE_ENDIAN
+ ODIL_SWAP
+
+ return result;
+#else
+ return value;
+#endif
+}
+
+template<typename T>
+T little_endian_to_host(T const & value)
+{
+#ifdef BOOST_BIG_ENDIAN
+ ODIL_SWAP
+
+ return result;
+#else
+ return value;
+#endif
+}
+
+}
+
+#undef ODIL_SWAP
+
+#endif // _05d00816_25d0_41d1_9768_afd39f0503da
diff --git a/src/dcmtkpp/json_converter.cpp b/src/odil/json_converter.cpp
similarity index 87%
rename from src/dcmtkpp/json_converter.cpp
rename to src/odil/json_converter.cpp
index ac53f04..3b5e5ef 100644
--- a/src/dcmtkpp/json_converter.cpp
+++ b/src/odil/json_converter.cpp
@@ -1,24 +1,24 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 "dcmtkpp/json_converter.h"
+#include "odil/json_converter.h"
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/ofstd/ofstd.h>
-#include <dcmtk/ofstd/ofstream.h>
-#include <jsoncpp/json/json.h>
+#include <iterator>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/Value.h"
-#include "dcmtkpp/VR.h"
+#include <json/json.h>
-namespace dcmtkpp
+#include "odil/base64.h"
+#include "odil/DataSet.h"
+#include "odil/Exception.h"
+#include "odil/Value.h"
+#include "odil/VR.h"
+
+namespace odil
{
struct ToJSONVisitor
@@ -141,11 +141,10 @@ struct ToJSONVisitor
result["vr"] = as_string(vr);
- unsigned char const * data =
- reinterpret_cast<unsigned char const *>(&value[0]);
- OFStringStream stream;
- OFStandard::encodeBase64(stream, data, value.size());
- result["InlineBinary"] = stream.str().c_str();
+ std::string encoded;
+ encoded.reserve(value.size()*4/3);
+ base64::encode(value.begin(), value.end(), std::back_inserter(encoded));
+ result["InlineBinary"] = encoded;
return result;
}
@@ -256,15 +255,10 @@ DataSet as_dataset(Json::Value const & json)
element = Element(Value::Binary(), vr);
auto const & encoded = json_element["InlineBinary"].asString();
- OFString const encoded_dcmtk(encoded.c_str());
- unsigned char * decoded;
- size_t const decoded_size =
- OFStandard::decodeBase64(encoded_dcmtk, decoded);
-
- element.as_binary().resize(decoded_size);
- std::copy(decoded, decoded+decoded_size, element.as_binary().begin());
-
- delete[] decoded;
+ auto & decoded = element.as_binary();
+ decoded.reserve(encoded.size()*3/4);
+ base64::decode(
+ encoded.begin(), encoded.end(), std::back_inserter(decoded));
}
else
{
diff --git a/src/dcmtkpp/json_converter.h b/src/odil/json_converter.h
similarity index 84%
rename from src/dcmtkpp/json_converter.h
rename to src/odil/json_converter.h
index b80c478..f6df7f2 100644
--- a/src/dcmtkpp/json_converter.h
+++ b/src/odil/json_converter.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -9,10 +9,10 @@
#ifndef _6f5dc463_a89a_4f77_a0ed_36dca74b9e59
#define _6f5dc463_a89a_4f77_a0ed_36dca74b9e59
-#include <jsoncpp/json/json.h>
-#include "dcmtkpp/DataSet.h"
+#include <json/json.h>
+#include "odil/DataSet.h"
-namespace dcmtkpp
+namespace odil
{
/// @brief Convert a data set to its JSON representation.
diff --git a/src/dcmtkpp/CEchoRequest.cpp b/src/odil/message/CEchoRequest.cpp
similarity index 86%
rename from src/dcmtkpp/CEchoRequest.cpp
rename to src/odil/message/CEchoRequest.cpp
index 858abbb..8f5458f 100644
--- a/src/dcmtkpp/CEchoRequest.cpp
+++ b/src/odil/message/CEchoRequest.cpp
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -8,11 +8,14 @@
#include "CEchoRequest.h"
-#include "dcmtkpp/Request.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Value.h"
+#include "odil/message/Request.h"
+#include "odil/registry.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace message
{
CEchoRequest
@@ -45,3 +48,5 @@ CEchoRequest
}
}
+
+}
diff --git a/src/dcmtkpp/CEchoRequest.h b/src/odil/message/CEchoRequest.h
similarity index 83%
rename from src/dcmtkpp/CEchoRequest.h
rename to src/odil/message/CEchoRequest.h
index deb60a5..d8a2f3c 100644
--- a/src/dcmtkpp/CEchoRequest.h
+++ b/src/odil/message/CEchoRequest.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -9,11 +9,14 @@
#ifndef _aec786b8_0074_4cb2_b9a1_4bf26bbd20fc
#define _aec786b8_0074_4cb2_b9a1_4bf26bbd20fc
-#include "dcmtkpp/Request.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Value.h"
+#include "odil/message/Request.h"
+#include "odil/registry.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace message
{
/// @brief C-ECHO-RQ message.
@@ -38,10 +41,12 @@ public:
/// @brief Destructor.
virtual ~CEchoRequest();
- DCMTKPP_MESSAGE_MANDATORY_FIELD_STRING_MACRO(
+ ODIL_MESSAGE_MANDATORY_FIELD_STRING_MACRO(
affected_sop_class_uid, registry::AffectedSOPClassUID)
};
}
+}
+
#endif // _aec786b8_0074_4cb2_b9a1_4bf26bbd20fc
diff --git a/src/dcmtkpp/CEchoResponse.cpp b/src/odil/message/CEchoResponse.cpp
similarity index 85%
rename from src/dcmtkpp/CEchoResponse.cpp
rename to src/odil/message/CEchoResponse.cpp
index ec176b3..655a2f0 100644
--- a/src/dcmtkpp/CEchoResponse.cpp
+++ b/src/odil/message/CEchoResponse.cpp
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -8,12 +8,15 @@
#include "CEchoResponse.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Response.h"
-#include "dcmtkpp/Value.h"
+#include "odil/Exception.h"
+#include "odil/registry.h"
+#include "odil/message/Response.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace message
{
CEchoResponse
@@ -47,3 +50,5 @@ CEchoResponse
}
}
+
+}
diff --git a/src/dcmtkpp/CEchoResponse.h b/src/odil/message/CEchoResponse.h
similarity index 84%
rename from src/dcmtkpp/CEchoResponse.h
rename to src/odil/message/CEchoResponse.h
index 51c19d9..45b13d6 100644
--- a/src/dcmtkpp/CEchoResponse.h
+++ b/src/odil/message/CEchoResponse.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -9,11 +9,14 @@
#ifndef _266252d9_e801_479e_a805_004b101c5250
#define _266252d9_e801_479e_a805_004b101c5250
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Response.h"
-#include "dcmtkpp/Value.h"
+#include "odil/registry.h"
+#include "odil/message/Response.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace message
{
/// @brief C-ECHO-RSP message.
@@ -38,10 +41,12 @@ public:
/// @brief Destructor.
virtual ~CEchoResponse();
- DCMTKPP_MESSAGE_MANDATORY_FIELD_STRING_MACRO(
+ ODIL_MESSAGE_MANDATORY_FIELD_STRING_MACRO(
affected_sop_class_uid, registry::AffectedSOPClassUID)
};
}
+}
+
#endif // _266252d9_e801_479e_a805_004b101c5250
diff --git a/src/dcmtkpp/CFindRequest.cpp b/src/odil/message/CFindRequest.cpp
similarity index 88%
rename from src/dcmtkpp/CFindRequest.cpp
rename to src/odil/message/CFindRequest.cpp
index 1c096c3..032a74d 100644
--- a/src/dcmtkpp/CFindRequest.cpp
+++ b/src/odil/message/CFindRequest.cpp
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -10,12 +10,15 @@
#include <string>
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Request.h"
-#include "dcmtkpp/Value.h"
+#include "odil/Exception.h"
+#include "odil/registry.h"
+#include "odil/message/Request.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace message
{
CFindRequest
@@ -63,3 +66,5 @@ CFindRequest
}
}
+
+}
diff --git a/src/dcmtkpp/CFindRequest.h b/src/odil/message/CFindRequest.h
similarity index 80%
rename from src/dcmtkpp/CFindRequest.h
rename to src/odil/message/CFindRequest.h
index 6b9e685..f3a23cc 100644
--- a/src/dcmtkpp/CFindRequest.h
+++ b/src/odil/message/CFindRequest.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -9,11 +9,14 @@
#ifndef _74cfa9e7_da35_4130_a941_e17cb6932f60
#define _74cfa9e7_da35_4130_a941_e17cb6932f60
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Request.h"
-#include "dcmtkpp/Value.h"
+#include "odil/registry.h"
+#include "odil/message/Request.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace message
{
/// @brief C-FIND-RQ message.
@@ -38,11 +41,13 @@ public:
/// @brief Destructor.
virtual ~CFindRequest();
- DCMTKPP_MESSAGE_MANDATORY_FIELD_STRING_MACRO(
+ ODIL_MESSAGE_MANDATORY_FIELD_STRING_MACRO(
affected_sop_class_uid, registry::AffectedSOPClassUID)
- DCMTKPP_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(priority, registry::Priority)
+ ODIL_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(priority, registry::Priority)
};
}
+}
+
#endif // _74cfa9e7_da35_4130_a941_e17cb6932f60
diff --git a/src/dcmtkpp/CFindResponse.cpp b/src/odil/message/CFindResponse.cpp
similarity index 82%
rename from src/dcmtkpp/CFindResponse.cpp
rename to src/odil/message/CFindResponse.cpp
index a70d6d8..c118dbe 100644
--- a/src/dcmtkpp/CFindResponse.cpp
+++ b/src/odil/message/CFindResponse.cpp
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -8,13 +8,16 @@
#include "CFindResponse.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Response.h"
-#include "dcmtkpp/Value.h"
+#include "odil/DataSet.h"
+#include "odil/Exception.h"
+#include "odil/registry.h"
+#include "odil/message/Response.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace message
{
CFindResponse
@@ -45,9 +48,9 @@ CFindResponse
}
this->set_command_field(message.get_command_field());
- DCMTKPP_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
+ ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
message.get_command_set(), message_id, registry::MessageID, as_int)
- DCMTKPP_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
+ ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
message.get_command_set(), affected_sop_class_uid,
registry::AffectedSOPClassUID, as_string)
@@ -64,3 +67,5 @@ CFindResponse
}
}
+
+}
diff --git a/src/dcmtkpp/CFindResponse.h b/src/odil/message/CFindResponse.h
similarity index 68%
rename from src/dcmtkpp/CFindResponse.h
rename to src/odil/message/CFindResponse.h
index af44833..a628fb0 100644
--- a/src/dcmtkpp/CFindResponse.h
+++ b/src/odil/message/CFindResponse.h
@@ -1,26 +1,40 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 _5fd36547_9498_4cf3_87cc_737af51e93a9
#define _5fd36547_9498_4cf3_87cc_737af51e93a9
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Response.h"
-#include "dcmtkpp/Value.h"
+#include "odil/DataSet.h"
+#include "odil/registry.h"
+#include "odil/message/Response.h"
+#include "odil/Value.h"
+
+namespace odil
+{
-namespace dcmtkpp
+namespace message
{
/// @brief C-FIND-RSP message.
class CFindResponse: public Response
{
public:
+ /// @brief C-FIND status codes, PS 3.4, C.4.1.1.4
+ enum Status
+ {
+ // Failure
+ RefusedOutOfResources=0xA700,
+ IdentifierDoesNotMatchSOPClass=0xA900,
+ UnableToProcess=0xC000,
+ // Pending
+ PendingWarningOptionalKeysNotSupported=0xFF01,
+ };
+
/**
* @brief Create an find response with given Message ID, and status.
*/
@@ -44,12 +58,14 @@ public:
/// @brief Destructor.
virtual ~CFindResponse();
-
- DCMTKPP_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(message_id, registry::MessageID)
- DCMTKPP_MESSAGE_OPTIONAL_FIELD_STRING_MACRO(
+
+ ODIL_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(message_id, registry::MessageID)
+ ODIL_MESSAGE_OPTIONAL_FIELD_STRING_MACRO(
affected_sop_class_uid, registry::AffectedSOPClassUID)
};
}
+}
+
#endif // _5fd36547_9498_4cf3_87cc_737af51e93a9
diff --git a/src/dcmtkpp/CGetRequest.cpp b/src/odil/message/CGetRequest.cpp
similarity index 87%
rename from src/dcmtkpp/CGetRequest.cpp
rename to src/odil/message/CGetRequest.cpp
index 3a0d5a6..6283fa3 100644
--- a/src/dcmtkpp/CGetRequest.cpp
+++ b/src/odil/message/CGetRequest.cpp
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -10,13 +10,16 @@
#include <string>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Request.h"
-#include "dcmtkpp/Value.h"
+#include "odil/DataSet.h"
+#include "odil/Exception.h"
+#include "odil/registry.h"
+#include "odil/message/Request.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace message
{
CGetRequest
@@ -64,3 +67,5 @@ CGetRequest
}
}
+
+}
diff --git a/src/dcmtkpp/CGetRequest.h b/src/odil/message/CGetRequest.h
similarity index 78%
rename from src/dcmtkpp/CGetRequest.h
rename to src/odil/message/CGetRequest.h
index a9061b8..8152f5e 100644
--- a/src/dcmtkpp/CGetRequest.h
+++ b/src/odil/message/CGetRequest.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -9,12 +9,15 @@
#ifndef _6a22f126_7cc6_47ab_81c2_5f66b2714345
#define _6a22f126_7cc6_47ab_81c2_5f66b2714345
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Request.h"
-#include "dcmtkpp/Value.h"
+#include "odil/DataSet.h"
+#include "odil/registry.h"
+#include "odil/message/Request.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace message
{
/// @brief C-GET-RQ message.
@@ -39,11 +42,13 @@ public:
/// @brief Destructor.
virtual ~CGetRequest();
- DCMTKPP_MESSAGE_MANDATORY_FIELD_STRING_MACRO(
+ ODIL_MESSAGE_MANDATORY_FIELD_STRING_MACRO(
affected_sop_class_uid, registry::AffectedSOPClassUID)
- DCMTKPP_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(priority, registry::Priority)
+ ODIL_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(priority, registry::Priority)
};
}
+}
+
#endif // _6a22f126_7cc6_47ab_81c2_5f66b2714345
diff --git a/src/dcmtkpp/CGetResponse.cpp b/src/odil/message/CGetResponse.cpp
similarity index 80%
rename from src/dcmtkpp/CGetResponse.cpp
rename to src/odil/message/CGetResponse.cpp
index 3deda8f..c624da0 100644
--- a/src/dcmtkpp/CGetResponse.cpp
+++ b/src/odil/message/CGetResponse.cpp
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -8,13 +8,16 @@
#include "CGetResponse.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Response.h"
-#include "dcmtkpp/Value.h"
+#include "odil/DataSet.h"
+#include "odil/Exception.h"
+#include "odil/registry.h"
+#include "odil/message/Response.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace message
{
CGetResponse
@@ -45,22 +48,22 @@ CGetResponse
}
this->set_command_field(message.get_command_field());
- DCMTKPP_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
+ ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
message.get_command_set(), message_id, registry::MessageID, as_int)
- DCMTKPP_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
+ ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
message.get_command_set(), affected_sop_class_uid,
registry::AffectedSOPClassUID, as_string)
- DCMTKPP_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
+ ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
message.get_command_set(), number_of_remaining_sub_operations,
registry::NumberOfRemainingSuboperations, as_int)
- DCMTKPP_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
+ ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
message.get_command_set(), number_of_completed_sub_operations,
registry::NumberOfCompletedSuboperations, as_int)
- DCMTKPP_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
+ ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
message.get_command_set(), number_of_failed_sub_operations,
registry::NumberOfFailedSuboperations, as_int)
- DCMTKPP_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
+ ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
message.get_command_set(), number_of_warning_sub_operations,
registry::NumberOfWarningSuboperations, as_int)
@@ -77,3 +80,5 @@ CGetResponse
}
}
+
+}
diff --git a/src/dcmtkpp/CGetResponse.h b/src/odil/message/CGetResponse.h
similarity index 64%
rename from src/dcmtkpp/CGetResponse.h
rename to src/odil/message/CGetResponse.h
index d51cba1..cc01120 100644
--- a/src/dcmtkpp/CGetResponse.h
+++ b/src/odil/message/CGetResponse.h
@@ -1,26 +1,41 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 _7b9819f1_d7a2_4898_a850_3ed6a61f08c6
#define _7b9819f1_d7a2_4898_a850_3ed6a61f08c6
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Response.h"
-#include "dcmtkpp/Value.h"
+#include "odil/DataSet.h"
+#include "odil/registry.h"
+#include "odil/message/Response.h"
+#include "odil/Value.h"
+
+namespace odil
+{
-namespace dcmtkpp
+namespace message
{
/// @brief C-GET-RSP message.
class CGetResponse: public Response
{
public:
+ /// @brief C-GET status codes, PS 3.4, C.4.3.1.4
+ enum Status
+ {
+ // Failure
+ RefusedOutOfResourcesUnableToCalculateNumberOfMatches=0xA701,
+ RefusedOutOfResourcesUnableToPerformSubOperations=0xA702,
+ IdentifierDoesNotMatchSOPClass=0xA900,
+ UnableToProcess=0xC000,
+ // Warning
+ SubOperationsCompleteOneOrMoreFailuresOrWarnings=0xB000
+ };
+
/**
* @brief Create an get response with given Message ID, and status.
*/
@@ -44,21 +59,23 @@ public:
/// @brief Destructor.
virtual ~CGetResponse();
-
- DCMTKPP_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(message_id, registry::MessageID)
- DCMTKPP_MESSAGE_OPTIONAL_FIELD_STRING_MACRO(
+
+ ODIL_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(message_id, registry::MessageID)
+ ODIL_MESSAGE_OPTIONAL_FIELD_STRING_MACRO(
affected_sop_class_uid, registry::AffectedSOPClassUID)
- DCMTKPP_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(
+ ODIL_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(
number_of_remaining_sub_operations, registry::NumberOfRemainingSuboperations)
- DCMTKPP_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(
+ ODIL_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(
number_of_completed_sub_operations, registry::NumberOfCompletedSuboperations)
- DCMTKPP_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(
+ ODIL_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(
number_of_failed_sub_operations, registry::NumberOfFailedSuboperations)
- DCMTKPP_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(
+ ODIL_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(
number_of_warning_sub_operations, registry::NumberOfWarningSuboperations)
};
}
+}
+
#endif // _7b9819f1_d7a2_4898_a850_3ed6a61f08c6
diff --git a/src/dcmtkpp/CMoveRequest.cpp b/src/odil/message/CMoveRequest.cpp
similarity index 88%
rename from src/dcmtkpp/CMoveRequest.cpp
rename to src/odil/message/CMoveRequest.cpp
index df21f83..15e81ab 100644
--- a/src/dcmtkpp/CMoveRequest.cpp
+++ b/src/odil/message/CMoveRequest.cpp
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -8,13 +8,16 @@
#include "CMoveRequest.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Request.h"
-#include "dcmtkpp/Value.h"
+#include "odil/DataSet.h"
+#include "odil/Exception.h"
+#include "odil/registry.h"
+#include "odil/message/Request.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace message
{
CMoveRequest
@@ -67,3 +70,5 @@ CMoveRequest
}
}
+
+}
diff --git a/src/dcmtkpp/CMoveRequest.h b/src/odil/message/CMoveRequest.h
similarity index 77%
rename from src/dcmtkpp/CMoveRequest.h
rename to src/odil/message/CMoveRequest.h
index 099e1f1..4977e34 100644
--- a/src/dcmtkpp/CMoveRequest.h
+++ b/src/odil/message/CMoveRequest.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -9,12 +9,15 @@
#ifndef _f6e243d2_6113_4fe3_8d04_3f034fc796bf
#define _f6e243d2_6113_4fe3_8d04_3f034fc796bf
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Request.h"
-#include "dcmtkpp/Value.h"
+#include "odil/DataSet.h"
+#include "odil/registry.h"
+#include "odil/message/Request.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace message
{
/// @brief C-MOVE-RQ message.
@@ -40,13 +43,15 @@ public:
/// @brief Destructor.
virtual ~CMoveRequest();
- DCMTKPP_MESSAGE_MANDATORY_FIELD_STRING_MACRO(
+ ODIL_MESSAGE_MANDATORY_FIELD_STRING_MACRO(
affected_sop_class_uid, registry::AffectedSOPClassUID)
- DCMTKPP_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(priority, registry::Priority)
- DCMTKPP_MESSAGE_MANDATORY_FIELD_STRING_MACRO(
+ ODIL_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(priority, registry::Priority)
+ ODIL_MESSAGE_MANDATORY_FIELD_STRING_MACRO(
move_destination, registry::MoveDestination)
};
}
+}
+
#endif // _f6e243d2_6113_4fe3_8d04_3f034fc796bf
diff --git a/src/dcmtkpp/CMoveResponse.cpp b/src/odil/message/CMoveResponse.cpp
similarity index 80%
rename from src/dcmtkpp/CMoveResponse.cpp
rename to src/odil/message/CMoveResponse.cpp
index b1d70b6..126fae8 100644
--- a/src/dcmtkpp/CMoveResponse.cpp
+++ b/src/odil/message/CMoveResponse.cpp
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -8,13 +8,16 @@
#include "CMoveResponse.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Response.h"
-#include "dcmtkpp/Value.h"
+#include "odil/DataSet.h"
+#include "odil/Exception.h"
+#include "odil/registry.h"
+#include "odil/message/Response.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace message
{
CMoveResponse
@@ -45,22 +48,22 @@ CMoveResponse
}
this->set_command_field(message.get_command_field());
- DCMTKPP_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
+ ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
message.get_command_set(), message_id, registry::MessageID, as_int)
- DCMTKPP_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
+ ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
message.get_command_set(), affected_sop_class_uid,
registry::AffectedSOPClassUID, as_string)
- DCMTKPP_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
+ ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
message.get_command_set(), number_of_remaining_sub_operations,
registry::NumberOfRemainingSuboperations, as_int)
- DCMTKPP_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
+ ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
message.get_command_set(), number_of_completed_sub_operations,
registry::NumberOfCompletedSuboperations, as_int)
- DCMTKPP_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
+ ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
message.get_command_set(), number_of_failed_sub_operations,
registry::NumberOfFailedSuboperations, as_int)
- DCMTKPP_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
+ ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
message.get_command_set(), number_of_warning_sub_operations,
registry::NumberOfWarningSuboperations, as_int)
@@ -77,3 +80,5 @@ CMoveResponse
}
}
+
+}
diff --git a/src/dcmtkpp/CMoveResponse.h b/src/odil/message/CMoveResponse.h
similarity index 63%
rename from src/dcmtkpp/CMoveResponse.h
rename to src/odil/message/CMoveResponse.h
index 21a5f48..abded08 100644
--- a/src/dcmtkpp/CMoveResponse.h
+++ b/src/odil/message/CMoveResponse.h
@@ -1,26 +1,42 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 _b245f6f2_50c3_4c7c_80e1_f03d9c831301
#define _b245f6f2_50c3_4c7c_80e1_f03d9c831301
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Response.h"
-#include "dcmtkpp/Value.h"
+#include "odil/DataSet.h"
+#include "odil/registry.h"
+#include "odil/message/Response.h"
+#include "odil/Value.h"
+
+namespace odil
+{
-namespace dcmtkpp
+namespace message
{
/// @brief C-MOVE-RSP message.
class CMoveResponse: public Response
{
public:
+ /// @brief C-MOVE status codes, PS 3.4, C.4.2.1.5
+ enum Status
+ {
+ // Failure
+ RefusedOutOfResourcesUnableToCalculateNumberOfMatches=0xA701,
+ RefusedOutOfResourcesUnableToPerformSubOperations=0xA702,
+ RefusedMoveDestinationUnknown=0xA801,
+ IdentifierDoesNotMatchSOPClass=0xA900,
+ UnableToProcess=0xC000,
+ // Warning
+ SubOperationsCompleteOneOrMoreFailuresOrWarnings=0xB000
+ };
+
/**
* @brief Create an move response with given Message ID, and status.
*/
@@ -44,21 +60,23 @@ public:
/// @brief Destructor.
virtual ~CMoveResponse();
-
- DCMTKPP_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(message_id, registry::MessageID)
- DCMTKPP_MESSAGE_OPTIONAL_FIELD_STRING_MACRO(
+
+ ODIL_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(message_id, registry::MessageID)
+ ODIL_MESSAGE_OPTIONAL_FIELD_STRING_MACRO(
affected_sop_class_uid, registry::AffectedSOPClassUID)
- DCMTKPP_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(
+ ODIL_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(
number_of_remaining_sub_operations, registry::NumberOfRemainingSuboperations)
- DCMTKPP_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(
+ ODIL_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(
number_of_completed_sub_operations, registry::NumberOfCompletedSuboperations)
- DCMTKPP_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(
+ ODIL_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(
number_of_failed_sub_operations, registry::NumberOfFailedSuboperations)
- DCMTKPP_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(
+ ODIL_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(
number_of_warning_sub_operations, registry::NumberOfWarningSuboperations)
};
}
+}
+
#endif // _b245f6f2_50c3_4c7c_80e1_f03d9c831301
diff --git a/src/dcmtkpp/CStoreRequest.cpp b/src/odil/message/CStoreRequest.cpp
similarity index 88%
rename from src/dcmtkpp/CStoreRequest.cpp
rename to src/odil/message/CStoreRequest.cpp
index 1627fc7..e6b1b56 100644
--- a/src/dcmtkpp/CStoreRequest.cpp
+++ b/src/odil/message/CStoreRequest.cpp
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -10,11 +10,13 @@
#include <string>
-#include "dcmtkpp/ElementAccessor.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/Request.h"
+#include "odil/Exception.h"
+#include "odil/message/Request.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace message
{
CStoreRequest
@@ -52,10 +54,10 @@ CStoreRequest
message.get_command_set().as_string(registry::AffectedSOPInstanceUID, 0));
this->set_priority(message.get_command_set().as_int(registry::Priority, 0));
- DCMTKPP_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
+ ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
message.get_command_set(), move_originator_ae_title,
registry::MoveOriginatorApplicationEntityTitle, as_string)
- DCMTKPP_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
+ ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
message.get_command_set(), move_originator_message_id,
registry::MoveOriginatorMessageID, as_int)
@@ -73,3 +75,5 @@ CStoreRequest
}
}
+
+}
diff --git a/src/dcmtkpp/CStoreRequest.h b/src/odil/message/CStoreRequest.h
similarity index 76%
rename from src/dcmtkpp/CStoreRequest.h
rename to src/odil/message/CStoreRequest.h
index e0dacc3..2f20ac8 100644
--- a/src/dcmtkpp/CStoreRequest.h
+++ b/src/odil/message/CStoreRequest.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -11,12 +11,15 @@
#include <string>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Request.h"
-#include "dcmtkpp/Value.h"
+#include "odil/DataSet.h"
+#include "odil/registry.h"
+#include "odil/message/Request.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace message
{
/// @brief C-STORE-RQ message.
@@ -42,17 +45,19 @@ public:
/// @brief Destructor.
virtual ~CStoreRequest();
- DCMTKPP_MESSAGE_MANDATORY_FIELD_STRING_MACRO(
+ ODIL_MESSAGE_MANDATORY_FIELD_STRING_MACRO(
affected_sop_class_uid, registry::AffectedSOPClassUID)
- DCMTKPP_MESSAGE_MANDATORY_FIELD_STRING_MACRO(
+ ODIL_MESSAGE_MANDATORY_FIELD_STRING_MACRO(
affected_sop_instance_uid, registry::AffectedSOPInstanceUID)
- DCMTKPP_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(priority, registry::Priority)
- DCMTKPP_MESSAGE_OPTIONAL_FIELD_STRING_MACRO(
+ ODIL_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(priority, registry::Priority)
+ ODIL_MESSAGE_OPTIONAL_FIELD_STRING_MACRO(
move_originator_ae_title, registry::MoveOriginatorApplicationEntityTitle)
- DCMTKPP_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(
+ ODIL_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(
move_originator_message_id, registry::MoveOriginatorMessageID)
};
}
+}
+
#endif // _b85a19af_b74d_4c96_89a0_f30a41b790b3
diff --git a/src/dcmtkpp/CStoreResponse.cpp b/src/odil/message/CStoreResponse.cpp
similarity index 80%
rename from src/dcmtkpp/CStoreResponse.cpp
rename to src/odil/message/CStoreResponse.cpp
index 1c3a4b7..12ba044 100644
--- a/src/dcmtkpp/CStoreResponse.cpp
+++ b/src/odil/message/CStoreResponse.cpp
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -8,12 +8,15 @@
#include "CStoreResponse.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Response.h"
-#include "dcmtkpp/Value.h"
+#include "odil/Exception.h"
+#include "odil/registry.h"
+#include "odil/message/Response.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace message
{
CStoreResponse
@@ -34,12 +37,12 @@ CStoreResponse
}
this->set_command_field(message.get_command_field());
- DCMTKPP_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
+ ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
message.get_command_set(), message_id, registry::MessageID, as_int)
- DCMTKPP_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
+ ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
message.get_command_set(), affected_sop_class_uid,
registry::AffectedSOPClassUID, as_string)
- DCMTKPP_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
+ ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(
message.get_command_set(),
affected_sop_instance_uid, registry::AffectedSOPInstanceUID, as_string)
@@ -57,3 +60,5 @@ CStoreResponse
}
}
+
+}
diff --git a/src/dcmtkpp/CStoreResponse.h b/src/odil/message/CStoreResponse.h
similarity index 62%
rename from src/dcmtkpp/CStoreResponse.h
rename to src/odil/message/CStoreResponse.h
index 0565073..7ed82d1 100644
--- a/src/dcmtkpp/CStoreResponse.h
+++ b/src/odil/message/CStoreResponse.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -9,17 +9,33 @@
#ifndef _7e193624_081c_47dd_a011_986e96916ea9
#define _7e193624_081c_47dd_a011_986e96916ea9
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Response.h"
-#include "dcmtkpp/Value.h"
+#include "odil/registry.h"
+#include "odil/message/Response.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace message
{
/// @brief C-STORE-RSP message.
class CStoreResponse: public Response
{
public:
+ /// @brief C-STORE status codes, PS 3.4, B.2.3
+ enum Status
+ {
+ // Failure
+ RefusedOutOfResources=0xA700,
+ ErrorDataSetDoesNotMatchSOPClass=0xA900,
+ ErrorCannotUnderstand=0xC000,
+ // Warning
+ CoercionOfDataElements=0xB000,
+ DataSetDoesNotMatchSOPClass=0xB007,
+ ElementsDiscarded=0xB006,
+ };
+
/**
* @brief Create an store response with given Message ID, and status.
*/
@@ -36,13 +52,15 @@ public:
/// @brief Destructor.
virtual ~CStoreResponse();
- DCMTKPP_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(message_id, registry::MessageID)
- DCMTKPP_MESSAGE_OPTIONAL_FIELD_STRING_MACRO(
+ ODIL_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(message_id, registry::MessageID)
+ ODIL_MESSAGE_OPTIONAL_FIELD_STRING_MACRO(
affected_sop_class_uid, registry::AffectedSOPClassUID)
- DCMTKPP_MESSAGE_OPTIONAL_FIELD_STRING_MACRO(
+ ODIL_MESSAGE_OPTIONAL_FIELD_STRING_MACRO(
affected_sop_instance_uid, registry::AffectedSOPInstanceUID)
};
}
+}
+
#endif // _7e193624_081c_47dd_a011_986e96916ea9
diff --git a/src/dcmtkpp/Cancellation.cpp b/src/odil/message/Cancellation.cpp
similarity index 76%
rename from src/dcmtkpp/Cancellation.cpp
rename to src/odil/message/Cancellation.cpp
index af23345..0a77a0f 100644
--- a/src/dcmtkpp/Cancellation.cpp
+++ b/src/odil/message/Cancellation.cpp
@@ -1,23 +1,26 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 "dcmtkpp/Cancellation.h"
+#include "odil/message/Cancellation.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/Message.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Value.h"
+#include "odil/Exception.h"
+#include "odil/message/Message.h"
+#include "odil/registry.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace message
{
Cancellation
-::Cancellation(Uint16 message_id_being_responded_to)
+::Cancellation(uint16_t message_id_being_responded_to)
: Message()
{
this->set_command_field(Command::C_CANCEL_RQ);
@@ -44,3 +47,5 @@ Cancellation
}
}
+
+}
diff --git a/src/dcmtkpp/Cancellation.h b/src/odil/message/Cancellation.h
similarity index 78%
rename from src/dcmtkpp/Cancellation.h
rename to src/odil/message/Cancellation.h
index d34d4f9..8290486 100644
--- a/src/dcmtkpp/Cancellation.h
+++ b/src/odil/message/Cancellation.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -9,11 +9,14 @@
#ifndef _97fc1bfc_4cff_40f2_a1ed_4550c71a0bda
#define _97fc1bfc_4cff_40f2_a1ed_4550c71a0bda
-#include "dcmtkpp/Message.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Value.h"
+#include "odil/message/Message.h"
+#include "odil/registry.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace message
{
/// @brief Base class for cancellation messages.
@@ -21,7 +24,7 @@ class Cancellation: public Message
{
public:
/// @brief Create a response with given message id being responded to.
- Cancellation(Uint16 message_id_being_responded_to);
+ Cancellation(uint16_t message_id_being_responded_to);
/**
* @brief Create a response from the Message ID Being Responded To.
@@ -33,10 +36,12 @@ public:
/// @brief Destructor.
~Cancellation();
- DCMTKPP_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(
+ ODIL_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(
message_id_being_responded_to, registry::MessageIDBeingRespondedTo);
};
}
+}
+
#endif // _97fc1bfc_4cff_40f2_a1ed_4550c71a0bda
diff --git a/src/dcmtkpp/Message.cpp b/src/odil/message/Message.cpp
similarity index 90%
rename from src/dcmtkpp/Message.cpp
rename to src/odil/message/Message.cpp
index fed075c..ff0836a 100644
--- a/src/dcmtkpp/Message.cpp
+++ b/src/odil/message/Message.cpp
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -8,12 +8,15 @@
#include "Message.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Value.h"
+#include "odil/DataSet.h"
+#include "odil/Exception.h"
+#include "odil/registry.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace message
{
Message
@@ -92,3 +95,5 @@ Message
}
}
+
+}
diff --git a/src/dcmtkpp/Message.h b/src/odil/message/Message.h
similarity index 77%
rename from src/dcmtkpp/Message.h
rename to src/odil/message/Message.h
index d540fd6..5a74391 100644
--- a/src/dcmtkpp/Message.h
+++ b/src/odil/message/Message.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -9,14 +9,17 @@
#ifndef _dcfa5213_ad7e_4194_8b4b_e630aa0df2e8
#define _dcfa5213_ad7e_4194_8b4b_e630aa0df2e8
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Value.h"
+#include "odil/DataSet.h"
+#include "odil/registry.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
{
-#define DCMTKPP_MESSAGE_MANDATORY_FIELD_MACRO(name, tag, TValueType, function) \
+namespace message
+{
+
+#define ODIL_MESSAGE_MANDATORY_FIELD_MACRO(name, tag, TValueType, function) \
/** @brief Return the tag element of the command set. */ \
TValueType const & get_##name() const \
{ \
@@ -37,8 +40,8 @@ namespace dcmtkpp
this->_command_set.function(tag) = { value }; \
}
-#define DCMTKPP_MESSAGE_OPTIONAL_FIELD_MACRO(name, tag, TValueType, function) \
- DCMTKPP_MESSAGE_MANDATORY_FIELD_MACRO(name, tag, TValueType, function) \
+#define ODIL_MESSAGE_OPTIONAL_FIELD_MACRO(name, tag, TValueType, function) \
+ ODIL_MESSAGE_MANDATORY_FIELD_MACRO(name, tag, TValueType, function) \
bool has_##name() const \
{ \
return this->_command_set.has(tag);; \
@@ -48,23 +51,23 @@ namespace dcmtkpp
this->_command_set.remove(tag); \
}
-#define DCMTKPP_MESSAGE_SET_OPTIONAL_FIELD_MACRO(dataset, name, tag, function) \
+#define ODIL_MESSAGE_SET_OPTIONAL_FIELD_MACRO(dataset, name, tag, function) \
if(dataset.has(tag)) \
{ \
this->set_##name(dataset.function(tag, 0)); \
}
-#define DCMTKPP_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(name, tag) \
- DCMTKPP_MESSAGE_MANDATORY_FIELD_MACRO(name, tag, Value::Integer, as_int)
+#define ODIL_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(name, tag) \
+ ODIL_MESSAGE_MANDATORY_FIELD_MACRO(name, tag, Value::Integer, as_int)
-#define DCMTKPP_MESSAGE_MANDATORY_FIELD_STRING_MACRO(name, tag) \
- DCMTKPP_MESSAGE_MANDATORY_FIELD_MACRO(name, tag, Value::String, as_string)
+#define ODIL_MESSAGE_MANDATORY_FIELD_STRING_MACRO(name, tag) \
+ ODIL_MESSAGE_MANDATORY_FIELD_MACRO(name, tag, Value::String, as_string)
-#define DCMTKPP_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(name, tag) \
- DCMTKPP_MESSAGE_OPTIONAL_FIELD_MACRO(name, tag, Value::Integer, as_int)
+#define ODIL_MESSAGE_OPTIONAL_FIELD_INTEGER_MACRO(name, tag) \
+ ODIL_MESSAGE_OPTIONAL_FIELD_MACRO(name, tag, Value::Integer, as_int)
-#define DCMTKPP_MESSAGE_OPTIONAL_FIELD_STRING_MACRO(name, tag) \
- DCMTKPP_MESSAGE_OPTIONAL_FIELD_MACRO(name, tag, Value::String, as_string)
+#define ODIL_MESSAGE_OPTIONAL_FIELD_STRING_MACRO(name, tag) \
+ ODIL_MESSAGE_OPTIONAL_FIELD_MACRO(name, tag, Value::String, as_string)
/**
* @brief Base class for all DIMSE messages.
@@ -162,7 +165,7 @@ public:
/// @brief Delete the data set in this message.
void delete_data_set();
- DCMTKPP_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(
+ ODIL_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(
command_field, registry::CommandField)
protected:
@@ -175,4 +178,6 @@ protected:
}
+}
+
#endif // _dcfa5213_ad7e_4194_8b4b_e630aa0df2e8
diff --git a/src/dcmtkpp/Request.cpp b/src/odil/message/Request.cpp
similarity index 79%
rename from src/dcmtkpp/Request.cpp
rename to src/odil/message/Request.cpp
index 403fbd0..d4ca5e4 100644
--- a/src/dcmtkpp/Request.cpp
+++ b/src/odil/message/Request.cpp
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -8,11 +8,14 @@
#include "Request.h"
-#include "dcmtkpp/Message.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Value.h"
+#include "odil/message/Message.h"
+#include "odil/registry.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace message
{
Request
@@ -36,3 +39,5 @@ Request
}
}
+
+}
diff --git a/src/dcmtkpp/Request.h b/src/odil/message/Request.h
similarity index 82%
rename from src/dcmtkpp/Request.h
rename to src/odil/message/Request.h
index 6b74ad2..63aba04 100644
--- a/src/dcmtkpp/Request.h
+++ b/src/odil/message/Request.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -9,11 +9,14 @@
#ifndef _8d06a300_6aee_4d1f_bf10_ecdf4916ae9f
#define _8d06a300_6aee_4d1f_bf10_ecdf4916ae9f
-#include "dcmtkpp/Message.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Value.h"
+#include "odil/message/Message.h"
+#include "odil/registry.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
+{
+
+namespace message
{
/// @brief Base class for all DIMSE request messages.
@@ -34,10 +37,12 @@ public:
/// @brief Destructor.
virtual ~Request();
- DCMTKPP_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(
+ ODIL_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(
message_id, registry::MessageID)
};
}
+}
+
#endif // _8d06a300_6aee_4d1f_bf10_ecdf4916ae9f
diff --git a/src/odil/message/Response.cpp b/src/odil/message/Response.cpp
new file mode 100644
index 0000000..fdeb491
--- /dev/null
+++ b/src/odil/message/Response.cpp
@@ -0,0 +1,97 @@
+/*************************************************************************
+ * 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 "Response.h"
+
+#include "odil/message/Message.h"
+#include "odil/registry.h"
+#include "odil/Value.h"
+
+namespace odil
+{
+
+namespace message
+{
+
+bool
+Response
+::is_pending(Value::Integer status)
+{
+ return (status == Response::Pending || status == 0xFF01);
+}
+
+bool
+Response
+::is_warning(Value::Integer status)
+{
+ return (
+ status == 0x0001 || (status>>12) == 0xB ||
+ status == Response::AttributeListError ||
+ status == Response::AttributeValueOutOfRange);
+}
+
+bool
+Response
+::is_failure(Value::Integer status)
+{
+ return (
+ (status>>12) == 0xA || (status>>12) == 0xC ||
+ ((status>>8) == 0x01 && !is_warning(status)) ||
+ (status>>8) == 0x02
+ );
+}
+
+Response
+::Response(Value::Integer message_id_being_responded_to, Value::Integer status)
+: Message()
+{
+ this->set_message_id_being_responded_to(message_id_being_responded_to);
+ this->set_status(status);
+}
+
+Response
+::Response(Message const & message)
+: Message()
+{
+ this->set_message_id_being_responded_to(
+ message.get_command_set().as_int(
+ registry::MessageIDBeingRespondedTo, 0));
+
+ this->set_status(message.get_command_set().as_int(registry::Status, 0));
+}
+
+Response
+::~Response()
+{
+ // Nothing to do.
+}
+
+bool
+Response
+::is_pending() const
+{
+ return Response::is_pending(this->get_status());
+}
+
+bool
+Response
+::is_warning() const
+{
+ return Response::is_warning(this->get_status());
+}
+
+bool
+Response
+::is_failure() const
+{
+ return Response::is_failure(this->get_status());
+}
+
+}
+
+}
diff --git a/src/odil/message/Response.h b/src/odil/message/Response.h
new file mode 100644
index 0000000..a1f44b6
--- /dev/null
+++ b/src/odil/message/Response.h
@@ -0,0 +1,103 @@
+/*************************************************************************
+ * 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 _0dd2e31e_212a_494a_a8d3_93b235336658
+#define _0dd2e31e_212a_494a_a8d3_93b235336658
+
+#include "odil/message/Message.h"
+#include "odil/registry.h"
+#include "odil/Value.h"
+
+namespace odil
+{
+
+namespace message
+{
+
+/// @brief Base class for all DIMSE response messages.
+class Response: public Message
+{
+public:
+ /// @brief General status codes, from PS3.7, C
+ enum Status
+ {
+ Success=0x0000,
+ // Warning: 0001 or Bxxx
+ // Failure: Axxx or Cxxx
+ Cancel=0xFE00,
+ Pending=0xFF00,
+
+ // Warning Status Classes, PS3.7, C.3
+ AttributeListError=0x0107,
+ AttributeValueOutOfRange=0x0116,
+
+ // Failure Status Classes, PS3.7, C.4
+ SOPClassNotSupported=0x0122,
+ ClassInstanceConflict=0x0119,
+ DuplicateSOPInstance=0x0111,
+ DuplicateInvocation=0x0210,
+ InvalidArgumentValue=0x0115,
+ InvalidAttributeValue=0x0106,
+ InvalidObjectInstance=0x0117,
+ MissingAttribute=0x0120,
+ MissingAttributeValue=0x0121,
+ MistypedArgument=0x0212,
+ NoSuchArgument=0x0114,
+ NoSuchAttribute=0x0105,
+ NoSuchEventType=0x0113,
+ NoSuchSOPInstance=0x0112,
+ NoSuchSOPClass=0x0118,
+ ProcessingFailure=0x0110,
+ ResourceLimitation=0x0213,
+ UnrecognizedOperation=0x0211,
+ NoSuchActionType=0x0123,
+ RefusedNotAuthorized=0x0124,
+ };
+
+ /// @brief Test whether the status class is pending.
+ static bool is_pending(Value::Integer status);
+
+ /// @brief Test whether the status class is warning.
+ static bool is_warning(Value::Integer status);
+
+ /// @brief Test whether the status class is failure.
+ static bool is_failure(Value::Integer status);
+
+ /// @brief Create a response with given message id and status;
+ Response(Value::Integer message_id_being_responded_to, Value::Integer status);
+
+ /**
+ * @brief Create a response from the Message ID Being Responded To and the
+ * Status stored in the message command set.
+ *
+ * Raise an exception is either of those elements is missing.
+ */
+ Response(Message const & message);
+
+ /// @brief Destructor.
+ virtual ~Response();
+
+ ODIL_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(
+ message_id_being_responded_to, registry::MessageIDBeingRespondedTo)
+ ODIL_MESSAGE_MANDATORY_FIELD_INTEGER_MACRO(status, registry::Status)
+
+ /// @brief Test whether the status class is pending.
+ bool is_pending() const;
+
+ /// @brief Test whether the status class is warning.
+ bool is_warning() const;
+
+ /// @brief Test whether the status class is failure.
+ bool is_failure() const;
+};
+
+}
+
+}
+
+#endif // _0dd2e31e_212a_494a_a8d3_93b235336658
diff --git a/src/odil/pdu/AAbort.cpp b/src/odil/pdu/AAbort.cpp
new file mode 100644
index 0000000..323c476
--- /dev/null
+++ b/src/odil/pdu/AAbort.cpp
@@ -0,0 +1,93 @@
+/*************************************************************************
+ * 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/pdu/AAbort.h"
+
+#include <cstdint>
+#include <istream>
+
+#include "odil/Exception.h"
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+AAbort
+::AAbort(unsigned char source, unsigned char reason)
+{
+ this->_item.add("PDU-type", uint8_t(0x07));
+ this->_item.add("Reserved-1", uint8_t(0));
+ this->_item.add("PDU-length", uint32_t(4));
+ this->_item.add("Reserved-2", uint8_t(0));
+ this->_item.add("Reserved-3", uint8_t(0));
+ this->_item.add("Source", uint8_t(0));
+ this->_item.add("Reason", uint8_t(0));
+
+ this->set_source(source);
+ this->set_reason(reason);
+}
+
+AAbort
+::AAbort(std::istream & stream)
+{
+ this->_item.read(stream, "PDU-type", Item::Field::Type::unsigned_int_8);
+ if(this->_item.as_unsigned_int_8("PDU-type") != 0x07)
+ {
+ throw Exception("Invalid PDU type");
+ }
+
+ this->_item.read(stream, "Reserved-1", Item::Field::Type::unsigned_int_8);
+ this->_item.read(stream, "PDU-length", Item::Field::Type::unsigned_int_32);
+ this->_item.read(stream, "Reserved-2", Item::Field::Type::unsigned_int_8);
+ this->_item.read(stream, "Reserved-3", Item::Field::Type::unsigned_int_8);
+ this->_item.read(stream, "Source", Item::Field::Type::unsigned_int_8);
+ this->_item.read(stream, "Reason", Item::Field::Type::unsigned_int_8);
+}
+
+unsigned char
+AAbort
+::get_source() const
+{
+ return this->_item.as_unsigned_int_8("Source");
+}
+
+void
+AAbort
+::set_source(unsigned char source)
+{
+ if(source > 2)
+ {
+ throw Exception("Unknown source");
+ }
+ this->_item.as_unsigned_int_8("Source") = source;
+}
+
+unsigned char
+AAbort
+::get_reason() const
+{
+ return this->_item.as_unsigned_int_8("Reason");
+}
+
+void
+AAbort
+::set_reason(unsigned char reason)
+{
+ if(reason > 6)
+ {
+ throw Exception("Unknown reason");
+ }
+ this->_item.as_unsigned_int_8("Reason") = reason;
+}
+
+}
+
+}
diff --git a/src/odil/pdu/AAbort.h b/src/odil/pdu/AAbort.h
new file mode 100644
index 0000000..7da2f07
--- /dev/null
+++ b/src/odil/pdu/AAbort.h
@@ -0,0 +1,49 @@
+/*************************************************************************
+ * 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 _536f93f0_914e_40ff_8917_36644acbc4b1
+#define _536f93f0_914e_40ff_8917_36644acbc4b1
+
+#include <istream>
+
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+/// @brief A-ABORT PDU, cf. PS 3.8, 9.3.8.
+class AAbort: public Object
+{
+public:
+ /// @brief Constructor.
+ AAbort(unsigned char source, unsigned char reason);
+
+ /// @brief Constructor for binary data.
+ AAbort(std::istream & stream);
+
+ /// @brief Return the source.
+ unsigned char get_source() const;
+
+ /// @brief Set the source, must be 0, 1 or 2.
+ void set_source(unsigned char source);
+
+ /// @brief Return the reason.
+ unsigned char get_reason() const;
+
+ /// @brief Set the reason, must be between 0 and 6.
+ void set_reason(unsigned char reason);
+};
+
+}
+
+}
+
+#endif // _536f93f0_914e_40ff_8917_36644acbc4b1
diff --git a/src/odil/pdu/AAssociate.cpp b/src/odil/pdu/AAssociate.cpp
new file mode 100644
index 0000000..9d820fd
--- /dev/null
+++ b/src/odil/pdu/AAssociate.cpp
@@ -0,0 +1,272 @@
+/*************************************************************************
+ * 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/pdu/AAssociate.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <istream>
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include "odil/Exception.h"
+#include "odil/pdu/ApplicationContext.h"
+#include "odil/pdu/Item.h"
+#include "odil/pdu/Object.h"
+#include "odil/pdu/PresentationContextAC.h"
+#include "odil/pdu/PresentationContextRQ.h"
+#include "odil/pdu/UserInformation.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+AAssociate
+::AAssociate()
+{
+ this->_item.add("PDU-type", uint8_t(0));
+ this->_item.add("Reserved-1", uint8_t(0));
+ this->_item.add("PDU-length", uint32_t(0));
+ this->_item.add("Protocol-version", uint16_t(0));
+ this->_item.add("Reserved-2", uint16_t(0));
+ this->_item.add("Called-AE-title", std::string());
+ this->_item.add("Calling-AE-title", std::string());
+ this->_item.add("Reserved-3", std::string(32, '\0'));
+ this->_item.add("Variable-items", std::vector<Item>());
+
+ this->_item.as_unsigned_int_32("PDU-length") = this->_compute_length();
+}
+
+AAssociate
+::AAssociate(std::istream & stream)
+{
+ this->_item.read(stream, "PDU-type", Item::Field::Type::unsigned_int_8);
+ auto const type = this->_item.as_unsigned_int_8("PDU-type");
+ if(type != 0x01 && type != 0x02)
+ {
+ throw Exception("Invalid PDU type");
+ }
+
+ this->_item.read(stream, "Reserved-1", Item::Field::Type::unsigned_int_8);
+ this->_item.read(stream, "PDU-length", Item::Field::Type::unsigned_int_32);
+
+ auto const begin = stream.tellg();
+
+ this->_item.read(
+ stream, "Protocol-version", Item::Field::Type::unsigned_int_16);
+ this->_item.read(stream, "Reserved-2", Item::Field::Type::unsigned_int_16);
+ this->_item.read(stream, "Called-AE-title", Item::Field::Type::string, 16);
+ this->_item.read(stream, "Calling-AE-title", Item::Field::Type::string, 16);
+ this->_item.read(stream, "Reserved-3", Item::Field::Type::string, 32);
+
+ auto const length = this->_item.as_unsigned_int_32("PDU-length");
+
+ this->_item.add("Variable-items", std::vector<Item>());
+ auto & variable_items = this->_item.as_items("Variable-items");
+ while(stream.tellg()-begin < length)
+ {
+ auto const type = stream.peek();
+ Item sub_item;
+ if(type == 0x10)
+ {
+ sub_item = ApplicationContext(stream).get_item();
+ }
+ else if(type == 0x20)
+ {
+ sub_item = PresentationContextRQ(stream).get_item();
+ }
+ else if(type == 0x21)
+ {
+ sub_item = PresentationContextAC(stream).get_item();
+ }
+ else if(type == 0x50)
+ {
+ sub_item = UserInformation(stream).get_item();
+ }
+ else
+ {
+ throw Exception("Invalid sub-item");
+ }
+
+ variable_items.push_back(sub_item);
+ }
+
+ this->_item.as_unsigned_int_32("PDU-length") = this->_compute_length();
+}
+
+AAssociate
+::~AAssociate()
+{
+ // Nothing to do.
+}
+
+uint16_t
+AAssociate
+::get_protocol_version() const
+{
+ return this->_item.as_unsigned_int_16("Protocol-version");
+}
+
+void
+AAssociate
+::set_protocol_version(uint16_t value)
+{
+ this->_item.as_unsigned_int_16("Protocol-version") = value;
+}
+
+std::string
+AAssociate
+::get_called_ae_title() const
+{
+ return this->_decode_ae_title(this->_item.as_string("Called-AE-title"));
+}
+
+void
+AAssociate
+::set_called_ae_title(std::string const & value)
+{
+ this->_item.as_string("Called-AE-title") = this->_encode_ae_title(value);
+}
+
+std::string
+AAssociate
+::get_calling_ae_title() const
+{
+ return this->_decode_ae_title(this->_item.as_string("Calling-AE-title"));
+}
+
+void
+AAssociate
+::set_calling_ae_title(std::string const & value)
+{
+ this->_item.as_string("Calling-AE-title") = this->_encode_ae_title(value);
+}
+
+ApplicationContext
+AAssociate
+::get_application_context() const
+{
+ auto const & sub_items = this->_item.as_items("Variable-items");
+ auto const it = std::find_if(
+ sub_items.begin(), sub_items.end(),
+ [](Item const & x) {
+ return x.as_unsigned_int_8("Item-type") == 0x10; });
+ if(it == sub_items.end())
+ {
+ throw Exception("No Application Context");
+ }
+
+ std::stringstream stream;
+ stream << *it;
+ return ApplicationContext(stream);
+}
+
+void
+AAssociate
+::set_application_context(ApplicationContext const & value)
+{
+ auto const & old_items = this->_item.as_items("Variable-items");
+ std::vector<Item> new_items;
+
+ new_items.push_back(value.get_item());
+
+ std::copy_if(
+ old_items.begin(), old_items.end(), std::back_inserter(new_items),
+ [](Item const & item) {
+ return item.as_unsigned_int_8("Item-type") == 0x21; });
+
+ std::copy_if(
+ old_items.begin(), old_items.end(), std::back_inserter(new_items),
+ [](Item const & item) {
+ return item.as_unsigned_int_8("Item-type") == 0x50; });
+
+ this->_item.as_items("Variable-items") = new_items;
+
+ this->_item.as_unsigned_int_32("PDU-length") = this->_compute_length();
+}
+
+UserInformation
+AAssociate
+::get_user_information() const
+{
+ auto const & sub_items = this->_item.as_items("Variable-items");
+ auto const it = std::find_if(
+ sub_items.begin(), sub_items.end(),
+ [](Item const & x) {
+ return x.as_unsigned_int_8("Item-type") == 0x50; });
+ if(it == sub_items.end())
+ {
+ throw Exception("No User Information");
+ }
+
+ std::stringstream stream;
+ stream << *it;
+ return UserInformation(stream);
+}
+
+void
+AAssociate
+::set_user_information(UserInformation const & value)
+{
+ auto const & old_items = this->_item.as_items("Variable-items");
+ std::vector<Item> new_items;
+
+ std::copy_if(
+ old_items.begin(), old_items.end(), std::back_inserter(new_items),
+ [](Item const & item) {
+ return item.as_unsigned_int_8("Item-type") == 0x10; });
+
+ std::copy_if(
+ old_items.begin(), old_items.end(), std::back_inserter(new_items),
+ [](Item const & item) {
+ return (
+ item.as_unsigned_int_8("Item-type") == 0x20 ||
+ item.as_unsigned_int_8("Item-type") == 0x21); });
+
+ new_items.push_back(value.get_item());
+
+ this->_item.as_items("Variable-items") = new_items;
+
+ this->_item.as_unsigned_int_32("PDU-length") = this->_compute_length();
+}
+
+std::string
+AAssociate
+::_encode_ae_title(std::string const & value)
+{
+ if(value.empty() || value.size() > 16)
+ {
+ throw Exception("Invalid AE title");
+ }
+
+ auto const padded = value+std::string(16-value.size(), ' ');
+ return padded;
+}
+
+std::string
+AAssociate
+::_decode_ae_title(std::string const & value)
+{
+ auto const begin = value.find_first_not_of(' ');
+ if(begin == std::string::npos)
+ {
+ return "";
+ }
+ else
+ {
+ auto const end = value.find_last_not_of(' ');
+ return value.substr(begin, end-begin+1);
+ }
+}
+
+}
+
+}
diff --git a/src/odil/pdu/AAssociate.h b/src/odil/pdu/AAssociate.h
new file mode 100644
index 0000000..ed18e27
--- /dev/null
+++ b/src/odil/pdu/AAssociate.h
@@ -0,0 +1,91 @@
+/*************************************************************************
+ * 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 _00e9792a_98a8_4ca7_9272_43dcd7e331ea
+#define _00e9792a_98a8_4ca7_9272_43dcd7e331ea
+
+#include <cstdint>
+#include <istream>
+#include <string>
+
+#include "odil/pdu/ApplicationContext.h"
+#include "odil/pdu/Object.h"
+#include "odil/pdu/UserInformation.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+/// @brief A-ASSOCIATE-RQ and A-ASSOCIATE-AC PDU, cf. PS 3.8, 9.3.2 and 9.3.3.
+class AAssociate: public Object
+{
+public:
+ /// @brief Constructor.
+ AAssociate();
+
+ /// @brief Constructor for binary data.
+ AAssociate(std::istream & stream);
+
+ /// @brief Destructor.
+ virtual ~AAssociate() =0;
+
+ /// @brief Return the protocol version.
+ uint16_t get_protocol_version() const;
+
+ /// @brief Set the protocol version.
+ void set_protocol_version(uint16_t value);
+
+ /// @brief Return the called AE title.
+ std::string get_called_ae_title() const;
+
+ /**
+ * @brief Set the called AE title.
+ *
+ * An exception is raised if the value is empty or if it is longer than
+ * 16 characters.
+ */
+ void set_called_ae_title(std::string const & value);
+
+ /// @brief Return the calling AE title.
+ std::string get_calling_ae_title() const;
+
+ /**
+ * @brief Set the calling AE title.
+ *
+ * An exception is raised if the value is empty or if it is longer than
+ * 16 characters.
+ */
+ void set_calling_ae_title(std::string const & value);
+
+ /// @brief Return the Application Context sub-item.
+ ApplicationContext get_application_context() const;
+
+ /// @brief Set the Application Context sub-item.
+ void set_application_context(ApplicationContext const & value);
+
+ /// @brief Return the User Information sub-item.
+ UserInformation get_user_information() const;
+
+ /// @brief Set the User Information sub-item.
+ void set_user_information(UserInformation const & value);
+
+private:
+ /// @brief Pad the value with spaces so that it is 16 characters long.
+ static std::string _encode_ae_title(std::string const & value);
+
+ /// @brief Remove the leading and trailing spaces.
+ static std::string _decode_ae_title(std::string const & value);
+};
+
+}
+
+}
+
+#endif // _00e9792a_98a8_4ca7_9272_43dcd7e331ea
diff --git a/src/odil/pdu/AAssociateAC.cpp b/src/odil/pdu/AAssociateAC.cpp
new file mode 100644
index 0000000..c57c209
--- /dev/null
+++ b/src/odil/pdu/AAssociateAC.cpp
@@ -0,0 +1,93 @@
+/*************************************************************************
+ * 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/pdu/AAssociateAC.h"
+
+#include <algorithm>
+#include <sstream>
+#include <vector>
+
+#include "odil/Exception.h"
+#include "odil/pdu/AAssociate.h"
+#include "odil/pdu/PresentationContextAC.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+AAssociateAC
+::AAssociateAC()
+: AAssociate()
+{
+ this->_item.as_unsigned_int_8("PDU-type") = 0x02;
+}
+
+AAssociateAC
+::AAssociateAC(std::istream & stream)
+: AAssociate(stream)
+{
+ if(this->_item.as_unsigned_int_8("PDU-type") != 0x02)
+ {
+ throw Exception("Invalid PDU type");
+ }
+}
+
+AAssociateAC
+::~AAssociateAC()
+{
+ // Nothing to do.
+}
+
+std::vector<PresentationContextAC>
+AAssociateAC
+::get_presentation_contexts() const
+{
+ std::vector<PresentationContextAC> result;
+ for(auto const & item: this->_item.as_items("Variable-items"))
+ {
+ if(item.as_unsigned_int_8("Item-type") == 0x21)
+ {
+ std::stringstream stream;
+ stream << item;
+ result.push_back(PresentationContextAC(stream));
+ }
+ }
+
+ return result;
+}
+
+void
+AAssociateAC
+::set_presentation_contexts(std::vector<PresentationContextAC> const & value)
+{
+ auto const & old_items = this->_item.as_items("Variable-items");
+ std::vector<Item> new_items;
+
+ std::copy_if(
+ old_items.begin(), old_items.end(), std::back_inserter(new_items),
+ [](Item const & item) {
+ return item.as_unsigned_int_8("Item-type") == 0x10; });
+
+ std::transform(
+ value.begin(), value.end(), std::back_inserter(new_items),
+ [](PresentationContext const & x) { return x.get_item(); });
+
+ std::copy_if(
+ old_items.begin(), old_items.end(), std::back_inserter(new_items),
+ [](Item const & item) {
+ return item.as_unsigned_int_8("Item-type") == 0x50; });
+
+ this->_item.as_items("Variable-items") = new_items;
+ this->_item.as_unsigned_int_32("PDU-length") = this->_compute_length();
+}
+
+}
+
+}
diff --git a/src/odil/pdu/AAssociateAC.h b/src/odil/pdu/AAssociateAC.h
new file mode 100644
index 0000000..3982bdd
--- /dev/null
+++ b/src/odil/pdu/AAssociateAC.h
@@ -0,0 +1,47 @@
+/*************************************************************************
+ * 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 _973ae1d9_c41c_4518_9c35_df13b2253369
+#define _973ae1d9_c41c_4518_9c35_df13b2253369
+
+#include <vector>
+
+#include "odil/pdu/AAssociate.h"
+#include "odil/pdu/PresentationContextAC.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+/// @brief A-ASSOCIATE-AC PDU, cf. PS 3.8, 9.3.3.
+class AAssociateAC: public AAssociate
+{
+public:
+ /// @brief Constructor.
+ AAssociateAC();
+
+ /// @brief Constructor for binary data.
+ AAssociateAC(std::istream & stream);
+
+ /// @brief Destructor.
+ ~AAssociateAC();
+
+ /// @brief Return the Presentation Context sub-items.
+ std::vector<PresentationContextAC> get_presentation_contexts() const;
+
+ /// @brief Set the Presentation Context sub-items.
+ void set_presentation_contexts(std::vector<PresentationContextAC> const & value);
+};
+
+}
+
+}
+
+#endif // _973ae1d9_c41c_4518_9c35_df13b2253369
diff --git a/src/odil/pdu/AAssociateRJ.cpp b/src/odil/pdu/AAssociateRJ.cpp
new file mode 100644
index 0000000..51d643b
--- /dev/null
+++ b/src/odil/pdu/AAssociateRJ.cpp
@@ -0,0 +1,112 @@
+/*************************************************************************
+ * 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/pdu/AAssociateRJ.h"
+
+#include <cstdint>
+#include <istream>
+
+#include "odil/Exception.h"
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+AAssociateRJ
+::AAssociateRJ(unsigned char result, unsigned char source, unsigned char reason)
+{
+ this->_item.add("PDU-type", uint8_t(0x03));
+ this->_item.add("Reserved-1", uint8_t(0));
+ this->_item.add("PDU-length", uint32_t(4));
+ this->_item.add("Reserved-2", uint8_t(0));
+ this->_item.add("Result", uint8_t(0));
+ this->_item.add("Source", uint8_t(0));
+ this->_item.add("Reason", uint8_t(0));
+
+ this->set_result(result);
+ this->set_source(source);
+ this->set_reason(reason);
+}
+
+AAssociateRJ
+::AAssociateRJ(std::istream & stream)
+{
+ this->_item.read(stream, "PDU-type", Item::Field::Type::unsigned_int_8);
+ if(this->_item.as_unsigned_int_8("PDU-type") != 0x03)
+ {
+ throw Exception("Invalid PDU type");
+ }
+
+ this->_item.read(stream, "Reserved-1", Item::Field::Type::unsigned_int_8);
+ this->_item.read(stream, "PDU-length", Item::Field::Type::unsigned_int_32);
+ this->_item.read(stream, "Reserved-2", Item::Field::Type::unsigned_int_8);
+ this->_item.read(stream, "Result", Item::Field::Type::unsigned_int_8);
+ this->_item.read(stream, "Source", Item::Field::Type::unsigned_int_8);
+ this->_item.read(stream, "Reason", Item::Field::Type::unsigned_int_8);
+}
+
+unsigned char
+AAssociateRJ
+::get_result() const
+{
+ return this->_item.as_unsigned_int_8("Result");
+}
+
+void
+AAssociateRJ
+::set_result(unsigned char result)
+{
+ if(result > 2)
+ {
+ throw Exception("Unknown result");
+ }
+ this->_item.as_unsigned_int_8("Result") = result;
+}
+
+unsigned char
+AAssociateRJ
+::get_source() const
+{
+ return this->_item.as_unsigned_int_8("Source");
+}
+
+void
+AAssociateRJ
+::set_source(unsigned char source)
+{
+ if(source > 3)
+ {
+ throw Exception("Unknown source");
+ }
+ this->_item.as_unsigned_int_8("Source") = source;
+}
+
+unsigned char
+AAssociateRJ
+::get_reason() const
+{
+ return this->_item.as_unsigned_int_8("Reason");
+}
+
+void
+AAssociateRJ
+::set_reason(unsigned char reason)
+{
+ if(reason > 10)
+ {
+ throw Exception("Unknown reason");
+ }
+ this->_item.as_unsigned_int_8("Reason") = reason;
+}
+
+}
+
+}
diff --git a/src/odil/pdu/AAssociateRJ.h b/src/odil/pdu/AAssociateRJ.h
new file mode 100644
index 0000000..2886113
--- /dev/null
+++ b/src/odil/pdu/AAssociateRJ.h
@@ -0,0 +1,54 @@
+/*************************************************************************
+ * 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 _3980566c_9185_40a2_8e7d_6286c2cd1959
+#define _3980566c_9185_40a2_8e7d_6286c2cd1959
+
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+/// @brief A-ASSOCIATE-RJ PDU, cf. PS 3.8, 9.3.4.
+class AAssociateRJ: public Object
+{
+public:
+ /// @brief Constructor.
+ AAssociateRJ(
+ unsigned char result, unsigned char source, unsigned char reason);
+
+ /// @brief Constructor from stream.
+ AAssociateRJ(std::istream & stream);
+
+ /// @brief Return the result.
+ unsigned char get_result() const;
+
+ /// @brief Set the result, must be 1 or 2.
+ void set_result(unsigned char result);
+
+ /// @brief Return the source.
+ unsigned char get_source() const;
+
+ /// @brief Set the source, must be 1, 2 or 3.
+ void set_source(unsigned char source);
+
+ /// @brief Return the reason.
+ unsigned char get_reason() const;
+
+ /// @brief Set the reason.
+ void set_reason(unsigned char reason);
+};
+
+}
+
+}
+
+#endif // _3980566c_9185_40a2_8e7d_6286c2cd1959
diff --git a/src/odil/pdu/AAssociateRQ.cpp b/src/odil/pdu/AAssociateRQ.cpp
new file mode 100644
index 0000000..72b0f93
--- /dev/null
+++ b/src/odil/pdu/AAssociateRQ.cpp
@@ -0,0 +1,93 @@
+/*************************************************************************
+ * 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/pdu/AAssociateRQ.h"
+
+#include <algorithm>
+#include <sstream>
+#include <vector>
+
+#include "odil/Exception.h"
+#include "odil/pdu/AAssociate.h"
+#include "odil/pdu/PresentationContextRQ.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+AAssociateRQ
+::AAssociateRQ()
+: AAssociate()
+{
+ this->_item.as_unsigned_int_8("PDU-type") = 0x01;
+}
+
+AAssociateRQ
+::AAssociateRQ(std::istream & stream)
+: AAssociate(stream)
+{
+ if(this->_item.as_unsigned_int_8("PDU-type") != 0x01)
+ {
+ throw Exception("Invalid PDU type");
+ }
+}
+
+AAssociateRQ
+::~AAssociateRQ()
+{
+ // Nothing to do.
+}
+
+std::vector<PresentationContextRQ>
+AAssociateRQ
+::get_presentation_contexts() const
+{
+ std::vector<PresentationContextRQ> result;
+ for(auto const & item: this->_item.as_items("Variable-items"))
+ {
+ if(item.as_unsigned_int_8("Item-type") == 0x20)
+ {
+ std::stringstream stream;
+ stream << item;
+ result.push_back(PresentationContextRQ(stream));
+ }
+ }
+
+ return result;
+}
+
+void
+AAssociateRQ
+::set_presentation_contexts(std::vector<PresentationContextRQ> const & value)
+{
+ auto const & old_items = this->_item.as_items("Variable-items");
+ std::vector<Item> new_items;
+
+ std::copy_if(
+ old_items.begin(), old_items.end(), std::back_inserter(new_items),
+ [](Item const & item) {
+ return item.as_unsigned_int_8("Item-type") == 0x10; });
+
+ std::transform(
+ value.begin(), value.end(), std::back_inserter(new_items),
+ [](PresentationContext const & x) { return x.get_item(); });
+
+ std::copy_if(
+ old_items.begin(), old_items.end(), std::back_inserter(new_items),
+ [](Item const & item) {
+ return item.as_unsigned_int_8("Item-type") == 0x50; });
+
+ this->_item.as_items("Variable-items") = new_items;
+ this->_item.as_unsigned_int_32("PDU-length") = this->_compute_length();
+}
+
+}
+
+}
diff --git a/src/odil/pdu/AAssociateRQ.h b/src/odil/pdu/AAssociateRQ.h
new file mode 100644
index 0000000..98b8809
--- /dev/null
+++ b/src/odil/pdu/AAssociateRQ.h
@@ -0,0 +1,47 @@
+/*************************************************************************
+ * 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 _22fc6fe5_f7d1_461b_932f_cedfcae13897
+#define _22fc6fe5_f7d1_461b_932f_cedfcae13897
+
+#include <vector>
+
+#include "odil/pdu/AAssociate.h"
+#include "odil/pdu/PresentationContextRQ.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+/// @brief A-ASSOCIATE-RQ, cf. PS 3.8, 9.3.2
+class AAssociateRQ: public AAssociate
+{
+public:
+ /// @brief Constructor.
+ AAssociateRQ();
+
+ /// @brief Constructor for binary data.
+ AAssociateRQ(std::istream & stream);
+
+ /// @brief Destructor.
+ ~AAssociateRQ();
+
+ /// @brief Return the Presentation Context sub-items.
+ std::vector<PresentationContextRQ> get_presentation_contexts() const;
+
+ /// @brief Set the Presentation Context sub-items.
+ void set_presentation_contexts(std::vector<PresentationContextRQ> const & value);
+};
+
+}
+
+}
+
+#endif // _22fc6fe5_f7d1_461b_932f_cedfcae13897
diff --git a/src/odil/pdu/AReleaseRP.cpp b/src/odil/pdu/AReleaseRP.cpp
new file mode 100644
index 0000000..98e76a3
--- /dev/null
+++ b/src/odil/pdu/AReleaseRP.cpp
@@ -0,0 +1,48 @@
+/*************************************************************************
+ * 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/pdu/AReleaseRP.h"
+
+#include <cstdint>
+#include <istream>
+
+#include "odil/Exception.h"
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+AReleaseRP
+::AReleaseRP()
+{
+ this->_item.add("PDU-type", uint8_t(0x06));
+ this->_item.add("Reserved-1", uint8_t(0));
+ this->_item.add("PDU-length", uint32_t(4));
+ this->_item.add("Reserved-2", uint32_t(0));
+}
+
+AReleaseRP
+::AReleaseRP(std::istream & stream)
+{
+ this->_item.read(stream, "PDU-type", Item::Field::Type::unsigned_int_8);
+ if(this->_item.as_unsigned_int_8("PDU-type") != 0x06)
+ {
+ throw Exception("Invalid PDU type");
+ }
+
+ this->_item.read(stream, "Reserved-1", Item::Field::Type::unsigned_int_8);
+ this->_item.read(stream, "PDU-length", Item::Field::Type::unsigned_int_32);
+ this->_item.read(stream, "Reserved-2", Item::Field::Type::unsigned_int_32);
+}
+
+}
+
+}
diff --git a/src/odil/pdu/AReleaseRP.h b/src/odil/pdu/AReleaseRP.h
new file mode 100644
index 0000000..d9f0cc2
--- /dev/null
+++ b/src/odil/pdu/AReleaseRP.h
@@ -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.
+ ************************************************************************/
+
+#ifndef _a70fb011_8d69_4768_a84d_7f535621c335
+#define _a70fb011_8d69_4768_a84d_7f535621c335
+
+#include <istream>
+
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+/// @brief A-RELEASE-RP PDU, cf. PS 3.8, 9.3.7.
+class AReleaseRP: public Object
+{
+public:
+ /// @brief Constructor.
+ AReleaseRP();
+
+ /// @brief Constructor from stream.
+ AReleaseRP(std::istream & stream);
+};
+
+}
+
+}
+
+#endif // _a70fb011_8d69_4768_a84d_7f535621c335
diff --git a/src/odil/pdu/AReleaseRQ.cpp b/src/odil/pdu/AReleaseRQ.cpp
new file mode 100644
index 0000000..a866720
--- /dev/null
+++ b/src/odil/pdu/AReleaseRQ.cpp
@@ -0,0 +1,48 @@
+/*************************************************************************
+ * 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/pdu/AReleaseRQ.h"
+
+#include <cstdint>
+#include <istream>
+
+#include "odil/Exception.h"
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+AReleaseRQ
+::AReleaseRQ()
+{
+ this->_item.add("PDU-type", uint8_t(0x05));
+ this->_item.add("Reserved-1", uint8_t(0));
+ this->_item.add("PDU-length", uint32_t(4));
+ this->_item.add("Reserved-2", uint32_t(0));
+}
+
+AReleaseRQ
+::AReleaseRQ(std::istream & stream)
+{
+ this->_item.read(stream, "PDU-type", Item::Field::Type::unsigned_int_8);
+ if(this->_item.as_unsigned_int_8("PDU-type") != 0x05)
+ {
+ throw Exception("Invalid PDU type");
+ }
+
+ this->_item.read(stream, "Reserved-1", Item::Field::Type::unsigned_int_8);
+ this->_item.read(stream, "PDU-length", Item::Field::Type::unsigned_int_32);
+ this->_item.read(stream, "Reserved-2", Item::Field::Type::unsigned_int_32);
+}
+
+}
+
+}
diff --git a/src/odil/pdu/AReleaseRQ.h b/src/odil/pdu/AReleaseRQ.h
new file mode 100644
index 0000000..87c2fcf
--- /dev/null
+++ b/src/odil/pdu/AReleaseRQ.h
@@ -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.
+ ************************************************************************/
+
+#ifndef _87f54a57_5741_4fde_871b_ea9c774795ad
+#define _87f54a57_5741_4fde_871b_ea9c774795ad
+
+#include <istream>
+
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+/// @brief A-RELEASE-RQ PDU, cf. PS 3.8, 9.3.6.
+class AReleaseRQ: public Object
+{
+public:
+ /// @brief Constructor.
+ AReleaseRQ();
+
+ /// @brief Constructor from stream.
+ AReleaseRQ(std::istream & stream);
+};
+
+}
+
+}
+
+#endif // _87f54a57_5741_4fde_871b_ea9c774795ad
diff --git a/src/odil/pdu/ApplicationContext.cpp b/src/odil/pdu/ApplicationContext.cpp
new file mode 100644
index 0000000..ea2f4d0
--- /dev/null
+++ b/src/odil/pdu/ApplicationContext.cpp
@@ -0,0 +1,68 @@
+/*************************************************************************
+ * 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/pdu/ApplicationContext.h"
+
+#include <cstdint>
+#include <istream>
+#include <string>
+
+#include "odil/Exception.h"
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+ApplicationContext
+::ApplicationContext(std::string const & name)
+{
+ this->_item.add("Item-type", uint8_t(0x10));
+ this->_item.add("Reserved", uint8_t(0));
+ this->_item.add("Item-length", uint16_t(0));
+ this->_item.add("Application-context-name", std::string());
+
+ this->set_name(name);
+}
+
+ApplicationContext
+::ApplicationContext(std::istream & stream)
+{
+ this->_item.read(stream, "Item-type", Item::Field::Type::unsigned_int_8);
+ if(this->_item.as_unsigned_int_8("Item-type") != 0x10)
+ {
+ throw Exception("Invalid item type");
+ }
+
+ this->_item.read(stream, "Reserved", Item::Field::Type::unsigned_int_8);
+ this->_item.read(stream, "Item-length", Item::Field::Type::unsigned_int_16);
+ this->_item.read(
+ stream, "Application-context-name", Item::Field::Type::string,
+ this->_item.as_unsigned_int_16("Item-length"));
+}
+
+std::string
+ApplicationContext
+::get_name() const
+{
+ return this->_item.as_string("Application-context-name");
+}
+
+void
+ApplicationContext
+::set_name(std::string const & name)
+{
+ this->_item.as_string("Application-context-name") = name;
+ this->_item.as_unsigned_int_16("Item-length") = this->_compute_length();
+}
+
+}
+
+}
diff --git a/src/odil/pdu/ApplicationContext.h b/src/odil/pdu/ApplicationContext.h
new file mode 100644
index 0000000..7d7260a
--- /dev/null
+++ b/src/odil/pdu/ApplicationContext.h
@@ -0,0 +1,46 @@
+/*************************************************************************
+ * 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 _12bdb8f1_48c2_44d0_957a_bc1fb6f6733a
+#define _12bdb8f1_48c2_44d0_957a_bc1fb6f6733a
+
+#include <istream>
+#include <string>
+
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+/**
+ * @brief Application Context item, (PS 3.8, 9.3.2.1).
+ */
+class ApplicationContext: public Object
+{
+public:
+ /// @brief Create an Application Context.
+ ApplicationContext(std::string const & name);
+
+ /// @brief Read an Application Context from a stream.
+ ApplicationContext(std::istream & stream);
+
+ /// @brief Return the name of the Application Context.
+ std::string get_name() const;
+
+ /// @brief Set the name of the Application Context.
+ void set_name(std::string const & name);
+};
+
+}
+
+}
+
+#endif // _12bdb8f1_48c2_44d0_957a_bc1fb6f6733a
diff --git a/src/odil/pdu/ImplementationClassUID.cpp b/src/odil/pdu/ImplementationClassUID.cpp
new file mode 100644
index 0000000..018f90b
--- /dev/null
+++ b/src/odil/pdu/ImplementationClassUID.cpp
@@ -0,0 +1,68 @@
+/*************************************************************************
+ * 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/pdu/ImplementationClassUID.h"
+
+#include <cstdint>
+#include <istream>
+#include <string>
+
+#include "odil/Exception.h"
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+ImplementationClassUID
+::ImplementationClassUID(std::string const & implementation_class_uid)
+{
+ this->_item.add("Item-type", this->type);
+ this->_item.add("Reserved", uint8_t(0));
+ this->_item.add("Item-length", uint16_t(0));
+ this->_item.add("Implementation-class-uid", std::string());
+
+ this->set_implementation_class_uid(implementation_class_uid);
+}
+
+ImplementationClassUID
+::ImplementationClassUID(std::istream & stream)
+{
+ this->_item.read(stream, "Item-type", Item::Field::Type::unsigned_int_8);
+ if(this->_item.as_unsigned_int_8("Item-type") != this->type)
+ {
+ throw Exception("Invalid item type");
+ }
+
+ this->_item.read(stream, "Reserved", Item::Field::Type::unsigned_int_8);
+ this->_item.read(stream, "Item-length", Item::Field::Type::unsigned_int_16);
+ this->_item.read(
+ stream, "Implementation-class-uid", Item::Field::Type::string,
+ this->_item.as_unsigned_int_16("Item-length"));
+}
+
+std::string
+ImplementationClassUID
+::get_implementation_class_uid() const
+{
+ return this->_item.as_string("Implementation-class-uid");
+}
+
+void
+ImplementationClassUID
+::set_implementation_class_uid(std::string const & value)
+{
+ this->_item.as_string("Implementation-class-uid") = value;
+ this->_item.as_unsigned_int_16("Item-length") = this->_compute_length();
+}
+
+}
+
+}
diff --git a/src/odil/pdu/ImplementationClassUID.h b/src/odil/pdu/ImplementationClassUID.h
new file mode 100644
index 0000000..3a3a41f
--- /dev/null
+++ b/src/odil/pdu/ImplementationClassUID.h
@@ -0,0 +1,48 @@
+/*************************************************************************
+ * 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 _6b085e0c_afd5_4ed7_ab4d_6bdb9b28ca67
+#define _6b085e0c_afd5_4ed7_ab4d_6bdb9b28ca67
+
+#include <cstdint>
+#include <istream>
+#include <string>
+
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+/// @brief Implementation Class UID Sub-Item (PS 3.7, D.3.3.2.1 and D.3.3.2.2).
+class ImplementationClassUID: public Object
+{
+public:
+ /// @brief Item type.
+ static uint8_t const type=0x52;
+
+ /// @brief Create a Implementation Class UID item.
+ ImplementationClassUID(std::string const & implementation_class_uid);
+
+ /// @brief Read a Implementation Class UID item from a stream.
+ ImplementationClassUID(std::istream & stream);
+
+ /// @brief Return the Implementation Class UID.
+ std::string get_implementation_class_uid() const;
+
+ /// @brief Set the Implementation Class UID.
+ void set_implementation_class_uid(std::string const & value);
+};
+
+}
+
+}
+
+#endif // _6b085e0c_afd5_4ed7_ab4d_6bdb9b28ca67
diff --git a/src/odil/pdu/ImplementationVersionName.cpp b/src/odil/pdu/ImplementationVersionName.cpp
new file mode 100644
index 0000000..c5f2424
--- /dev/null
+++ b/src/odil/pdu/ImplementationVersionName.cpp
@@ -0,0 +1,72 @@
+/*************************************************************************
+ * 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/pdu/ImplementationVersionName.h"
+
+#include <cstdint>
+#include <istream>
+#include <string>
+
+#include "odil/Exception.h"
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+ImplementationVersionName
+::ImplementationVersionName(std::string const & implementation_version_name)
+{
+ this->_item.add("Item-type", this->type);
+ this->_item.add("Reserved", uint8_t(0));
+ this->_item.add("Item-length", uint16_t(0));
+ this->_item.add("Implementation-version-name", std::string());
+
+ this->set_implementation_version_name(implementation_version_name);
+}
+
+ImplementationVersionName
+::ImplementationVersionName(std::istream & stream)
+{
+ this->_item.read(stream, "Item-type", Item::Field::Type::unsigned_int_8);
+ if(this->_item.as_unsigned_int_8("Item-type") != this->type)
+ {
+ throw Exception("Invalid item type");
+ }
+
+ this->_item.read(stream, "Reserved", Item::Field::Type::unsigned_int_8);
+ this->_item.read(stream, "Item-length", Item::Field::Type::unsigned_int_16);
+ this->_item.read(
+ stream, "Implementation-version-name", Item::Field::Type::string,
+ this->_item.as_unsigned_int_16("Item-length"));
+}
+
+std::string
+ImplementationVersionName
+::get_implementation_version_name() const
+{
+ return this->_item.as_string("Implementation-version-name");
+}
+
+void
+ImplementationVersionName
+::set_implementation_version_name(std::string const & value)
+{
+ if(value.empty() || value.size()>16)
+ {
+ throw Exception("Invalid implementation version name");
+ }
+ this->_item.as_string("Implementation-version-name") = value;
+ this->_item.as_unsigned_int_16("Item-length") = this->_compute_length();
+}
+
+}
+
+}
diff --git a/src/odil/pdu/ImplementationVersionName.h b/src/odil/pdu/ImplementationVersionName.h
new file mode 100644
index 0000000..1218709
--- /dev/null
+++ b/src/odil/pdu/ImplementationVersionName.h
@@ -0,0 +1,48 @@
+/*************************************************************************
+ * 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 _43545543_c8f8_44c4_b3ce_93cf2adfd365
+#define _43545543_c8f8_44c4_b3ce_93cf2adfd365
+
+#include <cstdint>
+#include <istream>
+#include <string>
+
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+/// @brief Implementation Version Name Sub-Item (PS 3.7, D.3.3.2.3 and D.3.3.2.4).
+class ImplementationVersionName: public Object
+{
+public:
+ /// @brief Item type.
+ static uint8_t const type=0x55;
+
+ /// @brief Create a Implementation Version Name item.
+ ImplementationVersionName(std::string const & implementation_version_name);
+
+ /// @brief Read a Implementation Version Name item from a stream.
+ ImplementationVersionName(std::istream & stream);
+
+ /// @brief Return the Implementation Version Name.
+ std::string get_implementation_version_name() const;
+
+ /// @brief Set the Implementation Version Name.
+ void set_implementation_version_name(std::string const & value);
+};
+
+}
+
+}
+
+#endif // _43545543_c8f8_44c4_b3ce_93cf2adfd365
diff --git a/src/odil/pdu/Item.cpp b/src/odil/pdu/Item.cpp
new file mode 100644
index 0000000..368a50a
--- /dev/null
+++ b/src/odil/pdu/Item.cpp
@@ -0,0 +1,454 @@
+/*************************************************************************
+ * 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/pdu/Item.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <istream>
+#include <ostream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "odil/endian.h"
+#include "odil/Exception.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+Item::Field
+::Field(uint8_t value)
+: _type(Type::unsigned_int_8), _uint8(value)
+{
+ // Nothing else.
+}
+
+Item::Field
+::Field(uint16_t value)
+: _type(Type::unsigned_int_16), _uint16(value)
+{
+ // Nothing else.
+}
+
+Item::Field
+::Field(uint32_t value)
+: _type(Type::unsigned_int_32), _uint32(value)
+{
+ // Nothing else.
+}
+
+Item::Field
+::Field(std::string const & value)
+: _type(Type::string), _string(value)
+{
+ // Nothing else.
+}
+
+Item::Field
+::Field(std::vector<Item> const & value)
+: _type(Type::items), _items(value)
+{
+ // Nothing else.
+}
+
+Item::Field::Type
+Item::Field
+::get_type() const
+{
+ return this->_type;
+}
+
+uint8_t
+Item::Field
+::as_unsigned_int_8() const
+{
+ if(this->_type != Type::unsigned_int_8)
+ {
+ throw odil::Exception("Incorrect type");
+ }
+ return this->_uint8;
+}
+
+uint8_t &
+Item::Field
+::as_unsigned_int_8()
+{
+ if(this->_type != Type::unsigned_int_8)
+ {
+ throw odil::Exception("Incorrect type");
+ }
+ return this->_uint8;
+}
+
+uint16_t
+Item::Field
+::as_unsigned_int_16() const
+{
+ if(this->_type != Type::unsigned_int_16)
+ {
+ throw odil::Exception("Incorrect type");
+ }
+ return this->_uint16;
+}
+
+uint16_t &
+Item::Field
+::as_unsigned_int_16()
+{
+ if(this->_type != Type::unsigned_int_16)
+ {
+ throw odil::Exception("Incorrect type");
+ }
+ return this->_uint16;
+}
+
+uint32_t
+Item::Field
+::as_unsigned_int_32() const
+{
+ if(this->_type != Type::unsigned_int_32)
+ {
+ throw odil::Exception("Incorrect type");
+ }
+ return this->_uint32;
+}
+
+uint32_t &
+Item::Field
+::as_unsigned_int_32()
+{
+ if(this->_type != Type::unsigned_int_32)
+ {
+ throw odil::Exception("Incorrect type");
+ }
+ return this->_uint32;
+}
+
+std::string const &
+Item::Field
+::as_string() const
+{
+ if(this->_type != Type::string)
+ {
+ throw odil::Exception("Incorrect type");
+ }
+ return this->_string;
+}
+
+std::string &
+Item::Field
+::as_string()
+{
+ if(this->_type != Type::string)
+ {
+ throw odil::Exception("Incorrect type");
+ }
+ return this->_string;
+}
+
+std::vector<Item> const &
+Item::Field
+::as_items() const
+{
+ if(this->_type != Type::items)
+ {
+ throw odil::Exception("Incorrect type");
+ }
+ return this->_items;
+}
+
+std::vector<Item> &
+Item::Field
+::as_items()
+{
+ if(this->_type != Type::items)
+ {
+ throw odil::Exception("Incorrect type");
+ }
+ return this->_items;
+}
+
+Item
+::Item()
+{
+ // Nothing to do.
+}
+
+Item
+::Item(std::vector<std::pair<std::string, Field>> const & fields)
+: _fields(fields)
+{
+ // Nothing else.
+}
+
+Item &
+Item
+::add(std::string const & name, Field const & field)
+{
+ this->_fields.push_back(std::make_pair(name, field));
+ return *this;
+}
+
+Item::Container::size_type
+Item
+::size() const
+{
+ return this->_fields.size();
+}
+
+bool
+Item
+::empty() const
+{
+ return this->_fields.empty();
+}
+
+bool
+Item
+::has_field(std::string const & name) const
+{
+ auto const iterator = std::find_if(
+ this->_fields.begin(), this->_fields.end(),
+ [&name](std::pair<std::string, Field> const & x)
+ { return (x.first == name); });
+ return (iterator != this->_fields.end());
+}
+
+Item::Field const &
+Item
+::operator[](std::string const & name) const
+{
+ auto const iterator = std::find_if(
+ this->_fields.begin(), this->_fields.end(),
+ [&name](std::pair<std::string, Field> const & x)
+ { return (x.first == name); });
+ if(iterator == this->_fields.end())
+ {
+ throw Exception("No such field");
+ }
+ return iterator->second;
+}
+
+Item::Field &
+Item
+::operator[](std::string const & name)
+{
+ auto const iterator = std::find_if(
+ this->_fields.begin(), this->_fields.end(),
+ [&name](std::pair<std::string, Field> const & x)
+ { return (x.first == name); });
+ if(iterator == this->_fields.end())
+ {
+ throw Exception("No such field");
+ }
+ return iterator->second;
+}
+
+uint8_t
+Item
+::as_unsigned_int_8(std::string const & name) const
+{
+ auto const & field = (*this)[name];
+ return field.as_unsigned_int_8();
+}
+
+uint8_t &
+Item
+::as_unsigned_int_8(std::string const & name)
+{
+ auto & field = (*this)[name];
+ return field.as_unsigned_int_8();
+}
+
+uint16_t
+Item
+::as_unsigned_int_16(std::string const & name) const
+{
+ auto const & field = (*this)[name];
+ return field.as_unsigned_int_16();
+}
+
+uint16_t &
+Item
+::as_unsigned_int_16(std::string const & name)
+{
+ auto & field = (*this)[name];
+ return field.as_unsigned_int_16();
+}
+
+uint32_t
+Item
+::as_unsigned_int_32(std::string const & name) const
+{
+ auto const & field = (*this)[name];
+ return field.as_unsigned_int_32();
+}
+
+uint32_t &
+Item
+::as_unsigned_int_32(std::string const & name)
+{
+ auto & field = (*this)[name];
+ return field.as_unsigned_int_32();
+}
+
+std::string const &
+Item
+::as_string(std::string const & name) const
+{
+ auto & field = (*this)[name];
+ return field.as_string();
+}
+
+std::string &
+Item
+::as_string(std::string const & name)
+{
+ auto & field = (*this)[name];
+ return field.as_string();
+}
+
+std::vector<Item> const &
+Item
+::as_items(std::string const & name) const
+{
+ auto & field = (*this)[name];
+ return field.as_items();
+}
+
+std::vector<Item> &
+Item
+::as_items(std::string const & name)
+{
+ auto & field = (*this)[name];
+ return field.as_items();
+}
+
+Item::const_iterator
+Item
+::begin() const
+{
+ return this->_fields.begin();
+}
+
+Item::const_iterator
+Item
+::end() const
+{
+ return this->_fields.end();
+}
+
+void
+Item
+::read(
+ std::istream & stream, std::string const & name, Field::Type type,
+ std::streamsize size)
+{
+ if(type == Field::Type::unsigned_int_8)
+ {
+ uint8_t value;
+ stream.read(reinterpret_cast<char*>(&value), sizeof(value));
+ if(!stream.good())
+ {
+ throw Exception("Could not read unsigned_int_8 field");
+ }
+
+ this->add(name, Field(value));
+ }
+ else if(type == Field::Type::unsigned_int_16)
+ {
+ uint16_t value;
+ stream.read(reinterpret_cast<char*>(&value), sizeof(value));
+ if(!stream.good())
+ {
+ throw Exception("Could not read unsigned_int_16 field");
+ }
+
+ value = big_endian_to_host(value);
+ this->add(name, Field(value));
+ }
+ else if(type == Field::Type::unsigned_int_32)
+ {
+ uint32_t value;
+ stream.read(reinterpret_cast<char*>(&value), sizeof(value));
+ if(!stream.good())
+ {
+ throw Exception("Could not read unsigned_int_32 field");
+ }
+
+ value = big_endian_to_host(value);
+ this->add(name, Field(value));
+ }
+ else if(type == Field::Type::string)
+ {
+ std::string value(size, '\0');
+ stream.read(reinterpret_cast<char*>(&value[0]), value.size());
+ if(!stream.good())
+ {
+ throw Exception("Could not read string field");
+ }
+
+ this->add(name, Field(value));
+ }
+ else
+ {
+ throw Exception("Unknown field type");
+ }
+}
+
+std::ostream &
+operator<<(std::ostream & stream, Item const & item)
+{
+ for(auto const & named_field: item)
+ {
+ auto const & field = named_field.second;
+ auto const type = field.get_type();
+ if(type == Item::Field::Type::unsigned_int_8)
+ {
+ auto const value = field.as_unsigned_int_8();
+ stream.write(reinterpret_cast<char const*>(&value), sizeof(value));
+ }
+ else if(type == Item::Field::Type::unsigned_int_16)
+ {
+ auto const value = host_to_big_endian(field.as_unsigned_int_16());
+ stream.write(reinterpret_cast<char const*>(&value), sizeof(value));
+ }
+ else if(type == Item::Field::Type::unsigned_int_32)
+ {
+ auto const value = host_to_big_endian(field.as_unsigned_int_32());
+ stream.write(reinterpret_cast<char const*>(&value), sizeof(value));
+ }
+ else if(type == Item::Field::Type::string)
+ {
+ auto const value = field.as_string();
+ stream.write(reinterpret_cast<char const*>(&value[0]), value.size());
+ }
+ else if(type == Item::Field::Type::items)
+ {
+ auto const value = field.as_items();
+ for(auto const & sub_item: value)
+ {
+ stream << sub_item;
+ }
+ }
+ else
+ {
+ throw Exception("Unknown type");
+ }
+ }
+
+ return stream;
+}
+
+}
+
+}
diff --git a/src/odil/pdu/Item.h b/src/odil/pdu/Item.h
new file mode 100644
index 0000000..37cb44f
--- /dev/null
+++ b/src/odil/pdu/Item.h
@@ -0,0 +1,266 @@
+/*************************************************************************
+ * 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 _1c7b57cc_79f5_497c_815c_920e0711a864
+#define _1c7b57cc_79f5_497c_815c_920e0711a864
+
+#include <cstdint>
+#include <istream>
+#include <ostream>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace odil
+{
+
+namespace pdu
+{
+
+/// @brief A sequence of fields forming a full PDU or a part of it.
+class Item
+{
+public:
+ /// @brief Generic field.
+ class Field
+ {
+ public:
+ /// @brief Possible types stored in the field.
+ enum class Type
+ {
+ unsigned_int_8,
+ unsigned_int_16,
+ unsigned_int_32,
+ string,
+ items
+ };
+
+ /// @brief Constructor.
+ Field(uint8_t value);
+
+ /// @brief Constructor.
+ Field(uint16_t value);
+
+ /// @brief Constructor.
+ Field(uint32_t value);
+
+ /// @brief Constructor.
+ Field(std::string const & value);
+
+ /// @brief Constructor.
+ Field(std::vector<Item> const & value);
+
+ /// @brief Return the concrete type stored in the field.
+ Type get_type() const;
+
+ /**
+ * @brief Return the uint8_t stored in the field.
+ *
+ * If the field does not contain an uint8_t, a odil::Exception is raised.
+ */
+ uint8_t as_unsigned_int_8() const;
+
+ /**
+ * @brief Return the uint8_t stored in the field.
+ *
+ * If the field does not contain an uint8_t, a odil::Exception is raised.
+ */
+ uint8_t & as_unsigned_int_8();
+
+ /**
+ * @brief Return the uint16_t stored in the field.
+ *
+ * If the field does not contain an uint16_t, a odil::Exception is raised.
+ */
+ uint16_t as_unsigned_int_16() const;
+
+ /**
+ * @brief Return the uint16_t stored in the field.
+ *
+ * If the field does not contain an uint16_t, a odil::Exception is raised.
+ */
+ uint16_t & as_unsigned_int_16();
+
+ /**
+ * @brief Return the uint32_t stored in the field.
+ *
+ * If the field does not contain an uint32_t, a odil::Exception is raised.
+ */
+ uint32_t as_unsigned_int_32() const;
+
+ /**
+ * @brief Return the uint32_t stored in the field.
+ *
+ * If the field does not contain an uint32_t, a odil::Exception is raised.
+ */
+ uint32_t & as_unsigned_int_32();
+
+ /**
+ * @brief Return the string stored in the field.
+ *
+ * If the field does not contain an string, a odil::Exception is raised.
+ */
+ std::string const & as_string() const;
+
+ /**
+ * @brief Return the string stored in the field.
+ *
+ * If the field does not contain an string, a odil::Exception is raised.
+ */
+ std::string & as_string();
+
+ /**
+ * @brief Return the items stored in the field.
+ *
+ * If the field does not contain items, a odil::Exception is raised.
+ */
+ std::vector<Item> const & as_items() const;
+
+ /**
+ * @brief Return the items stored in the field.
+ *
+ * If the field does not contain items, a odil::Exception is raised.
+ */
+ std::vector<Item> & as_items();
+
+ private:
+ Type _type;
+ uint8_t _uint8;
+ uint16_t _uint16;
+ uint32_t _uint32;
+ std::string _string;
+ std::vector<Item> _items;
+ };
+
+ typedef std::vector<std::pair<std::string, Field>> Container;
+
+ /// @brief Create an empty PDU item.
+ Item();
+
+ /// @brief Create an initialized PDU item.
+ Item(std::vector<std::pair<std::string, Field>> const & fields);
+
+ /// @brief Add a new field to the PDU item.
+ Item & add(std::string const & name, Field const & field);
+
+ /// @brief Return the number of fields.
+ Container::size_type size() const;
+
+ /// @brief Test whether the container is empty.
+ bool empty() const;
+
+ /// @brief Test whether the PDU item contains a field with a given name.
+ bool has_field(std::string const & name) const;
+
+ /// @brief Return the named field. Raise an exception if no such field exists.
+ Field const & operator[](std::string const & name) const;
+
+ /// @brief Return the named field. Raise an exception if no such field exists.
+ Field & operator[](std::string const & name);
+
+ /**
+ * @brief Return the named field as an uint8_t.
+ *
+ * Raise an exception if no such field exists or if the field does not
+ * contain an uint8_t.
+ */
+ uint8_t as_unsigned_int_8(std::string const & name) const;
+
+ /**
+ * @brief Return the named field as an uint8_t.
+ *
+ * Raise an exception if no such field exists or if the field does not
+ * contain an uint8_t.
+ */
+ uint8_t & as_unsigned_int_8(std::string const & name);
+
+ /**
+ * @brief Return the named field as an uint16_t.
+ *
+ * Raise an exception if no such field exists or if the field does not
+ * contain an uint16_t.
+ */
+ uint16_t as_unsigned_int_16(std::string const & name) const;
+
+ /**
+ * @brief Return the named field as an uint16_t.
+ *
+ * Raise an exception if no such field exists or if the field does not
+ * contain an uint16_t.
+ */
+ uint16_t & as_unsigned_int_16(std::string const & name);
+
+ /**
+ * @brief Return the named field as an uint32_t.
+ *
+ * Raise an exception if no such field exists or if the field does not
+ * contain an uint32_t.
+ */
+ uint32_t as_unsigned_int_32(std::string const & name) const;
+
+ /**
+ * @brief Return the named field as an uint32_t.
+ *
+ * Raise an exception if no such field exists or if the field does not
+ * contain an uint32_t.
+ */
+ uint32_t & as_unsigned_int_32(std::string const & name);
+
+ /**
+ * @brief Return the named field as a string.
+ *
+ * Raise an exception if no such field exists or if the field does not
+ * contain a string.
+ */
+ std::string const & as_string(std::string const & name) const;
+
+ /**
+ * @brief Return the named field as a sequence of items.
+ *
+ * Raise an exception if no such field exists or if the field does not
+ * contain a sequence of items.
+ */
+ std::string & as_string(std::string const & name);
+
+ std::vector<Item> const & as_items(std::string const & name) const;
+
+ /**
+ * @brief Return the named field as a sequence of items.
+ *
+ * Raise an exception if no such field exists or if the field does not
+ * contain a sequence of items.
+ */
+ std::vector<Item> & as_items(std::string const & name);
+
+ typedef Container::const_iterator const_iterator;
+ const_iterator begin() const;
+ const_iterator end() const;
+
+ /**
+ * @brief Read a field from a stream.
+ *
+ * The size parameter is ignored for non-string types.
+ */
+ void read(
+ std::istream & stream,std::string const & name, Field::Type type,
+ std::streamsize size=0);
+
+private:
+ std::vector<std::pair<std::string, Field>> _fields;
+};
+
+// No operator>> since we need explicit names and types.
+
+std::ostream &
+operator<<(std::ostream & stream, Item const & item);
+
+}
+
+}
+
+#endif // _1c7b57cc_79f5_497c_815c_920e0711a864
diff --git a/src/odil/pdu/MaximumLength.cpp b/src/odil/pdu/MaximumLength.cpp
new file mode 100644
index 0000000..0a7c0c7
--- /dev/null
+++ b/src/odil/pdu/MaximumLength.cpp
@@ -0,0 +1,65 @@
+/*************************************************************************
+ * 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/pdu/MaximumLength.h"
+
+#include <cstdint>
+#include <istream>
+
+#include "odil/Exception.h"
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+MaximumLength
+::MaximumLength(uint32_t maximum_length)
+{
+ this->_item.add("Item-type", uint8_t(0x51));
+ this->_item.add("Reserved", uint8_t(0));
+ this->_item.add("Item-length", uint16_t(4));
+ this->_item.add("Maximum-length-received", uint32_t(0));
+
+ this->set_maximum_length(maximum_length);
+}
+
+MaximumLength
+::MaximumLength(std::istream & stream)
+{
+ this->_item.read(stream, "Item-type", Item::Field::Type::unsigned_int_8);
+ if(this->_item.as_unsigned_int_8("Item-type") != 0x51)
+ {
+ throw Exception("Invalid item type");
+ }
+
+ this->_item.read(stream, "Reserved", Item::Field::Type::unsigned_int_8);
+ this->_item.read(stream, "Item-length", Item::Field::Type::unsigned_int_16);
+ this->_item.read(
+ stream, "Maximum-length-received", Item::Field::Type::unsigned_int_32);
+}
+
+uint32_t
+MaximumLength
+::get_maximum_length() const
+{
+ return this->_item.as_unsigned_int_32("Maximum-length-received");
+}
+
+void
+MaximumLength
+::set_maximum_length(uint32_t value)
+{
+ this->_item.as_unsigned_int_32("Maximum-length-received") = value;
+}
+
+}
+
+}
diff --git a/src/odil/pdu/MaximumLength.h b/src/odil/pdu/MaximumLength.h
new file mode 100644
index 0000000..cc01877
--- /dev/null
+++ b/src/odil/pdu/MaximumLength.h
@@ -0,0 +1,47 @@
+/*************************************************************************
+ * 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 _b2091b0b_1a6e_435c_b927_e5c54aff89c5
+#define _b2091b0b_1a6e_435c_b927_e5c54aff89c5
+
+#include <cstdint>
+#include <istream>
+
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+/// @brief Maximum Length Sub-Item Structure (PS 3.8, D.1).
+class MaximumLength: public Object
+{
+public:
+ /// @brief Item type.
+ static uint8_t const type=0x51;
+
+ /// @brief Create a Maximum Length item.
+ MaximumLength(uint32_t maximum_length=0);
+
+ /// @brief Read a Maximum Length item from a stream.
+ MaximumLength(std::istream & stream);
+
+ /// @brief Return the maximum length.
+ uint32_t get_maximum_length() const;
+
+ /// @brief Set the maximum length.
+ void set_maximum_length(uint32_t value);
+};
+
+}
+
+}
+
+#endif // _b2091b0b_1a6e_435c_b927_e5c54aff89c5
diff --git a/src/odil/pdu/Object.cpp b/src/odil/pdu/Object.cpp
new file mode 100644
index 0000000..c31d14d
--- /dev/null
+++ b/src/odil/pdu/Object.cpp
@@ -0,0 +1,116 @@
+/*************************************************************************
+ * 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/pdu/Object.h"
+
+#include <iterator>
+#include <ostream>
+
+#include "odil/Exception.h"
+#include "odil/pdu/Item.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+Object
+::~Object()
+{
+ // Nothing to do.
+}
+
+Item const &
+Object
+::get_item() const
+{
+ return this->_item;
+}
+
+uint32_t
+Object
+::_compute_length() const
+{
+ uint32_t length=0;
+
+ // First 3 fields are always the same: type, reserved and length.
+ auto it = this->_item.begin();
+ std::advance(it, 3);
+ for(; it!=this->_item.end(); ++it)
+ {
+ Item::Field const & field = it->second;
+ length += this->_compute_length(field);
+ }
+
+ return length;
+}
+
+uint32_t
+Object
+::_compute_length(Item const & item) const
+{
+ uint32_t length=0;
+
+ for(auto const & named_field: item)
+ {
+ Item::Field const & field = named_field.second;
+ length += this->_compute_length(field);
+ }
+
+ return length;
+}
+
+uint32_t
+Object
+::_compute_length(Item::Field const & field) const
+{
+ uint32_t length=0;
+
+ if(field.get_type() == Item::Field::Type::unsigned_int_8)
+ {
+ length += 1;
+ }
+ else if(field.get_type() == Item::Field::Type::unsigned_int_16)
+ {
+ length += 2;
+ }
+ else if(field.get_type() == Item::Field::Type::unsigned_int_32)
+ {
+ length += 4;
+ }
+ else if(field.get_type() == Item::Field::Type::string)
+ {
+ length += field.as_string().size();
+ }
+ else if(field.get_type() == Item::Field::Type::items)
+ {
+ auto const & items = field.as_items();
+ for(auto const & item: items)
+ {
+ length += this->_compute_length(item);
+ }
+ }
+ else
+ {
+ throw Exception("Unknown field type");
+ }
+
+ return length;
+}
+
+std::ostream &
+operator<<(std::ostream & stream, Object const & object)
+{
+ stream << object.get_item();
+ return stream;
+}
+
+}
+
+}
diff --git a/src/odil/pdu/Object.h b/src/odil/pdu/Object.h
new file mode 100644
index 0000000..db1bfcd
--- /dev/null
+++ b/src/odil/pdu/Object.h
@@ -0,0 +1,55 @@
+/*************************************************************************
+ * 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 _da2270e3_d393_415a_9c5c_6253152ed9da
+#define _da2270e3_d393_415a_9c5c_6253152ed9da
+
+#include <ostream>
+#include "odil/pdu/Item.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+/**
+ * @brief Base class for all PDU-related high-level objects (PDU, items and
+ * sub-items).
+ */
+class Object
+{
+public:
+ /// @brief Destructor, makes the type polymorphic.
+ virtual ~Object();
+
+ /// @brief Get the underlying item.
+ Item const & get_item() const;
+
+protected:
+ Item _item;
+
+ /// @brief Compute the value of the length field of the object.
+ uint32_t _compute_length() const;
+
+ /// @brief Compute the full size of an item.
+ uint32_t _compute_length(Item const & item) const;
+
+ /// @brief Compute the full size of a field.
+ uint32_t _compute_length(Item::Field const & field) const;
+};
+
+/// @brief Dump the PDU-object in its binary form.
+std::ostream &
+operator<<(std::ostream & stream, Object const & object);
+
+}
+
+}
+
+#endif // _da2270e3_d393_415a_9c5c_6253152ed9da
diff --git a/src/odil/pdu/PDataTF.cpp b/src/odil/pdu/PDataTF.cpp
new file mode 100644
index 0000000..6863669
--- /dev/null
+++ b/src/odil/pdu/PDataTF.cpp
@@ -0,0 +1,186 @@
+/*************************************************************************
+ * 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/pdu/PDataTF.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <istream>
+#include <string>
+#include <vector>
+
+#include "odil/Exception.h"
+#include "odil/pdu/Item.h"
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+PDataTF::PresentationDataValueItem
+::PresentationDataValueItem(
+ uint8_t presentation_context_id, uint8_t control_header,
+ std::string const & fragment)
+{
+ this->_item.add("Item-length", uint32_t(4));
+ this->_item.add("Presentation-Context-ID", uint8_t(0));
+ this->_item.add("Control-header", uint8_t(0));
+ this->_item.add("Fragment", std::string());
+
+ this->set_presentation_context_id(presentation_context_id);
+ this->set_control_header(control_header);
+ this->set_fragment(fragment);
+}
+
+PDataTF::PresentationDataValueItem
+::PresentationDataValueItem(std::istream & stream)
+{
+ this->_item.read(stream, "Item-length", Item::Field::Type::unsigned_int_32);
+ this->_item.read(
+ stream, "Presentation-Context-ID", Item::Field::Type::unsigned_int_8);
+ this->_item.read(
+ stream, "Control-header", Item::Field::Type::unsigned_int_8);
+ this->_item.read(
+ stream, "Fragment", Item::Field::Type::string,
+ this->_item.as_unsigned_int_32("Item-length")-2);
+}
+
+uint8_t
+PDataTF::PresentationDataValueItem
+::get_presentation_context_id() const
+{
+ return this->_item.as_unsigned_int_8("Presentation-Context-ID");
+}
+
+void
+PDataTF::PresentationDataValueItem
+::set_presentation_context_id(uint8_t value)
+{
+ if(value%2 == 0)
+ {
+ throw Exception("Invalid Presentation Context ID");
+ }
+ this->_item.as_unsigned_int_8("Presentation-Context-ID") = value;
+}
+
+uint8_t
+PDataTF::PresentationDataValueItem
+::get_control_header() const
+{
+ return this->_item.as_unsigned_int_8("Control-header");
+}
+
+void
+PDataTF::PresentationDataValueItem
+::set_control_header(uint8_t value)
+{
+ this->_item.as_unsigned_int_8("Control-header") = value;
+}
+
+bool
+PDataTF::PresentationDataValueItem
+::is_command() const
+{
+ return (this->get_control_header()%2==1);
+}
+
+bool
+PDataTF::PresentationDataValueItem
+::is_last_fragment() const
+{
+ return ((this->get_control_header()>>1)%2==1);
+}
+
+std::string const &
+PDataTF::PresentationDataValueItem
+::get_fragment() const
+{
+ return this->_item.as_string("Fragment");
+}
+
+void
+PDataTF::PresentationDataValueItem
+::set_fragment(std::string const & fragment)
+{
+ this->_item.as_string("Fragment") = fragment;
+ this->_item.as_unsigned_int_32("Item-length") = 2+fragment.size();
+}
+
+PDataTF
+::PDataTF(std::vector<PresentationDataValueItem> const & pdv_items)
+{
+ this->_item.add("PDU-type", uint8_t(0x04));
+ this->_item.add("Reserved-1", uint8_t(0));
+ this->_item.add("PDU-length", uint32_t(4));
+ this->_item.add("Presentation-data-value-Items", std::vector<Item>());
+
+ this->set_pdv_items(pdv_items);
+}
+
+PDataTF
+::PDataTF(std::istream & stream)
+{
+ this->_item.read(stream, "PDU-type", Item::Field::Type::unsigned_int_8);
+ if(this->_item.as_unsigned_int_8("PDU-type") != 0x04)
+ {
+ throw Exception("Invalid PDU type");
+ }
+
+ this->_item.read(stream, "Reserved-1", Item::Field::Type::unsigned_int_8);
+ this->_item.read(stream, "PDU-length", Item::Field::Type::unsigned_int_32);
+
+ auto const pdu_length = this->_item.as_unsigned_int_32("PDU-length");
+ auto const begin = stream.tellg();
+
+ std::vector<PresentationDataValueItem> pdv_items;
+ while(stream.tellg()-begin < pdu_length)
+ {
+ PresentationDataValueItem pdv_item(stream);
+ pdv_items.push_back(pdv_item);
+ }
+
+ this->_item.add("Presentation-data-value-Items", std::vector<Item>());
+ this->set_pdv_items(pdv_items);
+}
+
+std::vector<PDataTF::PresentationDataValueItem>
+PDataTF
+::get_pdv_items() const
+{
+ std::vector<PresentationDataValueItem> result;
+ for(auto const & item: this->_item.as_items("Presentation-data-value-Items"))
+ {
+ PresentationDataValueItem const pdv_item(
+ item.as_unsigned_int_8("Presentation-Context-ID"),
+ item.as_unsigned_int_8("Control-header"),
+ item.as_string("Fragment"));
+
+ result.push_back(pdv_item);
+ }
+ return result;
+}
+
+void
+PDataTF
+::set_pdv_items(std::vector<PresentationDataValueItem> const &pdv_items)
+{
+ auto & items = this->_item.as_items("Presentation-data-value-Items");
+
+ items.resize(pdv_items.size());
+ std::transform(
+ pdv_items.begin(), pdv_items.end(), items.begin(),
+ [](PresentationDataValueItem const& x){ return x.get_item(); });
+
+ this->_item.as_unsigned_int_32("PDU-length") = this->_compute_length();
+}
+
+}
+
+}
diff --git a/src/odil/pdu/PDataTF.h b/src/odil/pdu/PDataTF.h
new file mode 100644
index 0000000..cc3abbf
--- /dev/null
+++ b/src/odil/pdu/PDataTF.h
@@ -0,0 +1,67 @@
+/*************************************************************************
+ * 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 _b3062f12_8a06_46a8_9dda_8a7edf96e4a6
+#define _b3062f12_8a06_46a8_9dda_8a7edf96e4a6
+
+#include <cstdint>
+#include <istream>
+#include <vector>
+
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+/// @brief P-DATA-TF PDU, cf. PS 3.8, 9.3.5.
+class PDataTF: public Object
+{
+public:
+ class PresentationDataValueItem: public Object
+ {
+ public:
+ PresentationDataValueItem(
+ uint8_t presentation_context_id, uint8_t control_header,
+ std::string const & fragment);
+
+ PresentationDataValueItem(std::istream & stream);
+
+ uint8_t get_presentation_context_id() const;
+ void set_presentation_context_id(uint8_t value);
+
+ uint8_t get_control_header() const;
+ void set_control_header(uint8_t value);
+
+ bool is_command() const;
+ bool is_last_fragment() const;
+
+ std::string const & get_fragment() const;
+ void set_fragment(std::string const & fragment);
+ };
+
+ /// @brief Constructor.
+ PDataTF(std::vector<PresentationDataValueItem> const & pdv_items);
+
+ /// @brief Constructor from stream.
+ PDataTF(std::istream & stream);
+
+ /// @brief Return the Presentation Data Value Items.
+ std::vector<PresentationDataValueItem> get_pdv_items() const;
+
+ /// @brief Set the Presentation Data Value Items.
+ void set_pdv_items(std::vector<PresentationDataValueItem> const &pdv_items);
+};
+
+}
+
+}
+
+#endif // _b3062f12_8a06_46a8_9dda_8a7edf96e4a6
diff --git a/src/odil/pdu/PresentationContext.cpp b/src/odil/pdu/PresentationContext.cpp
new file mode 100644
index 0000000..d1f84f4
--- /dev/null
+++ b/src/odil/pdu/PresentationContext.cpp
@@ -0,0 +1,218 @@
+/*************************************************************************
+ * 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/pdu/PresentationContext.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include "odil/Exception.h"
+#include "odil/pdu/Object.h"
+#include "odil/pdu/Item.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+PresentationContext
+::PresentationContext()
+{
+ // Nothing to do.
+}
+
+PresentationContext
+::PresentationContext(std::istream & stream)
+{
+ this->_item.read(stream, "Item-type", Item::Field::Type::unsigned_int_8);
+ if(this->get_item_type() != 0x20 && this->get_item_type() != 0x21)
+ {
+ throw Exception("Invalid item type");
+ }
+
+ this->_item.read(stream, "Reserved-1", Item::Field::Type::unsigned_int_8);
+ this->_item.read(stream, "Item-length", Item::Field::Type::unsigned_int_16);
+ auto const item_length = this->_item.as_unsigned_int_16("Item-length");
+
+ auto const begin = stream.tellg();
+
+ this->_item.read(
+ stream, "Presentation-context-id", Item::Field::Type::unsigned_int_8);
+
+ this->_item.read(stream, "Reserved-2", Item::Field::Type::unsigned_int_8);
+ this->_item.read(
+ stream, "Result/Reason", Item::Field::Type::unsigned_int_8);
+ this->_item.read(stream, "Reserved-3", Item::Field::Type::unsigned_int_8);
+
+ std::vector<Item> sub_items;
+ while(stream.tellg()-begin < item_length)
+ {
+ Item item;
+ item.read(stream, "Item-type", Item::Field::Type::unsigned_int_8);
+
+ std::string type;
+
+ auto const & item_type = item.as_unsigned_int_8("Item-type");
+ if(item_type == 0x30)
+ {
+ type = "Abstract";
+ }
+ else if(item_type == 0x40)
+ {
+ type = "Transfer";
+ }
+ else
+ {
+ throw Exception("Invalid item");
+ }
+
+ item.read(stream, "Reserved", Item::Field::Type::unsigned_int_8);
+ item.read(stream, "Item-length", Item::Field::Type::unsigned_int_16);
+ item.read(
+ stream, type+"-syntax-name", Item::Field::Type::string,
+ item.as_unsigned_int_16("Item-length"));
+
+ sub_items.push_back(item);
+ }
+
+ this->_item.add("Abstract/Transfer Syntax Sub-Items", sub_items);
+}
+
+PresentationContext
+::~PresentationContext()
+{
+ // Nothing to do.
+}
+
+
+uint8_t
+PresentationContext
+::get_item_type() const
+{
+ return this->_item.as_unsigned_int_8("Item-type");
+}
+
+uint8_t
+PresentationContext
+::get_id() const
+{
+ return this->_item.as_unsigned_int_8("Presentation-context-id");
+}
+
+void
+PresentationContext
+::set_id(uint8_t id)
+{
+ this->_item.as_unsigned_int_8("Presentation-context-id") = id;
+}
+
+Item
+PresentationContext
+::_make_string_item(
+ std::string const & type, std::string const & value)
+{
+ Item item;
+
+ uint8_t item_type;
+ if(type == "Abstract")
+ {
+ item_type = 0x30;
+ }
+ else if(type == "Transfer")
+ {
+ item_type = 0x40;
+ }
+ else
+ {
+ throw Exception("Unknown sub-item type");
+ }
+ item.add("Item-type", item_type);
+
+ item.add("Reserved", uint8_t(0));
+
+ item.add("Item-length", uint16_t(value.size()));
+ item.add(type+"-syntax-name", value);
+
+ return item;
+}
+
+void
+PresentationContext
+::_add_fields()
+{
+ this->_item.add("Item-type", uint8_t(0x0));
+ this->_item.add("Reserved-1", uint8_t(0));
+ this->_item.add("Item-length", uint16_t(0));
+ this->_item.add("Presentation-context-id", uint8_t(0));
+ this->_item.add("Reserved-2", uint8_t(0));
+ this->_item.add("Result/Reason", uint8_t(0));
+ this->_item.add("Reserved-3", uint8_t(0));
+
+ this->_item.add("Abstract/Transfer Syntax Sub-Items", std::vector<Item>());
+}
+
+std::vector<std::string>
+PresentationContext
+::_get_syntaxes(std::string const & type) const
+{
+ auto const & sub_items = this->_item.as_items(
+ "Abstract/Transfer Syntax Sub-Items");
+
+ std::vector<std::string> result;
+ std::for_each(
+ sub_items.begin(), sub_items.end(),
+ [&type, &result](Item const & item)
+ {
+ uint8_t const item_type = (type=="Abstract")?0x30:0x40;
+ if(item.as_unsigned_int_8("Item-type") == item_type)
+ {
+ auto const value = item.as_string(type+"-syntax-name");
+ result.push_back(value);
+ }
+ });
+
+ return result;
+}
+
+void
+PresentationContext
+::_set_syntaxes(
+ std::string const & type, std::vector<std::string> const & syntaxes)
+{
+ std::string const other_type = (type=="Abstract")?"Transfer":"Abstract";
+ auto const other_syntaxes = this->_get_syntaxes(other_type);
+
+ auto & sub_items = this->_item.as_items(
+ "Abstract/Transfer Syntax Sub-Items");
+
+ sub_items.resize(other_syntaxes.size()+syntaxes.size());
+
+ auto const & abstract_syntaxes = (type=="Abstract")?syntaxes:other_syntaxes;
+ auto const & transfer_syntaxes = (type=="Abstract")?other_syntaxes:syntaxes;
+
+ std::transform(
+ abstract_syntaxes.begin(), abstract_syntaxes.end(),
+ sub_items.begin(),
+ [this](std::string const & name)
+ { return PresentationContext::_make_string_item("Abstract", name); });
+
+ std::transform(
+ transfer_syntaxes.begin(), transfer_syntaxes.end(),
+ sub_items.begin()+abstract_syntaxes.size(),
+ [this](std::string const & name)
+ { return PresentationContext::_make_string_item("Transfer", name); });
+
+ this->_item.as_unsigned_int_16("Item-length") = this->_compute_length();
+}
+
+}
+
+}
diff --git a/src/odil/pdu/PresentationContext.h b/src/odil/pdu/PresentationContext.h
new file mode 100644
index 0000000..2cf07c8
--- /dev/null
+++ b/src/odil/pdu/PresentationContext.h
@@ -0,0 +1,70 @@
+/*************************************************************************
+ * 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 _30f1a581_9ae6_4a02_a455_bf2b6ea58bc6
+#define _30f1a581_9ae6_4a02_a455_bf2b6ea58bc6
+
+#include <cstdint>
+#include <istream>
+#include <string>
+#include <vector>
+
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+/**
+ * @brief Presentation Context item, either for a A-ASSOCIATE-RQ PDU (PS 3.8,
+ * 9.3.2.2) or for a A-ASSOCIATE-AC PDU (PS 3.8, 9.3.3.2).
+ */
+class PresentationContext: public Object
+{
+public:
+ /// @brief Constructor
+ PresentationContext();
+
+ /// @brief Read from a stream.
+ PresentationContext(std::istream & stream);
+
+ /// @brief Destructor, make the class pure virtual.
+ virtual ~PresentationContext() =0;
+
+ /// @brief Return the Item-type.
+ uint8_t get_item_type() const;
+
+ /// @brief Return the Presentation context id.
+ uint8_t get_id() const;
+
+ /// @brief Set the Presentation context id.
+ void set_id(uint8_t id);
+
+protected:
+ /// @brief Create an Abstract Syntax or Transfer Syntax sub-item.
+ static Item _make_string_item(
+ std::string const & type, std::string const & value);
+
+ /// @brief Add common fields.
+ void _add_fields();
+
+ /// @brief Return the Abstract or Transfer syntaxes.
+ std::vector<std::string> _get_syntaxes(std::string const & type) const;
+
+ /// @brief Set the Abstract or Transfer syntaxes.
+ void _set_syntaxes(
+ std::string const & type, std::vector<std::string> const & syntaxes);
+};
+
+}
+
+}
+
+#endif // _30f1a581_9ae6_4a02_a455_bf2b6ea58bc6
diff --git a/src/odil/pdu/PresentationContextAC.cpp b/src/odil/pdu/PresentationContextAC.cpp
new file mode 100644
index 0000000..bf4aa78
--- /dev/null
+++ b/src/odil/pdu/PresentationContextAC.cpp
@@ -0,0 +1,91 @@
+/*************************************************************************
+ * 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/pdu/PresentationContextAC.h"
+
+#include <cstdint>
+#include <istream>
+#include <string>
+
+#include "odil/Exception.h"
+#include "odil/pdu/PresentationContext.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+PresentationContextAC
+::PresentationContextAC(
+ uint8_t id, std::string const & transfer_syntax, uint8_t result_reason)
+{
+ this->_add_fields();
+ this->_item.as_unsigned_int_8("Item-type") = 0x21;
+ this->set_id(id);
+ this->set_transfer_syntax(transfer_syntax);
+ this->set_result_reason(result_reason);
+}
+
+PresentationContextAC
+::PresentationContextAC(std::istream & stream)
+: PresentationContext(stream)
+{
+ if(this->get_item_type() != 0x21)
+ {
+ throw Exception("Invalid item type");
+ }
+}
+
+PresentationContextAC
+::~PresentationContextAC()
+{
+ // Nothing to do.
+}
+
+uint8_t
+PresentationContextAC
+::get_result_reason() const
+{
+ return this->_item.as_unsigned_int_8("Result/Reason");
+}
+
+void
+PresentationContextAC
+::set_result_reason(uint8_t result_reason)
+{
+ this->_item.as_unsigned_int_8("Result/Reason") = result_reason;
+}
+
+std::string
+PresentationContextAC
+::get_transfer_syntax() const
+{
+ auto const & syntaxes = this->_get_syntaxes("Transfer");
+ if(syntaxes.empty())
+ {
+ throw Exception("No Transfer Syntax sub-item");
+ }
+ else if(syntaxes.size() > 1)
+ {
+ throw Exception("Several Transfer Syntax sub-items");
+ }
+
+ return syntaxes[0];
+}
+
+void
+PresentationContextAC
+::set_transfer_syntax(std::string const & transfer_syntax)
+{
+ this->_set_syntaxes("Transfer", {transfer_syntax});
+}
+
+}
+
+}
diff --git a/src/odil/pdu/PresentationContextAC.h b/src/odil/pdu/PresentationContextAC.h
new file mode 100644
index 0000000..e04108e
--- /dev/null
+++ b/src/odil/pdu/PresentationContextAC.h
@@ -0,0 +1,55 @@
+/*************************************************************************
+ * 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 _69f1f7a9_d252_4ccd_a2d5_5df0ac4fb054
+#define _69f1f7a9_d252_4ccd_a2d5_5df0ac4fb054
+
+#include <cstdint>
+#include <istream>
+#include <string>
+
+#include "odil/pdu/PresentationContext.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+/// @brief Presentation Context item for a A-ASSOCIATE-AC PDU (PS 3.8, 9.3.3.2).
+class PresentationContextAC: public PresentationContext
+{
+public:
+ /// @brief Constructor.
+ PresentationContextAC(
+ uint8_t id, std::string const & transfer_syntax, uint8_t result_reason);
+
+ /// @brief Read a Presentation Context from a stream.
+ PresentationContextAC(std::istream & stream);
+
+ /// @brief Destructor.
+ ~PresentationContextAC();
+
+ /// @brief Return the Result/Reason.
+ uint8_t get_result_reason() const;
+
+ /// @brief Set the Result/Reason.
+ void set_result_reason(uint8_t result_reason);
+
+ /// @brief Return the Transfer Syntax.
+ std::string get_transfer_syntax() const;
+
+ /// @brief Set the Transfer Syntax.
+ void set_transfer_syntax(std::string const & transfer_syntax);
+};
+
+}
+
+}
+
+#endif // _69f1f7a9_d252_4ccd_a2d5_5df0ac4fb054
diff --git a/src/odil/pdu/PresentationContextRQ.cpp b/src/odil/pdu/PresentationContextRQ.cpp
new file mode 100644
index 0000000..1b4d6ec
--- /dev/null
+++ b/src/odil/pdu/PresentationContextRQ.cpp
@@ -0,0 +1,101 @@
+/*************************************************************************
+ * 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 _952c6d4c_b331_4033_8476_5b0e75022b68
+#define _952c6d4c_b331_4033_8476_5b0e75022b68
+
+#include "odil/pdu/PresentationContextRQ.h"
+
+#include <cstdint>
+#include <istream>
+#include <string>
+#include <vector>
+
+#include "odil/Exception.h"
+#include "odil/pdu/PresentationContext.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+PresentationContextRQ
+::PresentationContextRQ(
+ uint8_t id, std::string const & abstract_syntax,
+ std::vector<std::string> const & transfer_syntaxes)
+{
+ this->_add_fields();
+
+ this->_item.as_unsigned_int_8("Item-type") = 0x20;
+ this->set_id(id);
+ this->set_abstract_syntax(abstract_syntax);
+ this->set_transfer_syntaxes(transfer_syntaxes);
+}
+
+PresentationContextRQ
+::PresentationContextRQ(std::istream & stream)
+: PresentationContext(stream)
+{
+ if(this->get_item_type() != 0x20)
+ {
+ throw Exception("Invalid item type");
+ }
+}
+
+PresentationContextRQ
+::~PresentationContextRQ()
+{
+ // Nothing to do.
+}
+
+std::string
+PresentationContextRQ
+::get_abstract_syntax() const
+{
+ auto const & syntaxes = this->_get_syntaxes("Abstract");
+ if(syntaxes.empty())
+ {
+ throw Exception("No Abstract Syntax sub-item");
+ }
+ else if(syntaxes.size() > 1)
+ {
+ throw Exception("Several Abstract Syntax sub-items");
+ }
+
+ return syntaxes[0];
+}
+
+void
+PresentationContextRQ
+::set_abstract_syntax(std::string const & abstract_syntax)
+{
+ this->_set_syntaxes("Abstract", {abstract_syntax});
+}
+
+std::vector<std::string>
+PresentationContextRQ
+::get_transfer_syntaxes() const
+{
+ return this->_get_syntaxes("Transfer");
+}
+
+void
+PresentationContextRQ
+::set_transfer_syntaxes(std::vector<std::string> const & transfer_syntaxes)
+{
+ this->_set_syntaxes("Transfer", transfer_syntaxes);
+}
+
+}
+
+}
+
+#endif // _952c6d4c_b331_4033_8476_5b0e75022b68
+
+
diff --git a/src/odil/pdu/PresentationContextRQ.h b/src/odil/pdu/PresentationContextRQ.h
new file mode 100644
index 0000000..4582601
--- /dev/null
+++ b/src/odil/pdu/PresentationContextRQ.h
@@ -0,0 +1,58 @@
+/*************************************************************************
+ * 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 _e6e42ffd_7318_48f5_b35a_d44093564044
+#define _e6e42ffd_7318_48f5_b35a_d44093564044
+
+#include <cstdint>
+#include <istream>
+#include <string>
+#include <vector>
+
+#include "odil/pdu/PresentationContext.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+/// @brief Presentation Context item for a A-ASSOCIATE-RQ PDU (PS 3.8, 9.3.2.2).
+class PresentationContextRQ: public PresentationContext
+{
+public:
+ /// @brief Constructor.
+ PresentationContextRQ(
+ uint8_t id, std::string const & abstract_syntax,
+ std::vector<std::string> const & transfer_syntaxes);
+
+ /// @brief Read a Presentation Context from a stream.
+ PresentationContextRQ(std::istream & stream);
+
+ /// @brief Destructor.
+ ~PresentationContextRQ();
+
+ /// @brief Return the Abstract Syntax.
+ std::string get_abstract_syntax() const;
+
+ /// @brief Set the Abstract Syntax.
+ void set_abstract_syntax(std::string const & abstract_syntax);
+
+ /// @brief Return the Transfer Syntaxes.
+ std::vector<std::string> get_transfer_syntaxes() const;
+
+ /// @brief Set the Transfer Syntax sub-items.
+ void set_transfer_syntaxes(
+ std::vector<std::string> const & transfer_syntaxes);
+};
+
+}
+
+}
+
+#endif // _e6e42ffd_7318_48f5_b35a_d44093564044
diff --git a/src/odil/pdu/RoleSelection.cpp b/src/odil/pdu/RoleSelection.cpp
new file mode 100644
index 0000000..5ac0677
--- /dev/null
+++ b/src/odil/pdu/RoleSelection.cpp
@@ -0,0 +1,108 @@
+/*************************************************************************
+ * 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/pdu/RoleSelection.h"
+
+#include <cstdint>
+#include <istream>
+#include <string>
+
+#include "odil/Exception.h"
+#include "odil/pdu/Item.h"
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+RoleSelection
+::RoleSelection(
+ std::string const & sop_class_uid,
+ bool scu_role_support, bool scp_role_support)
+{
+ this->_item.add("Item-type", this->type);
+ this->_item.add("Reserved", uint8_t(0));
+ this->_item.add("Item-length", uint16_t(0));
+ this->_item.add("UID-length", uint16_t(0));
+ this->_item.add("SOP-class-uid", std::string());
+ this->_item.add("SCU-role", uint8_t(0));
+ this->_item.add("SCP-role", uint8_t(0));
+
+ this->set_sop_class_uid(sop_class_uid);
+ this->set_scu_role_support(scu_role_support);
+ this->set_scp_role_support(scp_role_support);
+}
+
+RoleSelection
+::RoleSelection(std::istream & stream)
+{
+ this->_item.read(stream, "Item-type", Item::Field::Type::unsigned_int_8);
+ if(this->_item.as_unsigned_int_8("Item-type") != this->type)
+ {
+ throw Exception("Invalid item type");
+ }
+
+ this->_item.read(stream, "Reserved", Item::Field::Type::unsigned_int_8);
+ this->_item.read(stream, "Item-length", Item::Field::Type::unsigned_int_16);
+ this->_item.read(stream, "UID-length", Item::Field::Type::unsigned_int_16);
+ this->_item.read(
+ stream, "SOP-class-uid", Item::Field::Type::string,
+ this->_item.as_unsigned_int_16("UID-length"));
+ this->_item.read(stream, "SCU-role", Item::Field::Type::unsigned_int_8);
+ this->_item.read(stream, "SCP-role", Item::Field::Type::unsigned_int_8);
+}
+
+std::string const &
+RoleSelection
+::get_sop_class_uid() const
+{
+ return this->_item.as_string("SOP-class-uid");
+}
+
+void
+RoleSelection
+::set_sop_class_uid(std::string const & value)
+{
+ this->_item.as_string("SOP-class-uid") = value;
+ this->_item.as_unsigned_int_16("UID-length") = value.size();
+ this->_item.as_unsigned_int_16("Item-length") = this->_compute_length();
+}
+
+bool
+RoleSelection
+::get_scu_role_support() const
+{
+ return (this->_item.as_unsigned_int_8("SCU-role")!=0);
+}
+
+void
+RoleSelection
+::set_scu_role_support(bool value)
+{
+ this->_item.as_unsigned_int_8("SCU-role") = value?1:0;
+}
+
+bool
+RoleSelection
+::get_scp_role_support() const
+{
+ return (this->_item.as_unsigned_int_8("SCP-role")!=0);
+}
+
+void
+RoleSelection
+::set_scp_role_support(bool value)
+{
+ this->_item.as_unsigned_int_8("SCP-role") = value?1:0;
+}
+
+}
+
+}
diff --git a/src/odil/pdu/RoleSelection.h b/src/odil/pdu/RoleSelection.h
new file mode 100644
index 0000000..edcaaee
--- /dev/null
+++ b/src/odil/pdu/RoleSelection.h
@@ -0,0 +1,65 @@
+/*************************************************************************
+ * 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 _8a990556_f9c3_49f3_bb84_6e604ec9b8f4
+#define _8a990556_f9c3_49f3_bb84_6e604ec9b8f4
+
+#include <cstdint>
+#include <istream>
+#include <string>
+
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+/// @brief SCU/SCP Role Selection Sub-Item (PS 3.7, D.3.3.4.1 and D.3.3.4.2).
+class RoleSelection: public Object
+{
+public:
+ /// @brief Item type.
+ static uint8_t const type=0x54;
+
+ /// @brief Create a Role Selection Sub-Item.
+ RoleSelection(
+ std::string const & sop_class_uid="",
+ bool scu_role_support=false, bool scp_role_support=false);
+
+ /// @brief Read a Role Selection Sub-Item from a stream.
+ RoleSelection(std::istream & stream);
+
+ /**
+ * @brief Return the SOP class uid identifying the abstract syntax,
+ * defaults to "".
+ */
+ std::string const & get_sop_class_uid() const;
+
+ /// @brief Set the SOP class uid identifying the abstract syntax.
+ void set_sop_class_uid(std::string const & value);
+
+ /// @brief Return whether the SCU role is supported, defaults to false.
+ bool get_scu_role_support() const;
+
+ /// @brief Set whether the SCP role is supported.
+ void set_scu_role_support(bool value);
+
+ /// @brief Return whether the SCP role is supported, defaults to false.
+ bool get_scp_role_support() const;
+
+ /// @brief Set whether the SCP role is supported.
+ void set_scp_role_support(bool value);
+};
+
+}
+
+}
+
+#endif // _8a990556_f9c3_49f3_bb84_6e604ec9b8f4
diff --git a/src/odil/pdu/UserIdentityAC.cpp b/src/odil/pdu/UserIdentityAC.cpp
new file mode 100644
index 0000000..842dd7d
--- /dev/null
+++ b/src/odil/pdu/UserIdentityAC.cpp
@@ -0,0 +1,75 @@
+/*************************************************************************
+ * 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/pdu/UserIdentityAC.h"
+
+#include <cstdint>
+#include <istream>
+#include <string>
+
+#include "odil/Exception.h"
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+UserIdentityAC
+::UserIdentityAC(std::string const & server_response)
+{
+ this->_item.add("Item-type", uint8_t(0x59));
+ this->_item.add("Reserved", uint8_t(0));
+ this->_item.add("Item-length", uint16_t(0));
+
+ this->_item.add("Server-response-length", uint16_t(0));
+ this->_item.add("Server-response", std::string(""));
+
+ this->set_server_response(server_response);
+}
+
+UserIdentityAC
+::UserIdentityAC(std::istream & stream)
+{
+ this->_item.read(stream, "Item-type", Item::Field::Type::unsigned_int_8);
+ if(this->_item.as_unsigned_int_8("Item-type") != 0x59)
+ {
+ throw Exception("Invalid item type");
+ }
+
+ this->_item.read(stream, "Reserved", Item::Field::Type::unsigned_int_8);
+ this->_item.read(stream, "Item-length", Item::Field::Type::unsigned_int_16);
+ this->_item.read(
+ stream, "Server-response-length", Item::Field::Type::unsigned_int_16);
+ this->_item.read(
+ stream, "Server-response", Item::Field::Type::string,
+ this->_item.as_unsigned_int_16("Server-response-length"));
+}
+
+std::string const &
+UserIdentityAC
+::get_server_response() const
+{
+ return this->_item.as_string("Server-response");
+}
+
+void
+UserIdentityAC
+::set_server_response(std::string const & value)
+{
+ this->_item.as_unsigned_int_16("Server-response-length") = value.size();
+ this->_item.as_string("Server-response") = value;
+
+ this->_item.as_unsigned_int_16("Item-length") =
+ 2+this->get_server_response().size();
+}
+
+}
+
+}
diff --git a/src/odil/pdu/UserIdentityAC.h b/src/odil/pdu/UserIdentityAC.h
new file mode 100644
index 0000000..c646ed6
--- /dev/null
+++ b/src/odil/pdu/UserIdentityAC.h
@@ -0,0 +1,47 @@
+/*************************************************************************
+ * 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 _1c3d6b3c_09a0_4452_9263_aa010ee1d973
+#define _1c3d6b3c_09a0_4452_9263_aa010ee1d973
+
+#include <istream>
+#include <string>
+
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+/// @brief User Identity Sub-Item Structure (A-ASSOCIATE-AC) (PS 3.7, D.3.3.7.2).
+class UserIdentityAC: public Object
+{
+public:
+ /// @brief Item type.
+ static uint8_t const type=0x59;
+
+ /// @brief Create an User Identity.
+ UserIdentityAC(std::string const & server_response="");
+
+ /// @brief Read an User Identity from a stream.
+ UserIdentityAC(std::istream & stream);
+
+ /// @brief Return the server response.
+ std::string const & get_server_response() const;
+
+ /// @brief Set the server response.
+ void set_server_response(std::string const & value);
+};
+
+}
+
+}
+
+#endif // _1c3d6b3c_09a0_4452_9263_aa010ee1d973
diff --git a/src/odil/pdu/UserIdentityRQ.cpp b/src/odil/pdu/UserIdentityRQ.cpp
new file mode 100644
index 0000000..d41fbcf
--- /dev/null
+++ b/src/odil/pdu/UserIdentityRQ.cpp
@@ -0,0 +1,169 @@
+/*************************************************************************
+ * 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/pdu/UserIdentityRQ.h"
+
+#include <cstdint>
+#include <istream>
+#include <string>
+
+#include "odil/Exception.h"
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+UserIdentityRQ
+::UserIdentityRQ()
+{
+ this->_item.add("Item-type", uint8_t(0x58));
+ this->_item.add("Reserved", uint8_t(0));
+ this->_item.add("Item-length", uint16_t(0));
+ this->_item.add("User-Identity-Type", uint8_t(1));
+ this->_item.add("Positive-response-requested", uint8_t(0));
+ this->_item.add("Primary-field-length", uint16_t(0));
+ this->_item.add("Primary-field", std::string(""));
+ this->_item.add("Secondary-field-length", uint16_t(0));
+ this->_item.add("Secondary-field", std::string(""));
+
+ this->_item.as_unsigned_int_16("Item-length") = this->_compute_length();
+}
+
+UserIdentityRQ
+::UserIdentityRQ(std::istream & stream)
+{
+ this->_item.read(stream, "Item-type", Item::Field::Type::unsigned_int_8);
+ if(this->_item.as_unsigned_int_8("Item-type") != 0x58)
+ {
+ throw Exception("Invalid item type");
+ }
+
+ this->_item.read(stream, "Reserved", Item::Field::Type::unsigned_int_8);
+ this->_item.read(stream, "Item-length", Item::Field::Type::unsigned_int_16);
+ this->_item.read(
+ stream, "User-Identity-Type", Item::Field::Type::unsigned_int_8);
+ this->_item.read(
+ stream, "Positive-response-requested",
+ Item::Field::Type::unsigned_int_8);
+ this->_item.read(
+ stream, "Primary-field-length", Item::Field::Type::unsigned_int_16);
+ this->_item.read(
+ stream, "Primary-field", Item::Field::Type::string,
+ this->_item.as_unsigned_int_16("Primary-field-length"));
+ this->_item.read(
+ stream, "Secondary-field-length", Item::Field::Type::unsigned_int_16);
+ this->_item.read(
+ stream, "Secondary-field", Item::Field::Type::string,
+ this->_item.as_unsigned_int_16("Secondary-field-length"));
+}
+
+uint8_t
+UserIdentityRQ
+::get_type() const
+{
+ return this->_item.as_unsigned_int_8("User-Identity-Type");
+}
+
+void
+UserIdentityRQ
+::set_type(uint8_t type)
+{
+ this->_item.as_unsigned_int_8("User-Identity-Type") = type;
+}
+
+bool
+UserIdentityRQ
+::get_positive_response_requested() const
+{
+ return
+ (this->_item.as_unsigned_int_8("Positive-response-requested") != 0);
+}
+
+void
+UserIdentityRQ
+::set_positive_response_requested(bool value)
+{
+ this->_item.as_unsigned_int_8("Positive-response-requested") =
+ value?1:0;
+}
+
+std::string const &
+UserIdentityRQ
+::get_primary_field() const
+{
+ return this->_item.as_string("Primary-field");
+}
+
+void
+UserIdentityRQ
+::set_primary_field(std::string const & value)
+{
+ this->_item.as_unsigned_int_16("Primary-field-length") = value.size();
+ this->_item.as_string("Primary-field") = value;
+ this->_item.as_unsigned_int_16("Item-length") = this->_compute_length();
+}
+
+std::string const &
+UserIdentityRQ
+::get_secondary_field() const
+{
+ return this->_item.as_string("Secondary-field");
+}
+
+void
+UserIdentityRQ
+::set_secondary_field(std::string const & value)
+{
+ this->_item.as_unsigned_int_16("Secondary-field-length") = value.size();
+ this->_item.as_string("Secondary-field") = value;
+ this->_item.as_unsigned_int_16("Item-length") = this->_compute_length();
+}
+
+void
+UserIdentityRQ
+::set_username(std::string const & username)
+{
+ this->set_type(1);
+ this->set_primary_field(username);
+ this->set_secondary_field("");
+}
+
+void
+UserIdentityRQ
+::set_username_and_passcode(
+ std::string const & username, std::string const & passcode)
+{
+ this->set_type(2);
+ this->set_primary_field(username);
+ this->set_secondary_field(passcode);
+}
+
+void
+UserIdentityRQ
+::set_kerberos_service_ticket(std::string const & ticket)
+{
+ this->set_type(3);
+ this->set_primary_field(ticket);
+ this->set_secondary_field("");
+}
+
+void
+UserIdentityRQ
+::set_saml_assertion(std::string const & assertion)
+{
+ this->set_type(4);
+ this->set_primary_field(assertion);
+ this->set_secondary_field("");
+}
+
+}
+
+}
diff --git a/src/odil/pdu/UserIdentityRQ.h b/src/odil/pdu/UserIdentityRQ.h
new file mode 100644
index 0000000..3289cfe
--- /dev/null
+++ b/src/odil/pdu/UserIdentityRQ.h
@@ -0,0 +1,75 @@
+/*************************************************************************
+ * 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 _b935542d_12c9_4c81_963c_32b7996af777
+#define _b935542d_12c9_4c81_963c_32b7996af777
+
+#include <cstdint>
+#include <istream>
+#include <string>
+
+#include "odil/pdu/Object.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+/// @brief User Identity Sub-Item Structure (A-ASSOCIATE-RQ) (PS 3.7, D.3.3.7.1).
+class UserIdentityRQ: public Object
+{
+public:
+ /// @brief Item type.
+ static uint8_t const type=0x58;
+
+ /// @brief Create an User Identity.
+ UserIdentityRQ();
+
+ /// @brief Read an User Identity from a stream.
+ UserIdentityRQ(std::istream & stream);
+
+ /// @brief Return the type of the User Identity.
+ uint8_t get_type() const;
+
+ /// @brief Set the type of the User Identity.
+ void set_type(uint8_t type);
+
+ /// @brief Return whether a positive response is requested.
+ bool get_positive_response_requested() const;
+
+ /// @brief Set whether a positive response is requested.
+ void set_positive_response_requested(bool value);
+
+ /// @brief Return the primary identity field.
+ std::string const & get_primary_field() const;
+
+ /// @brief Set the primary identity field.
+ void set_primary_field(std::string const & value);
+
+ /// @brief Return the secondary identity field.
+ std::string const & get_secondary_field() const;
+
+ /// @brief Set the secondary identity field.
+ void set_secondary_field(std::string const & value);
+
+ void set_username(std::string const & username);
+
+ void set_username_and_passcode(
+ std::string const & username, std::string const & passcode);
+
+ void set_kerberos_service_ticket(std::string const & ticket);
+
+ void set_saml_assertion(std::string const & assertion);
+};
+
+}
+
+}
+
+#endif // _b935542d_12c9_4c81_963c_32b7996af777
diff --git a/src/odil/pdu/UserInformation.cpp b/src/odil/pdu/UserInformation.cpp
new file mode 100644
index 0000000..32b4c52
--- /dev/null
+++ b/src/odil/pdu/UserInformation.cpp
@@ -0,0 +1,113 @@
+/*************************************************************************
+ * 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/pdu/UserInformation.h"
+
+#include <algorithm>
+#include <istream>
+#include <vector>
+
+#include "odil/Exception.h"
+#include "odil/pdu/ImplementationClassUID.h"
+#include "odil/pdu/ImplementationVersionName.h"
+#include "odil/pdu/MaximumLength.h"
+#include "odil/pdu/Object.h"
+#include "odil/pdu/RoleSelection.h"
+#include "odil/pdu/UserIdentityAC.h"
+#include "odil/pdu/UserIdentityRQ.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+UserInformation
+::UserInformation()
+{
+ this->_item.add("Item-type", uint8_t(0x50));
+ this->_item.add("Reserved", uint8_t(0));
+ this->_item.add("Item-length", uint16_t(4));
+ this->_item.add("User-data", std::vector<Item>());
+}
+
+UserInformation
+::UserInformation(std::istream & stream)
+{
+ this->_item.read(stream, "Item-type", Item::Field::Type::unsigned_int_8);
+ if(this->_item.as_unsigned_int_8("Item-type") != 0x50)
+ {
+ throw Exception("Invalid item type");
+ }
+
+ this->_item.read(stream, "Reserved", Item::Field::Type::unsigned_int_8);
+ this->_item.read(stream, "Item-length", Item::Field::Type::unsigned_int_16);
+
+ this->_item.add("User-data", std::vector<Item>());
+
+ auto const begin = stream.tellg();
+ auto const item_length = this->_item.as_unsigned_int_16("Item-length");
+
+ // Store sub-items so that all sub-items of a given type are adjacent, and
+ // that their type is in growing order.
+
+ std::vector<MaximumLength> maximum_length;
+ std::vector<ImplementationClassUID> implementation_class_uid;
+ std::vector<RoleSelection> role_selection;
+ std::vector<ImplementationVersionName> implementation_version_name;
+ std::vector<UserIdentityRQ> user_identity_rq;
+ std::vector<UserIdentityAC> user_identity_ac;
+
+ while(stream.tellg()-begin < item_length)
+ {
+ uint8_t const type = stream.peek();
+ if(type == 0x51)
+ {
+ maximum_length.push_back(MaximumLength(stream));
+ }
+ else if(type == 0x52)
+ {
+ implementation_class_uid.push_back(ImplementationClassUID(stream));
+ }
+ // 0x53: Asynchronous Operations Window, PS 3.7, D.3.3.3.1
+ else if(type == 0x54)
+ {
+ role_selection.push_back(RoleSelection(stream));
+ }
+ else if(type == 0x55)
+ {
+ implementation_version_name.push_back(
+ ImplementationVersionName(stream));
+ }
+ // 0x56: SOP Class Extended Negotiation, PS 3.7, D.3.3.5.1
+ // 0x57: SOP Class Common Extended Negotiation, PS 3.7, D.3.3.6.1
+ else if(type == 0x58)
+ {
+ user_identity_rq.push_back(UserIdentityRQ(stream));
+ }
+ else if(type == 0x59)
+ {
+ user_identity_ac.push_back(UserIdentityAC(stream));
+ }
+ else
+ {
+ throw Exception("Invalid sub-item type");
+ }
+ }
+
+ this->set_sub_items(maximum_length);
+ this->set_sub_items(implementation_class_uid);
+ this->set_sub_items(role_selection);
+ this->set_sub_items(implementation_version_name);
+ this->set_sub_items(user_identity_rq);
+ this->set_sub_items(user_identity_ac);
+}
+
+}
+
+}
diff --git a/src/odil/pdu/UserInformation.h b/src/odil/pdu/UserInformation.h
new file mode 100644
index 0000000..3f7c6fb
--- /dev/null
+++ b/src/odil/pdu/UserInformation.h
@@ -0,0 +1,66 @@
+/*************************************************************************
+ * 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 _7449339a_913f_4545_9846_311f055632c1
+#define _7449339a_913f_4545_9846_311f055632c1
+
+#include <initializer_list>
+#include <istream>
+#include <vector>
+
+#include "odil/pdu/MaximumLength.h"
+#include "odil/pdu/Object.h"
+#include "odil/pdu/UserIdentityAC.h"
+#include "odil/pdu/UserIdentityRQ.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+/// @brief User Information Item Structure (PS 3.8, 9.3.2.3 and 9.3.3.3).
+class UserInformation: public Object
+{
+public:
+ /// @brief Create a User Information item with no sub-items.
+ UserInformation();
+
+ /// @brief Read a User Information item from a stream.
+ UserInformation(std::istream & stream);
+
+ /// @brief Return sub-items of given type.
+ template<typename TObject>
+ std::vector<TObject> get_sub_items() const;
+
+ /// @brief Set a sequence of sub-items.
+ template<typename TObject>
+ void set_sub_items(std::vector<TObject> const & sub_item);
+
+ /// @brief Delete sub-items of given type.
+ template<typename TObject>
+ void delete_sub_items();
+private:
+ typedef std::vector<Item> Items;
+
+ /// @brief Return the iterators to the sub items of given type.
+ template<typename TObject>
+ std::vector<Items::const_iterator> _find_sub_items() const;
+
+ /// @brief Return the iterators to the sub items of given type.
+ template<typename TObject>
+ std::vector<Items::iterator> _find_sub_items();
+};
+
+}
+
+}
+
+#include "odil/pdu/UserInformation.txx"
+
+#endif // _7449339a_913f_4545_9846_311f055632c1
diff --git a/src/odil/pdu/UserInformation.txx b/src/odil/pdu/UserInformation.txx
new file mode 100644
index 0000000..ab1732c
--- /dev/null
+++ b/src/odil/pdu/UserInformation.txx
@@ -0,0 +1,157 @@
+/*************************************************************************
+ * 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 _bb325c55_d983_41e3_b6c4_e3b957baedba
+#define _bb325c55_d983_41e3_b6c4_e3b957baedba
+
+#include "odil/pdu/UserInformation.h"
+
+#include <algorithm>
+#include <initializer_list>
+#include <istream>
+#include <iterator>
+#include <sstream>
+#include <vector>
+
+#include "odil/Exception.h"
+#include "odil/pdu/Item.h"
+#include "odil/pdu/MaximumLength.h"
+#include "odil/pdu/Object.h"
+#include "odil/pdu/UserIdentityAC.h"
+#include "odil/pdu/UserIdentityRQ.h"
+
+namespace odil
+{
+
+namespace pdu
+{
+
+template<typename TObject>
+std::vector<TObject>
+UserInformation
+::get_sub_items() const
+{
+ auto const iterators = this->_find_sub_items<TObject>();
+
+ std::vector<TObject> result;
+ result.reserve(iterators.size());
+ std::transform(
+ iterators.begin(), iterators.end(), std::back_inserter(result),
+ [](Items::const_iterator const & it)
+ {
+ std::stringstream stream;
+ stream << *it;
+ return TObject(stream);
+ }
+ );
+
+ return result;
+}
+
+template<typename TObject>
+void
+UserInformation
+::set_sub_items(std::vector<TObject> const & sub_items)
+{
+ auto const & old_items = this->_item.as_items("User-data");
+ std::vector<Item> new_items;
+
+ auto old_items_iterator = old_items.begin();
+ while(old_items_iterator != old_items.end() &&
+ old_items_iterator->as_unsigned_int_8("Item-type") < TObject::type)
+ {
+ new_items.push_back(*old_items_iterator);
+ ++old_items_iterator;
+ }
+
+ std::transform(
+ sub_items.begin(), sub_items.end(), std::back_inserter(new_items),
+ [](Object const & object) { return object.get_item(); });
+
+ while(old_items_iterator != old_items.end() &&
+ old_items_iterator->as_unsigned_int_8("Item-type") == TObject::type)
+ {
+ ++old_items_iterator;
+ }
+
+ while(old_items_iterator != old_items.end())
+ {
+ new_items.push_back(*old_items_iterator);
+ ++old_items_iterator;
+ }
+
+ this->_item.as_items("User-data") = new_items;
+
+ this->_item.as_unsigned_int_16("Item-length") = this->_compute_length();
+}
+
+template<typename TObject>
+void
+UserInformation
+::delete_sub_items()
+{
+ auto const & old_items = this->_item.as_items("User-data");
+ std::vector<Item> new_items;
+ std::copy_if(
+ old_items.begin(), old_items.end(), std::back_inserter(new_items),
+ [](Item const & item)
+ {
+ return item.as_unsigned_int_8("Item-type") != TObject::type;
+ }
+ );
+
+ this->_item.as_items("User-data") = new_items;
+
+ this->_item.as_unsigned_int_16("Item-length") = this->_compute_length();
+}
+
+template<typename TObject>
+std::vector<UserInformation::Items::const_iterator>
+UserInformation
+::_find_sub_items() const
+{
+ std::vector<Items::const_iterator> result;
+
+ auto const & sub_items = this->_item.as_items("User-data");
+ auto iterator = sub_items.begin();
+ for(; iterator != sub_items.end(); ++iterator)
+ {
+ if(iterator->as_unsigned_int_8("Item-type") == TObject::type)
+ {
+ result.push_back(iterator);
+ }
+ }
+
+ return result;
+}
+
+template<typename TObject>
+std::vector<UserInformation::Items::iterator>
+UserInformation
+::_find_sub_items()
+{
+ std::vector<Items::iterator> result;
+
+ auto & sub_items = this->_item.as_items("User-data");
+ auto iterator = sub_items.begin();
+ for(; iterator != sub_items.end(); ++iterator)
+ {
+ if(iterator->as_unsigned_int_8("Item-type") == TObject::type)
+ {
+ result.push_back(iterator);
+ }
+ }
+
+ return result;
+}
+
+}
+
+}
+
+#endif // _bb325c55_d983_41e3_b6c4_e3b957baedba
diff --git a/src/dcmtkpp/registry.cpp b/src/odil/registry.cpp
similarity index 99%
rename from src/dcmtkpp/registry.cpp
rename to src/odil/registry.cpp
index ee40214..69347b8 100644
--- a/src/dcmtkpp/registry.cpp
+++ b/src/odil/registry.cpp
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -8,14 +8,14 @@
#include <stdint.h>
-#include "dcmtkpp/registry.h"
+#include "odil/registry.h"
-#include "dcmtkpp/ElementsDictionary.h"
-#include "dcmtkpp/Tag.h"
-#include "dcmtkpp/VR.h"
-#include "dcmtkpp/UIDsDictionary.h"
+#include "odil/ElementsDictionary.h"
+#include "odil/Tag.h"
+#include "odil/VR.h"
+#include "odil/UIDsDictionary.h"
-namespace dcmtkpp
+namespace odil
{
namespace registry
@@ -8170,5 +8170,5 @@ UIDsDictionary create_uids_dictionary()
}
-dcmtkpp::ElementsDictionary dcmtkpp::registry::public_dictionary=dcmtkpp::registry::create_public_dictionary();
-dcmtkpp::UIDsDictionary dcmtkpp::registry::uids_dictionary=dcmtkpp::registry::create_uids_dictionary();
\ No newline at end of file
+odil::ElementsDictionary odil::registry::public_dictionary=odil::registry::create_public_dictionary();
+odil::UIDsDictionary odil::registry::uids_dictionary=odil::registry::create_uids_dictionary();
\ No newline at end of file
diff --git a/src/dcmtkpp/registry.h b/src/odil/registry.h
similarity index 99%
rename from src/dcmtkpp/registry.h
rename to src/odil/registry.h
index 122216a..449c3e0 100644
--- a/src/dcmtkpp/registry.h
+++ b/src/odil/registry.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -9,11 +9,11 @@
#ifndef _afc7b2d7_0869_4fea_9a9b_7fe6228baca9
#define _afc7b2d7_0869_4fea_9a9b_7fe6228baca9
-#include "dcmtkpp/ElementsDictionary.h"
-#include "dcmtkpp/Tag.h"
-#include "dcmtkpp/UIDsDictionary.h"
+#include "odil/ElementsDictionary.h"
+#include "odil/Tag.h"
+#include "odil/UIDsDictionary.h"
-namespace dcmtkpp
+namespace odil
{
namespace registry
diff --git a/src/odil/uid.cpp b/src/odil/uid.cpp
new file mode 100644
index 0000000..8a8be2b
--- /dev/null
+++ b/src/odil/uid.cpp
@@ -0,0 +1,43 @@
+/*************************************************************************
+ * 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/uid.h"
+
+#include <random>
+#include <string>
+
+#include "odil/Exception.h"
+
+#define ODIL_STRINGIFY_HELPER(s) #s
+#define ODIL_STRINGIFY(s) ODIL_STRINGIFY_HELPER(s)
+
+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);
+#else
+#error ODIL_MAJOR_VERSION must be defined
+#endif
+
+std::string generate_uid()
+{
+ static std::random_device generator;
+ std::uniform_int_distribution<> non_zero(1, 9);
+ std::uniform_int_distribution<> digits(0, 9);
+
+ std::string result = uid_prefix + "." + std::to_string(non_zero(generator));
+ while(result.size()<64)
+ {
+ result += std::to_string(digits(generator));
+ }
+ return result;
+}
+
+}
diff --git a/src/dcmtkpp/uid.h b/src/odil/uid.h
similarity index 60%
rename from src/dcmtkpp/uid.h
rename to src/odil/uid.h
index 5248483..b1fd3c2 100644
--- a/src/dcmtkpp/uid.h
+++ b/src/odil/uid.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -11,20 +11,17 @@
#include <string>
-namespace dcmtkpp
+namespace odil
{
-/// @brief UID prefix of DCMTK++.
+/// @brief UID prefix of Odil.
std::string const uid_prefix="1.2.826.0.1.3680043.9.5560";
-#ifdef DCMTKPP_MAJOR_VERSION
-#define DCMTKPP_STRINGIFY_HELPER(s) #s
-#define DCMTKPP_STRINGIFY(s) DCMTKPP_STRINGIFY_HELPER(s)
-/// @brief Implementation class UID of DCMTK++.
-std::string const implementation_class_uid=uid_prefix+"0." DCMTKPP_STRINGIFY(DCMTKPP_MAJOR_VERSION);
-#else
-#error DCMTKPP_MAJOR_VERSION must be defined
-#endif
+/// @brief Implementation class UID of Odil.
+extern std::string const implementation_class_uid;
+
+/// @brief Implementation version name of Odil.
+extern std::string const implementation_version_name;
/// @brief Generate a UID under the UID prefix.
std::string generate_uid();
diff --git a/src/dcmtkpp/unicode.cpp b/src/odil/unicode.cpp
similarity index 98%
rename from src/dcmtkpp/unicode.cpp
rename to src/odil/unicode.cpp
index 76246c7..b971423 100644
--- a/src/dcmtkpp/unicode.cpp
+++ b/src/odil/unicode.cpp
@@ -1,14 +1,15 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 "dcmtkpp/unicode.h"
+#include "odil/unicode.h"
#include <algorithm>
+#include <iterator>
#include <map>
#include <set>
#include <sstream>
@@ -17,10 +18,10 @@
#include <unicode/errorcode.h>
#include <unicode/unistr.h>
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/Value.h"
+#include "odil/Exception.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
{
std::map<std::string, std::string> const icu_encodings = {
diff --git a/src/dcmtkpp/unicode.h b/src/odil/unicode.h
similarity index 87%
rename from src/dcmtkpp/unicode.h
rename to src/odil/unicode.h
index 4815d80..2628fc3 100644
--- a/src/dcmtkpp/unicode.h
+++ b/src/odil/unicode.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -10,9 +10,9 @@
#define _4a178325_e3d6_4f6f_9a18_ba6a983ee396
#include <string>
-#include "dcmtkpp/Value.h"
+#include "odil/Value.h"
-namespace dcmtkpp
+namespace odil
{
/// @brief Convert a string to its UTF-8 representation
diff --git a/src/dcmtkpp/xml_converter.cpp b/src/odil/xml_converter.cpp
similarity index 95%
rename from src/dcmtkpp/xml_converter.cpp
rename to src/odil/xml_converter.cpp
index 7a80781..227f2f5 100644
--- a/src/dcmtkpp/xml_converter.cpp
+++ b/src/odil/xml_converter.cpp
@@ -1,22 +1,20 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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 <iterator>
#include <map>
#include <vector>
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/ofstd/ofstd.h>
-#include <dcmtk/ofstd/ofstream.h>
+#include "odil/base64.h"
+#include "odil/registry.h"
+#include "odil/xml_converter.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/xml_converter.h"
-
-namespace dcmtkpp
+namespace odil
{
struct ToXMLVisitor
@@ -216,11 +214,10 @@ struct ToXMLVisitor
boost::property_tree::ptree tag_value;
- unsigned char const * data =
- reinterpret_cast<unsigned char const *>(&value[0]);
- OFStringStream stream;
- OFStandard::encodeBase64(stream, data, value.size());
- tag_value.put_value(stream.str().c_str());
+ std::string encoded;
+ encoded.reserve(value.size()*4/3);
+ base64::encode(value.begin(), value.end(), std::back_inserter(encoded));
+ tag_value.put_value(encoded);
result.add_child("InlineBinary", tag_value);
@@ -569,16 +566,11 @@ DataSet as_dataset(boost::property_tree::ptree const & xml)
}
auto const & encoded = it_value->second.get_value<std::string>();
- OFString const encoded_dcmtk(encoded.c_str());
- unsigned char * decoded;
- size_t const decoded_size =
- OFStandard::decodeBase64(encoded_dcmtk, decoded);
-
- element.as_binary().resize(decoded_size);
- std::copy(decoded, decoded+decoded_size,
- element.as_binary().begin());
-
- delete[] decoded;
+ auto & decoded = element.as_binary();
+ decoded.reserve(encoded.size()*3/4);
+ base64::decode(
+ encoded.begin(), encoded.end(),
+ std::back_inserter(decoded));
find_inline_binary = true;
}
@@ -594,4 +586,4 @@ DataSet as_dataset(boost::property_tree::ptree const & xml)
return data_set;
}
-} // namespace dcmtkpp
+} // namespace odil
diff --git a/src/dcmtkpp/xml_converter.h b/src/odil/xml_converter.h
similarity index 86%
rename from src/dcmtkpp/xml_converter.h
rename to src/odil/xml_converter.h
index fa990af..5b01ef2 100644
--- a/src/dcmtkpp/xml_converter.h
+++ b/src/odil/xml_converter.h
@@ -1,5 +1,5 @@
/*************************************************************************
- * dcmtkpp - Copyright (C) Universite de Strasbourg
+ * 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
@@ -11,9 +11,9 @@
#include <boost/property_tree/ptree.hpp>
-#include "dcmtkpp/DataSet.h"
+#include "odil/DataSet.h"
-namespace dcmtkpp
+namespace odil
{
/// @brief Convert a data set to its XML representation.
@@ -22,6 +22,6 @@ boost::property_tree::ptree as_xml(DataSet const & data_set);
/// @brief Create a data set from its XML representation.
DataSet as_dataset(boost::property_tree::ptree const & xml);
-} // namespace dcmtkpp
+} // namespace odil
#endif // _61eb6ed2_447a_43b5_a6ba_ca7c2a5fb492
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 80b3ced..d112b72 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,17 +1,28 @@
find_package(Boost COMPONENTS unit_test_framework REQUIRED)
-link_directories(${Boost_LIBRARY_DIRS})
-add_definitions(-DBOOST_TEST_DYN_LINK)
+find_package(DCMTK REQUIRED)
+find_package(JsonCpp REQUIRED)
+
+add_subdirectory(tools)
-include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src)
+file(GLOB headers *.h)
+file(GLOB_RECURSE tests code/*.cpp)
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src ${JsonCpp_INCLUDE_DIRS})
+add_definitions(
+ ${DCMTK_DEFINITIONS}
+ -D BOOST_ASIO_DYN_LINK
+ -D ODIL_MAJOR_VERSION=${odil_MAJOR_VERSION}
+ -DBOOST_TEST_DYN_LINK
+)
+link_directories(${Boost_LIBRARY_DIRS})
-add_definitions("-D HAVE_CONFIG_H")
-add_definitions("-D DCMTKPP_MAJOR_VERSION=${dcmtkpp_MAJOR_VERSION}")
-file(GLOB_RECURSE tests *.cpp)
foreach(test_file ${tests})
get_filename_component(test ${test_file} NAME_WE)
- add_executable(${test} ${test_file})
- target_link_libraries(${test} dcmtkpp ${Boost_LIBRARIES})
+
+ add_executable(${test} ${test_file} ${headers})
+ target_link_libraries(${test} odil ${Boost_LIBRARIES})
+
file(READ ${test_file} content)
set(pattern "BOOST_(AUTO|FIXTURE)_TEST_CASE\\(([^),]+)")
@@ -32,11 +43,13 @@ foreach(test_file ${tests})
foreach(match ${matches})
string(REGEX REPLACE ${pattern} "\\1" vr ${match})
- set(case "${vr}FromDcmtkpp")
+ set(case "${vr}FromOdil")
add_test("${test}_${case}" "${test}" "--run_test=${case}")
- set(case "${vr}ToDcmtkpp")
+ set(case "${vr}ToOdil")
add_test("${test}_${case}" "${test}" "--run_test=${case}")
endforeach()
endforeach()
+
+add_custom_target(Runner ${CMAKE_COMMAND} -E echo "Test runner" SOURCES run.sh)
diff --git a/tests/MessageFixtureBase.h b/tests/MessageFixtureBase.h
index 55abb73..43d5a23 100644
--- a/tests/MessageFixtureBase.h
+++ b/tests/MessageFixtureBase.h
@@ -1,9 +1,9 @@
#ifndef _779c46d6_b6ea_443a_9642_f1ec5b87d4b3
#define _779c46d6_b6ea_443a_9642_f1ec5b87d4b3
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/Message.h"
+#include "odil/DataSet.h"
+#include "odil/Exception.h"
+#include "odil/message/Message.h"
/// @brief Base class for fixtures of Message-derived classes.
template<typename TMessage>
@@ -11,40 +11,40 @@ struct MessageFixtureBase
{
/// @brief Check that a specific message can be constructed from a generic Message.
void check_message_constructor(
- dcmtkpp::DataSet const & command_set)
+ odil::DataSet const & command_set)
{
- dcmtkpp::Message const generic_message(command_set);
+ odil::message::Message const generic_message(command_set);
TMessage const message(generic_message);
this->check(message);
}
/// @brief Check that a specific message can be constructed from a generic Message.
void check_message_constructor(
- dcmtkpp::DataSet const & command_set, dcmtkpp::DataSet const & data_set)
+ odil::DataSet const & command_set, odil::DataSet const & data_set)
{
- dcmtkpp::Message const generic_message(command_set, data_set);
+ odil::message::Message const generic_message(command_set, data_set);
TMessage const message(generic_message);
this->check(message);
}
/// @brief Check that a specific message cannot be constructed from a generic Message.
void check_message_constructor_throw(
- dcmtkpp::DataSet const & command_set)
+ odil::DataSet const & command_set)
{
- dcmtkpp::Message const generic_message(command_set);
+ odil::message::Message const generic_message(command_set);
BOOST_CHECK_THROW(
TMessage const message(generic_message),
- dcmtkpp::Exception);
+ odil::Exception);
}
/// @brief Check that a specific message cannot be constructed from a generic Message.
void check_message_constructor_throw(
- dcmtkpp::DataSet const & command_set, dcmtkpp::DataSet const & data_set)
+ odil::DataSet const & command_set, odil::DataSet const & data_set)
{
- dcmtkpp::Message const generic_message(command_set, data_set);
+ odil::message::Message const generic_message(command_set, data_set);
BOOST_CHECK_THROW(
TMessage const message(generic_message),
- dcmtkpp::Exception);
+ odil::Exception);
}
/// @brief Check that the specific message attributes are set correctly
diff --git a/tests/PeerFixtureBase.h b/tests/PeerFixtureBase.h
index 532501c..edf62ad 100644
--- a/tests/PeerFixtureBase.h
+++ b/tests/PeerFixtureBase.h
@@ -1,72 +1,46 @@
#ifndef _b21e8d37_0125_4d64_84aa_f91d9d96612b
#define _b21e8d37_0125_4d64_84aa_f91d9d96612b
-#include <stdint.h>
-#include <stdlib.h>
+#include <cstdint>
+#include <cstdlib>
#include <string>
-#include <utility>
#include <vector>
#include <boost/lexical_cast.hpp>
-#include "dcmtkpp/Association.h"
-#include "dcmtkpp/Network.h"
+#include "odil/Association.h"
+#include "odil/AssociationParameters.h"
/// @brief Base class for fixtures of requiring a working association.
class PeerFixtureBase
{
public:
- dcmtkpp::Network network;
- dcmtkpp::Association association;
+ typedef
+ odil::AssociationParameters::PresentationContext PresentationContext;
+ odil::Association association;
- struct PresentationContext
+ PeerFixtureBase(std::vector<PresentationContext> const & contexts)
{
- std::string abstract_syntax;
- std::vector<std::string> transfer_syntaxes;
- T_ASC_SC_ROLE role;
-
- PresentationContext(std::string const & abstract_syntax, std::vector<std::string> const & transfer_syntaxes, T_ASC_SC_ROLE role=ASC_SC_ROLE_DEFAULT)
- : abstract_syntax(abstract_syntax),
- transfer_syntaxes(transfer_syntaxes), role(role)
- {
- // Nothing else
- }
- };
-
- PeerFixtureBase(T_ASC_NetworkRole role, int port, int timeout,
- std::vector<PresentationContext> const & presentation_contexts)
- : network(role, port, timeout), association()
- {
- this->network.initialize();
-
- this->association.set_own_ae_title(
- this->_get_environment_variable("DCMTKPP_OWN_AET"));
- this->association.set_peer_host_name(
- this->_get_environment_variable("DCMTKPP_PEER_HOST_NAME"));
+ this->association.set_peer_host(
+ this->get_environment_variable("ODIL_PEER_HOST_NAME"));
this->association.set_peer_port(
- this->_get_environment_variable<uint16_t>("DCMTKPP_PEER_PORT"));
- this->association.set_peer_ae_title(
- this->_get_environment_variable("DCMTKPP_PEER_AET"));
- for(auto const & presentation_context: presentation_contexts)
- {
- this->association.add_presentation_context(
- presentation_context.abstract_syntax,
- presentation_context.transfer_syntaxes,
- presentation_context.role);
- }
-
- this->association.associate(this->network);
+ this->get_environment_variable<uint16_t>("ODIL_PEER_PORT"));
+ this->association.update_parameters()
+ .set_calling_ae_title(
+ this->get_environment_variable("ODIL_OWN_AET"))
+ .set_called_ae_title(
+ this->get_environment_variable("ODIL_PEER_AET"))
+ .set_presentation_contexts(contexts);
+
+ this->association.associate();
}
~PeerFixtureBase()
{
this->association.release();
- this->network.drop();
}
-private:
-
- std::string _get_environment_variable(std::string const & name) const
+ static std::string get_environment_variable(std::string const & name)
{
char* value = getenv(name.c_str());
if(value == NULL)
@@ -77,9 +51,10 @@ private:
}
template<typename T>
- T _get_environment_variable(std::string const & name) const
+ static T get_environment_variable(std::string const & name)
{
- return boost::lexical_cast<T>(this->_get_environment_variable(name));
+ return boost::lexical_cast<T>(
+ PeerFixtureBase::get_environment_variable(name));
}
};
diff --git a/tests/code/Association.cpp b/tests/code/Association.cpp
index b80f222..e452cd1 100644
--- a/tests/code/Association.cpp
+++ b/tests/code/Association.cpp
@@ -3,250 +3,152 @@
#include <ostream>
-#include "dcmtkpp/Association.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/registry.h"
+#include "odil/Association.h"
+#include "odil/Exception.h"
+#include "odil/registry.h"
#include "../PeerFixtureBase.h"
-namespace dcmtkpp
+namespace odil
{
-std::ostream & operator<<(std::ostream & stream, UserIdentityType const & type)
+bool
+operator==(
+ AssociationParameters::PresentationContext const & left,
+ AssociationParameters::PresentationContext const & right)
{
- stream << int(type);
- return stream;
+ return (
+ left.abstract_syntax == right.abstract_syntax &&
+ left.transfer_syntaxes == right.transfer_syntaxes &&
+ left.scu_role_support == right.scu_role_support &&
+ left.scp_role_support == right.scp_role_support
+ );
+}
+
+bool
+operator==(
+ AssociationParameters const & left, AssociationParameters const & right)
+{
+ return (
+ left.get_calling_ae_title() == right.get_calling_ae_title() &&
+ left.get_called_ae_title() == right.get_called_ae_title() &&
+
+ left.get_presentation_contexts() == right.get_presentation_contexts() &&
+
+
+ left.get_user_identity().type == right.get_user_identity().type &&
+ left.get_user_identity().primary_field == right.get_user_identity().primary_field &&
+ left.get_user_identity().secondary_field == right.get_user_identity().secondary_field &&
+
+ left.get_maximum_length() == right.get_maximum_length()
+ );
}
}
+
BOOST_AUTO_TEST_CASE(DefaultConstructor)
{
- dcmtkpp::Association association;
- BOOST_CHECK_EQUAL(association.get_own_ae_title(), "");
-
- BOOST_CHECK_EQUAL(association.get_peer_host_name(), "");
+ odil::Association association;
+ BOOST_CHECK_EQUAL(association.get_peer_host(), "");
BOOST_CHECK_EQUAL(association.get_peer_port(), 104);
- BOOST_CHECK_EQUAL(association.get_peer_ae_title(), "");
-
- BOOST_CHECK_EQUAL(
- association.get_user_identity_type(), dcmtkpp::UserIdentityType::None);
- BOOST_CHECK_EQUAL(association.get_user_identity_primary_field(), "");
- BOOST_CHECK_EQUAL(association.get_user_identity_secondary_field(), "");
+ BOOST_CHECK(
+ association.get_parameters() ==
+ odil::AssociationParameters()
+ );
BOOST_CHECK(!association.is_associated());
- BOOST_CHECK_EQUAL(
- association.get_association(), static_cast<T_ASC_Association*>(NULL));
}
BOOST_AUTO_TEST_CASE(CopyConstructor)
{
- dcmtkpp::Association association;
- association.set_own_ae_title("local");
-
- association.set_peer_host_name("pacs.example.com");
+ odil::Association association;
+ association.set_peer_host("pacs.example.com");
association.set_peer_port(11112);
- association.set_peer_ae_title("remote");
-
- association.set_user_identity_to_username_and_password("foo", "bar");
+ association.update_parameters()
+ .set_called_ae_title("remote")
+ .set_user_identity_to_username_and_password("foo", "bar");
- dcmtkpp::Association const other(association);
+ odil::Association const other(association);
- BOOST_CHECK_EQUAL(other.get_own_ae_title(), association.get_own_ae_title());
-
- BOOST_CHECK_EQUAL(
- other.get_peer_host_name(), association.get_peer_host_name());
+ BOOST_CHECK_EQUAL(other.get_peer_host(), association.get_peer_host());
BOOST_CHECK_EQUAL(other.get_peer_port(), association.get_peer_port());
- BOOST_CHECK_EQUAL(
- other.get_peer_ae_title(), association.get_peer_ae_title());
-
- BOOST_CHECK_EQUAL(
- other.get_user_identity_type(), association.get_user_identity_type());
- BOOST_CHECK_EQUAL(
- other.get_user_identity_primary_field(),
- association.get_user_identity_primary_field());
- BOOST_CHECK_EQUAL(
- other.get_user_identity_secondary_field(),
- association.get_user_identity_secondary_field());
+ BOOST_CHECK(
+ association.get_parameters() ==
+ other.get_parameters()
+ );
}
BOOST_AUTO_TEST_CASE(Assignment)
{
- dcmtkpp::Association association;
- association.set_own_ae_title("local");
-
- association.set_peer_host_name("pacs.example.com");
+ odil::Association association;
+ association.set_peer_host("pacs.example.com");
association.set_peer_port(11112);
- association.set_peer_ae_title("remote");
-
- association.set_user_identity_to_username_and_password("foo", "bar");
+ association.update_parameters()
+ .set_called_ae_title("remote")
+ .set_user_identity_to_username_and_password("foo", "bar");
- dcmtkpp::Association other;
+ odil::Association other;
other = association;
- BOOST_CHECK_EQUAL(other.get_own_ae_title(), association.get_own_ae_title());
-
- BOOST_CHECK_EQUAL(
- other.get_peer_host_name(), association.get_peer_host_name());
+ BOOST_CHECK_EQUAL(other.get_peer_host(), association.get_peer_host());
BOOST_CHECK_EQUAL(other.get_peer_port(), association.get_peer_port());
- BOOST_CHECK_EQUAL(
- other.get_peer_ae_title(), association.get_peer_ae_title());
-
- BOOST_CHECK_EQUAL(
- other.get_user_identity_type(), association.get_user_identity_type());
- BOOST_CHECK_EQUAL(
- other.get_user_identity_primary_field(),
- association.get_user_identity_primary_field());
- BOOST_CHECK_EQUAL(
- other.get_user_identity_secondary_field(),
- association.get_user_identity_secondary_field());
-}
-
-BOOST_AUTO_TEST_CASE(OwnAETitle)
-{
- dcmtkpp::Association association;
- association.set_own_ae_title("myself");
- BOOST_CHECK_EQUAL(association.get_own_ae_title(), "myself");
+ BOOST_CHECK(
+ association.get_parameters() ==
+ other.get_parameters()
+ );
}
BOOST_AUTO_TEST_CASE(PeerHostName)
{
- dcmtkpp::Association association;
- association.set_peer_host_name("pacs.example.com");
- BOOST_CHECK_EQUAL(association.get_peer_host_name(), "pacs.example.com");
+ odil::Association association;
+ association.set_peer_host("pacs.example.com");
+ BOOST_CHECK_EQUAL(association.get_peer_host(), "pacs.example.com");
}
BOOST_AUTO_TEST_CASE(PeerPort)
{
- dcmtkpp::Association association;
+ odil::Association association;
association.set_peer_port(11112);
BOOST_CHECK_EQUAL(association.get_peer_port(), 11112);
}
-BOOST_AUTO_TEST_CASE(PeerAETitle)
-{
- dcmtkpp::Association association;
- association.set_peer_ae_title("remote");
- BOOST_CHECK_EQUAL(association.get_peer_ae_title(), "remote");
-}
-
-BOOST_AUTO_TEST_CASE(UserIdentityType)
-{
- dcmtkpp::Association association;
- association.set_user_identity_type(dcmtkpp::UserIdentityType::Username);
- BOOST_CHECK_EQUAL(
- association.get_user_identity_type(),
- dcmtkpp::UserIdentityType::Username);
-}
-
-BOOST_AUTO_TEST_CASE(UserIdentityPrimaryField)
-{
- dcmtkpp::Association association;
- association.set_user_identity_primary_field("user");
- BOOST_CHECK_EQUAL(association.get_user_identity_primary_field(), "user");
-}
-
-BOOST_AUTO_TEST_CASE(UserIdentitySecondaryField)
-{
- dcmtkpp::Association association;
- association.set_user_identity_secondary_field("password");
- BOOST_CHECK_EQUAL(
- association.get_user_identity_secondary_field(), "password");
-}
-
-BOOST_AUTO_TEST_CASE(UserIdentityNone)
+BOOST_AUTO_TEST_CASE(AssociationParameters)
{
- dcmtkpp::Association association;
- association.set_user_identity_to_none();
- BOOST_CHECK_EQUAL(association.get_user_identity_primary_field(), "");
- BOOST_CHECK_EQUAL(association.get_user_identity_secondary_field(), "");
-}
+ odil::Association association;
-BOOST_AUTO_TEST_CASE(UserIdentityUsername)
-{
- dcmtkpp::Association association;
- association.set_user_identity_to_username("user");
- BOOST_CHECK_EQUAL(
- association.get_user_identity_type(),
- dcmtkpp::UserIdentityType::Username);
- BOOST_CHECK_EQUAL(association.get_user_identity_primary_field(), "user");
- BOOST_CHECK_EQUAL(association.get_user_identity_secondary_field(), "");
-}
+ odil::AssociationParameters parameters;
+ parameters.set_calling_ae_title("foo");
+ association.set_parameters(parameters);
-BOOST_AUTO_TEST_CASE(UserIdentityUsernameAndPassword)
-{
- dcmtkpp::Association association;
- association.set_user_identity_to_username_and_password("user", "password");
- BOOST_CHECK_EQUAL(
- association.get_user_identity_type(),
- dcmtkpp::UserIdentityType::UsernameAndPassword);
- BOOST_CHECK_EQUAL(association.get_user_identity_primary_field(), "user");
- BOOST_CHECK_EQUAL(
- association.get_user_identity_secondary_field(), "password");
-}
-
-BOOST_AUTO_TEST_CASE(UserIdentityKerberos)
-{
- dcmtkpp::Association association;
- association.set_user_identity_to_kerberos("ticket");
- BOOST_CHECK_EQUAL(
- association.get_user_identity_type(),
- dcmtkpp::UserIdentityType::Kerberos);
- BOOST_CHECK_EQUAL(association.get_user_identity_primary_field(), "ticket");
- BOOST_CHECK_EQUAL(association.get_user_identity_secondary_field(), "");
-}
-
-BOOST_AUTO_TEST_CASE(UserIdentitySAML)
-{
- dcmtkpp::Association association;
- association.set_user_identity_to_saml("assertion");
- BOOST_CHECK_EQUAL(
- association.get_user_identity_type(), dcmtkpp::UserIdentityType::SAML);
- BOOST_CHECK_EQUAL(
- association.get_user_identity_primary_field(), "assertion");
- BOOST_CHECK_EQUAL(association.get_user_identity_secondary_field(), "");
+ BOOST_REQUIRE(association.get_parameters() == parameters);
}
BOOST_AUTO_TEST_CASE(Associate)
{
- PeerFixtureBase fixture(NET_REQUESTOR, 0, 10,
+ PeerFixtureBase fixture({
{
- { dcmtkpp::registry::VerificationSOPClass,
- {dcmtkpp::registry::ImplicitVRLittleEndian}
- }
- });
- BOOST_CHECK_THROW(
- fixture.association.set_own_ae_title("foo"), dcmtkpp::Exception);
- BOOST_CHECK_THROW(
- fixture.association.set_peer_host_name("foo"), dcmtkpp::Exception);
- BOOST_CHECK_THROW(
- fixture.association.set_peer_port(1234), dcmtkpp::Exception);
- BOOST_CHECK_THROW(
- fixture.association.set_peer_ae_title("foo"), dcmtkpp::Exception);
- BOOST_CHECK_THROW(
- fixture.association.add_presentation_context(
- dcmtkpp::registry::PatientRootQueryRetrieveInformationModelGET,
- { dcmtkpp::registry::ImplicitVRLittleEndian }),
- dcmtkpp::Exception);
+ 1, odil::registry::VerificationSOPClass,
+ { odil::registry::ImplicitVRLittleEndian }, true, false
+ }
+ });
BOOST_CHECK_THROW(
- fixture.association.set_user_identity_type(dcmtkpp::UserIdentityType::SAML),
- dcmtkpp::Exception);
+ fixture.association.set_peer_host("foo"), odil::Exception);
BOOST_CHECK_THROW(
- fixture.association.set_user_identity_primary_field("foo"),
- dcmtkpp::Exception);
+ fixture.association.set_peer_port(1234), odil::Exception);
BOOST_CHECK_THROW(
- fixture.association.set_user_identity_secondary_field("foo"),
- dcmtkpp::Exception);
+ fixture.association.update_parameters().set_maximum_length(123),
+ odil::Exception);
}
BOOST_AUTO_TEST_CASE(Release)
{
- dcmtkpp::Association association;
- BOOST_CHECK_THROW(association.release(), dcmtkpp::Exception);
+ odil::Association association;
+ BOOST_CHECK_THROW(association.release(), odil::Exception);
}
BOOST_AUTO_TEST_CASE(Abort)
{
- dcmtkpp::Association association;
- BOOST_CHECK_THROW(association.abort(), dcmtkpp::Exception);
+ odil::Association association;
+ BOOST_CHECK_THROW(association.abort(2, 4), odil::Exception);
}
diff --git a/tests/code/AssociationAcceptor.cpp b/tests/code/AssociationAcceptor.cpp
new file mode 100644
index 0000000..15c8cef
--- /dev/null
+++ b/tests/code/AssociationAcceptor.cpp
@@ -0,0 +1,13 @@
+#define BOOST_TEST_MODULE AssociationAcceptor
+#include <boost/test/unit_test.hpp>
+
+#include "odil/AssociationAcceptor.h"
+
+BOOST_AUTO_TEST_CASE(AssociationRejected)
+{
+ odil::AssociationRejected const exception(1, 2, 3, "foo");
+ BOOST_REQUIRE_EQUAL(exception.get_result(), 1);
+ BOOST_REQUIRE_EQUAL(exception.get_source(), 2);
+ BOOST_REQUIRE_EQUAL(exception.get_reason(), 3);
+ BOOST_REQUIRE_EQUAL(exception.what(), "foo");
+}
diff --git a/tests/code/AssociationParameters.cpp b/tests/code/AssociationParameters.cpp
new file mode 100644
index 0000000..e76540e
--- /dev/null
+++ b/tests/code/AssociationParameters.cpp
@@ -0,0 +1,178 @@
+#define BOOST_TEST_MODULE AssociationParameters
+#include <boost/test/unit_test.hpp>
+
+#include "odil/AssociationParameters.h"
+#include "odil/Exception.h"
+
+BOOST_AUTO_TEST_CASE(Constructor)
+{
+ odil::AssociationParameters const parameters;
+ BOOST_REQUIRE_EQUAL(parameters.get_called_ae_title(), "");
+ BOOST_REQUIRE_EQUAL(parameters.get_calling_ae_title(), "");
+ BOOST_REQUIRE(parameters.get_presentation_contexts().empty());
+
+ BOOST_REQUIRE(
+ parameters.get_user_identity().type ==
+ odil::AssociationParameters::UserIdentity::Type::None);
+ BOOST_REQUIRE(parameters.get_user_identity().primary_field.empty());
+ BOOST_REQUIRE(parameters.get_user_identity().secondary_field.empty());
+
+ BOOST_REQUIRE_EQUAL(parameters.get_maximum_length(), 16384);
+}
+
+BOOST_AUTO_TEST_CASE(CalledAETITLE)
+{
+ odil::AssociationParameters parameters;
+ parameters.set_called_ae_title("foo");
+ BOOST_REQUIRE_EQUAL(parameters.get_called_ae_title(), "foo");
+}
+
+BOOST_AUTO_TEST_CASE(CalledAETITLETooLong)
+{
+ odil::AssociationParameters parameters;
+ BOOST_REQUIRE_THROW(
+ parameters.set_called_ae_title("0123456789abcdefTOO_LONG"),
+ odil::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(CallingAETITLE)
+{
+ odil::AssociationParameters parameters;
+ parameters.set_calling_ae_title("foo");
+ BOOST_REQUIRE_EQUAL(parameters.get_calling_ae_title(), "foo");
+}
+
+BOOST_AUTO_TEST_CASE(CallingAETITLETooLong)
+{
+ odil::AssociationParameters parameters;
+ BOOST_REQUIRE_THROW(
+ parameters.set_calling_ae_title("0123456789abcdefTOO_LONG"),
+ odil::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(PresentationContexts)
+{
+ odil::AssociationParameters parameters;
+ parameters.set_presentation_contexts({
+ { 1, "abstract1", { "transfer1", "transfer2" }, true, false },
+ { 3, "abstract2", { "transfer3" }, false, true }
+ });
+
+ BOOST_REQUIRE_EQUAL(parameters.get_presentation_contexts().size(), 2);
+
+ BOOST_REQUIRE_EQUAL(parameters.get_presentation_contexts()[0].id, 1);
+ BOOST_REQUIRE_EQUAL(
+ parameters.get_presentation_contexts()[0].abstract_syntax, "abstract1");
+ BOOST_REQUIRE_EQUAL(
+ parameters.get_presentation_contexts()[0].transfer_syntaxes.size(), 2);
+ BOOST_REQUIRE_EQUAL(
+ parameters.get_presentation_contexts()[0].transfer_syntaxes[0],
+ "transfer1");
+ BOOST_REQUIRE_EQUAL(
+ parameters.get_presentation_contexts()[0].transfer_syntaxes[1],
+ "transfer2");
+ BOOST_REQUIRE(parameters.get_presentation_contexts()[0].scu_role_support);
+ BOOST_REQUIRE(!parameters.get_presentation_contexts()[0].scp_role_support);
+
+ BOOST_REQUIRE_EQUAL(parameters.get_presentation_contexts()[1].id, 3);
+ BOOST_REQUIRE_EQUAL(
+ parameters.get_presentation_contexts()[1].abstract_syntax, "abstract2");
+ BOOST_REQUIRE_EQUAL(
+ parameters.get_presentation_contexts()[1].transfer_syntaxes.size(), 1);
+ BOOST_REQUIRE_EQUAL(
+ parameters.get_presentation_contexts()[1].transfer_syntaxes[0],
+ "transfer3");
+ BOOST_REQUIRE(!parameters.get_presentation_contexts()[1].scu_role_support);
+ BOOST_REQUIRE(parameters.get_presentation_contexts()[1].scp_role_support);
+}
+
+BOOST_AUTO_TEST_CASE(UserIdentityNone)
+{
+ odil::AssociationParameters parameters;
+ parameters.set_user_identity_to_username_and_password("foo", "bar");
+ parameters.set_user_identity_to_none();
+
+ BOOST_REQUIRE(
+ parameters.get_user_identity().type ==
+ odil::AssociationParameters::UserIdentity::Type::None);
+ BOOST_REQUIRE(parameters.get_user_identity().primary_field.empty());
+ BOOST_REQUIRE(parameters.get_user_identity().secondary_field.empty());
+}
+
+BOOST_AUTO_TEST_CASE(UserIdentityUsername)
+{
+ odil::AssociationParameters parameters;
+ parameters.set_user_identity_to_username("foo");
+
+ BOOST_REQUIRE(
+ parameters.get_user_identity().type ==
+ odil::AssociationParameters::UserIdentity::Type::Username);
+ BOOST_REQUIRE_EQUAL(parameters.get_user_identity().primary_field, "foo");
+ BOOST_REQUIRE(parameters.get_user_identity().secondary_field.empty());
+}
+
+BOOST_AUTO_TEST_CASE(UserIdentityUsernameAndPassword)
+{
+ odil::AssociationParameters parameters;
+ parameters.set_user_identity_to_username_and_password("foo", "bar");
+
+ BOOST_REQUIRE(
+ parameters.get_user_identity().type ==
+ odil::AssociationParameters::UserIdentity::Type::UsernameAndPassword);
+ BOOST_REQUIRE_EQUAL(parameters.get_user_identity().primary_field, "foo");
+ BOOST_REQUIRE_EQUAL(parameters.get_user_identity().secondary_field, "bar");
+}
+
+BOOST_AUTO_TEST_CASE(UserIdentityKerberos)
+{
+ odil::AssociationParameters parameters;
+ parameters.set_user_identity_to_kerberos("foo");
+
+ BOOST_REQUIRE(
+ parameters.get_user_identity().type ==
+ odil::AssociationParameters::UserIdentity::Type::Kerberos);
+ BOOST_REQUIRE_EQUAL(parameters.get_user_identity().primary_field, "foo");
+ BOOST_REQUIRE(parameters.get_user_identity().secondary_field.empty());
+}
+
+BOOST_AUTO_TEST_CASE(UserIdentitySAML)
+{
+ odil::AssociationParameters parameters;
+ parameters.set_user_identity_to_saml("foo");
+
+ BOOST_REQUIRE(
+ parameters.get_user_identity().type ==
+ odil::AssociationParameters::UserIdentity::Type::SAML);
+ BOOST_REQUIRE_EQUAL(parameters.get_user_identity().primary_field, "foo");
+ BOOST_REQUIRE(parameters.get_user_identity().secondary_field.empty());
+}
+
+BOOST_AUTO_TEST_CASE(MaximumLength)
+{
+ odil::AssociationParameters parameters;
+ parameters.set_maximum_length(0x12345678);
+ BOOST_REQUIRE_EQUAL(parameters.get_maximum_length(), 0x12345678);
+}
+
+BOOST_AUTO_TEST_CASE(ChainedSetters)
+{
+ odil::AssociationParameters parameters;
+ parameters
+ .set_called_ae_title("called")
+ .set_calling_ae_title("calling")
+ .set_presentation_contexts({ { 1, "abstract", { "transfer" }, true, true } })
+ .set_user_identity_to_username_and_password("foo", "bar")
+ .set_maximum_length(0x12345678);
+
+ BOOST_REQUIRE_EQUAL(parameters.get_called_ae_title(), "called");
+ BOOST_REQUIRE_EQUAL(parameters.get_calling_ae_title(), "calling");
+ BOOST_REQUIRE_EQUAL(parameters.get_presentation_contexts().size(), 1);
+
+ BOOST_REQUIRE(
+ parameters.get_user_identity().type ==
+ odil::AssociationParameters::UserIdentity::Type::UsernameAndPassword);
+ BOOST_REQUIRE_EQUAL(parameters.get_user_identity().primary_field, "foo");
+ BOOST_REQUIRE_EQUAL(parameters.get_user_identity().secondary_field, "bar");
+
+ BOOST_REQUIRE_EQUAL(parameters.get_maximum_length(), 0x12345678);
+}
diff --git a/tests/code/BasicDirectoryCreator.cpp b/tests/code/BasicDirectoryCreator.cpp
new file mode 100644
index 0000000..ec75cf0
--- /dev/null
+++ b/tests/code/BasicDirectoryCreator.cpp
@@ -0,0 +1,159 @@
+#define BOOST_TEST_MODULE BasicDirectoryCreator
+#include <boost/test/unit_test.hpp>
+
+#include <fstream>
+
+#include "odil/BasicDirectoryCreator.h"
+#include "odil/json_converter.h"
+#include "odil/Reader.h"
+#include "odil/registry.h"
+#include "odil/Writer.h"
+
+#include <boost/filesystem.hpp>
+#include <boost/filesystem/fstream.hpp>
+
+BOOST_AUTO_TEST_CASE(DefaultConstructor)
+{
+ odil::BasicDirectoryCreator const creator;
+ BOOST_REQUIRE_EQUAL(creator.root.empty(), true);
+ BOOST_REQUIRE_EQUAL(creator.files.empty(), true);
+ BOOST_REQUIRE_EQUAL(creator.extra_record_keys.empty(), true);
+ BOOST_REQUIRE(
+ creator.item_encoding == odil::Writer::ItemEncoding::ExplicitLength);
+}
+
+BOOST_AUTO_TEST_CASE(Constructor)
+{
+ odil::BasicDirectoryCreator::RecordKeyMap const extra_records(
+ {
+ {
+ "PATIENT",
+ {
+ {odil::registry::PatientBirthDate, 1},
+ {odil::registry::OtherPatientIDs, 3},
+ }
+ }
+ });
+
+ odil::BasicDirectoryCreator const creator(
+ "root", {"a.dcm", "b.dcm"}, extra_records,
+ odil::Writer::ItemEncoding::UndefinedLength);
+ BOOST_REQUIRE_EQUAL(creator.root, "root");
+ BOOST_REQUIRE(
+ creator.files == std::vector<std::string>({"a.dcm", "b.dcm"}));
+ BOOST_REQUIRE(
+ creator.extra_record_keys == extra_records);
+ BOOST_REQUIRE(
+ creator.item_encoding == odil::Writer::ItemEncoding::UndefinedLength);
+}
+
+BOOST_AUTO_TEST_CASE(BasicDirectory)
+{
+ std::stringstream stream1;
+ stream1 << "{";
+
+ {
+ odil::DataSet data_set;
+ data_set.add("PatientID", {"DJ123"});
+ data_set.add("StudyDate", {"19100110"});
+ data_set.add("StudyTime", {"1234"});
+ data_set.add("StudyDescription", {"Study"});
+ data_set.add("StudyInstanceUID", {"1.2.3.4"});
+ data_set.add("StudyID", {"FOO"});
+ data_set.add("Modality", {"OT"});
+ data_set.add("SeriesInstanceUID", {"1.2.3.4.1"});
+ data_set.add("SeriesNumber", {1});
+ data_set.add("SeriesDescription", {"Series"});
+ data_set.add("InstanceNumber", {1});
+ data_set.add("SOPInstanceUID", {"1.2.3.4.1.1"});
+ data_set.add("SOPClassUID", {odil::registry::RawDataStorage});
+
+ std::ofstream stream("a.dcm");
+ odil::Writer::write_file(data_set, stream);
+ }
+
+ {
+ odil::DataSet data_set;
+ data_set.add("PatientID", {"DJ123"});
+ data_set.add("StudyDate", {"19100110"});
+ data_set.add("StudyTime", {"1234"});
+ data_set.add("StudyDescription", {"Study"});
+ data_set.add("StudyInstanceUID", {"1.2.3.4"});
+ data_set.add("StudyID", {"FOO"});
+ data_set.add("Modality", {"OT"});
+ data_set.add("SeriesInstanceUID", {"1.2.3.4.1"});
+ data_set.add("SeriesNumber", {1});
+ data_set.add("SeriesDescription", {"Series"});
+ data_set.add("InstanceNumber", {2});
+ data_set.add("SOPInstanceUID", {"1.2.3.4.1.2"});
+ data_set.add("SOPClassUID", {odil::registry::RawDataStorage});
+
+ std::ofstream stream("b.dcm");
+ odil::Writer::write_file(data_set, stream);
+ }
+
+ odil::BasicDirectoryCreator::RecordKeyMap const extra_records(
+ { { "SERIES", { {odil::registry::SeriesDescription, 1} } } });
+
+ odil::BasicDirectoryCreator const creator(
+ ".", {"a.dcm", "b.dcm"}, extra_records);
+ creator();
+
+ boost::filesystem::ifstream stream(boost::filesystem::path(".")/"DICOMDIR");
+ auto const dicomdir_and_header = odil::Reader::read_file(stream);
+
+ BOOST_REQUIRE(
+ dicomdir_and_header.first.as_string("MediaStorageSOPClassUID") ==
+ odil::Value::Strings({odil::registry::MediaStorageDirectoryStorage}));
+ BOOST_REQUIRE(
+ dicomdir_and_header.first.as_string("TransferSyntaxUID") ==
+ odil::Value::Strings({odil::registry::ExplicitVRLittleEndian}));
+
+ auto const & records =
+ dicomdir_and_header.second.as_data_set("DirectoryRecordSequence");
+ BOOST_REQUIRE_EQUAL(records.size(), 5);
+
+ BOOST_REQUIRE(
+ records[0].as_string("DirectoryRecordType") ==
+ odil::Value::Strings({"PATIENT"}));
+ BOOST_REQUIRE(
+ records[0].as_string("PatientID") ==
+ odil::Value::Strings({"DJ123"}));
+
+ BOOST_REQUIRE(
+ records[1].as_string("DirectoryRecordType") ==
+ odil::Value::Strings({"STUDY"}));
+ BOOST_REQUIRE(
+ records[1].as_string("StudyInstanceUID") ==
+ odil::Value::Strings({"1.2.3.4"}));
+ BOOST_REQUIRE(
+ records[1].as_string("StudyDescription") ==
+ odil::Value::Strings({"Study"}));
+
+ BOOST_REQUIRE(
+ records[2].as_string("DirectoryRecordType") ==
+ odil::Value::Strings({"SERIES"}));
+ BOOST_REQUIRE(
+ records[2].as_string("SeriesInstanceUID") ==
+ odil::Value::Strings({"1.2.3.4.1"}));
+ BOOST_REQUIRE(
+ records[2].as_string("SeriesDescription") ==
+ odil::Value::Strings({"Series"}));
+
+ BOOST_REQUIRE(
+ records[3].as_string("DirectoryRecordType") ==
+ odil::Value::Strings({"IMAGE"}));
+ BOOST_REQUIRE(
+ records[3].as_int("InstanceNumber") ==
+ odil::Value::Integers({1}));
+
+ BOOST_REQUIRE(
+ records[4].as_string("DirectoryRecordType") ==
+ odil::Value::Strings({"IMAGE"}));
+ BOOST_REQUIRE(
+ records[4].as_int("InstanceNumber") ==
+ odil::Value::Integers({2}));
+
+ boost::filesystem::remove("a.dcm");
+ boost::filesystem::remove("b.dcm");
+}
diff --git a/tests/code/CEchoRequest.cpp b/tests/code/CEchoRequest.cpp
deleted file mode 100644
index 8b2b7ba..0000000
--- a/tests/code/CEchoRequest.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-#define BOOST_TEST_MODULE CEchoRequest
-#include <boost/test/unit_test.hpp>
-
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/dimse.h>
-
-#include "dcmtkpp/CEchoRequest.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Message.h"
-#include "dcmtkpp/registry.h"
-
-#include "../MessageFixtureBase.h"
-
-struct Fixture: public MessageFixtureBase<dcmtkpp::CEchoRequest>
-{
- dcmtkpp::DataSet command_set;
-
- Fixture()
- {
- this->command_set.add("CommandField", {dcmtkpp::Message::Command::C_ECHO_RQ});
- this->command_set.add("MessageID", {1234});
- this->command_set.add("AffectedSOPClassUID", {dcmtkpp::registry::VerificationSOPClass});
- }
-
- void check(dcmtkpp::CEchoRequest const & message)
- {
- BOOST_CHECK_EQUAL(message.get_command_field(), dcmtkpp::Message::Command::C_ECHO_RQ);
- BOOST_CHECK_EQUAL(message.get_message_id(), 1234);
- BOOST_CHECK_EQUAL(
- message.get_affected_sop_class_uid(), dcmtkpp::registry::VerificationSOPClass);
- BOOST_CHECK(!message.has_data_set());
- }
-};
-
-BOOST_FIXTURE_TEST_CASE(Constructor, Fixture)
-{
- dcmtkpp::CEchoRequest const message(1234, dcmtkpp::registry::VerificationSOPClass);
- this->check(message);
-}
-
-BOOST_FIXTURE_TEST_CASE(MessageConstructor, Fixture)
-{
- this->check_message_constructor(this->command_set);
-}
-
-BOOST_FIXTURE_TEST_CASE(MessageConstructorWrongCommandField, Fixture)
-{
- this->command_set.as_int("CommandField") = {dcmtkpp::Message::Command::C_ECHO_RSP};
- this->check_message_constructor_throw(this->command_set);
-}
-
-BOOST_FIXTURE_TEST_CASE(MessageConstructorMissingAffectSOPClass, Fixture)
-{
- this->command_set.remove("AffectedSOPClassUID");
- this->check_message_constructor_throw(this->command_set);
-}
diff --git a/tests/code/CEchoResponse.cpp b/tests/code/CEchoResponse.cpp
deleted file mode 100644
index 3dc26bd..0000000
--- a/tests/code/CEchoResponse.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-#define BOOST_TEST_MODULE CEchoResponse
-#include <boost/test/unit_test.hpp>
-
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/dimse.h>
-
-#include "dcmtkpp/CEchoResponse.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Message.h"
-#include "dcmtkpp/registry.h"
-
-#include "../MessageFixtureBase.h"
-
-struct Fixture: public MessageFixtureBase<dcmtkpp::CEchoResponse>
-{
- dcmtkpp::DataSet command_set;
-
- Fixture()
- {
- this->command_set.add("CommandField", {dcmtkpp::Message::Command::C_ECHO_RSP});
- this->command_set.add("MessageIDBeingRespondedTo", {1234});
- this->command_set.add("Status", {STATUS_Success});
- this->command_set.add("AffectedSOPClassUID", {dcmtkpp::registry::VerificationSOPClass});
- }
-
- void check(dcmtkpp::CEchoResponse const & message)
- {
- BOOST_CHECK_EQUAL(message.get_command_field(), dcmtkpp::Message::Command::C_ECHO_RSP);
- BOOST_CHECK_EQUAL(message.get_message_id_being_responded_to(), 1234);
- BOOST_CHECK_EQUAL(message.get_status(), STATUS_Success);
- BOOST_CHECK_EQUAL(message.get_affected_sop_class_uid(), dcmtkpp::registry::VerificationSOPClass);
- BOOST_CHECK(!message.has_data_set());
- }
-};
-
-BOOST_FIXTURE_TEST_CASE(Constructor, Fixture)
-{
- dcmtkpp::CEchoResponse const message(
- 1234, STATUS_Success, dcmtkpp::registry::VerificationSOPClass);
- this->check(message);
-}
-
-BOOST_FIXTURE_TEST_CASE(MessageConstructor, Fixture)
-{
- this->check_message_constructor(this->command_set);
-}
-
-BOOST_FIXTURE_TEST_CASE(MessageConstructorWrongCommandField, Fixture)
-{
- this->command_set.as_int("CommandField") = {dcmtkpp::Message::Command::C_ECHO_RQ};
- this->check_message_constructor_throw(this->command_set);
-}
-
-BOOST_FIXTURE_TEST_CASE(MessageConstructorMissingAffectSOPClass, Fixture)
-{
- this->command_set.remove("AffectedSOPClassUID");
- this->check_message_constructor_throw(this->command_set);
-}
diff --git a/tests/code/CFindResponse.cpp b/tests/code/CFindResponse.cpp
deleted file mode 100644
index 49bd561..0000000
--- a/tests/code/CFindResponse.cpp
+++ /dev/null
@@ -1,72 +0,0 @@
-#define BOOST_TEST_MODULE CFindResponse
-#include <boost/test/unit_test.hpp>
-
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/dimse.h>
-
-#include "dcmtkpp/CFindResponse.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Message.h"
-#include "dcmtkpp/registry.h"
-
-#include "../MessageFixtureBase.h"
-
-struct Fixture: public MessageFixtureBase<dcmtkpp::CFindResponse>
-{
- dcmtkpp::DataSet command_set;
- dcmtkpp::DataSet data_set;
-
- Fixture()
- {
- command_set.add("CommandField", {dcmtkpp::Message::Command::C_FIND_RSP});
- command_set.add("MessageIDBeingRespondedTo", {1234});
- command_set.add("Status", {STATUS_Success});
-
- command_set.add("MessageID", {5678});
- command_set.add("AffectedSOPClassUID",
- {dcmtkpp::registry::StudyRootQueryRetrieveInformationModelFIND});
-
- data_set.add("PatientName", {"Doe^John"});
- data_set.add("PatientID", {"DJ123"});
- data_set.add("StudyDescription", {"Brain"});
- data_set.add("StudyInstanceUID", {"1.2.3"});
- }
-
- virtual void check(dcmtkpp::CFindResponse const & message)
- {
- BOOST_CHECK_EQUAL(message.get_command_field(), dcmtkpp::Message::Command::C_FIND_RSP);
- BOOST_CHECK_EQUAL(message.get_message_id_being_responded_to(), 1234);
- BOOST_CHECK_EQUAL(message.get_status(), STATUS_Success);
-
- BOOST_CHECK(message.has_message_id());
- BOOST_CHECK_EQUAL(message.get_message_id(), 5678);
-
- BOOST_CHECK(message.has_affected_sop_class_uid());
- BOOST_CHECK_EQUAL(
- message.get_affected_sop_class_uid(),
- dcmtkpp::registry::StudyRootQueryRetrieveInformationModelFIND);
-
- BOOST_CHECK(message.has_data_set());
- BOOST_CHECK(message.get_data_set() == this->data_set);
- }
-};
-
-BOOST_FIXTURE_TEST_CASE(Constructor, Fixture)
-{
- dcmtkpp::CFindResponse message(1234, STATUS_Success, this->data_set);
- message.set_message_id(5678);
- message.set_affected_sop_class_uid(
- dcmtkpp::registry::StudyRootQueryRetrieveInformationModelFIND);
- this->check(message);
-}
-
-BOOST_FIXTURE_TEST_CASE(MessageConstructor, Fixture)
-{
- this->check_message_constructor(this->command_set, this->data_set);
-}
-
-BOOST_FIXTURE_TEST_CASE(MessageConstructorWrongCommandField, Fixture)
-{
- this->command_set.as_int("CommandField") = {dcmtkpp::Message::Command::C_ECHO_RQ};
- this->check_message_constructor_throw(this->command_set, this->data_set);
-}
diff --git a/tests/code/CGetResponse.cpp b/tests/code/CGetResponse.cpp
deleted file mode 100644
index 0f035e0..0000000
--- a/tests/code/CGetResponse.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-#define BOOST_TEST_MODULE CGetResponse
-#include <boost/test/unit_test.hpp>
-
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/dimse.h>
-
-#include "dcmtkpp/CGetResponse.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Message.h"
-#include "dcmtkpp/registry.h"
-
-#include "../MessageFixtureBase.h"
-
-struct Fixture: public MessageFixtureBase<dcmtkpp::CGetResponse>
-{
- dcmtkpp::DataSet command_set;
- dcmtkpp::DataSet data_set;
-
- Fixture()
- {
- this->command_set.add("CommandField", {dcmtkpp::Message::Command::C_GET_RSP});
- this->command_set.add("MessageIDBeingRespondedTo", {1234});
- this->command_set.add("Status", {STATUS_Success});
-
- this->command_set.add("MessageID", {5678});
- this->command_set.add("AffectedSOPClassUID",
- {dcmtkpp::registry::StudyRootQueryRetrieveInformationModelGET});
- this->command_set.add(dcmtkpp::registry::NumberOfRemainingSuboperations, {1});
- this->command_set.add(dcmtkpp::registry::NumberOfCompletedSuboperations, {2});
- this->command_set.add(dcmtkpp::registry::NumberOfFailedSuboperations, {3});
- this->command_set.add(dcmtkpp::registry::NumberOfWarningSuboperations, {4});
-
- this->data_set.add("PatientName", {"Doe^John"});
- this->data_set.add("PatientID", {"DJ123"});
- this->data_set.add("StudyDescription", {"Brain"});
- this->data_set.add("StudyInstanceUID", {"1.2.3"});
- }
-
- virtual void check(dcmtkpp::CGetResponse const & message)
- {
- BOOST_CHECK_EQUAL(message.get_command_field(), dcmtkpp::Message::Command::C_GET_RSP);
- BOOST_CHECK_EQUAL(message.get_message_id_being_responded_to(), 1234);
- BOOST_CHECK_EQUAL(message.get_status(), STATUS_Success);
-
- BOOST_CHECK(message.has_message_id());
- BOOST_CHECK_EQUAL(message.get_message_id(), 5678);
-
- BOOST_CHECK(message.has_affected_sop_class_uid());
- BOOST_CHECK_EQUAL(
- message.get_affected_sop_class_uid(),
- dcmtkpp::registry::StudyRootQueryRetrieveInformationModelGET);
-
- BOOST_CHECK(message.has_number_of_remaining_sub_operations());
- BOOST_CHECK_EQUAL(message.get_number_of_remaining_sub_operations(), 1);
-
- BOOST_CHECK(message.has_number_of_completed_sub_operations());
- BOOST_CHECK_EQUAL(message.get_number_of_completed_sub_operations(), 2);
-
- BOOST_CHECK(message.has_number_of_failed_sub_operations());
- BOOST_CHECK_EQUAL(message.get_number_of_failed_sub_operations(), 3);
-
- BOOST_CHECK(message.has_number_of_warning_sub_operations());
- BOOST_CHECK_EQUAL(message.get_number_of_warning_sub_operations(), 4);
-
- BOOST_CHECK(message.has_data_set());
- BOOST_CHECK(message.get_data_set() == this->data_set);
- }
-};
-
-BOOST_FIXTURE_TEST_CASE(Constructor, Fixture)
-{
- dcmtkpp::CGetResponse message(1234, STATUS_Success, this->data_set);
- message.set_message_id(5678);
- message.set_affected_sop_class_uid(
- dcmtkpp::registry::StudyRootQueryRetrieveInformationModelGET);
- message.set_number_of_remaining_sub_operations(1);
- message.set_number_of_completed_sub_operations(2);
- message.set_number_of_failed_sub_operations(3);
- message.set_number_of_warning_sub_operations(4);
-
- this->check(message);
-}
-
-BOOST_FIXTURE_TEST_CASE(MessageConstructor, Fixture)
-{
- this->check_message_constructor(this->command_set, this->data_set);
-}
-
-BOOST_FIXTURE_TEST_CASE(MessageConstructorWrongCommandField, Fixture)
-{
- this->command_set.as_int(dcmtkpp::registry::CommandField) = { dcmtkpp::Message::Command::C_ECHO_RQ };
- this->check_message_constructor_throw(this->command_set, this->data_set);
-}
diff --git a/tests/code/CMoveResponse.cpp b/tests/code/CMoveResponse.cpp
deleted file mode 100644
index 28677e9..0000000
--- a/tests/code/CMoveResponse.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-#define BOOST_TEST_MODULE CMoveResponse
-#include <boost/test/unit_test.hpp>
-
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/dimse.h>
-
-#include "dcmtkpp/CMoveResponse.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Message.h"
-#include "dcmtkpp/registry.h"
-
-#include "../MessageFixtureBase.h"
-
-struct Fixture: public MessageFixtureBase<dcmtkpp::CMoveResponse>
-{
- dcmtkpp::DataSet command_set;
- dcmtkpp::DataSet data_set;
-
- Fixture()
- {
- this->command_set.add("CommandField", {dcmtkpp::Message::Command::C_MOVE_RSP});
- this->command_set.add("MessageIDBeingRespondedTo", {1234});
- this->command_set.add("Status", {STATUS_Success});
-
- this->command_set.add("MessageID", {5678});
- this->command_set.add("AffectedSOPClassUID",
- {dcmtkpp::registry::StudyRootQueryRetrieveInformationModelMOVE});
- this->command_set.add(dcmtkpp::registry::NumberOfRemainingSuboperations, {1});
- this->command_set.add(dcmtkpp::registry::NumberOfCompletedSuboperations, {2});
- this->command_set.add(dcmtkpp::registry::NumberOfFailedSuboperations, {3});
- this->command_set.add(dcmtkpp::registry::NumberOfWarningSuboperations, {4});
-
- this->data_set.add("PatientName", {"Doe^John"});
- this->data_set.add("PatientID", {"DJ123"});
- this->data_set.add("StudyDescription", {"Brain"});
- this->data_set.add("StudyInstanceUID", {"1.2.3"});
- }
-
- virtual void check(dcmtkpp::CMoveResponse const & message)
- {
- BOOST_CHECK_EQUAL(message.get_command_field(), dcmtkpp::Message::Command::C_MOVE_RSP);
- BOOST_CHECK_EQUAL(message.get_message_id_being_responded_to(), 1234);
- BOOST_CHECK_EQUAL(message.get_status(), STATUS_Success);
-
- BOOST_CHECK(message.has_message_id());
- BOOST_CHECK_EQUAL(message.get_message_id(), 5678);
-
- BOOST_CHECK(message.has_affected_sop_class_uid());
- BOOST_CHECK_EQUAL(
- message.get_affected_sop_class_uid(),
- dcmtkpp::registry::StudyRootQueryRetrieveInformationModelMOVE);
-
- BOOST_CHECK(message.has_number_of_remaining_sub_operations());
- BOOST_CHECK_EQUAL(message.get_number_of_remaining_sub_operations(), 1);
-
- BOOST_CHECK(message.has_number_of_completed_sub_operations());
- BOOST_CHECK_EQUAL(message.get_number_of_completed_sub_operations(), 2);
-
- BOOST_CHECK(message.has_number_of_failed_sub_operations());
- BOOST_CHECK_EQUAL(message.get_number_of_failed_sub_operations(), 3);
-
- BOOST_CHECK(message.has_number_of_warning_sub_operations());
- BOOST_CHECK_EQUAL(message.get_number_of_warning_sub_operations(), 4);
-
- BOOST_CHECK(message.has_data_set());
- BOOST_CHECK(message.get_data_set() == this->data_set);
- }
-};
-
-BOOST_FIXTURE_TEST_CASE(Constructor, Fixture)
-{
- dcmtkpp::CMoveResponse message(1234, STATUS_Success, this->data_set);
- message.set_message_id(5678);
- message.set_affected_sop_class_uid(
- dcmtkpp::registry::StudyRootQueryRetrieveInformationModelMOVE);
- message.set_number_of_remaining_sub_operations(1);
- message.set_number_of_completed_sub_operations(2);
- message.set_number_of_failed_sub_operations(3);
- message.set_number_of_warning_sub_operations(4);
-
- this->check(message);
-}
-
-BOOST_FIXTURE_TEST_CASE(MessageConstructor, Fixture)
-{
- this->check_message_constructor(this->command_set, this->data_set);
-}
-
-BOOST_FIXTURE_TEST_CASE(MessageConstructorWrongCommandField, Fixture)
-{
- this->command_set.as_int("CommandField") = {dcmtkpp::Message::Command::C_ECHO_RQ};
- this->check_message_constructor_throw(this->command_set, this->data_set);
-}
diff --git a/tests/code/CStoreResponse.cpp b/tests/code/CStoreResponse.cpp
deleted file mode 100644
index b22c920..0000000
--- a/tests/code/CStoreResponse.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-#define BOOST_TEST_MODULE CStoreResponse
-#include <boost/test/unit_test.hpp>
-
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/dimse.h>
-
-#include "dcmtkpp/CStoreResponse.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Message.h"
-#include "dcmtkpp/registry.h"
-
-#include "../MessageFixtureBase.h"
-
-struct Fixture: public MessageFixtureBase<dcmtkpp::CStoreResponse>
-{
- dcmtkpp::DataSet command_set;
-
- Fixture()
- {
- this->command_set.add("CommandField", {dcmtkpp::Message::Command::C_STORE_RSP});
- this->command_set.add("MessageIDBeingRespondedTo", {1234});
- this->command_set.add("Status", {STATUS_Success});
-
- this->command_set.add("MessageID", {5678});
- this->command_set.add("AffectedSOPClassUID", {dcmtkpp::registry::MRImageStorage});
- this->command_set.add("AffectedSOPInstanceUID", {"1.2.3.4"});
- }
-
- void check(dcmtkpp::CStoreResponse const & message)
- {
- BOOST_CHECK_EQUAL(message.get_command_field(), dcmtkpp::Message::Command::C_STORE_RSP);
- BOOST_CHECK_EQUAL(message.get_message_id_being_responded_to(), 1234);
- BOOST_CHECK_EQUAL(message.get_status(), STATUS_Success);
- BOOST_CHECK(!message.has_data_set());
-
- BOOST_CHECK(message.has_message_id());
- BOOST_CHECK_EQUAL(message.get_message_id(), 5678);
-
- BOOST_CHECK(message.has_affected_sop_class_uid());
- BOOST_CHECK_EQUAL(
- message.get_affected_sop_class_uid(), dcmtkpp::registry::MRImageStorage);
-
- BOOST_CHECK(message.has_affected_sop_instance_uid());
- BOOST_CHECK_EQUAL(message.get_affected_sop_instance_uid(), "1.2.3.4");
- }
-};
-
-BOOST_FIXTURE_TEST_CASE(Constructor, Fixture)
-{
- dcmtkpp::CStoreResponse message(1234, STATUS_Success);
- message.set_message_id(5678);
- message.set_affected_sop_class_uid(dcmtkpp::registry::MRImageStorage);
- message.set_affected_sop_instance_uid("1.2.3.4");
-
- this->check(message);
-}
-
-BOOST_FIXTURE_TEST_CASE(MessageConstructor, Fixture)
-{
- this->check_message_constructor(this->command_set);
-}
-
-BOOST_FIXTURE_TEST_CASE(MessageConstructorWrongCommandField, Fixture)
-{
- this->command_set.add("CommandField", {dcmtkpp::Message::Command::C_ECHO_RQ});
- this->check_message_constructor_throw(this->command_set);
-}
-
diff --git a/tests/code/Cancellation.cpp b/tests/code/Cancellation.cpp
deleted file mode 100644
index 5f2954f..0000000
--- a/tests/code/Cancellation.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-#define BOOST_TEST_MODULE Cancellation
-#include <boost/test/unit_test.hpp>
-
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Cancellation.h"
-
-#include "../MessageFixtureBase.h"
-
-struct Fixture: public MessageFixtureBase<dcmtkpp::Cancellation>
-{
- dcmtkpp::DataSet command_set;
- Fixture()
- {
- this->command_set.add("CommandField", {dcmtkpp::Message::Command::C_CANCEL_RQ});
- this->command_set.add("MessageIDBeingRespondedTo", {1234});
- }
-
- void check(dcmtkpp::Cancellation const & message)
- {
- BOOST_CHECK_EQUAL(message.get_command_field(), dcmtkpp::Message::Command::C_CANCEL_RQ);
- BOOST_CHECK_EQUAL(message.get_message_id_being_responded_to(), 1234);
- }
-};
-
-BOOST_FIXTURE_TEST_CASE(Constructor, Fixture)
-{
- dcmtkpp::Cancellation const message(1234);
- this->check(message);
-}
-
-BOOST_FIXTURE_TEST_CASE(MessageConstructor, Fixture)
-{
- this->check_message_constructor(this->command_set);
-}
-
-BOOST_FIXTURE_TEST_CASE(MessageConstructorWrongCommandField, Fixture)
-{
- this->command_set.as_int("CommandField") = {dcmtkpp::Message::Command::C_ECHO_RQ};
- this->check_message_constructor_throw(this->command_set);
-}
diff --git a/tests/code/DataSet.cpp b/tests/code/DataSet.cpp
index e6c516f..414bb78 100644
--- a/tests/code/DataSet.cpp
+++ b/tests/code/DataSet.cpp
@@ -1,88 +1,88 @@
#define BOOST_TEST_MODULE DataSet
#include <boost/test/unit_test.hpp>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/Tag.h"
-#include "dcmtkpp/VR.h"
+#include "odil/DataSet.h"
+#include "odil/Exception.h"
+#include "odil/Tag.h"
+#include "odil/VR.h"
-#include "dcmtkpp/registry.h"
+#include "odil/registry.h"
BOOST_AUTO_TEST_CASE(Empty)
{
- dcmtkpp::DataSet dataset;
+ odil::DataSet dataset;
BOOST_CHECK(dataset.empty());
BOOST_CHECK_EQUAL(dataset.size(), 0);
- BOOST_CHECK(!dataset.has(dcmtkpp::Tag("PatientName")));
+ BOOST_CHECK(!dataset.has(odil::Tag("PatientName")));
}
BOOST_AUTO_TEST_CASE(AddExplicitVR)
{
- dcmtkpp::Tag const tag("PatientName");
+ odil::Tag const tag("PatientName");
- dcmtkpp::DataSet dataset;
- dataset.add(tag, dcmtkpp::VR::PN);
+ odil::DataSet dataset;
+ dataset.add(tag, odil::VR::PN);
BOOST_CHECK(!dataset.empty());
BOOST_CHECK_EQUAL(dataset.size(), 1);
BOOST_CHECK(dataset.has(tag));
- BOOST_CHECK(dataset.get_vr(tag) == dcmtkpp::VR::PN);
+ BOOST_CHECK(dataset.get_vr(tag) == odil::VR::PN);
}
BOOST_AUTO_TEST_CASE(AddImplicitVR)
{
- dcmtkpp::Tag const tag("PatientName");
+ odil::Tag const tag("PatientName");
- dcmtkpp::DataSet dataset;
+ odil::DataSet dataset;
dataset.add(tag);
BOOST_CHECK(!dataset.empty());
BOOST_CHECK_EQUAL(dataset.size(), 1);
BOOST_CHECK(dataset.has(tag));
- BOOST_CHECK(dataset.get_vr(tag) == dcmtkpp::VR::PN);
+ BOOST_CHECK(dataset.get_vr(tag) == odil::VR::PN);
}
BOOST_AUTO_TEST_CASE(AddValueExplicitVR)
{
- dcmtkpp::Tag const tag("PatientName");
+ odil::Tag const tag("PatientName");
- dcmtkpp::DataSet dataset;
- dataset.add(tag, { "Doe^John"}, dcmtkpp::VR::PN);
+ odil::DataSet dataset;
+ dataset.add(tag, { "Doe^John"}, odil::VR::PN);
BOOST_CHECK(!dataset.empty());
BOOST_CHECK_EQUAL(dataset.size(), 1);
BOOST_CHECK(dataset.has(tag));
- BOOST_CHECK(dataset.get_vr(tag) == dcmtkpp::VR::PN);
- BOOST_CHECK(dataset.as_string(tag) == dcmtkpp::Value::Strings({ "Doe^John" }));
+ BOOST_CHECK(dataset.get_vr(tag) == odil::VR::PN);
+ BOOST_CHECK(dataset.as_string(tag) == odil::Value::Strings({ "Doe^John" }));
}
BOOST_AUTO_TEST_CASE(AddValueImplicitVR)
{
- dcmtkpp::Tag const tag("PatientName");
+ odil::Tag const tag("PatientName");
- dcmtkpp::DataSet dataset;
+ odil::DataSet dataset;
dataset.add(tag, { "Doe^John"});
BOOST_CHECK(!dataset.empty());
BOOST_CHECK_EQUAL(dataset.size(), 1);
BOOST_CHECK(dataset.has(tag));
- BOOST_CHECK(dataset.get_vr(tag) == dcmtkpp::VR::PN);
- BOOST_CHECK(dataset.as_string(tag) == dcmtkpp::Value::Strings({ "Doe^John" }));
+ BOOST_CHECK(dataset.get_vr(tag) == odil::VR::PN);
+ BOOST_CHECK(dataset.as_string(tag) == odil::Value::Strings({ "Doe^John" }));
}
BOOST_AUTO_TEST_CASE(AddInvalidTag)
{
- dcmtkpp::Tag const tag(0xdead, 0xbeef);
- dcmtkpp::DataSet dataset;
+ odil::Tag const tag(0xdead, 0xbeef);
+ odil::DataSet dataset;
- BOOST_CHECK_THROW(dataset.add(tag), dcmtkpp::Exception);
+ BOOST_CHECK_THROW(dataset.add(tag), odil::Exception);
}
-BOOST_AUTO_TEST_CASE(AddInt)
+BOOST_AUTO_TEST_CASE(AddIntEmpty)
{
- dcmtkpp::Tag const tag("Rows");
+ odil::Tag const tag("Rows");
- dcmtkpp::DataSet dataset;
+ odil::DataSet dataset;
dataset.add(tag);
BOOST_CHECK(dataset.is_int(tag));
@@ -93,11 +93,11 @@ BOOST_AUTO_TEST_CASE(AddInt)
BOOST_CHECK(value.empty());
}
-BOOST_AUTO_TEST_CASE(AddDouble)
+BOOST_AUTO_TEST_CASE(AddDoubleEmpty)
{
- dcmtkpp::Tag const tag("SpacingBetweenSlices");
+ odil::Tag const tag("SpacingBetweenSlices");
- dcmtkpp::DataSet dataset;
+ odil::DataSet dataset;
dataset.add(tag);
BOOST_CHECK(dataset.is_real(tag));
@@ -108,11 +108,11 @@ BOOST_AUTO_TEST_CASE(AddDouble)
BOOST_CHECK(value.empty());
}
-BOOST_AUTO_TEST_CASE(AddString)
+BOOST_AUTO_TEST_CASE(AddStringEmpty)
{
- dcmtkpp::Tag const tag("PatientID");
+ odil::Tag const tag("PatientID");
- dcmtkpp::DataSet dataset;
+ odil::DataSet dataset;
dataset.add(tag);
BOOST_CHECK(dataset.is_string(tag));
@@ -123,26 +123,167 @@ BOOST_AUTO_TEST_CASE(AddString)
BOOST_CHECK(value.empty());
}
-BOOST_AUTO_TEST_CASE(AddDataSet)
+BOOST_AUTO_TEST_CASE(AddDataSetEmpty)
{
- dcmtkpp::Tag const tag("ReferencedStudySequence");
+ odil::Tag const tag("ReferencedStudySequence");
- dcmtkpp::DataSet dataset;
+ odil::DataSet dataset;
dataset.add(tag);
BOOST_CHECK(dataset.is_data_set(tag));
BOOST_CHECK(dataset.empty(tag));
BOOST_CHECK_EQUAL(dataset.size(tag), 0);
- dcmtkpp::Value::DataSets const & value = dataset.as_data_set(tag);
+ odil::Value::DataSets const & value = dataset.as_data_set(tag);
BOOST_CHECK(value.empty());
}
+BOOST_AUTO_TEST_CASE(AddBinaryEmpty)
+{
+ odil::Tag const tag("BadPixelImage");
+
+ odil::DataSet dataset;
+ dataset.add(tag);
+
+ BOOST_CHECK(dataset.is_binary(tag));
+ BOOST_CHECK(dataset.empty(tag));
+ BOOST_CHECK_EQUAL(dataset.size(tag), 0);
+
+ auto const & value = dataset.as_binary(tag);
+ BOOST_CHECK(value.empty());
+}
+
+BOOST_AUTO_TEST_CASE(AddInt)
+{
+ odil::Tag const tag("Rows");
+
+ odil::DataSet dataset;
+ dataset.add(tag, odil::Value::Integers({123}));
+
+ BOOST_CHECK(dataset.is_int(tag));
+ BOOST_REQUIRE_EQUAL(dataset.size(tag), 1);
+ BOOST_REQUIRE(dataset.as_int(tag) == odil::Value::Integers({123}));
+}
+
+BOOST_AUTO_TEST_CASE(AddDouble)
+{
+ odil::Tag const tag("SpacingBetweenSlices");
+
+ odil::DataSet dataset;
+ dataset.add(tag, odil::Value::Reals({123.456}));
+
+ BOOST_CHECK(dataset.is_real(tag));
+ BOOST_REQUIRE_EQUAL(dataset.size(tag), 1);
+ BOOST_REQUIRE(dataset.as_real(tag) == odil::Value::Reals({123.456}));
+}
+
+BOOST_AUTO_TEST_CASE(AddString)
+{
+ odil::Tag const tag("PatientID");
+
+ odil::DataSet dataset;
+ dataset.add(tag, odil::Value::Strings({"DJ123"}));
+
+ BOOST_CHECK(dataset.is_string(tag));
+ BOOST_REQUIRE_EQUAL(dataset.size(tag), 1);
+ BOOST_REQUIRE(dataset.as_string(tag) == odil::Value::Strings({"DJ123"}));
+}
+
+BOOST_AUTO_TEST_CASE(AddDataSet)
+{
+ odil::Tag const tag("ReferencedStudySequence");
+ odil::DataSet item;
+ item.add(odil::registry::StudyInstanceUID, {"1.2.3"});
+
+ odil::DataSet dataset;
+ dataset.add(tag, odil::Value::DataSets({item}));
+
+ BOOST_CHECK(dataset.is_data_set(tag));
+ BOOST_REQUIRE_EQUAL(dataset.size(tag), 1);
+ BOOST_REQUIRE(dataset.as_data_set(tag, 0) == item);
+}
+
+BOOST_AUTO_TEST_CASE(AddIntInitializer1)
+{
+ odil::Tag const tag("Rows");
+
+ odil::DataSet dataset;
+ dataset.add(tag, {123});
+
+ BOOST_CHECK(dataset.is_int(tag));
+ BOOST_REQUIRE_EQUAL(dataset.size(tag), 1);
+ BOOST_REQUIRE(dataset.as_int(tag) == odil::Value::Integers({123}));
+}
+
+BOOST_AUTO_TEST_CASE(AddIntInitializer2)
+{
+ odil::Tag const tag("Rows");
+
+ odil::DataSet dataset;
+ dataset.add(tag, {odil::Value::Integer(123)});
+
+ BOOST_CHECK(dataset.is_int(tag));
+ BOOST_REQUIRE_EQUAL(dataset.size(tag), 1);
+ BOOST_REQUIRE(dataset.as_int(tag) == odil::Value::Integers({123}));
+}
+
+
+BOOST_AUTO_TEST_CASE(AddDoubleInitializer)
+{
+ odil::Tag const tag("SpacingBetweenSlices");
+
+ odil::DataSet dataset;
+ dataset.add(tag, {123.456});
+
+ BOOST_CHECK(dataset.is_real(tag));
+ BOOST_REQUIRE_EQUAL(dataset.size(tag), 1);
+ BOOST_REQUIRE(dataset.as_real(tag) == odil::Value::Reals({123.456}));
+}
+
+BOOST_AUTO_TEST_CASE(AddStringInitializer)
+{
+ odil::Tag const tag("PatientID");
+
+ odil::DataSet dataset;
+ dataset.add(tag, {"DJ123"});
+
+ BOOST_CHECK(dataset.is_string(tag));
+ BOOST_REQUIRE_EQUAL(dataset.size(tag), 1);
+ BOOST_REQUIRE(dataset.as_string(tag) == odil::Value::Strings({"DJ123"}));
+}
+
+BOOST_AUTO_TEST_CASE(AddDataSetInitializer)
+{
+ odil::Tag const tag("ReferencedStudySequence");
+ odil::DataSet item;
+ item.add(odil::registry::StudyInstanceUID, {"1.2.3"});
+
+ odil::DataSet dataset;
+ dataset.add(tag, {item});
+
+ BOOST_CHECK(dataset.is_data_set(tag));
+ BOOST_REQUIRE_EQUAL(dataset.size(tag), 1);
+ BOOST_REQUIRE(dataset.as_data_set(tag, 0) == item);
+}
+
+BOOST_AUTO_TEST_CASE(AddBinary)
+{
+ odil::Tag const tag("BadPixelImage");
+
+ odil::DataSet dataset;
+ dataset.add(tag, odil::Value::Binary({0x01, 0x02}));
+
+ BOOST_CHECK(dataset.is_binary(tag));
+ BOOST_REQUIRE_EQUAL(dataset.size(tag), 2);
+ BOOST_REQUIRE(
+ dataset.as_binary(tag) == odil::Value::Binary({ 0x01, 0x02 }));
+}
+
BOOST_AUTO_TEST_CASE(ModifyInt)
{
- dcmtkpp::Tag const tag("Rows");
+ odil::Tag const tag("Rows");
- dcmtkpp::DataSet dataset;
+ odil::DataSet dataset;
dataset.add(tag);
dataset.as_int(tag).push_back(256);
@@ -153,9 +294,9 @@ BOOST_AUTO_TEST_CASE(ModifyInt)
BOOST_AUTO_TEST_CASE(ModifyDouble)
{
- dcmtkpp::Tag const tag("SpacingBetweenSlices");
+ odil::Tag const tag("SpacingBetweenSlices");
- dcmtkpp::DataSet dataset;
+ odil::DataSet dataset;
dataset.add(tag);
dataset.as_real(tag).push_back(3.14);
@@ -166,9 +307,9 @@ BOOST_AUTO_TEST_CASE(ModifyDouble)
BOOST_AUTO_TEST_CASE(ModifyString)
{
- dcmtkpp::Tag const tag("PatientID");
+ odil::Tag const tag("PatientID");
- dcmtkpp::DataSet dataset;
+ odil::DataSet dataset;
dataset.add(tag);
dataset.as_string(tag).push_back("FooBar");
@@ -179,52 +320,64 @@ BOOST_AUTO_TEST_CASE(ModifyString)
BOOST_AUTO_TEST_CASE(ModifyDataSet)
{
- dcmtkpp::Tag const tag("ReferencedStudySequence");
+ odil::Tag const tag("ReferencedStudySequence");
- dcmtkpp::DataSet dataset;
+ odil::DataSet dataset;
dataset.add(tag);
- dcmtkpp::DataSet item;
+ odil::DataSet item;
item.add("PatientID", {"DJ1234"});
dataset.as_data_set(tag).push_back(item);
BOOST_CHECK(!dataset.empty(tag));
BOOST_CHECK_EQUAL(dataset.size(tag), 1);
- dcmtkpp::Value::DataSets const & value = dataset.as_data_set(tag);
+ odil::Value::DataSets const & value = dataset.as_data_set(tag);
BOOST_CHECK_EQUAL(value.size(), 1);
BOOST_CHECK_EQUAL(value.size(), 1);
BOOST_CHECK_EQUAL(value[0].size(), 1);
BOOST_CHECK(value[0].has("PatientID"));
BOOST_CHECK(
- value[0].as_string("PatientID") == dcmtkpp::Value::Strings({"DJ1234"}));
+ value[0].as_string("PatientID") == odil::Value::Strings({"DJ1234"}));
+}
+
+BOOST_AUTO_TEST_CASE(ElementAccessor)
+{
+ odil::Tag const tag("PatientID");
+
+ odil::DataSet dataset;
+ dataset.add(tag, {"Foo^Bar"});
+ BOOST_REQUIRE(
+ dataset[tag].as_string() == odil::Value::Strings({"Foo^Bar"}));
+ BOOST_REQUIRE_THROW(
+ dataset[odil::registry::PatientName], odil::Exception);
}
BOOST_AUTO_TEST_CASE(GetVRMissing)
{
- dcmtkpp::Tag const tag("PatientID");
- dcmtkpp::DataSet dataset;
- BOOST_CHECK_THROW(dataset.get_vr(tag), dcmtkpp::Exception);
+ odil::Tag const tag("PatientID");
+ odil::DataSet dataset;
+ BOOST_CHECK_THROW(dataset.get_vr(tag), odil::Exception);
}
BOOST_AUTO_TEST_CASE(TestEmptyMissing)
{
- dcmtkpp::Tag const tag("PatientID");
- dcmtkpp::DataSet dataset;
- BOOST_CHECK_THROW(dataset.empty(tag), dcmtkpp::Exception);
+ odil::Tag const tag("PatientID");
+ odil::DataSet dataset;
+ BOOST_CHECK_THROW(dataset.empty(tag), odil::Exception);
}
BOOST_AUTO_TEST_CASE(SizeMissing)
{
- dcmtkpp::Tag const tag("PatientID");
- dcmtkpp::DataSet dataset;
- BOOST_CHECK_THROW(dataset.size(tag), dcmtkpp::Exception);
+ odil::Tag const tag("PatientID");
+ odil::DataSet dataset;
+ BOOST_CHECK_THROW(dataset.size(tag), odil::Exception);
}
BOOST_AUTO_TEST_CASE(Remove)
{
- dcmtkpp::Tag const tag("PatientID");
- dcmtkpp::DataSet dataset;
+ odil::Tag const tag("PatientID");
+ odil::DataSet dataset;
dataset.add(tag);
dataset.remove(tag);
@@ -233,26 +386,26 @@ BOOST_AUTO_TEST_CASE(Remove)
BOOST_AUTO_TEST_CASE(RemoveMissing)
{
- dcmtkpp::Tag const tag("PatientID");
- dcmtkpp::DataSet dataset;
+ odil::Tag const tag("PatientID");
+ odil::DataSet dataset;
dataset.add(tag);
- dcmtkpp::Tag const other("PatientName");
+ odil::Tag const other("PatientName");
BOOST_CHECK_THROW(
dataset.remove(other),
- dcmtkpp::Exception);
+ odil::Exception);
}
BOOST_AUTO_TEST_CASE(Equality)
{
- dcmtkpp::DataSet dataset1;
+ odil::DataSet dataset1;
dataset1.add("PatientID", {"DJ1234"});
- dcmtkpp::DataSet dataset2;
+ odil::DataSet dataset2;
dataset2.add("PatientID", {"DJ1234"});
- dcmtkpp::DataSet dataset3;
+ odil::DataSet dataset3;
dataset3.add("PatientAge", {"042Y"});
BOOST_CHECK(dataset1 == dataset2);
@@ -261,13 +414,13 @@ BOOST_AUTO_TEST_CASE(Equality)
BOOST_AUTO_TEST_CASE(Difference)
{
- dcmtkpp::DataSet dataset1;
+ odil::DataSet dataset1;
dataset1.add("PatientID", {"DJ1234"});
- dcmtkpp::DataSet dataset2;
+ odil::DataSet dataset2;
dataset2.add("PatientID", {"DJ1234"});
- dcmtkpp::DataSet dataset3;
+ odil::DataSet dataset3;
dataset3.add("PatientAge", {"042Y"});
BOOST_CHECK(! (dataset1 != dataset2));
diff --git a/tests/code/DcmtkException.cpp b/tests/code/DcmtkException.cpp
new file mode 100644
index 0000000..61aab54
--- /dev/null
+++ b/tests/code/DcmtkException.cpp
@@ -0,0 +1,26 @@
+#define BOOST_TEST_MODULE DcmtkException
+#include <boost/test/unit_test.hpp>
+
+#include <dcmtk/config/osconfig.h>
+#include <dcmtk/ofstd/ofcond.h>
+
+#include "odil/dcmtk/Exception.h"
+
+BOOST_AUTO_TEST_CASE(StringConstructor)
+{
+ odil::dcmtk::Exception const exception("foo");
+ BOOST_REQUIRE(
+ exception.get_source() == odil::dcmtk::Exception::Source::Message);
+ BOOST_REQUIRE_EQUAL(exception.what(), "foo");
+ BOOST_REQUIRE_THROW(exception.get_condition(), odil::dcmtk::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(ConditionConstructor)
+{
+ odil::dcmtk::Exception const exception(EC_IllegalParameter);
+ BOOST_REQUIRE(
+ exception.get_source() == odil::dcmtk::Exception::Source::Condition);
+ BOOST_REQUIRE_EQUAL(
+ exception.what(), OFCondition(EC_IllegalParameter).text());
+ BOOST_REQUIRE(exception.get_condition() == EC_IllegalParameter);
+}
diff --git a/tests/code/EchoSCP.cpp b/tests/code/EchoSCP.cpp
new file mode 100644
index 0000000..8b29f65
--- /dev/null
+++ b/tests/code/EchoSCP.cpp
@@ -0,0 +1,122 @@
+#define BOOST_TEST_MODULE EchoSCP
+#include <boost/test/unit_test.hpp>
+
+#include <chrono>
+#include <cstdlib>
+#include <thread>
+
+#include <boost/asio.hpp>
+
+#include "odil/Association.h"
+#include "odil/EchoSCP.h"
+#include "odil/Exception.h"
+#include "odil/message/CEchoRequest.h"
+#include "odil/message/Response.h"
+
+struct Status
+{
+ int client;
+ std::string server;
+ bool called;
+};
+
+void run_server(Status * status)
+{
+ odil::Association association;
+ association.set_tcp_timeout(boost::posix_time::seconds(1));
+
+ try
+ {
+ association.receive_association(boost::asio::ip::tcp::v4(), 11113);
+
+ odil::EchoSCP echo_scp(association,
+ [status](odil::message::CEchoRequest const &)
+ {
+ status->called = true;
+ return odil::message::Response::Success;
+ });
+
+ // Get echo message
+ auto const message = association.receive_message();
+ echo_scp(message);
+ // Should throw with peer closing connection
+ association.receive_message();
+ }
+ catch(odil::AssociationAborted const &)
+ {
+ status->server = "abort";
+ }
+ catch(odil::AssociationReleased const &)
+ {
+ status->server = "release";
+ }
+ catch(odil::Exception const &)
+ {
+ status->server = "Other Odil exception";
+ }
+ catch(...)
+ {
+ status->server = "Other exception";
+ }
+}
+
+void run_client(Status * status, bool use_abort)
+{
+ std::string command = "echoscu";
+ if(use_abort)
+ {
+ command += " --abort";
+ }
+ command += " 127.0.0.1 11113";
+ status->client = system(command.c_str());
+}
+
+BOOST_AUTO_TEST_CASE(Callback)
+{
+ odil::Association association;
+ odil::EchoSCP scp(association);
+
+ bool called = false;
+ auto const callback =
+ [&called](odil::message::CEchoRequest const &)
+ {
+ called = true;
+ return odil::message::Response::Success;
+ };
+
+ scp.set_callback(callback);
+ scp.get_callback()(odil::message::CEchoRequest(1, ""));
+ BOOST_REQUIRE_EQUAL(called, true);
+}
+
+BOOST_AUTO_TEST_CASE(Release)
+{
+ Status status = { -1, "", false };
+
+ std::thread server(run_server, &status);
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ std::thread client(run_client, &status, false);
+
+ server.join();
+ client.join();
+
+ BOOST_REQUIRE_EQUAL(status.client, 0);
+ BOOST_REQUIRE_EQUAL(status.server, "release");
+ BOOST_REQUIRE_EQUAL(status.called, true);
+}
+
+BOOST_AUTO_TEST_CASE(Abort)
+{
+ Status status = { -1, "", false };
+
+ std::thread server(run_server, &status);
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ std::thread client(run_client, &status, true);
+
+ server.join();
+ client.join();
+
+ BOOST_REQUIRE_EQUAL(status.client, 0);
+ BOOST_REQUIRE_EQUAL(status.server, "abort");
+ BOOST_REQUIRE_EQUAL(status.called, true);
+}
diff --git a/tests/code/Element.cpp b/tests/code/Element.cpp
index 23c82e7..be392f7 100644
--- a/tests/code/Element.cpp
+++ b/tests/code/Element.cpp
@@ -1,20 +1,20 @@
#define BOOST_TEST_MODULE Element
#include <boost/test/unit_test.hpp>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Element.h"
-#include "dcmtkpp/Exception.h"
+#include "odil/DataSet.h"
+#include "odil/Element.h"
+#include "odil/Exception.h"
BOOST_AUTO_TEST_CASE(Empty)
{
- dcmtkpp::Element element;
+ odil::Element element;
BOOST_CHECK(element.empty());
BOOST_CHECK_EQUAL(element.size(), 0);
}
BOOST_AUTO_TEST_CASE(Int)
{
- dcmtkpp::Element const element((dcmtkpp::Value::Integers()));
+ odil::Element const element((odil::Value::Integers()));
BOOST_CHECK(element.is_int());
std::vector<int64_t> const & value = element.as_int();
@@ -23,26 +23,38 @@ BOOST_AUTO_TEST_CASE(Int)
BOOST_AUTO_TEST_CASE(ModifyInt)
{
- dcmtkpp::Element element({0});
+ odil::Element element({0});
element.as_int().push_back(1);
BOOST_CHECK(!element.empty());
BOOST_CHECK_EQUAL(element.size(), 2);
std::vector<int64_t> const & value = element.as_int();
- BOOST_CHECK(value == dcmtkpp::Value::Integers({0, 1}));
+ BOOST_CHECK(value == odil::Value::Integers({0, 1}));
+}
+
+BOOST_AUTO_TEST_CASE(ModifyInt2)
+{
+ odil::Element element({odil::Value::Integer(0)});
+ element.as_int().push_back(1);
+
+ BOOST_CHECK(!element.empty());
+ BOOST_CHECK_EQUAL(element.size(), 2);
+
+ std::vector<int64_t> const & value = element.as_int();
+ BOOST_CHECK(value == odil::Value::Integers({0, 1}));
}
BOOST_AUTO_TEST_CASE(IntWrong)
{
- dcmtkpp::Element element((dcmtkpp::Value::Integers()));
- BOOST_CHECK_THROW(element.as_real(), dcmtkpp::Exception);
- BOOST_CHECK_THROW(element.as_string(), dcmtkpp::Exception);
+ odil::Element element((odil::Value::Integers()));
+ BOOST_CHECK_THROW(element.as_real(), odil::Exception);
+ BOOST_CHECK_THROW(element.as_string(), odil::Exception);
}
BOOST_AUTO_TEST_CASE(Double)
{
- dcmtkpp::Element const element((dcmtkpp::Value::Reals()));
+ odil::Element const element((odil::Value::Reals()));
BOOST_CHECK(element.is_real());
std::vector<double> const & value = element.as_real();
@@ -51,27 +63,27 @@ BOOST_AUTO_TEST_CASE(Double)
BOOST_AUTO_TEST_CASE(ModifyDouble)
{
- dcmtkpp::Element element({0.});
+ odil::Element element({0.});
element.as_real().push_back(1.5);
BOOST_CHECK(!element.empty());
BOOST_CHECK_EQUAL(element.size(), 2);
std::vector<double> const & value = element.as_real();
- BOOST_CHECK(value == dcmtkpp::Value::Reals({0., 1.5}));
+ BOOST_CHECK(value == odil::Value::Reals({0., 1.5}));
}
BOOST_AUTO_TEST_CASE(DoubleWrong)
{
- dcmtkpp::Element element((dcmtkpp::Value::Reals()));
- BOOST_CHECK_THROW(element.as_int(), dcmtkpp::Exception);
- BOOST_CHECK_THROW(element.as_string(), dcmtkpp::Exception);
- BOOST_CHECK_THROW(element.as_data_set(), dcmtkpp::Exception);
+ odil::Element element((odil::Value::Reals()));
+ BOOST_CHECK_THROW(element.as_int(), odil::Exception);
+ BOOST_CHECK_THROW(element.as_string(), odil::Exception);
+ BOOST_CHECK_THROW(element.as_data_set(), odil::Exception);
}
BOOST_AUTO_TEST_CASE(String)
{
- dcmtkpp::Element const element((dcmtkpp::Value::Strings()));
+ odil::Element const element((odil::Value::Strings()));
BOOST_CHECK(element.is_string());
std::vector<std::string> const & value = element.as_string();
@@ -80,38 +92,38 @@ BOOST_AUTO_TEST_CASE(String)
BOOST_AUTO_TEST_CASE(ModifyString)
{
- dcmtkpp::Element element({""});
+ odil::Element element({""});
element.as_string().push_back("foo");
BOOST_CHECK(!element.empty());
BOOST_CHECK_EQUAL(element.size(), 2);
std::vector<std::string> const & value = element.as_string();
- BOOST_CHECK(value == dcmtkpp::Value::Strings({"", "foo"}));
+ BOOST_CHECK(value == odil::Value::Strings({"", "foo"}));
}
BOOST_AUTO_TEST_CASE(StringWrong)
{
- dcmtkpp::Element element((dcmtkpp::Value::Strings()));
- BOOST_CHECK_THROW(element.as_int(), dcmtkpp::Exception);
- BOOST_CHECK_THROW(element.as_real(), dcmtkpp::Exception);
- BOOST_CHECK_THROW(element.as_data_set(), dcmtkpp::Exception);
+ odil::Element element((odil::Value::Strings()));
+ BOOST_CHECK_THROW(element.as_int(), odil::Exception);
+ BOOST_CHECK_THROW(element.as_real(), odil::Exception);
+ BOOST_CHECK_THROW(element.as_data_set(), odil::Exception);
}
BOOST_AUTO_TEST_CASE(DataSet)
{
- dcmtkpp::Element const element((dcmtkpp::Value::DataSets()));
+ odil::Element const element((odil::Value::DataSets()));
BOOST_CHECK(element.is_data_set());
- dcmtkpp::Value::DataSets const & value = element.as_data_set();
+ odil::Value::DataSets const & value = element.as_data_set();
BOOST_CHECK(value.empty());
}
BOOST_AUTO_TEST_CASE(ModifyDataSet)
{
- dcmtkpp::Element element((dcmtkpp::Value::DataSets()));
+ odil::Element element((odil::Value::DataSets()));
- dcmtkpp::DataSet data_set;
+ odil::DataSet data_set;
data_set.add("PatientID");
data_set.as_string("PatientID").push_back("DJ1234");
element.as_data_set().push_back(data_set);
@@ -119,28 +131,28 @@ BOOST_AUTO_TEST_CASE(ModifyDataSet)
BOOST_CHECK(!element.empty());
BOOST_CHECK_EQUAL(element.size(), 1);
- dcmtkpp::Value::DataSets const & value = element.as_data_set();
+ odil::Value::DataSets const & value = element.as_data_set();
BOOST_CHECK_EQUAL(value.size(), 1);
BOOST_CHECK_EQUAL(value[0].size(), 1);
BOOST_CHECK(value[0].has("PatientID"));
BOOST_CHECK(
- value[0].as_string("PatientID") == dcmtkpp::Value::Strings({"DJ1234"}));
+ value[0].as_string("PatientID") == odil::Value::Strings({"DJ1234"}));
}
BOOST_AUTO_TEST_CASE(DataSetWrong)
{
- dcmtkpp::Element element((dcmtkpp::Value::DataSets()));
- BOOST_CHECK_THROW(element.as_int(), dcmtkpp::Exception);
- BOOST_CHECK_THROW(element.as_real(), dcmtkpp::Exception);
- BOOST_CHECK_THROW(element.as_string(), dcmtkpp::Exception);
+ odil::Element element((odil::Value::DataSets()));
+ BOOST_CHECK_THROW(element.as_int(), odil::Exception);
+ BOOST_CHECK_THROW(element.as_real(), odil::Exception);
+ BOOST_CHECK_THROW(element.as_string(), odil::Exception);
}
BOOST_AUTO_TEST_CASE(Equality)
{
- dcmtkpp::Element const element1({12,34}, dcmtkpp::VR::US);
- dcmtkpp::Element const element2({12,34}, dcmtkpp::VR::US);
- dcmtkpp::Element const element3({12.,34.}, dcmtkpp::VR::FL);
- dcmtkpp::Element const element4({12,34}, dcmtkpp::VR::UL);
+ odil::Element const element1({12,34}, odil::VR::US);
+ odil::Element const element2({12,34}, odil::VR::US);
+ odil::Element const element3({12.,34.}, odil::VR::FL);
+ odil::Element const element4({12,34}, odil::VR::UL);
BOOST_CHECK(element1 == element2);
BOOST_CHECK(! (element1 == element3));
@@ -149,10 +161,10 @@ BOOST_AUTO_TEST_CASE(Equality)
BOOST_AUTO_TEST_CASE(Difference)
{
- dcmtkpp::Element const element1({12,34}, dcmtkpp::VR::US);
- dcmtkpp::Element const element2({12,34}, dcmtkpp::VR::US);
- dcmtkpp::Element const element3({12.,34.}, dcmtkpp::VR::FL);
- dcmtkpp::Element const element4({12,34}, dcmtkpp::VR::UL);
+ odil::Element const element1({12,34}, odil::VR::US);
+ odil::Element const element2({12,34}, odil::VR::US);
+ odil::Element const element3({12.,34.}, odil::VR::FL);
+ odil::Element const element4({12,34}, odil::VR::UL);
BOOST_CHECK(! (element1 != element2));
BOOST_CHECK(element1 != element3);
diff --git a/tests/code/ElementAccessor.cpp b/tests/code/ElementAccessor.cpp
index 4e8ac3e..04f9ded 100644
--- a/tests/code/ElementAccessor.cpp
+++ b/tests/code/ElementAccessor.cpp
@@ -6,8 +6,8 @@
#include <dcmtk/dcmdata/dcdeftag.h>
#include <dcmtk/dcmnet/dimse.h>
-#include "dcmtkpp/ElementAccessor.h"
-#include "dcmtkpp/VRTraits.h"
+#include "odil/dcmtk/ElementAccessor.h"
+#include "odil/dcmtk/VRTraits.h"
struct Fixture
{
@@ -19,13 +19,13 @@ struct Fixture
condition = dataset.putAndInsertOFStringArray(DCM_PatientID, "DJ123");
if(condition.bad())
{
- throw dcmtkpp::Exception(condition);
+ throw odil::dcmtk::Exception(condition);
}
condition = dataset.putAndInsertUint16(DCM_MessageID, 1234);
if(condition.bad())
{
- throw dcmtkpp::Exception(condition);
+ throw odil::dcmtk::Exception(condition);
}
}
};
@@ -33,15 +33,15 @@ struct Fixture
BOOST_FIXTURE_TEST_CASE(GetCorrectTypeString, Fixture)
{
BOOST_CHECK_EQUAL(
- dcmtkpp::ElementAccessor<std::string>::get(this->dataset, DCM_PatientID),
+ odil::dcmtk::ElementAccessor<std::string>::get(this->dataset, DCM_PatientID),
"DJ123");
}
BOOST_FIXTURE_TEST_CASE(GetCorrectVRString, Fixture)
{
BOOST_CHECK_EQUAL(
- dcmtkpp::ElementAccessor<
- typename dcmtkpp::VRTraits<EVR_LO>::ValueType
+ odil::dcmtk::ElementAccessor<
+ typename odil::dcmtk::VRTraits<EVR_LO>::ValueType
>::get(this->dataset, DCM_PatientID),
"DJ123");
}
@@ -49,15 +49,15 @@ BOOST_FIXTURE_TEST_CASE(GetCorrectVRString, Fixture)
BOOST_FIXTURE_TEST_CASE(GetCorrectTypeNonString, Fixture)
{
BOOST_CHECK_EQUAL(
- dcmtkpp::ElementAccessor<Uint16>::get(this->dataset, DCM_MessageID),
+ odil::dcmtk::ElementAccessor<Uint16>::get(this->dataset, DCM_MessageID),
1234);
}
BOOST_FIXTURE_TEST_CASE(GetCorrectVRNonString, Fixture)
{
BOOST_CHECK_EQUAL(
- dcmtkpp::ElementAccessor<
- typename dcmtkpp::VRTraits<EVR_US>::ValueType
+ odil::dcmtk::ElementAccessor<
+ typename odil::dcmtk::VRTraits<EVR_US>::ValueType
>::get(this->dataset, DCM_MessageID),
1234);
}
@@ -65,20 +65,20 @@ BOOST_FIXTURE_TEST_CASE(GetCorrectVRNonString, Fixture)
BOOST_FIXTURE_TEST_CASE(GetWrongTypeString, Fixture)
{
BOOST_CHECK_THROW(
- dcmtkpp::ElementAccessor<Float32>::get(this->dataset, DCM_PatientID),
- dcmtkpp::Exception);
+ odil::dcmtk::ElementAccessor<Float32>::get(this->dataset, DCM_PatientID),
+ odil::dcmtk::Exception);
}
BOOST_FIXTURE_TEST_CASE(GetWrongTypeNonString, Fixture)
{
BOOST_CHECK_THROW(
- dcmtkpp::ElementAccessor<Float32>::get(this->dataset, DCM_MessageID),
- dcmtkpp::Exception);
+ odil::dcmtk::ElementAccessor<Float32>::get(this->dataset, DCM_MessageID),
+ odil::dcmtk::Exception);
}
BOOST_FIXTURE_TEST_CASE(SetEmpty, Fixture)
{
- dcmtkpp::ElementAccessor<std::string>::set(this->dataset, DCM_PatientName, "FOO");
+ odil::dcmtk::ElementAccessor<std::string>::set(this->dataset, DCM_PatientName, "FOO");
OFString value;
OFCondition const condition =
this->dataset.findAndGetOFString(DCM_PatientName, value);
@@ -89,22 +89,22 @@ BOOST_FIXTURE_TEST_CASE(SetEmpty, Fixture)
BOOST_FIXTURE_TEST_CASE(SetWrongTypeString, Fixture)
{
BOOST_CHECK_THROW(
- dcmtkpp::ElementAccessor<Float32>::set(
+ odil::dcmtk::ElementAccessor<Float32>::set(
this->dataset, DCM_PatientName, 1.234),
- dcmtkpp::Exception);
+ odil::dcmtk::Exception);
}
BOOST_FIXTURE_TEST_CASE(SetWrongTypeNonString, Fixture)
{
BOOST_CHECK_THROW(
- dcmtkpp::ElementAccessor<Float64>::set(
+ odil::dcmtk::ElementAccessor<Float64>::set(
this->dataset, DCM_Status, STATUS_Success),
- dcmtkpp::Exception);
+ odil::dcmtk::Exception);
}
BOOST_FIXTURE_TEST_CASE(SetExisting, Fixture)
{
- dcmtkpp::ElementAccessor<std::string>::set(this->dataset, DCM_PatientID, "FOO");
+ odil::dcmtk::ElementAccessor<std::string>::set(this->dataset, DCM_PatientID, "FOO");
OFString value;
OFCondition const condition =
this->dataset.findAndGetOFString(DCM_PatientID, value);
@@ -115,11 +115,11 @@ BOOST_FIXTURE_TEST_CASE(SetExisting, Fixture)
BOOST_FIXTURE_TEST_CASE(Has, Fixture)
{
BOOST_CHECK(
- dcmtkpp::ElementAccessor<std::string>::has(this->dataset, DCM_PatientID));
+ odil::dcmtk::ElementAccessor<std::string>::has(this->dataset, DCM_PatientID));
}
BOOST_FIXTURE_TEST_CASE(HasNot, Fixture)
{
BOOST_CHECK(
- !dcmtkpp::ElementAccessor<std::string>::has(this->dataset, DCM_PatientName));
+ !odil::dcmtk::ElementAccessor<std::string>::has(this->dataset, DCM_PatientName));
}
diff --git a/tests/code/Exception.cpp b/tests/code/Exception.cpp
index c0939d5..1b46e41 100644
--- a/tests/code/Exception.cpp
+++ b/tests/code/Exception.cpp
@@ -1,36 +1,10 @@
-#define BOOST_TEST_MODULE Message
+#define BOOST_TEST_MODULE Exception
#include <boost/test/unit_test.hpp>
-#include <ostream>
+#include "odil/Exception.h"
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/ofstd/ofcond.h>
-
-#include "dcmtkpp/Exception.h"
-
-namespace dcmtkpp
-{
-
-std::ostream & operator<<(std::ostream & stream, Exception::Source const & type)
-{
- stream << int(type);
- return stream;
-}
-
-}
-
-BOOST_AUTO_TEST_CASE(StringConstructor)
-{
- dcmtkpp::Exception const exception("foo");
- BOOST_CHECK_EQUAL(exception.get_source(), dcmtkpp::Exception::Source::Message);
- BOOST_CHECK_EQUAL(exception.what(), "foo");
- BOOST_CHECK_THROW(exception.get_condition(), dcmtkpp::Exception);
-}
-
-BOOST_AUTO_TEST_CASE(ConditionConstructor)
+BOOST_AUTO_TEST_CASE(Exception)
{
- dcmtkpp::Exception const exception(EC_IllegalParameter);
- BOOST_CHECK_EQUAL(exception.get_source(), dcmtkpp::Exception::Source::Condition);
- BOOST_CHECK_EQUAL(exception.what(), EC_IllegalParameter.text());
- BOOST_CHECK(exception.get_condition() == EC_IllegalParameter);
+ odil::Exception const exception("foo");
+ BOOST_REQUIRE_EQUAL(exception.what(), "foo");
}
diff --git a/tests/code/FindSCP.cpp b/tests/code/FindSCP.cpp
new file mode 100644
index 0000000..fdc4f6a
--- /dev/null
+++ b/tests/code/FindSCP.cpp
@@ -0,0 +1,166 @@
+#define BOOST_TEST_MODULE FindSCP
+#include <boost/test/unit_test.hpp>
+
+#include <chrono>
+#include <cstdlib>
+#include <memory>
+#include <thread>
+#include <vector>
+
+#include <boost/asio.hpp>
+#include <boost/filesystem.hpp>
+
+#include "odil/Association.h"
+#include "odil/DataSet.h"
+#include "odil/FindSCP.h"
+#include "odil/Exception.h"
+#include "odil/SCP.h"
+#include "odil/Reader.h"
+#include "odil/message/Response.h"
+
+struct Status
+{
+ int client;
+ std::string server;
+ std::vector<odil::DataSet> responses;
+};
+
+class Generator: public odil::SCP::DataSetGenerator
+{
+public:
+ Generator()
+ {
+ // Nothing else.
+ }
+
+ virtual ~Generator()
+ {
+ // Nothing to do.
+ }
+
+ virtual void initialize(odil::message::Request const & )
+ {
+ odil::DataSet data_set_1;
+ data_set_1.add(odil::registry::PatientName, {"Hello^World"});
+ data_set_1.add(odil::registry::PatientID, {"1234"});
+ this->_responses.push_back(data_set_1);
+
+ odil::DataSet data_set_2;
+ data_set_2.add(odil::registry::PatientName, {"Doe^John"});
+ data_set_2.add(odil::registry::PatientID, {"5678"});
+ this->_responses.push_back(data_set_2);
+
+ this->_response_iterator = this->_responses.begin();
+ }
+
+ virtual bool done() const
+ {
+ return (this->_response_iterator == this->_responses.end());
+ }
+
+ virtual odil::DataSet get() const
+ {
+ return *this->_response_iterator;
+ }
+
+ virtual void next()
+ {
+ ++this->_response_iterator;
+ }
+
+
+private:
+ std::vector<odil::DataSet> _responses;
+ std::vector<odil::DataSet>::const_iterator _response_iterator;
+};
+
+void run_server(Status * status)
+{
+ odil::Association association;
+ association.set_tcp_timeout(boost::posix_time::seconds(1));
+
+ try
+ {
+ association.receive_association(boost::asio::ip::tcp::v4(), 11113);
+
+ odil::FindSCP find_scp(association);
+ auto const generator = std::make_shared<Generator>();
+ find_scp.set_generator(generator);
+
+ // Get echo message
+ auto const message = association.receive_message();
+ find_scp(message);
+ // Should throw with peer closing connection
+ association.receive_message();
+ }
+ catch(odil::AssociationAborted const &)
+ {
+ status->server = "abort";
+ }
+ catch(odil::AssociationReleased const &)
+ {
+ status->server = "release";
+ }
+ catch(odil::Exception const &)
+ {
+ status->server = "Other Odil exception";
+ }
+ catch(...)
+ {
+ status->server = "Other exception";
+ }
+}
+
+void run_client(Status * status)
+{
+ std::string command = "findscu "
+ "-P -k QueryRetrieveLevel=PATIENT "
+ "-k PatientID=* -k PatientName "
+ "-q -X "
+ "127.0.0.1 11113";
+ status->client = system(command.c_str());
+
+ boost::filesystem::directory_iterator end;
+ for(boost::filesystem::directory_iterator it("."); it != end; ++it )
+ {
+ if(!boost::filesystem::is_regular_file(it->status()))
+ {
+ continue;
+ }
+ auto const filename = it->path().stem().string();
+ if(filename.substr(0, 3) != "rsp")
+ {
+ continue;
+ }
+
+ std::ifstream stream(it->path().string());
+ auto const data_set = odil::Reader::read_file(stream).second;
+ status->responses.push_back(data_set);
+
+ boost::filesystem::remove(it->path());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(Release)
+{
+ Status status = { -1, "", {} };
+
+ std::thread server(run_server, &status);
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ std::thread client(run_client, &status);
+
+ server.join();
+ client.join();
+
+ BOOST_REQUIRE_EQUAL(status.client, 0);
+ BOOST_REQUIRE_EQUAL(status.server, "release");
+ BOOST_REQUIRE_EQUAL(status.responses.size(), 2);
+
+ BOOST_REQUIRE_EQUAL(status.responses[0].size(), 2);
+ BOOST_REQUIRE_EQUAL(status.responses[0].as_string("PatientName", 0), "Hello^World");
+ BOOST_REQUIRE_EQUAL(status.responses[0].as_string("PatientID", 0), "1234");
+
+ BOOST_REQUIRE_EQUAL(status.responses[1].size(), 2);
+ BOOST_REQUIRE_EQUAL(status.responses[1].as_string("PatientName", 0), "Doe^John");
+ BOOST_REQUIRE_EQUAL(status.responses[1].as_string("PatientID", 0), "5678");
+}
diff --git a/tests/code/FindSCU.cpp b/tests/code/FindSCU.cpp
index 2328c8b..33d4fa7 100644
--- a/tests/code/FindSCU.cpp
+++ b/tests/code/FindSCU.cpp
@@ -1,9 +1,9 @@
#define BOOST_TEST_MODULE FindSCU
#include <boost/test/unit_test.hpp>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/FindSCU.h"
-#include "dcmtkpp/registry.h"
+#include "odil/DataSet.h"
+#include "odil/FindSCU.h"
+#include "odil/registry.h"
#include "../PeerFixtureBase.h"
@@ -11,14 +11,13 @@ struct Fixture: public PeerFixtureBase
{
static bool called;
- dcmtkpp::DataSet query;
+ odil::DataSet query;
Fixture()
- : PeerFixtureBase(NET_REQUESTOR, 104, 10,
- {
+ : PeerFixtureBase({
{
- dcmtkpp::registry::PatientRootQueryRetrieveInformationModelFIND,
- { dcmtkpp::registry::ImplicitVRLittleEndian }
+ 1, odil::registry::PatientRootQueryRetrieveInformationModelFIND,
+ { odil::registry::ImplicitVRLittleEndian }, true, false
}
})
{
@@ -30,7 +29,7 @@ struct Fixture: public PeerFixtureBase
}
- static void callback(dcmtkpp::DataSet const &)
+ static void callback(odil::DataSet const &)
{
Fixture::called = true;
}
@@ -40,26 +39,22 @@ bool Fixture::called = false;
BOOST_FIXTURE_TEST_CASE(Find, Fixture)
{
- dcmtkpp::FindSCU scu;
- scu.set_network(&this->network);
- scu.set_association(&this->association);
+ odil::FindSCU scu(this->association);
- scu.set_affected_sop_class(dcmtkpp::registry::PatientRootQueryRetrieveInformationModelFIND);
+ scu.set_affected_sop_class(odil::registry::PatientRootQueryRetrieveInformationModelFIND);
auto const results = scu.find(this->query);
BOOST_REQUIRE_EQUAL(results.size(), 1);
BOOST_CHECK(
results[0].as_string("PatientID") ==
- dcmtkpp::Value::Strings({"DJ001"}));
+ odil::Value::Strings({"DJ001"}));
}
BOOST_FIXTURE_TEST_CASE(FindCallback, Fixture)
{
- dcmtkpp::FindSCU scu;
- scu.set_network(&this->network);
- scu.set_association(&this->association);
+ odil::FindSCU scu(this->association);
- scu.set_affected_sop_class(dcmtkpp::registry::PatientRootQueryRetrieveInformationModelFIND);
+ scu.set_affected_sop_class(odil::registry::PatientRootQueryRetrieveInformationModelFIND);
scu.find(this->query, Fixture::callback);
BOOST_CHECK(Fixture::called);
diff --git a/tests/code/GetSCP.cpp b/tests/code/GetSCP.cpp
new file mode 100644
index 0000000..f1588f7
--- /dev/null
+++ b/tests/code/GetSCP.cpp
@@ -0,0 +1,201 @@
+#define BOOST_TEST_MODULE GetSCP
+#include <boost/test/unit_test.hpp>
+
+#include <algorithm>
+#include <chrono>
+#include <cstdlib>
+#include <memory>
+#include <thread>
+#include <vector>
+
+#include <boost/asio.hpp>
+#include <boost/filesystem.hpp>
+
+#include "odil/Association.h"
+#include "odil/DataSet.h"
+#include "odil/GetSCP.h"
+#include "odil/Exception.h"
+#include "odil/SCP.h"
+#include "odil/uid.h"
+#include "odil/Reader.h"
+#include "odil/message/Response.h"
+
+struct Status
+{
+ int client;
+ std::string server;
+ std::vector<odil::DataSet> responses;
+};
+
+class Generator: public odil::GetSCP::DataSetGenerator
+{
+public:
+ Generator()
+ {
+ // Nothing else.
+ }
+
+ virtual ~Generator()
+ {
+ // Nothing to do.
+ }
+
+ virtual void initialize(odil::message::Request const & )
+ {
+ odil::DataSet data_set_1;
+ data_set_1.add("SOPClassUID", {odil::registry::RawDataStorage});
+ data_set_1.add(
+ "SOPInstanceUID",
+ {"1.2.826.0.1.3680043.9.5560.3127449359877365688774406533090568532"});
+ data_set_1.add("PatientName", {"Hello^World"});
+ data_set_1.add("PatientID", {"1234"});
+ this->_responses.push_back(data_set_1);
+
+ odil::DataSet data_set_2;
+ data_set_2.add("SOPClassUID", {odil::registry::RawDataStorage});
+ data_set_2.add(
+ "SOPInstanceUID",
+ {"1.2.826.0.1.3680043.9.5560.3221615743193123463515381981101110692"});
+ data_set_2.add("PatientName", {"Doe^John"});
+ data_set_2.add("PatientID", {"5678"});
+ this->_responses.push_back(data_set_2);
+
+ this->_response_iterator = this->_responses.begin();
+ }
+
+ virtual bool done() const
+ {
+ return (this->_response_iterator == this->_responses.end());
+ }
+
+ virtual odil::DataSet get() const
+ {
+ return *this->_response_iterator;
+ }
+
+ virtual void next()
+ {
+ ++this->_response_iterator;
+ }
+
+ virtual unsigned int count() const
+ {
+ return 2;
+ }
+
+private:
+ std::vector<odil::DataSet> _responses;
+ std::vector<odil::DataSet>::const_iterator _response_iterator;
+};
+
+void run_server(Status * status)
+{
+ odil::Association association;
+ association.set_tcp_timeout(boost::posix_time::seconds(1));
+
+ try
+ {
+ association.receive_association(boost::asio::ip::tcp::v4(), 11113);
+
+ odil::GetSCP get_scp(association);
+ auto const generator = std::make_shared<Generator>();
+ get_scp.set_generator(generator);
+
+ // Get echo message
+ auto const message = association.receive_message();
+ get_scp(message);
+ // Should throw with peer closing connection
+ association.receive_message();
+ }
+ catch(odil::AssociationAborted const &)
+ {
+ status->server = "abort";
+ }
+ catch(odil::AssociationReleased const &)
+ {
+ status->server = "release";
+ }
+ catch(odil::Exception const &)
+ {
+ status->server = "Other Odil exception";
+ }
+ catch(...)
+ {
+ status->server = "Other exception";
+ }
+}
+
+void run_client(Status * status)
+{
+ std::string command = "getscu "
+ "-P -k QueryRetrieveLevel=PATIENT "
+ "-k PatientID=* -k PatientName "
+ "+B "
+ "127.0.0.1 11113";
+ status->client = system(command.c_str());
+
+ boost::filesystem::directory_iterator end;
+ for(boost::filesystem::directory_iterator it("."); it != end; ++it )
+ {
+ if(!boost::filesystem::is_regular_file(it->status()))
+ {
+ continue;
+ }
+ auto const filename = it->path().stem().string();
+ if(filename.substr(0, odil::uid_prefix.size()) != odil::uid_prefix)
+ {
+ continue;
+ }
+
+ std::ifstream stream(it->path().string());
+ auto const data_set = odil::Reader::read_file(stream).second;
+ status->responses.push_back(data_set);
+
+ boost::filesystem::remove(it->path());
+ }
+
+ std::sort(
+ status->responses.begin(), status->responses.end(),
+ [](odil::DataSet const & left, odil::DataSet const & right)
+ {
+ auto const & left_uid = left.as_string("SOPInstanceUID", 0);
+ auto const & right_uid = right.as_string("SOPInstanceUID", 0);
+ return (left_uid < right_uid);
+ });
+}
+
+BOOST_AUTO_TEST_CASE(Release)
+{
+ Status status = { -1, "", {} };
+
+ std::thread server(run_server, &status);
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ std::thread client(run_client, &status);
+
+ server.join();
+ client.join();
+
+ BOOST_REQUIRE_EQUAL(status.client, 0);
+ BOOST_REQUIRE_EQUAL(status.server, "release");
+ BOOST_REQUIRE_EQUAL(status.responses.size(), 2);
+
+ BOOST_REQUIRE_EQUAL(status.responses[0].size(), 4);
+ BOOST_REQUIRE_EQUAL(
+ status.responses[0].as_string("SOPInstanceUID", 0),
+ "1.2.826.0.1.3680043.9.5560.3127449359877365688774406533090568532");
+ BOOST_REQUIRE_EQUAL(
+ status.responses[0].as_string("SOPClassUID", 0),
+ odil::registry::RawDataStorage);
+ BOOST_REQUIRE_EQUAL(status.responses[0].as_string("PatientName", 0), "Hello^World");
+ BOOST_REQUIRE_EQUAL(status.responses[0].as_string("PatientID", 0), "1234");
+
+ BOOST_REQUIRE_EQUAL(status.responses[1].size(), 4);
+ BOOST_REQUIRE_EQUAL(
+ status.responses[1].as_string("SOPInstanceUID", 0),
+ "1.2.826.0.1.3680043.9.5560.3221615743193123463515381981101110692");
+ BOOST_REQUIRE_EQUAL(
+ status.responses[1].as_string("SOPClassUID", 0),
+ odil::registry::RawDataStorage);
+ BOOST_REQUIRE_EQUAL(status.responses[1].as_string("PatientName", 0), "Doe^John");
+ BOOST_REQUIRE_EQUAL(status.responses[1].as_string("PatientID", 0), "5678");
+}
diff --git a/tests/code/GetSCU.cpp b/tests/code/GetSCU.cpp
index 7f3f979..c71014b 100644
--- a/tests/code/GetSCU.cpp
+++ b/tests/code/GetSCU.cpp
@@ -1,9 +1,9 @@
#define BOOST_TEST_MODULE GetSCU
#include <boost/test/unit_test.hpp>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/GetSCU.h"
-#include "dcmtkpp/registry.h"
+#include "odil/DataSet.h"
+#include "odil/GetSCU.h"
+#include "odil/registry.h"
#include "../PeerFixtureBase.h"
@@ -11,18 +11,17 @@ struct Fixture: public PeerFixtureBase
{
static bool called;
- dcmtkpp::DataSet query;
+ odil::DataSet query;
Fixture()
- : PeerFixtureBase(NET_REQUESTOR, 104, 10,
- {
+ : PeerFixtureBase({
{
- dcmtkpp::registry::PatientRootQueryRetrieveInformationModelGET,
- { dcmtkpp::registry::ImplicitVRLittleEndian }
+ 1, odil::registry::PatientRootQueryRetrieveInformationModelGET,
+ { odil::registry::ImplicitVRLittleEndian }, true, false
},
{
- dcmtkpp::registry::RawDataStorage, { dcmtkpp::registry::ImplicitVRLittleEndian },
- ASC_SC_ROLE_SCP
+ 3, odil::registry::RawDataStorage,
+ { odil::registry::ImplicitVRLittleEndian }, false, true
}
})
{
@@ -33,7 +32,7 @@ struct Fixture: public PeerFixtureBase
}
- static void callback(dcmtkpp::DataSet const &)
+ static void callback(odil::DataSet const &)
{
Fixture::called = true;
}
@@ -43,26 +42,25 @@ bool Fixture::called = false;
BOOST_FIXTURE_TEST_CASE(Get, Fixture)
{
- dcmtkpp::GetSCU scu;
- scu.set_network(&this->network);
- scu.set_association(&this->association);
+ odil::GetSCU scu(this->association);
- scu.set_affected_sop_class(dcmtkpp::registry::PatientRootQueryRetrieveInformationModelGET);
+ scu.set_affected_sop_class(
+ odil::registry::PatientRootQueryRetrieveInformationModelGET);
auto const results = scu.get(this->query);
BOOST_REQUIRE_EQUAL(results.size(), 1);
BOOST_CHECK(
results[0].as_string("SOPInstanceUID") ==
- dcmtkpp::Value::Strings({"2.25.95090344942250266709587559073467305647"}));
+ odil::Value::Strings(
+ {"2.25.95090344942250266709587559073467305647"}));
}
BOOST_FIXTURE_TEST_CASE(GetCallback, Fixture)
{
- dcmtkpp::GetSCU scu;
- scu.set_network(&this->network);
- scu.set_association(&this->association);
+ odil::GetSCU scu(this->association);
- scu.set_affected_sop_class(dcmtkpp::registry::PatientRootQueryRetrieveInformationModelGET);
+ scu.set_affected_sop_class(
+ odil::registry::PatientRootQueryRetrieveInformationModelGET);
scu.get(this->query, Fixture::callback);
BOOST_CHECK(Fixture::called);
diff --git a/tests/code/Message.cpp b/tests/code/Message.cpp
deleted file mode 100644
index 0b8e836..0000000
--- a/tests/code/Message.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-#define BOOST_TEST_MODULE Message
-#include <boost/test/unit_test.hpp>
-
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Message.h"
-
-BOOST_AUTO_TEST_CASE(DefaultConstructor)
-{
- dcmtkpp::Message const message;
-
- // Command Set might not be empty (Command Group Length, Data Set Type)
- BOOST_CHECK(!message.has_data_set());
-}
-
-BOOST_AUTO_TEST_CASE(Constructor)
-{
- dcmtkpp::DataSet command_set;
- command_set.add("CommandField", {dcmtkpp::Message::Command::C_ECHO_RQ});
-
- dcmtkpp::DataSet data_set;
-
- dcmtkpp::Message const message(command_set, data_set);
-
- BOOST_CHECK_EQUAL(
- message.get_command_set().as_int("CommandField", 0),
- dcmtkpp::Message::Command::C_ECHO_RQ);
-
- BOOST_CHECK(message.has_data_set());
- BOOST_CHECK(message.get_data_set().empty());
-
- BOOST_CHECK_EQUAL(message.get_command_field(), dcmtkpp::Message::Command::C_ECHO_RQ);
-}
-
-BOOST_AUTO_TEST_CASE(CommandField)
-{
- dcmtkpp::Message message;
- message.set_command_field(dcmtkpp::Message::Command::C_FIND_RSP);
-
- BOOST_CHECK(
- message.get_command_set().as_int("CommandField") ==
- dcmtkpp::Value::Integers({dcmtkpp::Message::Command::C_FIND_RSP}));
- BOOST_CHECK_EQUAL(message.get_command_field(), dcmtkpp::Message::Command::C_FIND_RSP);
-}
-
-BOOST_AUTO_TEST_CASE(DeleteDataSet)
-{
- dcmtkpp::DataSet command_set;
- dcmtkpp::DataSet data_set;
-
- dcmtkpp::Message message(command_set, data_set);
-
- BOOST_CHECK(message.has_data_set());
-
- message.delete_data_set();
-
- BOOST_CHECK(!message.has_data_set());
-}
diff --git a/tests/code/MoveSCP.cpp b/tests/code/MoveSCP.cpp
new file mode 100644
index 0000000..18fb15e
--- /dev/null
+++ b/tests/code/MoveSCP.cpp
@@ -0,0 +1,237 @@
+#define BOOST_TEST_MODULE MoveSCP
+#include <boost/test/unit_test.hpp>
+
+#include <chrono>
+#include <cstdlib>
+#include <memory>
+#include <thread>
+#include <vector>
+
+#include <boost/asio.hpp>
+#include <boost/filesystem.hpp>
+
+#include "odil/Association.h"
+#include "odil/AssociationParameters.h"
+#include "odil/DataSet.h"
+#include "odil/MoveSCP.h"
+#include "odil/Exception.h"
+#include "odil/SCP.h"
+#include "odil/uid.h"
+#include "odil/Reader.h"
+#include "odil/registry.h"
+#include "odil/message/Response.h"
+
+struct Status
+{
+ int client;
+ std::string server;
+ std::vector<odil::DataSet> responses;
+};
+
+class Generator: public odil::MoveSCP::DataSetGenerator
+{
+public:
+ Generator(odil::Association & association)
+ : _association(association)
+ {
+ // Nothing else.
+ }
+
+ virtual ~Generator()
+ {
+ // Nothing to do.
+ }
+
+ virtual void initialize(odil::message::Request const & )
+ {
+ odil::DataSet data_set_1;
+ data_set_1.add("SOPClassUID", {odil::registry::RawDataStorage});
+ data_set_1.add(
+ "SOPInstanceUID",
+ {"1.2.826.0.1.3680043.9.5560.3127449359877365688774406533090568532"});
+ data_set_1.add("PatientName", {"Hello^World"});
+ data_set_1.add("PatientID", {"1234"});
+ this->_responses.push_back(data_set_1);
+
+ odil::DataSet data_set_2;
+ data_set_2.add("SOPClassUID", {odil::registry::RawDataStorage});
+ data_set_2.add(
+ "SOPInstanceUID",
+ {"1.2.826.0.1.3680043.9.5560.3221615743193123463515381981101110692"});
+ data_set_2.add("PatientName", {"Doe^John"});
+ data_set_2.add("PatientID", {"5678"});
+ this->_responses.push_back(data_set_2);
+
+ this->_response_iterator = this->_responses.begin();
+ }
+
+ virtual bool done() const
+ {
+ return (this->_response_iterator == this->_responses.end());
+ }
+
+ virtual odil::DataSet get() const
+ {
+ return *this->_response_iterator;
+ }
+
+ virtual void next()
+ {
+ ++this->_response_iterator;
+ }
+
+ virtual unsigned int count() const
+ {
+ return 2;
+ }
+
+ virtual odil::Association get_association(
+ odil::message::CMoveRequest const & request) const
+ {
+ odil::Association move_association;
+ move_association.set_peer_host(this->_association.get_peer_host());
+ move_association.set_peer_port(11114);
+
+ // FIXME: max length, user_identity
+ std::vector<odil::AssociationParameters::PresentationContext>
+ presentation_contexts;
+ for(auto const & uid: odil::registry::uids_dictionary)
+ {
+ auto const & name = uid.second.name;
+ if(name.substr(name.size()-7, std::string::npos) == "Storage")
+ {
+ odil::AssociationParameters::PresentationContext
+ presentation_context = {
+ static_cast<uint8_t>(1+2*presentation_contexts.size()),
+ uid.first,
+ { odil::registry::ImplicitVRLittleEndian },
+ true, false
+ };
+ presentation_contexts.push_back(presentation_context);
+ }
+ }
+ move_association.update_parameters()
+ .set_calling_ae_title(
+ this->_association.get_negotiated_parameters().get_called_ae_title())
+ .set_called_ae_title(request.get_move_destination())
+ .set_presentation_contexts(presentation_contexts);
+
+ return move_association;
+ }
+
+private:
+ std::vector<odil::DataSet> _responses;
+ std::vector<odil::DataSet>::const_iterator _response_iterator;
+ odil::Association & _association;
+};
+
+void run_server(Status * status)
+{
+ odil::Association association;
+ association.set_tcp_timeout(boost::posix_time::seconds(1));
+
+ try
+ {
+ association.receive_association(boost::asio::ip::tcp::v4(), 11113);
+
+ odil::MoveSCP move_scp(association);
+ auto const generator = std::make_shared<Generator>(association);
+ move_scp.set_generator(generator);
+
+ // Get move message
+ auto const message = association.receive_message();
+ move_scp(message);
+ // Should throw with peer closing connection
+ association.receive_message();
+ }
+ catch(odil::AssociationAborted const &)
+ {
+ status->server = "abort";
+ }
+ catch(odil::AssociationReleased const &)
+ {
+ status->server = "release";
+ }
+ catch(odil::Exception const &)
+ {
+ status->server = "Other Odil exception";
+ }
+ catch(...)
+ {
+ status->server = "Other exception";
+ }
+}
+
+void run_client(Status * status)
+{
+ std::string command = "movescu "
+ "-P -k QueryRetrieveLevel=PATIENT "
+ "-k PatientID=* -k PatientName "
+ "+P 11114 127.0.0.1 11113";
+ status->client = system(command.c_str());
+
+ boost::filesystem::directory_iterator end;
+ for(boost::filesystem::directory_iterator it("."); it != end; ++it )
+ {
+ if(!boost::filesystem::is_regular_file(it->status()))
+ {
+ continue;
+ }
+ auto const filename = it->path().stem().string();
+ if(filename.substr(0, 3) != "RAW")
+ {
+ continue;
+ }
+
+ std::ifstream stream(it->path().string());
+ auto const data_set = odil::Reader::read_file(stream).second;
+ status->responses.push_back(data_set);
+
+ boost::filesystem::remove(it->path());
+ }
+
+ std::sort(
+ status->responses.begin(), status->responses.end(),
+ [](odil::DataSet const & left, odil::DataSet const & right)
+ {
+ auto const & left_uid = left.as_string("SOPInstanceUID", 0);
+ auto const & right_uid = right.as_string("SOPInstanceUID", 0);
+ return (left_uid < right_uid);
+ });
+}
+
+BOOST_AUTO_TEST_CASE(Release)
+{
+ Status status = { -1, "", {} };
+
+ std::thread server(run_server, &status);
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ std::thread client(run_client, &status);
+
+ server.join();
+ client.join();
+
+ BOOST_REQUIRE_EQUAL(status.client, 0);
+ BOOST_REQUIRE_EQUAL(status.server, "release");
+ BOOST_REQUIRE_EQUAL(status.responses.size(), 2);
+
+ BOOST_REQUIRE_EQUAL(status.responses[0].size(), 4);
+ BOOST_REQUIRE_EQUAL(
+ status.responses[0].as_string("SOPInstanceUID", 0),
+ "1.2.826.0.1.3680043.9.5560.3127449359877365688774406533090568532");
+ BOOST_REQUIRE_EQUAL(
+ status.responses[0].as_string("SOPClassUID", 0),
+ odil::registry::RawDataStorage);
+ BOOST_REQUIRE_EQUAL(status.responses[0].as_string("PatientName", 0), "Hello^World");
+ BOOST_REQUIRE_EQUAL(status.responses[0].as_string("PatientID", 0), "1234");
+
+ BOOST_REQUIRE_EQUAL(status.responses[1].size(), 4);
+ BOOST_REQUIRE_EQUAL(
+ status.responses[1].as_string("SOPInstanceUID", 0),
+ "1.2.826.0.1.3680043.9.5560.3221615743193123463515381981101110692");
+ BOOST_REQUIRE_EQUAL(
+ status.responses[1].as_string("SOPClassUID", 0),
+ odil::registry::RawDataStorage);
+ BOOST_REQUIRE_EQUAL(status.responses[1].as_string("PatientName", 0), "Doe^John");
+ BOOST_REQUIRE_EQUAL(status.responses[1].as_string("PatientID", 0), "5678");
+}
diff --git a/tests/code/MoveSCU.cpp b/tests/code/MoveSCU.cpp
index 5a131e7..598c043 100644
--- a/tests/code/MoveSCU.cpp
+++ b/tests/code/MoveSCU.cpp
@@ -1,9 +1,9 @@
#define BOOST_TEST_MODULE MoveSCU
#include <boost/test/unit_test.hpp>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/MoveSCU.h"
-#include "dcmtkpp/registry.h"
+#include "odil/DataSet.h"
+#include "odil/MoveSCU.h"
+#include "odil/registry.h"
#include "../PeerFixtureBase.h"
@@ -11,18 +11,17 @@ struct Fixture: public PeerFixtureBase
{
static bool called;
- dcmtkpp::DataSet query;
+ odil::DataSet query;
Fixture()
- : PeerFixtureBase(NET_ACCEPTORREQUESTOR, 11113, 10,
- {
+ : PeerFixtureBase({
{
- dcmtkpp::registry::PatientRootQueryRetrieveInformationModelMOVE,
- { dcmtkpp::registry::ImplicitVRLittleEndian }
+ 1, odil::registry::PatientRootQueryRetrieveInformationModelMOVE,
+ { odil::registry::ImplicitVRLittleEndian }, true, false
},
{
- dcmtkpp::registry::RawDataStorage, { dcmtkpp::registry::ImplicitVRLittleEndian },
- ASC_SC_ROLE_SCP
+ 3, odil::registry::RawDataStorage,
+ { odil::registry::ImplicitVRLittleEndian }, false, true
}
})
{
@@ -32,7 +31,7 @@ struct Fixture: public PeerFixtureBase
this->query.add("PatientName", {"Doe^John"});
}
- static void callback(dcmtkpp::DataSet const &)
+ static void callback(odil::DataSet const &)
{
Fixture::called = true;
}
@@ -40,43 +39,41 @@ struct Fixture: public PeerFixtureBase
bool Fixture::called = false;
-BOOST_AUTO_TEST_CASE(DefaultConstructor)
+BOOST_FIXTURE_TEST_CASE(DefaultConstructor, Fixture)
{
- dcmtkpp::MoveSCU const scu;
+ odil::MoveSCU const scu(this->association);
BOOST_CHECK_EQUAL(scu.get_move_destination(), "");
}
-BOOST_AUTO_TEST_CASE(MoveDestination)
+BOOST_FIXTURE_TEST_CASE(MoveDestination, Fixture)
{
- dcmtkpp::MoveSCU scu;
+ odil::MoveSCU scu(this->association);
scu.set_move_destination("remote");
BOOST_CHECK_EQUAL(scu.get_move_destination(), "remote");
}
BOOST_FIXTURE_TEST_CASE(Move, Fixture)
{
- dcmtkpp::MoveSCU scu;
- scu.set_network(&this->network);
- scu.set_association(&this->association);
+ odil::MoveSCU scu(this->association);
scu.set_move_destination("LOCAL");
- scu.set_affected_sop_class(dcmtkpp::registry::PatientRootQueryRetrieveInformationModelMOVE);
+ scu.set_affected_sop_class(
+ odil::registry::PatientRootQueryRetrieveInformationModelMOVE);
auto const results = scu.move(this->query);
BOOST_REQUIRE_EQUAL(results.size(), 1);
BOOST_CHECK(
results[0].as_string("SOPInstanceUID") ==
- dcmtkpp::Value::Strings{"2.25.95090344942250266709587559073467305647"});
+ odil::Value::Strings{"2.25.95090344942250266709587559073467305647"});
}
BOOST_FIXTURE_TEST_CASE(MoveCallback, Fixture)
{
- dcmtkpp::MoveSCU scu;
- scu.set_network(&this->network);
- scu.set_association(&this->association);
+ odil::MoveSCU scu(this->association);
scu.set_move_destination("LOCAL");
- scu.set_affected_sop_class(dcmtkpp::registry::PatientRootQueryRetrieveInformationModelMOVE);
+ scu.set_affected_sop_class(
+ odil::registry::PatientRootQueryRetrieveInformationModelMOVE);
scu.move(this->query, Fixture::callback);
BOOST_CHECK(Fixture::called);
diff --git a/tests/code/Network.cpp b/tests/code/Network.cpp
deleted file mode 100644
index f2594f9..0000000
--- a/tests/code/Network.cpp
+++ /dev/null
@@ -1,156 +0,0 @@
-#define BOOST_TEST_MODULE Network
-#include <boost/test/unit_test.hpp>
-
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/assoc.h>
-
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/Network.h"
-
-BOOST_AUTO_TEST_CASE(DefaultConstructor)
-{
- dcmtkpp::Network network;
-
- BOOST_CHECK_EQUAL(network.get_role(), NET_REQUESTOR);
- BOOST_CHECK_EQUAL(network.get_port(), 0);
- BOOST_CHECK_EQUAL(network.get_timeout(), 30);
- BOOST_CHECK_EQUAL(network.get_options(), 0);
-
- BOOST_CHECK(!network.is_initialized());
- BOOST_CHECK_EQUAL(network.get_network(), static_cast<T_ASC_Network*>(NULL));
-}
-
-BOOST_AUTO_TEST_CASE(Constructor)
-{
- dcmtkpp::Network network(NET_ACCEPTOR, 11112, 10, 1);
-
- BOOST_CHECK_EQUAL(network.get_role(), NET_ACCEPTOR);
- BOOST_CHECK_EQUAL(network.get_port(), 11112);
- BOOST_CHECK_EQUAL(network.get_timeout(), 10);
- BOOST_CHECK_EQUAL(network.get_options(), 1);
-
- BOOST_CHECK(!network.is_initialized());
- BOOST_CHECK_EQUAL(network.get_network(), static_cast<T_ASC_Network*>(NULL));
-}
-
-BOOST_AUTO_TEST_CASE(CopyConstructor)
-{
- dcmtkpp::Network network(NET_ACCEPTOR, 11112, 10, 1);
- dcmtkpp::Network other(network);
-
- BOOST_CHECK_EQUAL(other.get_role(), network.get_role());
- BOOST_CHECK_EQUAL(other.get_port(), network.get_port());
- BOOST_CHECK_EQUAL(other.get_timeout(), network.get_timeout());
- BOOST_CHECK_EQUAL(other.get_options(), network.get_options());
- BOOST_CHECK_EQUAL(other.is_initialized(), network.is_initialized());
- BOOST_CHECK_EQUAL(other.get_network(), network.get_network());
-}
-
-BOOST_AUTO_TEST_CASE(Assignment)
-{
- dcmtkpp::Network network(NET_ACCEPTOR, 11112, 10, 1);
- dcmtkpp::Network other(NET_REQUESTOR, 112, 30, 0);
- other = network;
-
- BOOST_CHECK_EQUAL(other.get_role(), network.get_role());
- BOOST_CHECK_EQUAL(other.get_port(), network.get_port());
- BOOST_CHECK_EQUAL(other.get_timeout(), network.get_timeout());
- BOOST_CHECK_EQUAL(other.get_options(), network.get_options());
- BOOST_CHECK_EQUAL(other.is_initialized(), network.is_initialized());
- BOOST_CHECK_EQUAL(other.get_network(), network.get_network());
-}
-
-BOOST_AUTO_TEST_CASE(Role)
-{
- dcmtkpp::Network network;
- network.set_role(NET_ACCEPTOR);
-
- BOOST_CHECK_EQUAL(network.get_role(), NET_ACCEPTOR);
-}
-
-BOOST_AUTO_TEST_CASE(RoleAssociated)
-{
- dcmtkpp::Network network;
- network.initialize();
-
- BOOST_CHECK_THROW(network.set_role(NET_ACCEPTOR), dcmtkpp::Exception);
-}
-
-BOOST_AUTO_TEST_CASE(Port)
-{
- dcmtkpp::Network network;
- network.set_port(1234);
-
- BOOST_CHECK_EQUAL(network.get_port(), 1234);
-}
-
-BOOST_AUTO_TEST_CASE(PortAssociated)
-{
- dcmtkpp::Network network;
- network.initialize();
-
- BOOST_CHECK_THROW(network.set_port(1234), dcmtkpp::Exception);
-}
-
-BOOST_AUTO_TEST_CASE(Timeout)
-{
- dcmtkpp::Network network;
- network.set_timeout(10);
-
- BOOST_CHECK_EQUAL(network.get_timeout(), 10);
-}
-
-BOOST_AUTO_TEST_CASE(TimeoutAssociated)
-{
- dcmtkpp::Network network;
- network.initialize();
-
- BOOST_CHECK_THROW(network.set_timeout(10), dcmtkpp::Exception);
-}
-
-BOOST_AUTO_TEST_CASE(Options)
-{
- dcmtkpp::Network network;
- network.set_options(1);
-
- BOOST_CHECK_EQUAL(network.get_options(), 1);
-}
-
-BOOST_AUTO_TEST_CASE(OptionsAssociated)
-{
- dcmtkpp::Network network;
- network.initialize();
-
- BOOST_CHECK_THROW(network.set_options(1), dcmtkpp::Exception);
-}
-
-BOOST_AUTO_TEST_CASE(Initialize)
-{
- dcmtkpp::Network network(NET_REQUESTOR, 11112, 1, 0);
- network.initialize();
-
- BOOST_CHECK(network.is_initialized());
- BOOST_CHECK_NE(network.get_network(), static_cast<T_ASC_Network*>(NULL));
-}
-
-BOOST_AUTO_TEST_CASE(Drop)
-{
- dcmtkpp::Network network(NET_REQUESTOR, 11112, 1, 0);
- network.initialize();
- network.drop();
- BOOST_CHECK(!network.is_initialized());
- BOOST_CHECK_EQUAL(network.get_network(), static_cast<T_ASC_Network*>(NULL));
-}
-
-BOOST_AUTO_TEST_CASE(InitializeTwice)
-{
- dcmtkpp::Network network(NET_REQUESTOR, 11112, 1, 0);
- network.initialize();
- BOOST_REQUIRE_THROW(network.initialize(), dcmtkpp::Exception);
-}
-
-BOOST_AUTO_TEST_CASE(DropNotInitialized)
-{
- dcmtkpp::Network network;
- BOOST_REQUIRE_THROW(network.drop(), dcmtkpp::Exception);
-}
diff --git a/tests/code/Reader.cpp b/tests/code/Reader.cpp
index ebc71e6..2999031 100644
--- a/tests/code/Reader.cpp
+++ b/tests/code/Reader.cpp
@@ -1,7 +1,6 @@
#define BOOST_TEST_MODULE Reader
#include <boost/test/unit_test.hpp>
-#include <endian.h>
#include <sstream>
#include <tuple>
@@ -9,26 +8,28 @@
#include <dcmtk/dcmdata/dctk.h>
#include <dcmtk/dcmdata/dcostrmb.h>
-#include "dcmtkpp/conversion.h"
-#include "dcmtkpp/Element.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Reader.h"
-#include "dcmtkpp/VR.h"
+#include "odil/endian.h"
+#include "odil/Element.h"
+#include "odil/registry.h"
+#include "odil/Reader.h"
+#include "odil/VR.h"
+#include "odil/dcmtk/conversion.h"
BOOST_AUTO_TEST_CASE(Constructor)
{
std::istringstream stream;
- dcmtkpp::Reader const reader(stream, dcmtkpp::registry::ExplicitVRBigEndian_Retired);
- BOOST_REQUIRE(reader.byte_ordering == BIG_ENDIAN);
+ odil::Reader const reader(stream, odil::registry::ExplicitVRBigEndian_Retired);
+ BOOST_REQUIRE(reader.byte_ordering == odil::ByteOrdering::BigEndian);
BOOST_REQUIRE(reader.explicit_vr == true);
}
void do_test(
- dcmtkpp::DataSet const & dcmtkpp_data_set, std::string transfer_syntax,
+ odil::DataSet const & odil_data_set, std::string transfer_syntax,
E_EncodingType item_encoding, E_GrpLenEncoding group_length_encoding)
{
// Write input data set
- auto const dcmtk_data_set = dynamic_cast<DcmDataset*>(dcmtkpp::convert(dcmtkpp_data_set));
+ auto const dcmtk_data_set =
+ dynamic_cast<DcmDataset*>(odil::dcmtk::convert(odil_data_set));
std::string data(1000000, '\0');
DcmOutputBufferStream dcmtk_stream(&data[0], data.size());
dcmtk_data_set->transferInit();
@@ -43,19 +44,19 @@ void do_test(
std::istringstream stream(data);
// Read output data set
- dcmtkpp::Reader reader(stream, transfer_syntax);
- auto const other_dcmtkpp_data_set = reader.read_data_set();
+ odil::Reader reader(stream, transfer_syntax);
+ auto const other_odil_data_set = reader.read_data_set();
- BOOST_REQUIRE(other_dcmtkpp_data_set == dcmtkpp_data_set);
+ BOOST_REQUIRE(other_odil_data_set == odil_data_set);
}
-void do_test(dcmtkpp::DataSet const & dcmtkpp_data_set)
+void do_test(odil::DataSet const & odil_data_set)
{
std::vector<std::string> const transfer_syntaxes =
{
- dcmtkpp::registry::ImplicitVRLittleEndian,
- dcmtkpp::registry::ExplicitVRLittleEndian,
- dcmtkpp::registry::ExplicitVRBigEndian_Retired
+ odil::registry::ImplicitVRLittleEndian,
+ odil::registry::ExplicitVRLittleEndian,
+ odil::registry::ExplicitVRBigEndian_Retired
};
std::vector<E_EncodingType> item_encodings =
{ EET_ExplicitLength, EET_UndefinedLength };
@@ -69,7 +70,7 @@ void do_test(dcmtkpp::DataSet const & dcmtkpp_data_set)
for(auto const & use_group_length: use_group_length_values)
{
do_test(
- dcmtkpp_data_set, transfer_syntax,
+ odil_data_set, transfer_syntax,
item_encoding, use_group_length);
}
}
@@ -78,171 +79,172 @@ void do_test(dcmtkpp::DataSet const & dcmtkpp_data_set)
BOOST_AUTO_TEST_CASE(AT)
{
- dcmtkpp::Element dcmtkpp_element({"12345678", "9abcdef0"}, dcmtkpp::VR::AT);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::SelectorATValue, dcmtkpp_element);
+ odil::Element odil_element({"12345678", "9abcdef0"}, odil::VR::AT);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::SelectorATValue, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(CS)
{
- dcmtkpp::Element dcmtkpp_element({"ABC", "DEF"}, dcmtkpp::VR::CS);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::SelectorCSValue, dcmtkpp_element);
+ odil::Element odil_element({"ABC", "DEF"}, odil::VR::CS);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::SelectorCSValue, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(DS)
{
- dcmtkpp::Element dcmtkpp_element({1.23, -4.56}, dcmtkpp::VR::DS);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::SelectorDSValue, dcmtkpp_element);
+ odil::Element odil_element({1.23, -4.56}, odil::VR::DS);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::SelectorDSValue, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(FD)
{
- dcmtkpp::Element dcmtkpp_element({1.23, -4.56}, dcmtkpp::VR::FD);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::SelectorFDValue, dcmtkpp_element);
+ odil::Element odil_element({1.23, -4.56}, odil::VR::FD);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::SelectorFDValue, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(FL)
{
- dcmtkpp::Element dcmtkpp_element({0.5, -0.125}, dcmtkpp::VR::FL);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::SelectorFLValue, dcmtkpp_element);
+ odil::Element odil_element({0.5, -0.125}, odil::VR::FL);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::SelectorFLValue, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(IS)
{
- dcmtkpp::Element dcmtkpp_element({123, -456}, dcmtkpp::VR::IS);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::SelectorISValue, dcmtkpp_element);
+ odil::Element odil_element({123, -456}, odil::VR::IS);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::SelectorISValue, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(OB)
{
- dcmtkpp::Element dcmtkpp_element(
+ odil::Element odil_element(
std::vector<uint8_t>({0x01, 0x02, 0x03, 0x04}),
- dcmtkpp::VR::OB);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::EncapsulatedDocument, dcmtkpp_element);
+ odil::VR::OB);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::EncapsulatedDocument, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(OF)
{
- dcmtkpp::Element dcmtkpp_element(
+ odil::Element odil_element(
std::vector<uint8_t>({0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}),
- dcmtkpp::VR::OF);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::VectorGridData, dcmtkpp_element);
+ odil::VR::OF);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::VectorGridData, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(OW)
{
- dcmtkpp::Element dcmtkpp_element(
+ odil::Element odil_element(
std::vector<uint8_t>({0x01, 0x02, 0x03, 0x04}),
- dcmtkpp::VR::OW);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::RedPaletteColorLookupTableData, dcmtkpp_element);
+ odil::VR::OW);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::RedPaletteColorLookupTableData, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(SL)
{
- dcmtkpp::Element dcmtkpp_element({12345678, -8765432}, dcmtkpp::VR::SL);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::SelectorSLValue, dcmtkpp_element);
+ odil::Element odil_element({12345678, -8765432}, odil::VR::SL);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::SelectorSLValue, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(SQ)
{
- dcmtkpp::DataSet item1;
+ odil::DataSet item1;
item1.add(
- dcmtkpp::registry::SelectorSLValue,
- dcmtkpp::Element({12345678, -8765432}, dcmtkpp::VR::SL));
- dcmtkpp::DataSet item2;
+ odil::registry::SelectorSLValue,
+ odil::Element({12345678, -8765432}, odil::VR::SL));
+ odil::DataSet item2;
item2.add(
- dcmtkpp::registry::SelectorFDValue,
- dcmtkpp::Element({1.23, -4.56}, dcmtkpp::VR::FD));
+ odil::registry::SelectorFDValue,
+ odil::Element({1.23, -4.56}, odil::VR::FD));
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::FrameExtractionSequence,
- dcmtkpp::Element({item1, item2}, dcmtkpp::VR::SQ));
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::FrameExtractionSequence,
+ odil::Element({item1, item2}, odil::VR::SQ));
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(SS)
{
- dcmtkpp::Element dcmtkpp_element({1234, -5678}, dcmtkpp::VR::SS);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::SelectorSSValue, dcmtkpp_element);
+ odil::Element odil_element({1234, -5678}, odil::VR::SS);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::SelectorSSValue, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(UI)
{
- dcmtkpp::Element dcmtkpp_element({"1.2", "3.4"}, dcmtkpp::VR::UI);
- dcmtkpp::DataSet dcmtkpp_data_set;
+ odil::Element odil_element({"1.2", "3.4"}, odil::VR::UI);
+ odil::DataSet odil_data_set;
// SelectorUIValue is not in current DCMTK
- dcmtkpp_data_set.add(dcmtkpp::registry::SOPInstanceUID, dcmtkpp_element);
+ odil_data_set.add(odil::registry::SOPInstanceUID, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(UL)
{
- dcmtkpp::Element dcmtkpp_element({12345678, 8765432}, dcmtkpp::VR::UL);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::SelectorULValue, dcmtkpp_element);
+ odil::Element odil_element({12345678, 8765432}, odil::VR::UL);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::SelectorULValue, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(US)
{
- dcmtkpp::Element dcmtkpp_element({1234, 5678}, dcmtkpp::VR::US);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::SelectorUSValue, dcmtkpp_element);
+ odil::Element odil_element({1234, 5678}, odil::VR::US);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::SelectorUSValue, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
void do_file_test(
- dcmtkpp::DataSet const & dcmtkpp_data_set, std::string transfer_syntax,
+ odil::DataSet const & odil_data_set, std::string transfer_syntax,
E_EncodingType item_encoding, E_GrpLenEncoding group_length_encoding)
{
// Write input data set
- auto const dcmtk_data_set = dynamic_cast<DcmDataset*>(dcmtkpp::convert(dcmtkpp_data_set));
+ auto const dcmtk_data_set =
+ dynamic_cast<DcmDataset*>(odil::dcmtk::convert(odil_data_set));
DcmFileFormat file_format(dcmtk_data_set);
file_format.getMetaInfo()->putAndInsertString(
DCM_TransferSyntaxUID, transfer_syntax.c_str());
file_format.getMetaInfo()->putAndInsertString(
DCM_MediaStorageSOPClassUID,
- dcmtkpp_data_set.as_string(dcmtkpp::registry::SOPClassUID)[0].c_str());
+ odil_data_set.as_string(odil::registry::SOPClassUID)[0].c_str());
file_format.getMetaInfo()->putAndInsertString(
DCM_MediaStorageSOPInstanceUID,
- dcmtkpp_data_set.as_string(dcmtkpp::registry::SOPInstanceUID)[0].c_str());
+ odil_data_set.as_string(odil::registry::SOPInstanceUID)[0].c_str());
std::string data(1000000, '\0');
DcmOutputBufferStream dcmtk_stream(&data[0], data.size());
@@ -259,29 +261,29 @@ void do_file_test(
std::istringstream stream(data);
// Read output data set
- dcmtkpp::DataSet meta_information, other_dcmtkpp_data_set;
- std::tie(meta_information, other_dcmtkpp_data_set) = dcmtkpp::Reader::read_file(stream);
+ odil::DataSet meta_information, other_odil_data_set;
+ std::tie(meta_information, other_odil_data_set) = odil::Reader::read_file(stream);
- BOOST_REQUIRE(other_dcmtkpp_data_set == dcmtkpp_data_set);
+ BOOST_REQUIRE(other_odil_data_set == odil_data_set);
BOOST_REQUIRE(
- meta_information.as_string(dcmtkpp::registry::TransferSyntaxUID) ==
- dcmtkpp::Value::Strings({transfer_syntax}));
+ meta_information.as_string(odil::registry::TransferSyntaxUID) ==
+ odil::Value::Strings({transfer_syntax}));
BOOST_REQUIRE(
- meta_information.as_string(dcmtkpp::registry::MediaStorageSOPClassUID) ==
- other_dcmtkpp_data_set.as_string(dcmtkpp::registry::SOPClassUID));
+ meta_information.as_string(odil::registry::MediaStorageSOPClassUID) ==
+ other_odil_data_set.as_string(odil::registry::SOPClassUID));
BOOST_REQUIRE(
- meta_information.as_string(dcmtkpp::registry::MediaStorageSOPInstanceUID) ==
- other_dcmtkpp_data_set.as_string(dcmtkpp::registry::SOPInstanceUID));
+ meta_information.as_string(odil::registry::MediaStorageSOPInstanceUID) ==
+ other_odil_data_set.as_string(odil::registry::SOPInstanceUID));
}
-void do_file_test(dcmtkpp::DataSet const & dcmtkpp_data_set)
+void do_file_test(odil::DataSet const & odil_data_set)
{
std::vector<std::string> const transfer_syntaxes =
{
- dcmtkpp::registry::ImplicitVRLittleEndian,
- dcmtkpp::registry::ExplicitVRLittleEndian,
- dcmtkpp::registry::ExplicitVRBigEndian_Retired
+ odil::registry::ImplicitVRLittleEndian,
+ odil::registry::ExplicitVRLittleEndian,
+ odil::registry::ExplicitVRBigEndian_Retired
};
std::vector<E_EncodingType> item_encodings =
{ EET_ExplicitLength, EET_UndefinedLength };
@@ -295,7 +297,7 @@ void do_file_test(dcmtkpp::DataSet const & dcmtkpp_data_set)
for(auto const & use_group_length: use_group_length_values)
{
do_file_test(
- dcmtkpp_data_set, transfer_syntax,
+ odil_data_set, transfer_syntax,
item_encoding, use_group_length);
}
}
@@ -304,25 +306,25 @@ void do_file_test(dcmtkpp::DataSet const & dcmtkpp_data_set)
BOOST_AUTO_TEST_CASE(File)
{
- dcmtkpp::DataSet item1;
+ odil::DataSet item1;
item1.add(
- dcmtkpp::registry::SelectorSLValue,
- dcmtkpp::Element({12345678, -8765432}, dcmtkpp::VR::SL));
- dcmtkpp::DataSet item2;
+ odil::registry::SelectorSLValue,
+ odil::Element({12345678, -8765432}, odil::VR::SL));
+ odil::DataSet item2;
item2.add(
- dcmtkpp::registry::SelectorFDValue,
- dcmtkpp::Element({1.23, -4.56}, dcmtkpp::VR::FD));
-
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(
- dcmtkpp::registry::SOPClassUID,
- {dcmtkpp::registry::RawDataStorage}, dcmtkpp::VR::UI);
- dcmtkpp_data_set.add(
- dcmtkpp::registry::SOPInstanceUID,
- {"1.2.3.4"}, dcmtkpp::VR::UI);
- dcmtkpp_data_set.add(
- dcmtkpp::registry::FrameExtractionSequence,
- dcmtkpp::Element({item1, item2}, dcmtkpp::VR::SQ));
-
- do_file_test(dcmtkpp_data_set);
+ odil::registry::SelectorFDValue,
+ odil::Element({1.23, -4.56}, odil::VR::FD));
+
+ odil::DataSet odil_data_set;
+ odil_data_set.add(
+ odil::registry::SOPClassUID,
+ {odil::registry::RawDataStorage}, odil::VR::UI);
+ odil_data_set.add(
+ odil::registry::SOPInstanceUID,
+ {"1.2.3.4"}, odil::VR::UI);
+ odil_data_set.add(
+ odil::registry::FrameExtractionSequence,
+ odil::Element({item1, item2}, odil::VR::SQ));
+
+ do_file_test(odil_data_set);
}
diff --git a/tests/code/Response.cpp b/tests/code/Response.cpp
deleted file mode 100644
index 85070a2..0000000
--- a/tests/code/Response.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#define BOOST_TEST_MODULE Response
-#include <boost/test/unit_test.hpp>
-
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/dimse.h>
-
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Message.h"
-#include "dcmtkpp/Response.h"
-
-BOOST_AUTO_TEST_CASE(Constructor)
-{
- dcmtkpp::Response const message(1234, STATUS_Pending);
-
- BOOST_CHECK_EQUAL(message.get_message_id_being_responded_to(), 1234);
- BOOST_CHECK_EQUAL(message.get_status(), STATUS_Pending);
-}
-
-BOOST_AUTO_TEST_CASE(MessageConstructor)
-{
- dcmtkpp::DataSet command_set;
- command_set.add("MessageIDBeingRespondedTo", {1234});
- command_set.add("Status", {STATUS_Pending});
- dcmtkpp::Message const generic_message(command_set);
-
- dcmtkpp::Response const message(generic_message);
-
- BOOST_CHECK_EQUAL(message.get_message_id_being_responded_to(), 1234);
- BOOST_CHECK_EQUAL(message.get_status(), STATUS_Pending);
- BOOST_CHECK(!message.has_data_set());
-}
diff --git a/tests/code/SCPDispatcher.cpp b/tests/code/SCPDispatcher.cpp
new file mode 100644
index 0000000..f1d746c
--- /dev/null
+++ b/tests/code/SCPDispatcher.cpp
@@ -0,0 +1,132 @@
+#define BOOST_TEST_MODULE SCPDispatcher
+#include <boost/test/unit_test.hpp>
+
+#include <chrono>
+#include <memory>
+#include <thread>
+
+#include "odil/Association.h"
+#include "odil/EchoSCP.h"
+#include "odil/Exception.h"
+#include "odil/SCPDispatcher.h"
+#include "odil/message/Message.h"
+#include "odil/message/Response.h"
+
+struct Status
+{
+ int client;
+ std::string server;
+ bool called;
+};
+
+void run_server(Status * status, bool with_echo)
+{
+ odil::Association association;
+ association.set_tcp_timeout(boost::posix_time::seconds(1));
+
+ try
+ {
+ association.receive_association(boost::asio::ip::tcp::v4(), 11113);
+
+ auto echo_scp = std::make_shared<odil::EchoSCP>(association,
+ [&status](odil::message::CEchoRequest const &)
+ {
+ status->called = true;
+ return odil::message::Response::Success;
+ });
+
+ odil::SCPDispatcher dispatcher(association);
+
+ if(with_echo)
+ {
+ dispatcher.set_scp(
+ odil::message::Message::Command::C_ECHO_RQ, echo_scp);
+ }
+
+ dispatcher.dispatch();
+
+ // Should throw with peer closing connection
+ association.receive_message();
+ }
+ catch(odil::AssociationAborted const &)
+ {
+ status->server = "abort";
+ }
+ catch(odil::AssociationReleased const &)
+ {
+ status->server = "release";
+ }
+ catch(odil::Exception const & e)
+ {
+ status->server = e.what();
+ }
+ catch(...)
+ {
+ status->server = "Other exception";
+ }
+}
+
+void run_echo_client(Status * status)
+{
+ std::string command = "echoscu";
+ command += " 127.0.0.1 11113";
+ status->client = system(command.c_str());
+}
+
+BOOST_AUTO_TEST_CASE(Empty)
+{
+ odil::Association association;
+ odil::SCPDispatcher dispatcher(association);
+ BOOST_REQUIRE_EQUAL(
+ dispatcher.has_scp(odil::message::Message::Command::C_ECHO_RQ),
+ false);
+ BOOST_REQUIRE_THROW(
+ dispatcher.get_scp(odil::message::Message::Command::C_ECHO_RQ),
+ odil::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(SCP)
+{
+ odil::Association association;
+ odil::SCPDispatcher dispatcher(association);
+
+ auto scp = std::make_shared<odil::EchoSCP>(association);
+ dispatcher.set_scp(
+ odil::message::Message::Command::C_ECHO_RQ,
+ scp);
+
+ BOOST_REQUIRE_NO_THROW(
+ dispatcher.get_scp(odil::message::Message::Command::C_ECHO_RQ));
+}
+
+BOOST_AUTO_TEST_CASE(DispatchWithEcho)
+{
+ Status status = { -1, "", false };
+
+ std::thread server(run_server, &status, true);
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ std::thread client(run_echo_client, &status);
+
+ server.join();
+ client.join();
+
+ BOOST_REQUIRE_EQUAL(status.client, 0);
+ BOOST_REQUIRE_EQUAL(status.server, "release");
+ BOOST_REQUIRE_EQUAL(status.called, true);
+}
+
+BOOST_AUTO_TEST_CASE(DispatchWithoutEcho)
+{
+ Status status = { -1, "", false };
+
+ std::thread server(run_server, &status, false);
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ std::thread client(run_echo_client, &status);
+
+ server.join();
+ client.join();
+
+ BOOST_REQUIRE_EQUAL(status.client, 0);
+ BOOST_REQUIRE_EQUAL(status.server, "No such provider");
+ BOOST_REQUIRE_EQUAL(status.called, false);
+}
diff --git a/tests/code/SCU.cpp b/tests/code/SCU.cpp
index d21df99..090d0ed 100644
--- a/tests/code/SCU.cpp
+++ b/tests/code/SCU.cpp
@@ -1,42 +1,40 @@
#define BOOST_TEST_MODULE SCU
#include <boost/test/unit_test.hpp>
-#include "dcmtkpp/SCU.h"
-#include "dcmtkpp/registry.h"
+#include "odil/SCU.h"
+#include "odil/registry.h"
#include "../PeerFixtureBase.h"
struct Fixture: public PeerFixtureBase
{
Fixture()
- : PeerFixtureBase(NET_REQUESTOR, 104, 10,
+ : PeerFixtureBase({
{
- { dcmtkpp::registry::VerificationSOPClass,
- {dcmtkpp::registry::ImplicitVRLittleEndian}
- }
- })
+ 1, odil::registry::VerificationSOPClass,
+ {odil::registry::ImplicitVRLittleEndian}, true, false
+ }
+ })
{
// Nothing else
}
};
-BOOST_AUTO_TEST_CASE(DefaultConstructor)
+BOOST_FIXTURE_TEST_CASE(DefaultConstructor, Fixture)
{
- dcmtkpp::SCU const scu;
+ odil::SCU const scu(this->association);
BOOST_CHECK_EQUAL(scu.get_affected_sop_class(), "");
}
-BOOST_AUTO_TEST_CASE(AffectedSOPClassUID)
+BOOST_FIXTURE_TEST_CASE(AffectedSOPClassUID, Fixture)
{
- dcmtkpp::SCU scu;
+ odil::SCU scu(this->association);
scu.set_affected_sop_class("1.2.3");
BOOST_CHECK_EQUAL(scu.get_affected_sop_class(), "1.2.3");
}
BOOST_FIXTURE_TEST_CASE(Echo, Fixture)
{
- dcmtkpp::SCU scu;
- scu.set_network(&this->network);
- scu.set_association(&this->association);
+ odil::SCU scu(this->association);
scu.echo();
}
diff --git a/tests/code/ServiceRole.cpp b/tests/code/ServiceRole.cpp
deleted file mode 100644
index 609e244..0000000
--- a/tests/code/ServiceRole.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-#define BOOST_TEST_MODULE ServiceRole
-#include <boost/test/unit_test.hpp>
-
-#include "dcmtkpp/Association.h"
-#include "dcmtkpp/Network.h"
-#include "dcmtkpp/ServiceRole.h"
-
-BOOST_AUTO_TEST_CASE(DefaultConstructor)
-{
- dcmtkpp::ServiceRole const role;
- BOOST_CHECK_EQUAL(
- role.get_network(), static_cast<dcmtkpp::Network *>(NULL));
- BOOST_CHECK_EQUAL(
- role.get_association(), static_cast<dcmtkpp::Association *>(NULL));
-}
-
-BOOST_AUTO_TEST_CASE(Network)
-{
- dcmtkpp::ServiceRole role;
-
- dcmtkpp::Network network;
- role.set_network(&network);
-
- BOOST_CHECK_EQUAL(role.get_network(), &network);
-}
-
-BOOST_AUTO_TEST_CASE(Association)
-{
- dcmtkpp::ServiceRole role;
-
- dcmtkpp::Association association;
- role.set_association(&association);
-
- BOOST_CHECK_EQUAL(role.get_association(), &association);
-}
-
-BOOST_AUTO_TEST_CASE(CopyConstructor)
-{
- dcmtkpp::Network network;
- dcmtkpp::Association association;
- dcmtkpp::ServiceRole role;
- role.set_network(&network);
- role.set_association(&association);
-
- dcmtkpp::ServiceRole const other(role);
- BOOST_CHECK_EQUAL(other.get_network(), role.get_network());
- BOOST_CHECK_EQUAL(other.get_association(), role.get_association());
-}
-
-BOOST_AUTO_TEST_CASE(Assignement)
-{
- dcmtkpp::Network network;
- dcmtkpp::Association association;
- dcmtkpp::ServiceRole role;
- role.set_network(&network);
- role.set_association(&association);
-
- dcmtkpp::ServiceRole other;
- other = role;
- BOOST_CHECK_EQUAL(other.get_network(), role.get_network());
- BOOST_CHECK_EQUAL(other.get_association(), role.get_association());
-}
diff --git a/tests/code/StoreSCU.cpp b/tests/code/StoreSCU.cpp
index d1465ee..f4f6c86 100644
--- a/tests/code/StoreSCU.cpp
+++ b/tests/code/StoreSCU.cpp
@@ -1,80 +1,66 @@
#define BOOST_TEST_MODULE StoreSCU
#include <boost/test/unit_test.hpp>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/StoreSCU.h"
+#include "odil/DataSet.h"
+#include "odil/Exception.h"
+#include "odil/registry.h"
+#include "odil/StoreSCU.h"
#include "../PeerFixtureBase.h"
struct Fixture: public PeerFixtureBase
{
- static bool called;
-
- dcmtkpp::DataSet dataset;
+ odil::DataSet dataset;
Fixture()
- : PeerFixtureBase(NET_REQUESTOR, 104, 10,
+ : PeerFixtureBase({
{
- { dcmtkpp::registry::RawDataStorage,
- {dcmtkpp::registry::ImplicitVRLittleEndian}
- }
- })
+ 1, odil::registry::RawDataStorage,
+ {odil::registry::ImplicitVRLittleEndian}, true, false
+ }
+ })
{
- Fixture::called = false;
-
this->dataset.add("ImageType", {"ORIGINAL", "PRIMARY", "OTHER"});
this->dataset.add("PatientID", {"1234"});
this->dataset.add("StudyInstanceUID", {"2.25.386726390606491051215227596277040710"});
this->dataset.add("SeriesInstanceUID", {"2.25.235367796740370588607388995952651763168"});
- this->dataset.add("SOPClassUID", {dcmtkpp::registry::RawDataStorage});
+ this->dataset.add("SOPClassUID", {odil::registry::RawDataStorage});
this->dataset.add("SOPInstanceUID", {"2.25.294312554735929033890522327215919068328"});
}
-
- static void callback(void*, unsigned long)
- {
- Fixture::called = true;
- }
};
-bool Fixture::called = false;
-
-BOOST_AUTO_TEST_CASE(AffectedSOPClassUID)
+BOOST_FIXTURE_TEST_CASE(AffectedSOPClassUID, Fixture)
{
- dcmtkpp::DataSet dataset;
- dataset.add("SOPClassUID", {dcmtkpp::registry::RawDataStorage});
+ odil::DataSet dataset;
+ dataset.add("SOPClassUID", {odil::registry::RawDataStorage});
- dcmtkpp::StoreSCU scu;
+ odil::StoreSCU scu(this->association);
scu.set_affected_sop_class(dataset);
- BOOST_CHECK_EQUAL(scu.get_affected_sop_class(), dcmtkpp::registry::RawDataStorage);
+ BOOST_CHECK_EQUAL(scu.get_affected_sop_class(), odil::registry::RawDataStorage);
}
-BOOST_AUTO_TEST_CASE(AffectedSOPClassUIDNoSOPClassUID)
+BOOST_FIXTURE_TEST_CASE(AffectedSOPClassUIDNoSOPClassUID, Fixture)
{
- dcmtkpp::DataSet dataset;
+ odil::DataSet dataset;
- dcmtkpp::StoreSCU scu;
- BOOST_CHECK_THROW(scu.set_affected_sop_class(dataset), dcmtkpp::Exception);
+ odil::StoreSCU scu(this->association);
+ BOOST_CHECK_THROW(scu.set_affected_sop_class(dataset), odil::Exception);
}
-BOOST_AUTO_TEST_CASE(AffectedSOPClassUIDUnknownSOPClassUID)
+BOOST_FIXTURE_TEST_CASE(AffectedSOPClassUIDUnknownSOPClassUID, Fixture)
{
- dcmtkpp::DataSet dataset;
+ odil::DataSet dataset;
dataset.add("SOPClassUID", {"invalid"});
- dcmtkpp::StoreSCU scu;
- BOOST_CHECK_THROW(scu.set_affected_sop_class(dataset), dcmtkpp::Exception);
+ odil::StoreSCU scu(this->association);
+ BOOST_CHECK_THROW(scu.set_affected_sop_class(dataset), odil::Exception);
}
BOOST_FIXTURE_TEST_CASE(Store, Fixture)
{
- dcmtkpp::StoreSCU scu;
- scu.set_network(&this->network);
- scu.set_association(&this->association);
+ odil::StoreSCU scu(this->association);
scu.set_affected_sop_class(this->dataset);
- scu.store(this->dataset, Fixture::callback);
- BOOST_CHECK(this->called);
+ scu.store(this->dataset);
}
diff --git a/tests/code/Tag.cpp b/tests/code/Tag.cpp
index 6c4e4a7..1c35855 100644
--- a/tests/code/Tag.cpp
+++ b/tests/code/Tag.cpp
@@ -1,83 +1,83 @@
#define BOOST_TEST_MODULE Tag
#include <boost/test/unit_test.hpp>
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/Tag.h"
+#include "odil/Exception.h"
+#include "odil/Tag.h"
BOOST_AUTO_TEST_CASE(TwoArgumentsConstructor)
{
- dcmtkpp::Tag const tag(0xdead, 0xbeef);
+ odil::Tag const tag(0xdead, 0xbeef);
BOOST_CHECK_EQUAL(tag.group, 0xdead);
BOOST_CHECK_EQUAL(tag.element, 0xbeef);
}
BOOST_AUTO_TEST_CASE(OneArgumentConstructor)
{
- dcmtkpp::Tag const tag(0xdeadbeef);
+ odil::Tag const tag(0xdeadbeef);
BOOST_CHECK_EQUAL(tag.group, 0xdead);
BOOST_CHECK_EQUAL(tag.element, 0xbeef);
}
BOOST_AUTO_TEST_CASE(StringConstructor)
{
- dcmtkpp::Tag const tag(std::string("PixelData"));
+ odil::Tag const tag(std::string("PixelData"));
BOOST_CHECK_EQUAL(tag.group, 0x7fe0);
BOOST_CHECK_EQUAL(tag.element, 0x0010);
}
BOOST_AUTO_TEST_CASE(CharConstructor)
{
- dcmtkpp::Tag const tag("PixelData");
+ odil::Tag const tag("PixelData");
BOOST_CHECK_EQUAL(tag.group, 0x7fe0);
BOOST_CHECK_EQUAL(tag.element, 0x0010);
}
BOOST_AUTO_TEST_CASE(StringConstructorNumeric)
{
- dcmtkpp::Tag const tag(std::string("7fe00010"));
+ odil::Tag const tag(std::string("7fe00010"));
BOOST_CHECK_EQUAL(tag.group, 0x7fe0);
BOOST_CHECK_EQUAL(tag.element, 0x0010);
}
BOOST_AUTO_TEST_CASE(CharConstructorNumeric)
{
- dcmtkpp::Tag const tag("7fe00010");
+ odil::Tag const tag("7fe00010");
BOOST_CHECK_EQUAL(tag.group, 0x7fe0);
BOOST_CHECK_EQUAL(tag.element, 0x0010);
}
BOOST_AUTO_TEST_CASE(StringConstructorWrong)
{
- BOOST_CHECK_THROW(dcmtkpp::Tag(std::string("Foobar")), dcmtkpp::Exception);
+ BOOST_CHECK_THROW(odil::Tag(std::string("Foobar")), odil::Exception);
}
BOOST_AUTO_TEST_CASE(CharConstructorWrong)
{
- BOOST_CHECK_THROW(dcmtkpp::Tag("Foobar"), dcmtkpp::Exception);
+ BOOST_CHECK_THROW(odil::Tag("Foobar"), odil::Exception);
}
BOOST_AUTO_TEST_CASE(StringConstructorNumericWrong)
{
- BOOST_CHECK_THROW(dcmtkpp::Tag(std::string("XXXXYYYY")), dcmtkpp::Exception);
+ BOOST_CHECK_THROW(odil::Tag(std::string("XXXXYYYY")), odil::Exception);
}
BOOST_AUTO_TEST_CASE(CharConstructorNumericWrong)
{
- BOOST_CHECK_THROW(dcmtkpp::Tag("XXXXYYYY"), dcmtkpp::Exception);
+ BOOST_CHECK_THROW(odil::Tag("XXXXYYYY"), odil::Exception);
}
BOOST_AUTO_TEST_CASE(CopyConstructor)
{
- dcmtkpp::Tag const tag(0xdead, 0xbeef);
- dcmtkpp::Tag const other(tag);
+ odil::Tag const tag(0xdead, 0xbeef);
+ odil::Tag const other(tag);
BOOST_CHECK_EQUAL(other.group, 0xdead);
BOOST_CHECK_EQUAL(other.element, 0xbeef);
}
BOOST_AUTO_TEST_CASE(IsPrivate)
{
- dcmtkpp::Tag const tag1(0xdead, 0xbeef);
- dcmtkpp::Tag const tag2(0x7fe0, 0x0010);
+ odil::Tag const tag1(0xdead, 0xbeef);
+ odil::Tag const tag2(0x7fe0, 0x0010);
BOOST_CHECK(tag1.is_private());
BOOST_CHECK(!tag2.is_private());
@@ -85,22 +85,22 @@ BOOST_AUTO_TEST_CASE(IsPrivate)
BOOST_AUTO_TEST_CASE(Name)
{
- dcmtkpp::Tag const tag(0x7fe0, 0x0010);
+ odil::Tag const tag(0x7fe0, 0x0010);
std::string const name = tag.get_name();
BOOST_CHECK_EQUAL(name, "PixelData");
}
BOOST_AUTO_TEST_CASE(NameWrong)
{
- dcmtkpp::Tag const tag(0xEEEE, 0xEEEE);
- BOOST_CHECK_THROW(tag.get_name(), dcmtkpp::Exception);
+ odil::Tag const tag(0xEEEE, 0xEEEE);
+ BOOST_CHECK_THROW(tag.get_name(), odil::Exception);
}
BOOST_AUTO_TEST_CASE(Equality)
{
- dcmtkpp::Tag const tag1(0xdead, 0xbeef);
- dcmtkpp::Tag const tag2(0xdead, 0xbeef);
- dcmtkpp::Tag const tag3(0xbeef, 0xf00d);
+ odil::Tag const tag1(0xdead, 0xbeef);
+ odil::Tag const tag2(0xdead, 0xbeef);
+ odil::Tag const tag3(0xbeef, 0xf00d);
BOOST_CHECK(tag1 == tag2);
BOOST_CHECK( ! (tag1 == tag3) );
@@ -108,9 +108,9 @@ BOOST_AUTO_TEST_CASE(Equality)
BOOST_AUTO_TEST_CASE(Difference)
{
- dcmtkpp::Tag const tag1(0xdead, 0xbeef);
- dcmtkpp::Tag const tag2(0xdead, 0xbeef);
- dcmtkpp::Tag const tag3(0xbeef, 0xf00d);
+ odil::Tag const tag1(0xdead, 0xbeef);
+ odil::Tag const tag2(0xdead, 0xbeef);
+ odil::Tag const tag3(0xbeef, 0xf00d);
BOOST_CHECK( ! (tag1 != tag2) );
BOOST_CHECK(tag1 != tag3);
@@ -118,9 +118,9 @@ BOOST_AUTO_TEST_CASE(Difference)
BOOST_AUTO_TEST_CASE(Inferior)
{
- dcmtkpp::Tag const tag1(0xdead, 0xbeef);
- dcmtkpp::Tag const tag2(0xdead, 0xf00d);
- dcmtkpp::Tag const tag3(0xbeef, 0xf00d);
+ odil::Tag const tag1(0xdead, 0xbeef);
+ odil::Tag const tag2(0xdead, 0xf00d);
+ odil::Tag const tag3(0xbeef, 0xf00d);
BOOST_CHECK(tag1 < tag2);
BOOST_CHECK(tag3 < tag1);
@@ -131,9 +131,9 @@ BOOST_AUTO_TEST_CASE(Inferior)
BOOST_AUTO_TEST_CASE(Superior)
{
- dcmtkpp::Tag const tag1(0xdead, 0xbeef);
- dcmtkpp::Tag const tag2(0xdead, 0xf00d);
- dcmtkpp::Tag const tag3(0xbeef, 0xf00d);
+ odil::Tag const tag1(0xdead, 0xbeef);
+ odil::Tag const tag2(0xdead, 0xf00d);
+ odil::Tag const tag3(0xbeef, 0xf00d);
BOOST_CHECK(tag2 > tag1);
BOOST_CHECK(tag1 > tag3);
@@ -144,10 +144,10 @@ BOOST_AUTO_TEST_CASE(Superior)
BOOST_AUTO_TEST_CASE(InferiorOrEqual)
{
- dcmtkpp::Tag const tag1(0xdead, 0xbeef);
- dcmtkpp::Tag const tag2(0xdead, 0xf00d);
- dcmtkpp::Tag const tag3(0xbeef, 0xf00d);
- dcmtkpp::Tag const tag4(0xdead, 0xbeef);
+ odil::Tag const tag1(0xdead, 0xbeef);
+ odil::Tag const tag2(0xdead, 0xf00d);
+ odil::Tag const tag3(0xbeef, 0xf00d);
+ odil::Tag const tag4(0xdead, 0xbeef);
BOOST_CHECK(tag1 <= tag2);
BOOST_CHECK(tag3 <= tag1);
@@ -159,10 +159,10 @@ BOOST_AUTO_TEST_CASE(InferiorOrEqual)
BOOST_AUTO_TEST_CASE(SuperiorOrEqual)
{
- dcmtkpp::Tag const tag1(0xdead, 0xbeef);
- dcmtkpp::Tag const tag2(0xdead, 0xf00d);
- dcmtkpp::Tag const tag3(0xbeef, 0xf00d);
- dcmtkpp::Tag const tag4(0xdead, 0xbeef);
+ odil::Tag const tag1(0xdead, 0xbeef);
+ odil::Tag const tag2(0xdead, 0xf00d);
+ odil::Tag const tag3(0xbeef, 0xf00d);
+ odil::Tag const tag4(0xdead, 0xbeef);
BOOST_CHECK(tag2 >= tag1);
BOOST_CHECK(tag1 >= tag3);
@@ -174,7 +174,7 @@ BOOST_AUTO_TEST_CASE(SuperiorOrEqual)
BOOST_AUTO_TEST_CASE(StreamInsertion)
{
- dcmtkpp::Tag const tag(0xdead, 0xbeef);
+ odil::Tag const tag(0xdead, 0xbeef);
std::ostringstream stream;
stream << tag;
BOOST_CHECK_EQUAL(stream.str(), "deadbeef");
@@ -182,6 +182,6 @@ BOOST_AUTO_TEST_CASE(StreamInsertion)
BOOST_AUTO_TEST_CASE(StringConversion)
{
- dcmtkpp::Tag const tag(0xdead, 0xbeef);
+ odil::Tag const tag(0xdead, 0xbeef);
BOOST_CHECK_EQUAL(std::string(tag), "deadbeef");
}
diff --git a/tests/code/VR.cpp b/tests/code/VR.cpp
index f231879..bdd8a63 100644
--- a/tests/code/VR.cpp
+++ b/tests/code/VR.cpp
@@ -1,31 +1,31 @@
#define BOOST_TEST_MODULE VR
#include <boost/test/unit_test.hpp>
-#include "dcmtkpp/Exception.h"
-#include "dcmtkpp/VR.h"
+#include "odil/Exception.h"
+#include "odil/VR.h"
BOOST_AUTO_TEST_CASE(as_string)
{
- dcmtkpp::VR const vr(dcmtkpp::VR::AT);
- std::string const string = dcmtkpp::as_string(vr);
+ odil::VR const vr(odil::VR::AT);
+ std::string const string = odil::as_string(vr);
BOOST_CHECK_EQUAL(string, "AT");
}
BOOST_AUTO_TEST_CASE(as_string_invalid)
{
- dcmtkpp::VR const vr(dcmtkpp::VR::INVALID);
- BOOST_CHECK_THROW(dcmtkpp::as_string(vr), dcmtkpp::Exception);
+ odil::VR const vr(odil::VR::INVALID);
+ BOOST_CHECK_THROW(odil::as_string(vr), odil::Exception);
}
BOOST_AUTO_TEST_CASE(as_vr)
{
std::string const string("AT");
- dcmtkpp::VR const vr = dcmtkpp::as_vr(string);
- BOOST_CHECK(vr == dcmtkpp::VR::AT);
+ odil::VR const vr = odil::as_vr(string);
+ BOOST_CHECK(vr == odil::VR::AT);
}
BOOST_AUTO_TEST_CASE(as_vr_wrong)
{
std::string const string("XX");
- BOOST_CHECK_THROW(dcmtkpp::as_vr(string), dcmtkpp::Exception);
+ BOOST_CHECK_THROW(odil::as_vr(string), odil::Exception);
}
diff --git a/tests/code/VRFinder.cpp b/tests/code/VRFinder.cpp
new file mode 100644
index 0000000..8c78fc7
--- /dev/null
+++ b/tests/code/VRFinder.cpp
@@ -0,0 +1,177 @@
+#define BOOST_TEST_MODULE VRFinder
+#include <boost/test/unit_test.hpp>
+
+#include <string>
+
+#include "odil/DataSet.h"
+#include "odil/Exception.h"
+#include "odil/registry.h"
+#include "odil/Tag.h"
+#include "odil/VR.h"
+#include "odil/VRFinder.h"
+
+odil::VR
+user_defined_finder(
+ odil::Tag const & tag, odil::DataSet const &, std::string const &,
+ bool & called)
+{
+ if(tag == odil::Tag(0xeeee, 0xeeee))
+ {
+ called = true;
+ return odil::VR::UR;
+ }
+ else
+ {
+ throw odil::Exception("Not applicable");
+ }
+}
+
+BOOST_AUTO_TEST_CASE(DefaultConstructor)
+{
+ odil::VRFinder finder;
+ BOOST_REQUIRE_EQUAL(finder.finders.empty(), true);
+}
+
+BOOST_AUTO_TEST_CASE(PublicDictionary)
+{
+ auto const vr = odil::VRFinder::public_dictionary(
+ odil::Tag(0x0010, 0x0010), odil::DataSet(),
+ odil::registry::ImplicitVRLittleEndian);
+ BOOST_REQUIRE(vr == odil::VR::PN);
+}
+
+BOOST_AUTO_TEST_CASE(PublicDictionaryNotApplicable)
+{
+ BOOST_REQUIRE_THROW(
+ odil::VRFinder::public_dictionary(
+ odil::Tag(0x0011, 0x0011), odil::DataSet(),
+ odil::registry::ImplicitVRLittleEndian),
+ odil::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(GroupLength)
+{
+ auto const vr = odil::VRFinder::group_length(
+ odil::Tag(0x0010, 0x0000), odil::DataSet(),
+ odil::registry::ImplicitVRLittleEndian);
+ BOOST_REQUIRE(vr == odil::VR::UL);
+}
+
+BOOST_AUTO_TEST_CASE(GroupLengthNotApplicable)
+{
+ BOOST_REQUIRE_THROW(
+ odil::VRFinder::group_length(
+ odil::Tag(0x0010, 0x0010), odil::DataSet(),
+ odil::registry::ImplicitVRLittleEndian),
+ odil::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(PrivateTag)
+{
+ auto const vr = odil::VRFinder::private_tag(
+ odil::Tag(0x0011, 0x0011), odil::DataSet(),
+ odil::registry::ImplicitVRLittleEndian);
+ BOOST_REQUIRE(vr == odil::VR::UN);
+}
+
+BOOST_AUTO_TEST_CASE(PrivateTagNotApplicable)
+{
+ BOOST_REQUIRE_THROW(
+ odil::VRFinder::private_tag(
+ odil::Tag(0x0010, 0x0010), odil::DataSet(),
+ odil::registry::ImplicitVRLittleEndian),
+ odil::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(ImplictiVRLittleEndian)
+{
+ auto const vr = odil::VRFinder::implicit_vr_little_endian(
+ odil::Tag(0x7fe0, 0x0010), odil::DataSet(),
+ odil::registry::ImplicitVRLittleEndian);
+ BOOST_REQUIRE(vr == odil::VR::OW);
+}
+
+BOOST_AUTO_TEST_CASE(ImplictiVRLittleEndianNotApplicableTag)
+{
+ BOOST_REQUIRE_THROW(
+ odil::VRFinder::implicit_vr_little_endian(
+ odil::Tag(0x0010, 0x0010), odil::DataSet(),
+ odil::registry::ImplicitVRLittleEndian),
+ odil::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(ImplictiVRLittleEndianNotApplicableVR)
+{
+ BOOST_REQUIRE_THROW(
+ odil::VRFinder::implicit_vr_little_endian(
+ odil::Tag(0x7fe0, 0x0010), odil::DataSet(),
+ odil::registry::ExplicitVRLittleEndian),
+ odil::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(PublicElement)
+{
+ odil::VRFinder finder;
+ auto const vr = finder(
+ odil::Tag(0x0010, 0x0010), odil::DataSet(),
+ odil::registry::ImplicitVRLittleEndian);
+ BOOST_REQUIRE(vr == odil::VR::PN);
+}
+
+BOOST_AUTO_TEST_CASE(GroupLengthElement)
+{
+ odil::VRFinder finder;
+ auto const vr = finder(
+ odil::Tag(0x0010, 0x0000), odil::DataSet(),
+ odil::registry::ImplicitVRLittleEndian);
+ BOOST_REQUIRE(vr == odil::VR::UL);
+}
+
+BOOST_AUTO_TEST_CASE(PrivateElement)
+{
+ odil::VRFinder finder;
+ auto const vr = finder(
+ odil::Tag(0x0011, 0x0011), odil::DataSet(),
+ odil::registry::ImplicitVRLittleEndian);
+ BOOST_REQUIRE(vr == odil::VR::UN);
+}
+
+BOOST_AUTO_TEST_CASE(TSDependentElement)
+{
+ odil::VRFinder finder;
+ auto const vr = finder(
+ odil::Tag(0x7fe0, 0x0010), odil::DataSet(),
+ odil::registry::ImplicitVRLittleEndian);
+ BOOST_REQUIRE(vr == odil::VR::OW);
+}
+
+BOOST_AUTO_TEST_CASE(UserFinder)
+{
+ bool called = false;
+ auto const my_finder_function =
+ [&called]
+ (
+ odil::Tag const & tag, odil::DataSet const & data_set,
+ std::string const & ts
+ )
+ {
+ return user_defined_finder(tag, data_set, ts, called);
+ };
+ odil::VRFinder finder;
+ finder.finders.push_back(my_finder_function);
+ finder(
+ odil::Tag(0xeeee, 0xeeee), odil::DataSet(),
+ odil::registry::ImplicitVRLittleEndian
+ );
+ BOOST_REQUIRE_EQUAL(called, true);
+}
+
+BOOST_AUTO_TEST_CASE(NoVRFound)
+{
+ odil::VRFinder finder;
+ BOOST_REQUIRE_THROW(
+ finder(
+ odil::Tag(0xcccc, 0xcccc), odil::DataSet(),
+ odil::registry::ImplicitVRLittleEndian
+ ), odil::Exception);
+}
diff --git a/tests/code/Value.cpp b/tests/code/Value.cpp
index d62017a..3365305 100644
--- a/tests/code/Value.cpp
+++ b/tests/code/Value.cpp
@@ -1,128 +1,128 @@
#define BOOST_TEST_MODULE Value
#include <boost/test/unit_test.hpp>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Element.h"
-#include "dcmtkpp/Exception.h"
+#include "odil/DataSet.h"
+#include "odil/Element.h"
+#include "odil/Exception.h"
BOOST_AUTO_TEST_CASE(Empty)
{
- dcmtkpp::Value const value;
+ odil::Value const value;
- BOOST_CHECK(value.get_type() == dcmtkpp::Value::Type::Empty);
- BOOST_CHECK_THROW(value.as_integers(), dcmtkpp::Exception);
- BOOST_CHECK_THROW(value.as_reals(), dcmtkpp::Exception);
- BOOST_CHECK_THROW(value.as_strings(), dcmtkpp::Exception);
- BOOST_CHECK_THROW(value.as_data_sets(), dcmtkpp::Exception);
+ BOOST_CHECK(value.get_type() == odil::Value::Type::Empty);
+ BOOST_CHECK_THROW(value.as_integers(), odil::Exception);
+ BOOST_CHECK_THROW(value.as_reals(), odil::Exception);
+ BOOST_CHECK_THROW(value.as_strings(), odil::Exception);
+ BOOST_CHECK_THROW(value.as_data_sets(), odil::Exception);
}
BOOST_AUTO_TEST_CASE(Integers)
{
- dcmtkpp::Value const value({1234});
+ odil::Value const value({1234});
- BOOST_CHECK(value.get_type() == dcmtkpp::Value::Type::Integers);
- BOOST_CHECK(value.as_integers() == dcmtkpp::Value::Integers({1234}));
+ BOOST_CHECK(value.get_type() == odil::Value::Type::Integers);
+ BOOST_CHECK(value.as_integers() == odil::Value::Integers({1234}));
- BOOST_CHECK_THROW(value.as_reals(), dcmtkpp::Exception);
- BOOST_CHECK_THROW(value.as_strings(), dcmtkpp::Exception);
- BOOST_CHECK_THROW(value.as_data_sets(), dcmtkpp::Exception);
+ BOOST_CHECK_THROW(value.as_reals(), odil::Exception);
+ BOOST_CHECK_THROW(value.as_strings(), odil::Exception);
+ BOOST_CHECK_THROW(value.as_data_sets(), odil::Exception);
}
BOOST_AUTO_TEST_CASE(ModifyIntegers)
{
- dcmtkpp::Value value({1234});
+ odil::Value value({1234});
value.as_integers().push_back(5678);
- BOOST_CHECK(value.as_integers() == dcmtkpp::Value::Integers({1234, 5678}));
+ BOOST_CHECK(value.as_integers() == odil::Value::Integers({1234, 5678}));
}
BOOST_AUTO_TEST_CASE(Reals)
{
- dcmtkpp::Value const value({12.34});
+ odil::Value const value({12.34});
- BOOST_CHECK(value.get_type() == dcmtkpp::Value::Type::Reals);
- BOOST_CHECK(value.as_reals() == dcmtkpp::Value::Reals({12.34}));
+ BOOST_CHECK(value.get_type() == odil::Value::Type::Reals);
+ BOOST_CHECK(value.as_reals() == odil::Value::Reals({12.34}));
- BOOST_CHECK_THROW(value.as_integers(), dcmtkpp::Exception);
- BOOST_CHECK_THROW(value.as_strings(), dcmtkpp::Exception);
- BOOST_CHECK_THROW(value.as_data_sets(), dcmtkpp::Exception);
+ BOOST_CHECK_THROW(value.as_integers(), odil::Exception);
+ BOOST_CHECK_THROW(value.as_strings(), odil::Exception);
+ BOOST_CHECK_THROW(value.as_data_sets(), odil::Exception);
}
BOOST_AUTO_TEST_CASE(ModifyReals)
{
- dcmtkpp::Value value({12.34});
+ odil::Value value({12.34});
value.as_reals().push_back(56.78);
- BOOST_CHECK(value.as_reals() == dcmtkpp::Value::Reals({12.34, 56.78}));
+ BOOST_CHECK(value.as_reals() == odil::Value::Reals({12.34, 56.78}));
}
BOOST_AUTO_TEST_CASE(Strings)
{
- dcmtkpp::Value const value({"foo"});
+ odil::Value const value({"foo"});
- BOOST_CHECK(value.get_type() == dcmtkpp::Value::Type::Strings);
- BOOST_CHECK(value.as_strings() == dcmtkpp::Value::Strings({"foo"}));
+ BOOST_CHECK(value.get_type() == odil::Value::Type::Strings);
+ BOOST_CHECK(value.as_strings() == odil::Value::Strings({"foo"}));
- BOOST_CHECK_THROW(value.as_integers(), dcmtkpp::Exception);
- BOOST_CHECK_THROW(value.as_reals(), dcmtkpp::Exception);
- BOOST_CHECK_THROW(value.as_data_sets(), dcmtkpp::Exception);
+ BOOST_CHECK_THROW(value.as_integers(), odil::Exception);
+ BOOST_CHECK_THROW(value.as_reals(), odil::Exception);
+ BOOST_CHECK_THROW(value.as_data_sets(), odil::Exception);
}
BOOST_AUTO_TEST_CASE(ModifyStrings)
{
- dcmtkpp::Value value({"foo"});
+ odil::Value value({"foo"});
value.as_strings().push_back("bar");
- BOOST_CHECK(value.as_strings() == dcmtkpp::Value::Strings({"foo", "bar"}));
+ BOOST_CHECK(value.as_strings() == odil::Value::Strings({"foo", "bar"}));
}
BOOST_AUTO_TEST_CASE(DataSets)
{
- dcmtkpp::DataSet data_set;
+ odil::DataSet data_set;
data_set.add("PatientID", {"DJ1234"});
- dcmtkpp::Value const value({data_set});
+ odil::Value const value({data_set});
- BOOST_CHECK(value.get_type() == dcmtkpp::Value::Type::DataSets);
+ BOOST_CHECK(value.get_type() == odil::Value::Type::DataSets);
BOOST_CHECK_EQUAL(value.as_data_sets().size(), 1);
BOOST_CHECK(value.as_data_sets()[0].has("PatientID"));
BOOST_CHECK(
value.as_data_sets()[0].as_string("PatientID") ==
- dcmtkpp::Value::Strings({"DJ1234"}));
+ odil::Value::Strings({"DJ1234"}));
- BOOST_CHECK_THROW(value.as_integers(), dcmtkpp::Exception);
- BOOST_CHECK_THROW(value.as_strings(), dcmtkpp::Exception);
- BOOST_CHECK_THROW(value.as_reals(), dcmtkpp::Exception);
+ BOOST_CHECK_THROW(value.as_integers(), odil::Exception);
+ BOOST_CHECK_THROW(value.as_strings(), odil::Exception);
+ BOOST_CHECK_THROW(value.as_reals(), odil::Exception);
}
BOOST_AUTO_TEST_CASE(ModifyDataSets)
{
- dcmtkpp::DataSet data_set;
+ odil::DataSet data_set;
data_set.add("PatientID");
data_set.as_string("PatientID").push_back("DJ1234");
- dcmtkpp::Value value({data_set});
+ odil::Value value({data_set});
value.as_data_sets()[0].as_string("PatientID")[0] = "XXX";
- BOOST_CHECK(value.get_type() == dcmtkpp::Value::Type::DataSets);
+ BOOST_CHECK(value.get_type() == odil::Value::Type::DataSets);
BOOST_CHECK_EQUAL(value.as_data_sets().size(), 1);
BOOST_CHECK(value.as_data_sets()[0].has("PatientID"));
BOOST_CHECK(
value.as_data_sets()[0].as_string("PatientID") ==
- dcmtkpp::Value::Strings({"XXX"}));
+ odil::Value::Strings({"XXX"}));
}
BOOST_AUTO_TEST_CASE(EqualityEmpty)
{
- dcmtkpp::Value const value1;
- dcmtkpp::Value const value2;
- dcmtkpp::Value const value3((dcmtkpp::Value::Integers()));
+ odil::Value const value1;
+ odil::Value const value2;
+ odil::Value const value3((odil::Value::Integers()));
BOOST_CHECK(value1 == value2);
BOOST_CHECK( ! (value1 == value3));
}
BOOST_AUTO_TEST_CASE(EqualityIntegers)
{
- dcmtkpp::Value const value1({1,2});
- dcmtkpp::Value const value2({1,2});
- dcmtkpp::Value const value3({3,4});
- dcmtkpp::Value const value4({3,4});
+ odil::Value const value1({1,2});
+ odil::Value const value2({1,2});
+ odil::Value const value3({3,4});
+ odil::Value const value4({3,4});
BOOST_CHECK(value1 == value2);
BOOST_CHECK( ! (value1 == value3));
BOOST_CHECK( ! (value1 == value4));
@@ -130,10 +130,10 @@ BOOST_AUTO_TEST_CASE(EqualityIntegers)
BOOST_AUTO_TEST_CASE(EqualityReals)
{
- dcmtkpp::Value const value1({1,2});
- dcmtkpp::Value const value2({1,2});
- dcmtkpp::Value const value3({3,4});
- dcmtkpp::Value const value4({3,4});
+ odil::Value const value1({1,2});
+ odil::Value const value2({1,2});
+ odil::Value const value3({3,4});
+ odil::Value const value4({3,4});
BOOST_CHECK(value1 == value2);
BOOST_CHECK( ! (value1 == value3));
BOOST_CHECK( ! (value1 == value4));
@@ -141,10 +141,10 @@ BOOST_AUTO_TEST_CASE(EqualityReals)
BOOST_AUTO_TEST_CASE(EqualityStrings)
{
- dcmtkpp::Value const value1({"1","2"});
- dcmtkpp::Value const value2({"1","2"});
- dcmtkpp::Value const value3({"3","4"});
- dcmtkpp::Value const value4({3,4});
+ odil::Value const value1({"1","2"});
+ odil::Value const value2({"1","2"});
+ odil::Value const value3({"3","4"});
+ odil::Value const value4({3,4});
BOOST_CHECK(value1 == value2);
BOOST_CHECK( ! (value1 == value3));
BOOST_CHECK( ! (value1 == value4));
@@ -152,18 +152,18 @@ BOOST_AUTO_TEST_CASE(EqualityStrings)
BOOST_AUTO_TEST_CASE(EqualityDataSets)
{
- dcmtkpp::DataSet dataset1;
+ odil::DataSet dataset1;
dataset1.add("PatientID", {"DJ1234"});
dataset1.add("PixelSpacing", {1.5, 2.5});
- dcmtkpp::DataSet dataset2;
+ odil::DataSet dataset2;
dataset1.add("PatientName", {"Doe^John"});
dataset1.add("PatientAge", {"042Y"});
- dcmtkpp::Value const value1({dataset1});
- dcmtkpp::Value const value2({dataset1});
- dcmtkpp::Value const value3({dataset2});
- dcmtkpp::Value const value4({3,4});
+ odil::Value const value1({dataset1});
+ odil::Value const value2({dataset1});
+ odil::Value const value3({dataset2});
+ odil::Value const value4({3,4});
BOOST_CHECK(value1 == value2);
BOOST_CHECK( ! (value1 == value3));
BOOST_CHECK( ! (value1 == value4));
@@ -171,19 +171,19 @@ BOOST_AUTO_TEST_CASE(EqualityDataSets)
BOOST_AUTO_TEST_CASE(DifferenceEmpty)
{
- dcmtkpp::Value const value1;
- dcmtkpp::Value const value2;
- dcmtkpp::Value const value3((dcmtkpp::Value::Integers()));
+ odil::Value const value1;
+ odil::Value const value2;
+ odil::Value const value3((odil::Value::Integers()));
BOOST_CHECK(! (value1 != value2));
BOOST_CHECK(value1 != value3);
}
BOOST_AUTO_TEST_CASE(DifferenceIntegers)
{
- dcmtkpp::Value const value1({1,2});
- dcmtkpp::Value const value2({1,2});
- dcmtkpp::Value const value3({3,4});
- dcmtkpp::Value const value4({3,4});
+ odil::Value const value1({1,2});
+ odil::Value const value2({1,2});
+ odil::Value const value3({3,4});
+ odil::Value const value4({3,4});
BOOST_CHECK(! (value1 != value2));
BOOST_CHECK(value1 != value3);
BOOST_CHECK(value1 != value4);
@@ -191,10 +191,10 @@ BOOST_AUTO_TEST_CASE(DifferenceIntegers)
BOOST_AUTO_TEST_CASE(DifferenceReals)
{
- dcmtkpp::Value const value1({1.,2.});
- dcmtkpp::Value const value2({1.,2.});
- dcmtkpp::Value const value3({3.,4.});
- dcmtkpp::Value const value4({3,4});
+ odil::Value const value1({1.,2.});
+ odil::Value const value2({1.,2.});
+ odil::Value const value3({3.,4.});
+ odil::Value const value4({3,4});
BOOST_CHECK(! (value1 != value2));
BOOST_CHECK(value1 != value3);
BOOST_CHECK(value1 != value4);
@@ -202,10 +202,10 @@ BOOST_AUTO_TEST_CASE(DifferenceReals)
BOOST_AUTO_TEST_CASE(DifferenceStrings)
{
- dcmtkpp::Value const value1({"1","2"});
- dcmtkpp::Value const value2({"1","2"});
- dcmtkpp::Value const value3({"3","4"});
- dcmtkpp::Value const value4({3,4});
+ odil::Value const value1({"1","2"});
+ odil::Value const value2({"1","2"});
+ odil::Value const value3({"3","4"});
+ odil::Value const value4({3,4});
BOOST_CHECK(! (value1 != value2));
BOOST_CHECK(value1 != value3);
BOOST_CHECK(value1 != value4);
@@ -213,18 +213,18 @@ BOOST_AUTO_TEST_CASE(DifferenceStrings)
BOOST_AUTO_TEST_CASE(DifferenceDataSets)
{
- dcmtkpp::DataSet dataset1;
+ odil::DataSet dataset1;
dataset1.add("PatientID", {"DJ1234"});
dataset1.add("PixelSpacing", {1.5, 2.5});
- dcmtkpp::DataSet dataset2;
+ odil::DataSet dataset2;
dataset1.add("PatientName", {"Doe^John"});
dataset1.add("PatientAge", {"042Y"});
- dcmtkpp::Value const value1({dataset1});
- dcmtkpp::Value const value2({dataset1});
- dcmtkpp::Value const value3({dataset2});
- dcmtkpp::Value const value4({3,4});
+ odil::Value const value1({dataset1});
+ odil::Value const value2({dataset1});
+ odil::Value const value3({dataset2});
+ odil::Value const value4({3,4});
BOOST_CHECK(! (value1 != value2));
BOOST_CHECK(value1 != value3);
BOOST_CHECK(value1 != value4);
@@ -232,19 +232,59 @@ BOOST_AUTO_TEST_CASE(DifferenceDataSets)
struct Visitor
{
- typedef bool result_type;
+ typedef std::string result_type;
template<typename T>
- bool operator()(T const & container) const
+ result_type operator()(T const & container) const
{
- return true;
+ return typeid(container).name();
}
};
BOOST_AUTO_TEST_CASE(VisitorEmpty)
{
- dcmtkpp::Value const value;
+ odil::Value const value;
BOOST_CHECK_THROW(
- dcmtkpp::apply_visitor(Visitor(), value),
- dcmtkpp::Exception);
+ odil::apply_visitor(Visitor(), value),
+ odil::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(VisitorIntegers)
+{
+ odil::Value const value({1234});
+ BOOST_REQUIRE_EQUAL(
+ odil::apply_visitor(Visitor(), value),
+ typeid(odil::Value::Integers).name());
+}
+
+BOOST_AUTO_TEST_CASE(VisitorReals)
+{
+ odil::Value const value({12.34});
+ BOOST_REQUIRE_EQUAL(
+ odil::apply_visitor(Visitor(), value),
+ typeid(odil::Value::Reals).name());
+}
+
+BOOST_AUTO_TEST_CASE(VisitorStrings)
+{
+ odil::Value const value({"foo"});
+ BOOST_REQUIRE_EQUAL(
+ odil::apply_visitor(Visitor(), value),
+ typeid(odil::Value::Strings).name());
+}
+
+BOOST_AUTO_TEST_CASE(VisitorDataSets)
+{
+ odil::Value const value({odil::DataSet()});
+ BOOST_REQUIRE_EQUAL(
+ odil::apply_visitor(Visitor(), value),
+ typeid(odil::Value::DataSets).name());
+}
+
+BOOST_AUTO_TEST_CASE(VisitorBinary)
+{
+ odil::Value const value((odil::Value::Binary()));
+ BOOST_REQUIRE_EQUAL(
+ odil::apply_visitor(Visitor(), value),
+ typeid(odil::Value::Binary).name());
}
diff --git a/tests/code/Writer.cpp b/tests/code/Writer.cpp
index 0c86f75..dc6a28b 100644
--- a/tests/code/Writer.cpp
+++ b/tests/code/Writer.cpp
@@ -1,44 +1,44 @@
#define BOOST_TEST_MODULE Writer
#include <boost/test/unit_test.hpp>
-#include <endian.h>
#include <sstream>
#include <dcmtk/config/osconfig.h>
#include <dcmtk/dcmdata/dctk.h>
#include <dcmtk/dcmdata/dcistrmb.h>
-#include "dcmtkpp/conversion.h"
-#include "dcmtkpp/Element.h"
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Writer.h"
-#include "dcmtkpp/VR.h"
+#include "odil/endian.h"
+#include "odil/Element.h"
+#include "odil/registry.h"
+#include "odil/Writer.h"
+#include "odil/VR.h"
+#include "odil/dcmtk/conversion.h"
BOOST_AUTO_TEST_CASE(Constructor)
{
std::ostringstream stream;
- dcmtkpp::Writer const writer(stream, BIG_ENDIAN, true);
- BOOST_REQUIRE(writer.byte_ordering == BIG_ENDIAN);
+ odil::Writer const writer(stream, odil::ByteOrdering::BigEndian, true);
+ BOOST_REQUIRE(writer.byte_ordering == odil::ByteOrdering::BigEndian);
BOOST_REQUIRE(writer.explicit_vr == true);
}
BOOST_AUTO_TEST_CASE(ConstructorTransferSyntax)
{
std::ostringstream stream;
- dcmtkpp::Writer const writer(stream, dcmtkpp::registry::ExplicitVRBigEndian_Retired);
- BOOST_REQUIRE(writer.byte_ordering == BIG_ENDIAN);
+ odil::Writer const writer(stream, odil::registry::ExplicitVRBigEndian_Retired);
+ BOOST_REQUIRE(writer.byte_ordering == odil::ByteOrdering::BigEndian);
BOOST_REQUIRE(writer.explicit_vr == true);
}
void do_test(
- dcmtkpp::DataSet const & dcmtkpp_data_set, std::string transfer_syntax,
- dcmtkpp::Writer::ItemEncoding item_encoding, bool use_group_length)
+ odil::DataSet const & odil_data_set, std::string transfer_syntax,
+ odil::Writer::ItemEncoding item_encoding, bool use_group_length)
{
// Write input data set
std::stringstream stream;
- dcmtkpp::Writer const writer(
+ odil::Writer const writer(
stream, transfer_syntax, item_encoding, use_group_length);
- writer.write_data_set(dcmtkpp_data_set);
+ writer.write_data_set(odil_data_set);
// Store data in a DCMTK stream
std::string const data = stream.str();
@@ -54,22 +54,22 @@ void do_test(
BOOST_REQUIRE_EQUAL(dcmtk_stream.avail(), 0);
dcmtk_data_set.transferEnd();
- auto const other_dcmtkpp_dataset = dcmtkpp::convert(&dcmtk_data_set);
- BOOST_REQUIRE(dcmtkpp_data_set == other_dcmtkpp_dataset);
+ auto const other_odil_dataset = odil::dcmtk::convert(&dcmtk_data_set);
+ BOOST_REQUIRE(odil_data_set == other_odil_dataset);
}
-void do_test(dcmtkpp::DataSet const & dcmtkpp_data_set)
+void do_test(odil::DataSet const & odil_data_set)
{
std::vector<std::string> const transfer_syntaxes =
{
- dcmtkpp::registry::ImplicitVRLittleEndian,
- dcmtkpp::registry::ExplicitVRLittleEndian,
- dcmtkpp::registry::ExplicitVRBigEndian_Retired
+ odil::registry::ImplicitVRLittleEndian,
+ odil::registry::ExplicitVRLittleEndian,
+ odil::registry::ExplicitVRBigEndian_Retired
};
- std::vector<dcmtkpp::Writer::ItemEncoding> item_encodings =
+ std::vector<odil::Writer::ItemEncoding> item_encodings =
{
- dcmtkpp::Writer::ItemEncoding::ExplicitLength,
- dcmtkpp::Writer::ItemEncoding::UndefinedLength
+ odil::Writer::ItemEncoding::ExplicitLength,
+ odil::Writer::ItemEncoding::UndefinedLength
};
std::vector<bool> use_group_length_values = {/*true, */false};
@@ -80,164 +80,173 @@ void do_test(dcmtkpp::DataSet const & dcmtkpp_data_set)
for(auto const & use_group_length: use_group_length_values)
{
do_test(
- dcmtkpp_data_set, transfer_syntax,
+ odil_data_set, transfer_syntax,
item_encoding, use_group_length);
}
}
}
}
+BOOST_AUTO_TEST_CASE(AT)
+{
+ odil::Element odil_element({"00100020", "0008103e"}, odil::VR::AT);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::SelectorATValue, odil_element);
+
+ do_test(odil_data_set);
+}
+
BOOST_AUTO_TEST_CASE(CS)
{
- dcmtkpp::Element dcmtkpp_element({"ABC", "DEF"}, dcmtkpp::VR::CS);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::SelectorCSValue, dcmtkpp_element);
+ odil::Element odil_element({"ABC", "DEF"}, odil::VR::CS);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::SelectorCSValue, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(DS)
{
- dcmtkpp::Element dcmtkpp_element({1.23, -4.56}, dcmtkpp::VR::DS);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::SelectorDSValue, dcmtkpp_element);
+ odil::Element odil_element({1.23, -4.56}, odil::VR::DS);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::SelectorDSValue, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(FD)
{
- dcmtkpp::Element dcmtkpp_element({1.23, -4.56}, dcmtkpp::VR::FD);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::SelectorFDValue, dcmtkpp_element);
+ odil::Element odil_element({1.23, -4.56}, odil::VR::FD);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::SelectorFDValue, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(FL)
{
- dcmtkpp::Element dcmtkpp_element({0.5, -0.125}, dcmtkpp::VR::FL);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::SelectorFLValue, dcmtkpp_element);
+ odil::Element odil_element({0.5, -0.125}, odil::VR::FL);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::SelectorFLValue, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(IS)
{
- dcmtkpp::Element dcmtkpp_element({123, -456}, dcmtkpp::VR::IS);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::SelectorISValue, dcmtkpp_element);
+ odil::Element odil_element({123, -456}, odil::VR::IS);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::SelectorISValue, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(OB)
{
- dcmtkpp::Element dcmtkpp_element(
+ odil::Element odil_element(
std::vector<uint8_t>({0x01, 0x02, 0x03, 0x04}),
- dcmtkpp::VR::OB);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::EncapsulatedDocument, dcmtkpp_element);
+ odil::VR::OB);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::EncapsulatedDocument, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(OF)
{
- dcmtkpp::Element dcmtkpp_element(
+ odil::Element odil_element(
std::vector<uint8_t>({0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}),
- dcmtkpp::VR::OF);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::VectorGridData, dcmtkpp_element);
+ odil::VR::OF);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::VectorGridData, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(OW)
{
- dcmtkpp::Element dcmtkpp_element(
+ odil::Element odil_element(
std::vector<uint8_t>({0x01, 0x02, 0x03, 0x04}),
- dcmtkpp::VR::OW);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::RedPaletteColorLookupTableData, dcmtkpp_element);
+ odil::VR::OW);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::RedPaletteColorLookupTableData, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(SL)
{
- dcmtkpp::Element dcmtkpp_element({12345678, -8765432}, dcmtkpp::VR::SL);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::SelectorSLValue, dcmtkpp_element);
+ odil::Element odil_element({12345678, -8765432}, odil::VR::SL);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::SelectorSLValue, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(SQ)
{
- dcmtkpp::DataSet item1;
+ odil::DataSet item1;
item1.add(
- dcmtkpp::registry::SelectorSLValue,
- dcmtkpp::Element({12345678, -8765432}, dcmtkpp::VR::SL));
- dcmtkpp::DataSet item2;
+ odil::registry::SelectorSLValue,
+ odil::Element({12345678, -8765432}, odil::VR::SL));
+ odil::DataSet item2;
item2.add(
- dcmtkpp::registry::SelectorFDValue,
- dcmtkpp::Element({1.23, -4.56}, dcmtkpp::VR::FD));
+ odil::registry::SelectorFDValue,
+ odil::Element({1.23, -4.56}, odil::VR::FD));
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::FrameExtractionSequence,
- dcmtkpp::Element({item1, item2}, dcmtkpp::VR::SQ));
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::FrameExtractionSequence,
+ odil::Element({item1, item2}, odil::VR::SQ));
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(SS)
{
- dcmtkpp::Element dcmtkpp_element({1234, -5678}, dcmtkpp::VR::SS);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::SelectorSSValue, dcmtkpp_element);
+ odil::Element odil_element({1234, -5678}, odil::VR::SS);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::SelectorSSValue, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(UI)
{
- dcmtkpp::Element dcmtkpp_element({"1.2", "3.4"}, dcmtkpp::VR::UI);
- dcmtkpp::DataSet dcmtkpp_data_set;
+ odil::Element odil_element({"1.2", "3.4"}, odil::VR::UI);
+ odil::DataSet odil_data_set;
// SelectorUIValue is not in current DCMTK
- dcmtkpp_data_set.add(dcmtkpp::registry::SOPInstanceUID, dcmtkpp_element);
+ odil_data_set.add(odil::registry::SOPInstanceUID, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(UL)
{
- dcmtkpp::Element dcmtkpp_element({12345678, 8765432}, dcmtkpp::VR::UL);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::SelectorULValue, dcmtkpp_element);
+ odil::Element odil_element({12345678, 8765432}, odil::VR::UL);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::SelectorULValue, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
BOOST_AUTO_TEST_CASE(US)
{
- dcmtkpp::Element dcmtkpp_element({1234, 5678}, dcmtkpp::VR::US);
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(dcmtkpp::registry::SelectorUSValue, dcmtkpp_element);
+ odil::Element odil_element({1234, 5678}, odil::VR::US);
+ odil::DataSet odil_data_set;
+ odil_data_set.add(odil::registry::SelectorUSValue, odil_element);
- do_test(dcmtkpp_data_set);
+ do_test(odil_data_set);
}
void do_file_test(
- dcmtkpp::DataSet const & dcmtkpp_data_set, std::string transfer_syntax,
- dcmtkpp::Writer::ItemEncoding item_encoding, bool use_group_length)
+ odil::DataSet const & odil_data_set, std::string transfer_syntax,
+ odil::Writer::ItemEncoding item_encoding, bool use_group_length)
{
// Write input data set
std::stringstream stream;
- dcmtkpp::Writer::write_file(
- dcmtkpp_data_set, stream, transfer_syntax,
+ odil::Writer::write_file(
+ odil_data_set, stream, odil::DataSet(), transfer_syntax,
item_encoding, use_group_length);
// Store data in a DCMTK stream
@@ -255,35 +264,36 @@ void do_file_test(
format.transferEnd();
DcmMetaInfo * dcmtk_meta_information = format.getMetaInfo();
- auto const dcmtkpp_meta_information = dcmtkpp::convert(dcmtk_meta_information);
+ auto const odil_meta_information =
+ odil::dcmtk::convert(dcmtk_meta_information);
BOOST_REQUIRE(
- dcmtkpp_meta_information.as_string(dcmtkpp::registry::TransferSyntaxUID) ==
- dcmtkpp::Value::Strings({ transfer_syntax }));
+ odil_meta_information.as_string(odil::registry::TransferSyntaxUID) ==
+ odil::Value::Strings({ transfer_syntax }));
DcmDataset * dcmtk_data_set = format.getDataset();
- auto const other_dcmtkpp_dataset = dcmtkpp::convert(dcmtk_data_set);
- BOOST_REQUIRE(dcmtkpp_data_set == other_dcmtkpp_dataset);
+ auto const other_odil_dataset = odil::dcmtk::convert(dcmtk_data_set);
+ BOOST_REQUIRE(odil_data_set == other_odil_dataset);
BOOST_REQUIRE(
- dcmtkpp_meta_information.as_string(dcmtkpp::registry::MediaStorageSOPClassUID) ==
- other_dcmtkpp_dataset.as_string(dcmtkpp::registry::SOPClassUID));
+ odil_meta_information.as_string(odil::registry::MediaStorageSOPClassUID) ==
+ other_odil_dataset.as_string(odil::registry::SOPClassUID));
BOOST_REQUIRE(
- dcmtkpp_meta_information.as_string(dcmtkpp::registry::MediaStorageSOPInstanceUID) ==
- other_dcmtkpp_dataset.as_string(dcmtkpp::registry::SOPInstanceUID));
+ odil_meta_information.as_string(odil::registry::MediaStorageSOPInstanceUID) ==
+ other_odil_dataset.as_string(odil::registry::SOPInstanceUID));
}
-void do_file_test(dcmtkpp::DataSet const & dcmtkpp_data_set)
+void do_file_test(odil::DataSet const & odil_data_set)
{
std::vector<std::string> const transfer_syntaxes =
{
- dcmtkpp::registry::ImplicitVRLittleEndian,
- dcmtkpp::registry::ExplicitVRLittleEndian,
- dcmtkpp::registry::ExplicitVRBigEndian_Retired
+ odil::registry::ImplicitVRLittleEndian,
+ odil::registry::ExplicitVRLittleEndian,
+ odil::registry::ExplicitVRBigEndian_Retired
};
- std::vector<dcmtkpp::Writer::ItemEncoding> item_encodings =
+ std::vector<odil::Writer::ItemEncoding> item_encodings =
{
- dcmtkpp::Writer::ItemEncoding::ExplicitLength,
- dcmtkpp::Writer::ItemEncoding::UndefinedLength
+ odil::Writer::ItemEncoding::ExplicitLength,
+ odil::Writer::ItemEncoding::UndefinedLength
};
std::vector<bool> use_group_length_values = {/*true, */false};
@@ -294,7 +304,7 @@ void do_file_test(dcmtkpp::DataSet const & dcmtkpp_data_set)
for(auto const & use_group_length: use_group_length_values)
{
do_file_test(
- dcmtkpp_data_set, transfer_syntax,
+ odil_data_set, transfer_syntax,
item_encoding, use_group_length);
}
}
@@ -303,25 +313,25 @@ void do_file_test(dcmtkpp::DataSet const & dcmtkpp_data_set)
BOOST_AUTO_TEST_CASE(File)
{
- dcmtkpp::DataSet item1;
+ odil::DataSet item1;
item1.add(
- dcmtkpp::registry::SelectorSLValue,
- dcmtkpp::Element({12345678, -8765432}, dcmtkpp::VR::SL));
- dcmtkpp::DataSet item2;
+ odil::registry::SelectorSLValue,
+ odil::Element({12345678, -8765432}, odil::VR::SL));
+ odil::DataSet item2;
item2.add(
- dcmtkpp::registry::SelectorFDValue,
- dcmtkpp::Element({1.23, -4.56}, dcmtkpp::VR::FD));
-
- dcmtkpp::DataSet dcmtkpp_data_set;
- dcmtkpp_data_set.add(
- dcmtkpp::registry::SOPClassUID,
- {dcmtkpp::registry::RawDataStorage}, dcmtkpp::VR::UI);
- dcmtkpp_data_set.add(
- dcmtkpp::registry::SOPInstanceUID,
- {"1.2.3.4"}, dcmtkpp::VR::UI);
- dcmtkpp_data_set.add(
- dcmtkpp::registry::FrameExtractionSequence,
- dcmtkpp::Element({item1, item2}, dcmtkpp::VR::SQ));
-
- do_file_test(dcmtkpp_data_set);
+ odil::registry::SelectorFDValue,
+ odil::Element({1.23, -4.56}, odil::VR::FD));
+
+ odil::DataSet odil_data_set;
+ odil_data_set.add(
+ odil::registry::SOPClassUID,
+ {odil::registry::RawDataStorage}, odil::VR::UI);
+ odil_data_set.add(
+ odil::registry::SOPInstanceUID,
+ {"1.2.3.4"}, odil::VR::UI);
+ odil_data_set.add(
+ odil::registry::FrameExtractionSequence,
+ odil::Element({item1, item2}, odil::VR::SQ));
+
+ do_file_test(odil_data_set);
}
diff --git a/tests/code/base64.cpp b/tests/code/base64.cpp
new file mode 100644
index 0000000..96830f2
--- /dev/null
+++ b/tests/code/base64.cpp
@@ -0,0 +1,75 @@
+#define BOOST_TEST_MODULE Base64
+#include <boost/test/unit_test.hpp>
+
+#include <iterator>
+#include <string>
+
+#include "odil/base64.h"
+
+void test_encode(std::string const & data, std::string const & expected)
+{
+ std::string encoded;
+ encoded.reserve(data.size()*4/3);
+
+ odil::base64::encode(
+ data.begin(), data.end(), std::back_inserter(encoded));
+
+ BOOST_REQUIRE_EQUAL(encoded, expected);
+}
+
+void test_decode(std::string const & data, std::string const & expected)
+{
+ std::string decoded;
+ decoded.reserve(data.size()*3/4);
+
+ odil::base64::decode(
+ data.begin(), data.end(), std::back_inserter(decoded));
+
+ BOOST_REQUIRE_EQUAL(decoded, expected);
+}
+
+BOOST_AUTO_TEST_CASE(EncodeEmpty)
+{
+ test_encode("", "");
+}
+
+BOOST_AUTO_TEST_CASE(Encode1)
+{
+ test_encode(
+ "Lorem ipsum dolor sit amet.", "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQu");
+}
+
+BOOST_AUTO_TEST_CASE(Encode2)
+{
+ test_encode(
+ "Lorem ipsum dolor sit amet", "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ=");
+}
+
+BOOST_AUTO_TEST_CASE(Encode3)
+{
+ test_encode(
+ "Lorem ipsum dolor sit ame", "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZQ==");
+}
+
+BOOST_AUTO_TEST_CASE(DecodeEmpty)
+{
+ test_decode("", "");
+}
+
+BOOST_AUTO_TEST_CASE(Decode1)
+{
+ test_decode(
+ "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQu", "Lorem ipsum dolor sit amet.");
+}
+
+BOOST_AUTO_TEST_CASE(Decode2)
+{
+ test_decode(
+ "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ=", "Lorem ipsum dolor sit amet");
+}
+
+BOOST_AUTO_TEST_CASE(Decode3)
+{
+ test_decode(
+ "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZQ==", "Lorem ipsum dolor sit ame");
+}
diff --git a/tests/code/conversion.cpp b/tests/code/conversion.cpp
index 109c2cb..7510479 100644
--- a/tests/code/conversion.cpp
+++ b/tests/code/conversion.cpp
@@ -4,18 +4,18 @@
#include <dcmtk/config/osconfig.h>
#include <dcmtk/dcmdata/dctk.h>
-#include "dcmtkpp/conversion.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/ElementAccessor.h"
-#include "dcmtkpp/Tag.h"
-#include "dcmtkpp/Value.h"
-#include "dcmtkpp/VR.h"
-#include "dcmtkpp/VRTraits.h"
+#include "odil/DataSet.h"
+#include "odil/Tag.h"
+#include "odil/Value.h"
+#include "odil/VR.h"
+#include "odil/dcmtk/VRTraits.h"
+#include "odil/dcmtk/conversion.h"
+#include "odil/dcmtk/ElementAccessor.h"
BOOST_AUTO_TEST_CASE(TagFromDcmtkpp)
{
- dcmtkpp::Tag const source(0xdead, 0xbeef);
- DcmTagKey const destination = dcmtkpp::convert(source);
+ odil::Tag const source(0xdead, 0xbeef);
+ DcmTagKey const destination = odil::dcmtk::convert(source);
BOOST_CHECK_EQUAL(destination.getGroup(), 0xdead);
BOOST_CHECK_EQUAL(destination.getElement(), 0xbeef);
@@ -24,7 +24,7 @@ BOOST_AUTO_TEST_CASE(TagFromDcmtkpp)
BOOST_AUTO_TEST_CASE(TagFromDcmtk)
{
DcmTagKey const source(0xdead, 0xbeef);
- dcmtkpp::Tag const destination = dcmtkpp::convert(source);
+ odil::Tag const destination = odil::dcmtk::convert(source);
BOOST_CHECK_EQUAL(destination.group, 0xdead);
BOOST_CHECK_EQUAL(destination.element, 0xbeef);
@@ -37,24 +37,24 @@ void compare(TValueType const & t1, TValueType const & t2)
}
template<>
-void compare<dcmtkpp::Value::Reals::value_type>(
- dcmtkpp::Value::Reals::value_type const & t1,
- dcmtkpp::Value::Reals::value_type const & t2)
+void compare<odil::Value::Reals::value_type>(
+ odil::Value::Reals::value_type const & t1,
+ odil::Value::Reals::value_type const & t2)
{
BOOST_CHECK_CLOSE(t1, t2, 1e-6);
}
-template<dcmtkpp::VR VVR, DcmEVR VEVR, typename TInputType, typename TOutputType>
-void test_element_from_dcmtkpp(
+template<odil::VR VVR, DcmEVR VEVR, typename TInputType, typename TOutputType>
+void test_element_from_odil(
TInputType const & source_value,
- TInputType const & (dcmtkpp::Element::*getter)() const)
+ TInputType const & (odil::Element::*getter)() const)
{
- dcmtkpp::Tag const source_tag(0xdead, 0xbeef);
- DcmTagKey const destination_tag = dcmtkpp::convert(source_tag);
+ odil::Tag const source_tag(0xdead, 0xbeef);
+ DcmTagKey const destination_tag = odil::dcmtk::convert(source_tag);
- dcmtkpp::Element const source(source_value, VVR);
+ odil::Element const source(source_value, VVR);
- DcmElement * destination = dcmtkpp::convert(source_tag, source);
+ DcmElement * destination = odil::dcmtk::convert(source_tag, source);
BOOST_CHECK_NE(destination, (DcmElement const *)(NULL));
BOOST_CHECK_EQUAL(destination->getVR(), VEVR);
@@ -64,30 +64,30 @@ void test_element_from_dcmtkpp(
BOOST_CHECK_EQUAL(destination->getVM(), source.size());
for(std::size_t i=0; i<source.size(); ++i)
{
- typedef typename dcmtkpp::VRTraits<VEVR>::ValueType ValueType;
- if(typeid(TInputType) == typeid(dcmtkpp::Value::Reals))
+ typedef typename odil::dcmtk::VRTraits<VEVR>::ValueType ValueType;
+ if(typeid(TInputType) == typeid(odil::Value::Reals))
{
compare<ValueType>(
- dcmtkpp::ElementAccessor<ValueType>::element_get(*destination, i),
+ odil::dcmtk::ElementAccessor<ValueType>::element_get(*destination, i),
(source.*getter)()[i]);
}
else
{
BOOST_CHECK_EQUAL(
- dcmtkpp::ElementAccessor<ValueType>::element_get(*destination, i),
+ odil::dcmtk::ElementAccessor<ValueType>::element_get(*destination, i),
(source.*getter)()[i]);
}
}
}
-template<dcmtkpp::VR VVR, DcmEVR VEVR, typename TInputType, typename TElementType>
-void test_element_to_dcmtkpp(
+template<odil::VR VVR, DcmEVR VEVR, typename TInputType, typename TElementType>
+void test_element_to_odil(
TInputType const & source_value,
- TInputType const & (dcmtkpp::Element::*getter)() const)
+ TInputType const & (odil::Element::*getter)() const)
{
DcmTag const source_tag(0xdead, 0xbeef, VEVR);
TElementType source(source_tag);
- if(typeid(TInputType) == typeid(dcmtkpp::Value::Strings) ||
+ if(typeid(TInputType) == typeid(odil::Value::Strings) ||
VEVR == EVR_IS || VEVR == EVR_DS)
{
OFString value;
@@ -117,156 +117,156 @@ void test_element_to_dcmtkpp(
for(unsigned int i=0; i<source_value.size(); ++i)
{
auto const & item = source_value[i];
- dcmtkpp::ElementAccessor<typename dcmtkpp::VRTraits<VEVR>::ValueType>::element_set(
+ odil::dcmtk::ElementAccessor<typename odil::dcmtk::VRTraits<VEVR>::ValueType>::element_set(
source, item, i);
}
}
- dcmtkpp::Element const destination = dcmtkpp::convert(&source);
+ odil::Element const destination = odil::dcmtk::convert(&source);
BOOST_CHECK(VVR == destination.vr);
BOOST_CHECK_EQUAL(source.getVM(), destination.size());
for(std::size_t i=0; i<destination.size(); ++i)
{
- typedef typename dcmtkpp::VRTraits<VEVR>::ValueType ValueType;
- if(typeid(TInputType) == typeid(dcmtkpp::Value::Reals))
+ typedef typename odil::dcmtk::VRTraits<VEVR>::ValueType ValueType;
+ if(typeid(TInputType) == typeid(odil::Value::Reals))
{
compare<ValueType>(
- dcmtkpp::ElementAccessor<ValueType>::element_get(source, i),
+ odil::dcmtk::ElementAccessor<ValueType>::element_get(source, i),
(destination.*getter)()[i]);
}
else
{
BOOST_CHECK_EQUAL(
- dcmtkpp::ElementAccessor<ValueType>::element_get(source, i),
+ odil::dcmtk::ElementAccessor<ValueType>::element_get(source, i),
(destination.*getter)()[i]);
}
}
}
#define ElementTest(vr, InputType, ElementType, value, getter) \
-BOOST_AUTO_TEST_CASE(vr##FromDcmtkpp) \
+BOOST_AUTO_TEST_CASE(vr##FromOdil) \
{ \
- test_element_from_dcmtkpp< \
- dcmtkpp::VR::vr, EVR_##vr, InputType, ElementType>(value, getter); \
+ test_element_from_odil< \
+ odil::VR::vr, EVR_##vr, InputType, ElementType>(value, getter); \
} \
-BOOST_AUTO_TEST_CASE(vr##ToDcmtkpp) \
+BOOST_AUTO_TEST_CASE(vr##ToOdil) \
{ \
- test_element_to_dcmtkpp< \
- dcmtkpp::VR::vr, EVR_##vr, InputType, ElementType>(value, getter); \
+ test_element_to_odil< \
+ odil::VR::vr, EVR_##vr, InputType, ElementType>(value, getter); \
}
ElementTest(
- AE, dcmtkpp::Value::Strings, DcmApplicationEntity,
- dcmtkpp::Value::Strings({"foo", "bar"}), &dcmtkpp::Element::as_string);
+ AE, odil::Value::Strings, DcmApplicationEntity,
+ odil::Value::Strings({"foo", "bar"}), &odil::Element::as_string);
ElementTest(
- AS, dcmtkpp::Value::Strings, DcmAgeString,
- dcmtkpp::Value::Strings({"012Y", "345D"}), &dcmtkpp::Element::as_string);
+ AS, odil::Value::Strings, DcmAgeString,
+ odil::Value::Strings({"012Y", "345D"}), &odil::Element::as_string);
ElementTest(
- CS, dcmtkpp::Value::Strings, DcmCodeString,
- dcmtkpp::Value::Strings({"foo", "bar"}), &dcmtkpp::Element::as_string);
+ CS, odil::Value::Strings, DcmCodeString,
+ odil::Value::Strings({"foo", "bar"}), &odil::Element::as_string);
ElementTest(
- DA, dcmtkpp::Value::Strings, DcmDate,
- dcmtkpp::Value::Strings({"19000101", "20131215"}),
- &dcmtkpp::Element::as_string);
+ DA, odil::Value::Strings, DcmDate,
+ odil::Value::Strings({"19000101", "20131215"}),
+ &odil::Element::as_string);
ElementTest(
- DS, dcmtkpp::Value::Reals, DcmDecimalString,
- dcmtkpp::Value::Reals({12.34, 56.78}), &dcmtkpp::Element::as_real);
+ DS, odil::Value::Reals, DcmDecimalString,
+ odil::Value::Reals({12.34, 56.78}), &odil::Element::as_real);
ElementTest(
- DT, dcmtkpp::Value::Strings, DcmDateTime,
- dcmtkpp::Value::Strings({"19000101123456", "201312150123"}),
- &dcmtkpp::Element::as_string);
+ DT, odil::Value::Strings, DcmDateTime,
+ odil::Value::Strings({"19000101123456", "201312150123"}),
+ &odil::Element::as_string);
ElementTest(
- FL, dcmtkpp::Value::Reals, DcmFloatingPointSingle,
- dcmtkpp::Value::Reals({12.34, 56.78}), &dcmtkpp::Element::as_real);
+ FL, odil::Value::Reals, DcmFloatingPointSingle,
+ odil::Value::Reals({12.34, 56.78}), &odil::Element::as_real);
ElementTest(
- FD, dcmtkpp::Value::Reals, DcmFloatingPointDouble,
- dcmtkpp::Value::Reals({12.34, 56.78}), &dcmtkpp::Element::as_real);
+ FD, odil::Value::Reals, DcmFloatingPointDouble,
+ odil::Value::Reals({12.34, 56.78}), &odil::Element::as_real);
ElementTest(
- IS, dcmtkpp::Value::Integers, DcmIntegerString,
- dcmtkpp::Value::Integers({34567, -67890}), &dcmtkpp::Element::as_int);
+ IS, odil::Value::Integers, DcmIntegerString,
+ odil::Value::Integers({34567, -67890}), &odil::Element::as_int);
ElementTest(
- LO, dcmtkpp::Value::Strings, DcmLongString,
- dcmtkpp::Value::Strings({"foo bar", "something else"}),
- &dcmtkpp::Element::as_string);
+ LO, odil::Value::Strings, DcmLongString,
+ odil::Value::Strings({"foo bar", "something else"}),
+ &odil::Element::as_string);
ElementTest(
- LT, dcmtkpp::Value::Strings, DcmLongText,
- dcmtkpp::Value::Strings({"foo\nbar\\something else"}),
- &dcmtkpp::Element::as_string);
+ LT, odil::Value::Strings, DcmLongText,
+ odil::Value::Strings({"foo\nbar\\something else"}),
+ &odil::Element::as_string);
// OB
// OF
// OW
ElementTest(
- PN, dcmtkpp::Value::Strings, DcmPersonName,
- dcmtkpp::Value::Strings({"Doe^John", "^Bob^Dr."}),
- &dcmtkpp::Element::as_string);
+ PN, odil::Value::Strings, DcmPersonName,
+ odil::Value::Strings({"Doe^John", "^Bob^Dr."}),
+ &odil::Element::as_string);
ElementTest(
- SH, dcmtkpp::Value::Strings, DcmShortString,
- dcmtkpp::Value::Strings({"foo", "bar"}), &dcmtkpp::Element::as_string);
+ SH, odil::Value::Strings, DcmShortString,
+ odil::Value::Strings({"foo", "bar"}), &odil::Element::as_string);
ElementTest(
- SL, dcmtkpp::Value::Integers, DcmSignedLong,
- dcmtkpp::Value::Integers({34567, -56789}), &dcmtkpp::Element::as_int);
+ SL, odil::Value::Integers, DcmSignedLong,
+ odil::Value::Integers({34567, -56789}), &odil::Element::as_int);
ElementTest(
- SS, dcmtkpp::Value::Integers, DcmSignedShort,
- dcmtkpp::Value::Integers({1234, -5678}), &dcmtkpp::Element::as_int);
+ SS, odil::Value::Integers, DcmSignedShort,
+ odil::Value::Integers({1234, -5678}), &odil::Element::as_int);
ElementTest(
- ST, dcmtkpp::Value::Strings, DcmShortText,
- dcmtkpp::Value::Strings({"foo\nbar\\something else"}),
- &dcmtkpp::Element::as_string);
+ ST, odil::Value::Strings, DcmShortText,
+ odil::Value::Strings({"foo\nbar\\something else"}),
+ &odil::Element::as_string);
ElementTest(
- TM, dcmtkpp::Value::Strings, DcmTime,
- dcmtkpp::Value::Strings({"123456", "0123"}),
- &dcmtkpp::Element::as_string);
+ TM, odil::Value::Strings, DcmTime,
+ odil::Value::Strings({"123456", "0123"}),
+ &odil::Element::as_string);
ElementTest(
- UI, dcmtkpp::Value::Strings, DcmUniqueIdentifier,
- dcmtkpp::Value::Strings(
+ UI, odil::Value::Strings, DcmUniqueIdentifier,
+ odil::Value::Strings(
{"1.2.840.10008.5.1.4.1.1.4", "1.2.840.10008.5.1.4.1.1.4.1"}),
- &dcmtkpp::Element::as_string);
+ &odil::Element::as_string);
ElementTest(
- UL, dcmtkpp::Value::Integers, DcmUnsignedLong,
- dcmtkpp::Value::Integers({123456, 789012}), &dcmtkpp::Element::as_int);
+ UL, odil::Value::Integers, DcmUnsignedLong,
+ odil::Value::Integers({123456, 789012}), &odil::Element::as_int);
// UN
ElementTest(
- US, dcmtkpp::Value::Integers, DcmUnsignedShort,
- dcmtkpp::Value::Integers({12345, 6789}), &dcmtkpp::Element::as_int);
+ US, odil::Value::Integers, DcmUnsignedShort,
+ odil::Value::Integers({12345, 6789}), &odil::Element::as_int);
ElementTest(
- UT, dcmtkpp::Value::Strings, DcmUnlimitedText,
- dcmtkpp::Value::Strings({"foo\nbar\\something else"}),
- &dcmtkpp::Element::as_string);
+ UT, odil::Value::Strings, DcmUnlimitedText,
+ odil::Value::Strings({"foo\nbar\\something else"}),
+ &odil::Element::as_string);
BOOST_AUTO_TEST_CASE(ATFromDcmtkpp)
{
- dcmtkpp::Element const source(
- dcmtkpp::Value::Strings({"deadbeef", "beeff00d"}), dcmtkpp::VR::AT);
+ odil::Element const source(
+ odil::Value::Strings({"deadbeef", "beeff00d"}), odil::VR::AT);
- DcmElement * destination = dcmtkpp::convert(
- dcmtkpp::Tag(0x1234, 0x5678), source);
+ DcmElement * destination = odil::dcmtk::convert(
+ odil::Tag(0x1234, 0x5678), source);
BOOST_CHECK_NE(destination, (DcmElement const *)(NULL));
- BOOST_CHECK_EQUAL(destination->getVR(), dcmtkpp::convert(source.vr));
+ BOOST_CHECK_EQUAL(destination->getVR(), odil::dcmtk::convert(source.vr));
BOOST_CHECK_NE(
dynamic_cast<DcmAttributeTag *>(destination),
(DcmAttributeTag *)(NULL));
@@ -274,13 +274,13 @@ BOOST_AUTO_TEST_CASE(ATFromDcmtkpp)
BOOST_CHECK_EQUAL(destination->getVM(), source.size());
for(std::size_t i=0; i<source.size(); ++i)
{
- dcmtkpp::Tag const & source_tag = source.as_string()[i];
+ odil::Tag const & source_tag = source.as_string()[i];
DcmTagKey destination_tag;
OFCondition const condition = destination->getTagVal(destination_tag, i);
BOOST_CHECK(condition.good());
- BOOST_CHECK(source_tag == dcmtkpp::convert(destination_tag));
+ BOOST_CHECK(source_tag == odil::dcmtk::convert(destination_tag));
}
}
@@ -290,36 +290,36 @@ BOOST_AUTO_TEST_CASE(ATToDcmtkpp)
source.putTagVal(DcmTagKey(0xdead, 0xbeef), 0);
source.putTagVal(DcmTagKey(0xbeef, 0xf00d), 1);
- dcmtkpp::Element const destination = dcmtkpp::convert(&source);
+ odil::Element const destination = odil::dcmtk::convert(&source);
- BOOST_CHECK(destination.vr == dcmtkpp::convert(source.getVR()));
+ BOOST_CHECK(destination.vr == odil::dcmtk::convert(source.getVR()));
BOOST_CHECK_EQUAL(source.getVM(), destination.size());
for(std::size_t i=0; i<destination.size(); ++i)
{
DcmTagKey source_tag;
source.getTagVal(source_tag, i);
- dcmtkpp::Tag const & destination_tag = destination.as_string()[i];
- BOOST_CHECK(dcmtkpp::convert(source_tag) == destination_tag);
+ odil::Tag const & destination_tag = destination.as_string()[i];
+ BOOST_CHECK(odil::dcmtk::convert(source_tag) == destination_tag);
}
}
BOOST_AUTO_TEST_CASE(SQFromDcmtkpp)
{
- dcmtkpp::DataSet item;
+ odil::DataSet item;
item.add("PatientID");
item.as_string("PatientID").push_back("DJ1234");
- dcmtkpp::Element const source(
- dcmtkpp::Value::DataSets({item}), dcmtkpp::VR::SQ);
+ odil::Element const source(
+ odil::Value::DataSets({item}), odil::VR::SQ);
- dcmtkpp::Tag const source_tag(0xdead, 0xbeef);
+ odil::Tag const source_tag(0xdead, 0xbeef);
- DcmElement * destination = dcmtkpp::convert(source_tag, source);
+ DcmElement * destination = odil::dcmtk::convert(source_tag, source);
BOOST_CHECK_NE(destination, (DcmElement const *)(NULL));
- BOOST_CHECK_EQUAL(destination->getVR(), dcmtkpp::convert(source.vr));
+ BOOST_CHECK_EQUAL(destination->getVR(), odil::dcmtk::convert(source.vr));
BOOST_CHECK_NE(
dynamic_cast<DcmSequenceOfItems *>(destination),
(DcmSequenceOfItems *)(NULL));
@@ -327,9 +327,9 @@ BOOST_AUTO_TEST_CASE(SQFromDcmtkpp)
BOOST_CHECK_EQUAL(destination->getVM(), source.size());
for(std::size_t i=0; i<source.size(); ++i)
{
- dcmtkpp::DataSet const & source_item = source.as_data_set()[i];
+ odil::DataSet const & source_item = source.as_data_set()[i];
DcmItem * destination_item = dynamic_cast<DcmSequenceOfItems *>(destination)->getItem(i);
- BOOST_CHECK(source_item == dcmtkpp::convert(destination_item));
+ BOOST_CHECK(source_item == odil::dcmtk::convert(destination_item));
}
}
@@ -341,9 +341,9 @@ BOOST_AUTO_TEST_CASE(SQToDcmtkpp)
DcmSequenceOfItems source(DcmTag(0xdead, 0xbeef, EVR_SQ));
source.append(item);
- dcmtkpp::Element const destination = dcmtkpp::convert(&source);
+ odil::Element const destination = odil::dcmtk::convert(&source);
- BOOST_CHECK(destination.vr == dcmtkpp::convert(source.getVR()));
+ BOOST_CHECK(destination.vr == odil::dcmtk::convert(source.getVR()));
BOOST_CHECK_EQUAL(source.getVM(), destination.size());
for(std::size_t i=0; i<destination.size(); ++i)
{
@@ -351,37 +351,37 @@ BOOST_AUTO_TEST_CASE(SQToDcmtkpp)
DcmDataset * source_item = dynamic_cast<DcmDataset *>(item);
BOOST_REQUIRE(source_item != NULL);
- dcmtkpp::DataSet const & destination_item = destination.as_data_set()[i];
- BOOST_CHECK(dcmtkpp::convert(source_item) == destination_item);
+ odil::DataSet const & destination_item = destination.as_data_set()[i];
+ BOOST_CHECK(odil::dcmtk::convert(source_item) == destination_item);
}
}
BOOST_AUTO_TEST_CASE(EmptyDataSetFromDcmtkpp)
{
- dcmtkpp::DataSet empty;
- DcmItem * result = dcmtkpp::convert(empty);
+ odil::DataSet empty;
+ DcmItem * result = odil::dcmtk::convert(empty);
BOOST_CHECK_EQUAL(result->card(), 0);
}
BOOST_AUTO_TEST_CASE(EmptyDataSetFromDcmtk)
{
DcmDataset empty;
- dcmtkpp::DataSet const result = dcmtkpp::convert(&empty);
+ odil::DataSet const result = odil::dcmtk::convert(&empty);
BOOST_CHECK(result.empty());
}
BOOST_AUTO_TEST_CASE(DataSetFromDcmtkpp)
{
- dcmtkpp::Element const patient_id_source(
- dcmtkpp::Value::Strings({"DJ1234"}), dcmtkpp::VR::CS);
- dcmtkpp::Element const pixel_spacing_source(
- dcmtkpp::Value::Reals({1.23, 4.56}), dcmtkpp::VR::DS);
+ odil::Element const patient_id_source(
+ odil::Value::Strings({"DJ1234"}), odil::VR::CS);
+ odil::Element const pixel_spacing_source(
+ odil::Value::Reals({1.23, 4.56}), odil::VR::DS);
- dcmtkpp::DataSet source;
- source.add(dcmtkpp::Tag("PatientID"), patient_id_source);
- source.add(dcmtkpp::Tag("PixelSpacing"), pixel_spacing_source);
+ odil::DataSet source;
+ source.add(odil::Tag("PatientID"), patient_id_source);
+ source.add(odil::Tag("PixelSpacing"), pixel_spacing_source);
- DcmItem * result = dcmtkpp::convert(source);
+ DcmItem * result = odil::dcmtk::convert(source);
BOOST_CHECK_EQUAL(result->card(), 2);
DcmElement * patient_id;
@@ -389,7 +389,7 @@ BOOST_AUTO_TEST_CASE(DataSetFromDcmtkpp)
result->findAndGetElement(DCM_PatientID, patient_id);
BOOST_CHECK(patient_id_ok.good());
BOOST_CHECK(
- dcmtkpp::convert(patient_id).as_string() ==
+ odil::dcmtk::convert(patient_id).as_string() ==
patient_id_source.as_string());
DcmElement * pixel_spacing;
@@ -397,27 +397,27 @@ BOOST_AUTO_TEST_CASE(DataSetFromDcmtkpp)
result->findAndGetElement(DCM_PixelSpacing, pixel_spacing);
BOOST_CHECK(pixel_spacing_ok.good());
BOOST_CHECK(
- dcmtkpp::convert(pixel_spacing).as_real() ==
+ odil::dcmtk::convert(pixel_spacing).as_real() ==
pixel_spacing_source.as_real());
}
BOOST_AUTO_TEST_CASE(DataSetFromDcmtk)
{
- dcmtkpp::Element const patient_id_source(
- dcmtkpp::Value::Strings({"DJ1234"}), dcmtkpp::VR::CS);
- dcmtkpp::Element const pixel_spacing_source(
- dcmtkpp::Value::Reals({1.23, 4.56}), dcmtkpp::VR::DS);
+ odil::Element const patient_id_source(
+ odil::Value::Strings({"DJ1234"}), odil::VR::CS);
+ odil::Element const pixel_spacing_source(
+ odil::Value::Reals({1.23, 4.56}), odil::VR::DS);
DcmDataset source;
- source.insert(dcmtkpp::convert(dcmtkpp::Tag("PatientID"), patient_id_source));
- source.insert(dcmtkpp::convert(dcmtkpp::Tag("PixelSpacing"), pixel_spacing_source));
+ source.insert(odil::dcmtk::convert(odil::Tag("PatientID"), patient_id_source));
+ source.insert(odil::dcmtk::convert(odil::Tag("PixelSpacing"), pixel_spacing_source));
- dcmtkpp::DataSet const result = dcmtkpp::convert(&source);
+ odil::DataSet const result = odil::dcmtk::convert(&source);
BOOST_CHECK_EQUAL(result.size(), 2);
BOOST_CHECK(
- result.as_string(dcmtkpp::Tag("PatientID")) ==
+ result.as_string(odil::Tag("PatientID")) ==
patient_id_source.as_string());
BOOST_CHECK(
- result.as_real(dcmtkpp::Tag("PixelSpacing")) ==
+ result.as_real(odil::Tag("PixelSpacing")) ==
pixel_spacing_source.as_real());
}
diff --git a/tests/code/dul/StateMachine.cpp b/tests/code/dul/StateMachine.cpp
new file mode 100644
index 0000000..0b6a9ad
--- /dev/null
+++ b/tests/code/dul/StateMachine.cpp
@@ -0,0 +1,22 @@
+#define BOOST_TEST_MODULE StateMachine
+#include <boost/test/unit_test.hpp>
+
+#include "odil/Exception.h"
+#include "odil/dul/EventData.h"
+#include "odil/dul/StateMachine.h"
+
+BOOST_AUTO_TEST_CASE(Constructor)
+{
+ odil::dul::StateMachine state_machine;
+ BOOST_REQUIRE(
+ state_machine.get_state() == odil::dul::StateMachine::State::Sta1);
+}
+
+BOOST_AUTO_TEST_CASE(WrongTransition)
+{
+ odil::dul::StateMachine state_machine;
+ odil::dul::EventData data;
+ BOOST_REQUIRE_THROW(
+ state_machine.transition(odil::dul::StateMachine::Event::None, data),
+ odil::Exception);
+}
diff --git a/tests/code/dul/Transport.cpp b/tests/code/dul/Transport.cpp
new file mode 100644
index 0000000..fd50fea
--- /dev/null
+++ b/tests/code/dul/Transport.cpp
@@ -0,0 +1,45 @@
+#define BOOST_TEST_MODULE Transport
+#include <boost/test/unit_test.hpp>
+
+#include <chrono>
+#include <boost/asio.hpp>
+
+#include "odil/Exception.h"
+#include "odil/dul/Transport.h"
+
+BOOST_AUTO_TEST_CASE(Constructor)
+{
+ odil::dul::Transport transport;
+ BOOST_REQUIRE(!transport.is_open());
+}
+
+BOOST_AUTO_TEST_CASE(Connect)
+{
+ odil::dul::Transport transport;
+
+ boost::asio::ip::tcp::resolver resolver(transport.get_service());
+ boost::asio::ip::tcp::resolver::query const query(
+ odil::dul::Transport::Socket::protocol_type::v4(),
+ "www.example.com", "80");
+ auto const endpoint_it = resolver.resolve(query);
+
+ transport.connect(*endpoint_it);
+ BOOST_REQUIRE(transport.is_open());
+
+ transport.write("HEAD / HTTP/1.1\r\nHost: www.example.com\r\n\r\n");
+
+ auto const response = transport.read(128);
+ BOOST_REQUIRE(!response.empty());
+
+ transport.close();
+ BOOST_REQUIRE(!transport.is_open());
+}
+
+BOOST_AUTO_TEST_CASE(NotConnected)
+{
+ odil::dul::Transport transport;
+
+ BOOST_REQUIRE_THROW(transport.write("..."), odil::Exception);
+ BOOST_REQUIRE_THROW(transport.read(1), odil::Exception);
+ BOOST_REQUIRE_THROW(transport.close(), odil::Exception);
+}
diff --git a/tests/code/endian.cpp b/tests/code/endian.cpp
new file mode 100644
index 0000000..c02bf11
--- /dev/null
+++ b/tests/code/endian.cpp
@@ -0,0 +1,89 @@
+#define BOOST_TEST_MODULE endian
+#include <boost/test/unit_test.hpp>
+
+#include <cstdint>
+#include "odil/endian.h"
+
+BOOST_AUTO_TEST_CASE(ToLittleEndian16)
+{
+ uint16_t const input = 0x1234;
+ std::string const expected("\x34\x12");
+ BOOST_REQUIRE_EQUAL(
+ odil::host_to_little_endian(input),
+ *reinterpret_cast<uint16_t const *>(&expected[0])
+ );
+}
+
+BOOST_AUTO_TEST_CASE(ToLittleEndian32)
+{
+ uint32_t const input = 0x12345678;
+ std::string const expected("\x78\x56\x34\x12");
+ BOOST_REQUIRE_EQUAL(
+ odil::host_to_little_endian(input),
+ *reinterpret_cast<uint32_t const *>(&expected[0])
+ );
+}
+
+BOOST_AUTO_TEST_CASE(ToBigEndian16)
+{
+ uint16_t const input = 0x1234;
+ std::string const expected("\x12\x34");
+ BOOST_REQUIRE_EQUAL(
+ odil::host_to_big_endian(input),
+ *reinterpret_cast<uint16_t const *>(&expected[0])
+ );
+}
+
+BOOST_AUTO_TEST_CASE(ToBigEndian32)
+{
+ uint32_t const input = 0x12345678;
+ std::string const expected("\x12\x34\x56\x78");
+ BOOST_REQUIRE_EQUAL(
+ odil::host_to_big_endian(input),
+ *reinterpret_cast<uint32_t const *>(&expected[0])
+ );
+}
+
+BOOST_AUTO_TEST_CASE(FromLittleEndian16)
+{
+ std::string const input("\x34\x12");
+ uint16_t const expected = 0x1234;
+ BOOST_REQUIRE_EQUAL(
+ odil::little_endian_to_host(
+ *reinterpret_cast<uint16_t const *>(&input[0])),
+ expected
+ );
+}
+
+BOOST_AUTO_TEST_CASE(FromLittleEndian32)
+{
+ std::string const input("\x78\x56\x34\x12");
+ uint32_t const expected = 0x12345678;
+ BOOST_REQUIRE_EQUAL(
+ odil::little_endian_to_host(
+ *reinterpret_cast<uint32_t const *>(&input[0])),
+ expected
+ );
+}
+
+BOOST_AUTO_TEST_CASE(FromBigEndian16)
+{
+ std::string const input("\x12\x34");
+ uint16_t const expected = 0x1234;
+ BOOST_REQUIRE_EQUAL(
+ odil::big_endian_to_host(
+ *reinterpret_cast<uint16_t const *>(&input[0])),
+ expected
+ );
+}
+
+BOOST_AUTO_TEST_CASE(FromBigEndian32)
+{
+ std::string const input("\x12\x34\x56\x78");
+ uint32_t const expected = 0x12345678;
+ BOOST_REQUIRE_EQUAL(
+ odil::big_endian_to_host(
+ *reinterpret_cast<uint32_t const *>(&input[0])),
+ expected
+ );
+}
diff --git a/tests/code/json_converter.cpp b/tests/code/json_converter.cpp
index 8e1200a..c4f9607 100644
--- a/tests/code/json_converter.cpp
+++ b/tests/code/json_converter.cpp
@@ -5,18 +5,18 @@
#include <string>
#include <vector>
-#include <jsoncpp/json/json.h>
+#include <json/json.h>
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Element.h"
-#include "dcmtkpp/json_converter.h"
-#include "dcmtkpp/Value.h"
-#include "dcmtkpp/VR.h"
+#include "odil/DataSet.h"
+#include "odil/Element.h"
+#include "odil/json_converter.h"
+#include "odil/Value.h"
+#include "odil/VR.h"
-BOOST_AUTO_TEST_CASE(AsJSONEmpty)
+BOOST_AUTO_TEST_CASE(AsJSONEmptyDataSet)
{
- dcmtkpp::DataSet data_set;
- auto const json = dcmtkpp::as_json(data_set);
+ odil::DataSet data_set;
+ auto const json = odil::as_json(data_set);
BOOST_REQUIRE(json.empty());
}
@@ -49,12 +49,22 @@ void check_json_string(Json::Value const & object, std::string const & expected_
BOOST_REQUIRE_EQUAL(object.asString(), expected_value);
}
+BOOST_AUTO_TEST_CASE(AsJSONEmptyElement)
+{
+ odil::DataSet data_set;
+ data_set.add(0xdeadbeef, odil::VR::SS);
+ auto const json = odil::as_json(data_set);
+ check_json_object(json, {"deadbeef"});
+ check_json_object(json["deadbeef"], {"vr"});
+ check_json_string(json["deadbeef"]["vr"], "SS");
+}
+
BOOST_AUTO_TEST_CASE(AsJSONIntegers)
{
- dcmtkpp::DataSet data_set;
+ odil::DataSet data_set;
data_set.add(0xdeadbeef,
- dcmtkpp::Element(dcmtkpp::Value::Integers({1, 2}), dcmtkpp::VR::SS));
- auto const json = dcmtkpp::as_json(data_set);
+ odil::Element(odil::Value::Integers({1, 2}), odil::VR::SS));
+ auto const json = odil::as_json(data_set);
check_json_object(json, {"deadbeef"});
check_json_object(json["deadbeef"], {"vr", "Value"});
check_json_string(json["deadbeef"]["vr"], "SS");
@@ -64,10 +74,10 @@ BOOST_AUTO_TEST_CASE(AsJSONIntegers)
BOOST_AUTO_TEST_CASE(AsJSONReals)
{
- dcmtkpp::DataSet data_set;
+ odil::DataSet data_set;
data_set.add(0xdeadbeef,
- dcmtkpp::Element(dcmtkpp::Value::Reals({1.2, 3.4}), dcmtkpp::VR::FL));
- auto const json = dcmtkpp::as_json(data_set);
+ odil::Element(odil::Value::Reals({1.2, 3.4}), odil::VR::FL));
+ auto const json = odil::as_json(data_set);
check_json_object(json, {"deadbeef"});
check_json_object(json["deadbeef"], {"vr", "Value"});
check_json_string(json["deadbeef"]["vr"], "FL");
@@ -77,12 +87,12 @@ BOOST_AUTO_TEST_CASE(AsJSONReals)
BOOST_AUTO_TEST_CASE(AsJSONStrings)
{
- dcmtkpp::DataSet data_set;
+ odil::DataSet data_set;
data_set.add(0xdeadbeef,
- dcmtkpp::Element(
- dcmtkpp::Value::Strings({"FOO", "BAR"}),
- dcmtkpp::VR::CS));
- auto const json = dcmtkpp::as_json(data_set);
+ odil::Element(
+ odil::Value::Strings({"FOO", "BAR"}),
+ odil::VR::CS));
+ auto const json = odil::as_json(data_set);
check_json_object(json, {"deadbeef"});
check_json_object(json["deadbeef"], {"vr", "Value"});
check_json_string(json["deadbeef"]["vr"], "CS");
@@ -92,12 +102,12 @@ BOOST_AUTO_TEST_CASE(AsJSONStrings)
BOOST_AUTO_TEST_CASE(AsJSONPersonName)
{
- dcmtkpp::DataSet data_set;
+ odil::DataSet data_set;
data_set.add(0xdeadbeef,
- dcmtkpp::Element(
- dcmtkpp::Value::Strings({"Alpha^Betic=Ideo^Graphic=Pho^Netic"}),
- dcmtkpp::VR::PN));
- auto const json = dcmtkpp::as_json(data_set);
+ odil::Element(
+ odil::Value::Strings({"Alpha^Betic=Ideo^Graphic=Pho^Netic"}),
+ odil::VR::PN));
+ auto const json = odil::as_json(data_set);
check_json_object(json, {"deadbeef"});
check_json_object(json["deadbeef"], {"vr", "Value"});
check_json_string(json["deadbeef"]["vr"], "PN");
@@ -113,16 +123,16 @@ BOOST_AUTO_TEST_CASE(AsJSONPersonName)
BOOST_AUTO_TEST_CASE(AsJSONDataSets)
{
- dcmtkpp::DataSet item;
+ odil::DataSet item;
item.add(0xbeeff00d,
- dcmtkpp::Element(dcmtkpp::Value::Integers({1,2}), dcmtkpp::VR::SS));
- dcmtkpp::DataSet data_set;
+ odil::Element(odil::Value::Integers({1,2}), odil::VR::SS));
+ odil::DataSet data_set;
data_set.add(0xdeadbeef,
- dcmtkpp::Element(
- dcmtkpp::Value::DataSets({item}),
- dcmtkpp::VR::SQ));
+ odil::Element(
+ odil::Value::DataSets({item}),
+ odil::VR::SQ));
- auto const json = dcmtkpp::as_json(data_set);
+ auto const json = odil::as_json(data_set);
check_json_object(json, {"deadbeef"});
check_json_object(json["deadbeef"], {"vr", "Value"});
@@ -140,13 +150,13 @@ BOOST_AUTO_TEST_CASE(AsJSONDataSets)
BOOST_AUTO_TEST_CASE(AsJSONBinary)
{
- dcmtkpp::DataSet data_set;
+ odil::DataSet data_set;
data_set.add(0xdeadbeef,
- dcmtkpp::Element(
- dcmtkpp::Value::Binary({0x1, 0x2, 0x3, 0x4, 0x5}),
- dcmtkpp::VR::OB));
+ odil::Element(
+ odil::Value::Binary({0x1, 0x2, 0x3, 0x4, 0x5}),
+ odil::VR::OB));
- auto const json = dcmtkpp::as_json(data_set);
+ auto const json = odil::as_json(data_set);
check_json_object(json, {"deadbeef"});
check_json_object(json["deadbeef"], {"vr", "InlineBinary"});
@@ -161,7 +171,7 @@ BOOST_AUTO_TEST_CASE(AsDataSetEmpty)
Json::Value json;
data >> json;
- dcmtkpp::DataSet const data_set = dcmtkpp::as_dataset(json);
+ odil::DataSet const data_set = odil::as_dataset(json);
BOOST_REQUIRE(data_set.empty());
}
@@ -172,12 +182,12 @@ BOOST_AUTO_TEST_CASE(AsDataSetIntegers)
Json::Value json;
data >> json;
- dcmtkpp::DataSet const data_set = dcmtkpp::as_dataset(json);
+ odil::DataSet const data_set = odil::as_dataset(json);
BOOST_REQUIRE_EQUAL(data_set.size(), 1);
BOOST_REQUIRE(data_set.has("deadbeef"));
- BOOST_REQUIRE(data_set.get_vr("deadbeef") == dcmtkpp::VR::SS);
+ BOOST_REQUIRE(data_set.get_vr("deadbeef") == odil::VR::SS);
BOOST_REQUIRE(data_set.is_int("deadbeef"));
- BOOST_REQUIRE(data_set.as_int("deadbeef") == dcmtkpp::Value::Integers({1, 2}));
+ BOOST_REQUIRE(data_set.as_int("deadbeef") == odil::Value::Integers({1, 2}));
}
BOOST_AUTO_TEST_CASE(AsDataSetReals)
@@ -187,12 +197,12 @@ BOOST_AUTO_TEST_CASE(AsDataSetReals)
Json::Value json;
data >> json;
- dcmtkpp::DataSet const data_set = dcmtkpp::as_dataset(json);
+ odil::DataSet const data_set = odil::as_dataset(json);
BOOST_REQUIRE_EQUAL(data_set.size(), 1);
BOOST_REQUIRE(data_set.has("deadbeef"));
- BOOST_REQUIRE(data_set.get_vr("deadbeef") == dcmtkpp::VR::FL);
+ BOOST_REQUIRE(data_set.get_vr("deadbeef") == odil::VR::FL);
BOOST_REQUIRE(data_set.is_real("deadbeef"));
- BOOST_REQUIRE(data_set.as_real("deadbeef") == dcmtkpp::Value::Reals({1.2, 3.4}));
+ BOOST_REQUIRE(data_set.as_real("deadbeef") == odil::Value::Reals({1.2, 3.4}));
}
BOOST_AUTO_TEST_CASE(AsDataSetStrings)
@@ -202,12 +212,12 @@ BOOST_AUTO_TEST_CASE(AsDataSetStrings)
Json::Value json;
data >> json;
- dcmtkpp::DataSet const data_set = dcmtkpp::as_dataset(json);
+ odil::DataSet const data_set = odil::as_dataset(json);
BOOST_REQUIRE_EQUAL(data_set.size(), 1);
BOOST_REQUIRE(data_set.has("deadbeef"));
- BOOST_REQUIRE(data_set.get_vr("deadbeef") == dcmtkpp::VR::CS);
+ BOOST_REQUIRE(data_set.get_vr("deadbeef") == odil::VR::CS);
BOOST_REQUIRE(data_set.is_string("deadbeef"));
- BOOST_REQUIRE(data_set.as_string("deadbeef") == dcmtkpp::Value::Strings({"FOO", "BAR"}));
+ BOOST_REQUIRE(data_set.as_string("deadbeef") == odil::Value::Strings({"FOO", "BAR"}));
}
BOOST_AUTO_TEST_CASE(AsDataSetPersonName)
@@ -221,12 +231,12 @@ BOOST_AUTO_TEST_CASE(AsDataSetPersonName)
Json::Value json;
data >> json;
- dcmtkpp::DataSet const data_set = dcmtkpp::as_dataset(json);
+ odil::DataSet const data_set = odil::as_dataset(json);
BOOST_REQUIRE_EQUAL(data_set.size(), 1);
BOOST_REQUIRE(data_set.has("deadbeef"));
- BOOST_REQUIRE(data_set.get_vr("deadbeef") == dcmtkpp::VR::PN);
+ BOOST_REQUIRE(data_set.get_vr("deadbeef") == odil::VR::PN);
BOOST_REQUIRE(data_set.is_string("deadbeef"));
- BOOST_REQUIRE(data_set.as_string("deadbeef") == dcmtkpp::Value::Strings(
+ BOOST_REQUIRE(data_set.as_string("deadbeef") == odil::Value::Strings(
{"Alpha^Betic=Ideo^Graphic=Pho^Netic"}));
}
@@ -239,16 +249,16 @@ BOOST_AUTO_TEST_CASE(AsDataSetDataSets)
Json::Value json;
data >> json;
- dcmtkpp::DataSet const data_set = dcmtkpp::as_dataset(json);
+ odil::DataSet const data_set = odil::as_dataset(json);
BOOST_REQUIRE_EQUAL(data_set.size(), 1);
BOOST_REQUIRE(data_set.has("deadbeef"));
- BOOST_REQUIRE(data_set.get_vr("deadbeef") == dcmtkpp::VR::SQ);
+ BOOST_REQUIRE(data_set.get_vr("deadbeef") == odil::VR::SQ);
BOOST_REQUIRE(data_set.is_data_set("deadbeef"));
- dcmtkpp::DataSet item;
+ odil::DataSet item;
item.add(0xbeeff00d,
- dcmtkpp::Element(dcmtkpp::Value::Integers({1,2}), dcmtkpp::VR::SS));
- BOOST_REQUIRE(data_set.as_data_set("deadbeef") == dcmtkpp::Value::DataSets({item}));
+ odil::Element(odil::Value::Integers({1,2}), odil::VR::SS));
+ BOOST_REQUIRE(data_set.as_data_set("deadbeef") == odil::Value::DataSets({item}));
}
@@ -259,11 +269,11 @@ BOOST_AUTO_TEST_CASE(AsDataSetBinary)
Json::Value json;
data >> json;
- dcmtkpp::DataSet const data_set = dcmtkpp::as_dataset(json);
+ odil::DataSet const data_set = odil::as_dataset(json);
BOOST_REQUIRE_EQUAL(data_set.size(), 1);
BOOST_REQUIRE(data_set.has("deadbeef"));
- BOOST_REQUIRE(data_set.get_vr("deadbeef") == dcmtkpp::VR::OB);
+ BOOST_REQUIRE(data_set.get_vr("deadbeef") == odil::VR::OB);
BOOST_REQUIRE(data_set.is_binary("deadbeef"));
- BOOST_REQUIRE(data_set.as_binary("deadbeef") == dcmtkpp::Value::Binary(
+ BOOST_REQUIRE(data_set.as_binary("deadbeef") == odil::Value::Binary(
{0x1, 0x2, 0x3, 0x4, 0x5}));
}
diff --git a/tests/code/message/CEchoRequest.cpp b/tests/code/message/CEchoRequest.cpp
new file mode 100644
index 0000000..5748aaa
--- /dev/null
+++ b/tests/code/message/CEchoRequest.cpp
@@ -0,0 +1,60 @@
+#define BOOST_TEST_MODULE CEchoRequest
+#include <boost/test/unit_test.hpp>
+
+#include "odil/message/CEchoRequest.h"
+#include "odil/DataSet.h"
+#include "odil/message/Message.h"
+#include "odil/registry.h"
+
+#include "../../MessageFixtureBase.h"
+
+struct Fixture: public MessageFixtureBase<odil::message::CEchoRequest>
+{
+ odil::DataSet command_set;
+
+ Fixture()
+ {
+ this->command_set.add(
+ "CommandField", {odil::message::Message::Command::C_ECHO_RQ});
+ this->command_set.add("MessageID", {1234});
+ this->command_set.add(
+ "AffectedSOPClassUID", {odil::registry::VerificationSOPClass});
+ }
+
+ void check(odil::message::CEchoRequest const & message)
+ {
+ BOOST_CHECK_EQUAL(
+ message.get_command_field(),
+ odil::message::Message::Command::C_ECHO_RQ);
+ BOOST_CHECK_EQUAL(message.get_message_id(), 1234);
+ BOOST_CHECK_EQUAL(
+ message.get_affected_sop_class_uid(),
+ odil::registry::VerificationSOPClass);
+ BOOST_CHECK(!message.has_data_set());
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(Constructor, Fixture)
+{
+ odil::message::CEchoRequest const message(
+ 1234, odil::registry::VerificationSOPClass);
+ this->check(message);
+}
+
+BOOST_FIXTURE_TEST_CASE(MessageConstructor, Fixture)
+{
+ this->check_message_constructor(this->command_set);
+}
+
+BOOST_FIXTURE_TEST_CASE(MessageConstructorWrongCommandField, Fixture)
+{
+ this->command_set.as_int("CommandField") = {
+ odil::message::Message::Command::C_ECHO_RSP};
+ this->check_message_constructor_throw(this->command_set);
+}
+
+BOOST_FIXTURE_TEST_CASE(MessageConstructorMissingAffectSOPClass, Fixture)
+{
+ this->command_set.remove("AffectedSOPClassUID");
+ this->check_message_constructor_throw(this->command_set);
+}
diff --git a/tests/code/message/CEchoResponse.cpp b/tests/code/message/CEchoResponse.cpp
new file mode 100644
index 0000000..82cdeec
--- /dev/null
+++ b/tests/code/message/CEchoResponse.cpp
@@ -0,0 +1,64 @@
+#define BOOST_TEST_MODULE CEchoResponse
+#include <boost/test/unit_test.hpp>
+
+#include "odil/message/CEchoResponse.h"
+#include "odil/DataSet.h"
+#include "odil/message/Message.h"
+#include "odil/registry.h"
+
+#include "../../MessageFixtureBase.h"
+
+struct Fixture: public MessageFixtureBase<odil::message::CEchoResponse>
+{
+ odil::DataSet command_set;
+
+ Fixture()
+ {
+ this->command_set.add(
+ "CommandField", {odil::message::Message::Command::C_ECHO_RSP});
+ this->command_set.add("MessageIDBeingRespondedTo", {1234});
+ this->command_set.add("Status", {odil::message::Response::Success});
+ this->command_set.add(
+ "AffectedSOPClassUID", {odil::registry::VerificationSOPClass});
+ }
+
+ void check(odil::message::CEchoResponse const & message)
+ {
+ BOOST_CHECK_EQUAL(
+ message.get_command_field(),
+ odil::message::Message::Command::C_ECHO_RSP);
+ BOOST_CHECK_EQUAL(message.get_message_id_being_responded_to(), 1234);
+ BOOST_CHECK_EQUAL(
+ message.get_status(), odil::message::Response::Success);
+ BOOST_CHECK_EQUAL(
+ message.get_affected_sop_class_uid(),
+ odil::registry::VerificationSOPClass);
+ BOOST_CHECK(!message.has_data_set());
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(Constructor, Fixture)
+{
+ odil::message::CEchoResponse const message(
+ 1234, odil::message::Response::Success,
+ odil::registry::VerificationSOPClass);
+ this->check(message);
+}
+
+BOOST_FIXTURE_TEST_CASE(MessageConstructor, Fixture)
+{
+ this->check_message_constructor(this->command_set);
+}
+
+BOOST_FIXTURE_TEST_CASE(MessageConstructorWrongCommandField, Fixture)
+{
+ this->command_set.as_int("CommandField") = {
+ odil::message::Message::Command::C_ECHO_RQ};
+ this->check_message_constructor_throw(this->command_set);
+}
+
+BOOST_FIXTURE_TEST_CASE(MessageConstructorMissingAffectSOPClass, Fixture)
+{
+ this->command_set.remove("AffectedSOPClassUID");
+ this->check_message_constructor_throw(this->command_set);
+}
diff --git a/tests/code/CFindRequest.cpp b/tests/code/message/CFindRequest.cpp
similarity index 58%
rename from tests/code/CFindRequest.cpp
rename to tests/code/message/CFindRequest.cpp
index 32f7fac..6054e2b 100644
--- a/tests/code/CFindRequest.cpp
+++ b/tests/code/message/CFindRequest.cpp
@@ -1,50 +1,51 @@
#define BOOST_TEST_MODULE CFindRequest
#include <boost/test/unit_test.hpp>
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/dimse.h>
+#include "odil/message/CFindRequest.h"
+#include "odil/DataSet.h"
+#include "odil/message/Message.h"
+#include "odil/registry.h"
-#include "dcmtkpp/CFindRequest.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Message.h"
-#include "dcmtkpp/registry.h"
+#include "../../MessageFixtureBase.h"
-#include "../MessageFixtureBase.h"
-
-struct Fixture: public MessageFixtureBase<dcmtkpp::CFindRequest>
+struct Fixture: public MessageFixtureBase<odil::message::CFindRequest>
{
- dcmtkpp::DataSet command_set;
- dcmtkpp::DataSet query;
+ odil::DataSet command_set;
+ odil::DataSet query;
Fixture()
{
- this->command_set.add("CommandField", {dcmtkpp::Message::Command::C_FIND_RQ});
+ this->command_set.add(
+ "CommandField", {odil::message::Message::Command::C_FIND_RQ});
this->command_set.add("MessageID", {1234});
this->command_set.add("AffectedSOPClassUID",
- {dcmtkpp::registry::PatientRootQueryRetrieveInformationModelFIND});
- this->command_set.add("Priority", {DIMSE_PRIORITY_MEDIUM});
+ {odil::registry::PatientRootQueryRetrieveInformationModelFIND});
+ this->command_set.add(
+ "Priority", {odil::message::Message::Priority::MEDIUM});
this->query.add("PatientName", {"Doe^John"});
this->query.add("StudyDescription", {"Brain"});
this->query.add("QueryRetrieveLevel", {"STUDY"});
}
- virtual void check(dcmtkpp::CFindRequest const & message)
+ virtual void check(odil::message::CFindRequest const & message)
{
- BOOST_CHECK_EQUAL(message.get_command_field(), dcmtkpp::Message::Command::C_FIND_RQ);
+ BOOST_CHECK_EQUAL(
+ message.get_command_field(),
+ odil::message::Message::Command::C_FIND_RQ);
BOOST_CHECK_EQUAL(message.get_message_id(), 1234);
BOOST_CHECK_EQUAL(
message.get_affected_sop_class_uid(),
- dcmtkpp::registry::PatientRootQueryRetrieveInformationModelFIND);
+ odil::registry::PatientRootQueryRetrieveInformationModelFIND);
BOOST_CHECK(message.get_data_set() == this->query);
}
};
BOOST_FIXTURE_TEST_CASE(Constructor, Fixture)
{
- dcmtkpp::CFindRequest const message(
- 1234, dcmtkpp::registry::PatientRootQueryRetrieveInformationModelFIND,
- DIMSE_PRIORITY_MEDIUM, this->query);
+ odil::message::CFindRequest const message(
+ 1234, odil::registry::PatientRootQueryRetrieveInformationModelFIND,
+ odil::message::Message::Priority::MEDIUM, this->query);
this->check(message);
}
@@ -55,7 +56,8 @@ BOOST_FIXTURE_TEST_CASE(MessageConstructor, Fixture)
BOOST_FIXTURE_TEST_CASE(MessageConstructorWrongCommandField, Fixture)
{
- this->command_set.as_int("CommandField") = {dcmtkpp::Message::Command::C_ECHO_RQ};
+ this->command_set.as_int("CommandField") = {
+ odil::message::Message::Command::C_ECHO_RQ};
this->check_message_constructor_throw(this->command_set, this->query);
}
@@ -78,6 +80,6 @@ BOOST_FIXTURE_TEST_CASE(MessageConstructorMissingQuery, Fixture)
BOOST_FIXTURE_TEST_CASE(MessageConstructorEmptyQuery, Fixture)
{
- dcmtkpp::DataSet empty;
+ odil::DataSet empty;
this->check_message_constructor_throw(this->command_set, empty);
}
diff --git a/tests/code/message/CFindResponse.cpp b/tests/code/message/CFindResponse.cpp
new file mode 100644
index 0000000..cd160f5
--- /dev/null
+++ b/tests/code/message/CFindResponse.cpp
@@ -0,0 +1,107 @@
+#define BOOST_TEST_MODULE CFindResponse
+#include <boost/test/unit_test.hpp>
+
+#include "odil/message/CFindResponse.h"
+#include "odil/DataSet.h"
+#include "odil/message/Message.h"
+#include "odil/registry.h"
+
+#include "../../MessageFixtureBase.h"
+
+struct Fixture: public MessageFixtureBase<odil::message::CFindResponse>
+{
+ odil::DataSet command_set;
+ odil::DataSet data_set;
+
+ Fixture()
+ {
+ command_set.add(
+ "CommandField", {odil::message::Message::Command::C_FIND_RSP});
+ command_set.add("MessageIDBeingRespondedTo", {1234});
+ command_set.add("Status", {odil::message::Response::Success});
+
+ command_set.add("MessageID", {5678});
+ command_set.add("AffectedSOPClassUID",
+ {odil::registry::StudyRootQueryRetrieveInformationModelFIND});
+
+ data_set.add("PatientName", {"Doe^John"});
+ data_set.add("PatientID", {"DJ123"});
+ data_set.add("StudyDescription", {"Brain"});
+ data_set.add("StudyInstanceUID", {"1.2.3"});
+ }
+
+ virtual void check(odil::message::CFindResponse const & message)
+ {
+ BOOST_CHECK_EQUAL(
+ message.get_command_field(),
+ odil::message::Message::Command::C_FIND_RSP);
+ BOOST_CHECK_EQUAL(message.get_message_id_being_responded_to(), 1234);
+ BOOST_CHECK_EQUAL(
+ message.get_status(), odil::message::Response::Success);
+
+ BOOST_CHECK(message.has_message_id());
+ BOOST_CHECK_EQUAL(message.get_message_id(), 5678);
+
+ BOOST_CHECK(message.has_affected_sop_class_uid());
+ BOOST_CHECK_EQUAL(
+ message.get_affected_sop_class_uid(),
+ odil::registry::StudyRootQueryRetrieveInformationModelFIND);
+
+ BOOST_CHECK(message.has_data_set());
+ BOOST_CHECK(message.get_data_set() == this->data_set);
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(Constructor, Fixture)
+{
+ odil::message::CFindResponse message(
+ 1234, odil::message::Response::Success, this->data_set);
+ message.set_message_id(5678);
+ message.set_affected_sop_class_uid(
+ odil::registry::StudyRootQueryRetrieveInformationModelFIND);
+ this->check(message);
+}
+
+BOOST_FIXTURE_TEST_CASE(MessageConstructor, Fixture)
+{
+ this->check_message_constructor(this->command_set, this->data_set);
+}
+
+BOOST_FIXTURE_TEST_CASE(MessageConstructorWrongCommandField, Fixture)
+{
+ this->command_set.as_int("CommandField") = {
+ odil::message::Message::Command::C_ECHO_RQ};
+ this->check_message_constructor_throw(this->command_set, this->data_set);
+}
+
+BOOST_AUTO_TEST_CASE(StatusPending)
+{
+ std::vector<odil::Value::Integer> const statuses = {
+ odil::message::CFindResponse::PendingWarningOptionalKeysNotSupported,
+ };
+
+ for(auto const status:statuses)
+ {
+ odil::message::CFindResponse response(1234, status);
+ BOOST_REQUIRE(response.is_pending());
+ BOOST_REQUIRE(!response.is_warning());
+ BOOST_REQUIRE(!response.is_failure());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(StatusFailure)
+{
+ std::vector<odil::Value::Integer> const statuses = {
+ odil::message::CFindResponse::RefusedOutOfResources,
+ odil::message::CFindResponse::IdentifierDoesNotMatchSOPClass,
+ odil::message::CFindResponse::UnableToProcess
+ };
+
+ for(auto const status:statuses)
+ {
+ odil::message::CFindResponse response(1234, status);
+ BOOST_REQUIRE(!response.is_pending());
+ BOOST_REQUIRE(!response.is_warning());
+ BOOST_REQUIRE(response.is_failure());
+ }
+}
diff --git a/tests/code/CGetRequest.cpp b/tests/code/message/CGetRequest.cpp
similarity index 56%
rename from tests/code/CGetRequest.cpp
rename to tests/code/message/CGetRequest.cpp
index 5e63da4..f820f03 100644
--- a/tests/code/CGetRequest.cpp
+++ b/tests/code/message/CGetRequest.cpp
@@ -1,42 +1,43 @@
#define BOOST_TEST_MODULE CGetRequest
#include <boost/test/unit_test.hpp>
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/dimse.h>
+#include "odil/message/CGetRequest.h"
+#include "odil/DataSet.h"
+#include "odil/DataSet.h"
+#include "odil/message/Message.h"
+#include "odil/registry.h"
-#include "dcmtkpp/CGetRequest.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Message.h"
-#include "dcmtkpp/registry.h"
+#include "../../MessageFixtureBase.h"
-#include "../MessageFixtureBase.h"
-
-struct Fixture: public MessageFixtureBase<dcmtkpp::CGetRequest>
+struct Fixture: public MessageFixtureBase<odil::message::CGetRequest>
{
- dcmtkpp::DataSet command_set;
- dcmtkpp::DataSet query;
+ odil::DataSet command_set;
+ odil::DataSet query;
Fixture()
{
- this->command_set.add("CommandField", {dcmtkpp::Message::Command::C_GET_RQ});
+ this->command_set.add(
+ "CommandField", {odil::message::Message::Command::C_GET_RQ});
this->command_set.add("MessageID", {1234});
this->command_set.add("AffectedSOPClassUID",
- {dcmtkpp::registry::PatientRootQueryRetrieveInformationModelGET});
- this->command_set.add("Priority", {DIMSE_PRIORITY_MEDIUM});
+ {odil::registry::PatientRootQueryRetrieveInformationModelGET});
+ this->command_set.add(
+ "Priority", {odil::message::Message::Priority::MEDIUM});
this->query.add("PatientName", {"Doe^John"});
this->query.add("StudyDescription", {"Brain"});
this->query.add("QueryRetrieveLevel", {"STUDY"});
}
- virtual void check(dcmtkpp::CGetRequest const & message)
+ virtual void check(odil::message::CGetRequest const & message)
{
- BOOST_CHECK_EQUAL(message.get_command_field(), dcmtkpp::Message::Command::C_GET_RQ);
+ BOOST_CHECK_EQUAL(
+ message.get_command_field(),
+ odil::message::Message::Command::C_GET_RQ);
BOOST_CHECK_EQUAL(message.get_message_id(), 1234);
BOOST_CHECK_EQUAL(
message.get_affected_sop_class_uid(),
- dcmtkpp::registry::PatientRootQueryRetrieveInformationModelGET);
+ odil::registry::PatientRootQueryRetrieveInformationModelGET);
BOOST_CHECK(message.has_data_set());
BOOST_CHECK(message.get_data_set() == this->query);
}
@@ -44,9 +45,9 @@ struct Fixture: public MessageFixtureBase<dcmtkpp::CGetRequest>
BOOST_FIXTURE_TEST_CASE(Constructor, Fixture)
{
- dcmtkpp::CGetRequest const message(
- 1234, dcmtkpp::registry::PatientRootQueryRetrieveInformationModelGET,
- DIMSE_PRIORITY_MEDIUM, this->query);
+ odil::message::CGetRequest const message(
+ 1234, odil::registry::PatientRootQueryRetrieveInformationModelGET,
+ odil::message::Message::Priority::MEDIUM, this->query);
this->check(message);
}
@@ -57,7 +58,8 @@ BOOST_FIXTURE_TEST_CASE(MessageConstructor, Fixture)
BOOST_FIXTURE_TEST_CASE(MessageConstructorWrongCommandField, Fixture)
{
- this->command_set.as_int("CommandField") = {dcmtkpp::Message::Command::C_ECHO_RQ};
+ this->command_set.as_int("CommandField") = {
+ odil::message::Message::Command::C_ECHO_RQ};
this->check_message_constructor_throw(this->command_set, this->query);
}
@@ -75,6 +77,6 @@ BOOST_FIXTURE_TEST_CASE(MessageConstructorMissingPriority, Fixture)
BOOST_FIXTURE_TEST_CASE(MessageConstructorEmptyQuery, Fixture)
{
- dcmtkpp::DataSet empty;
+ odil::DataSet empty;
this->check_message_constructor_throw(this->command_set, empty);
}
diff --git a/tests/code/message/CGetResponse.cpp b/tests/code/message/CGetResponse.cpp
new file mode 100644
index 0000000..d99a97e
--- /dev/null
+++ b/tests/code/message/CGetResponse.cpp
@@ -0,0 +1,129 @@
+#define BOOST_TEST_MODULE CGetResponse
+#include <boost/test/unit_test.hpp>
+
+#include "odil/message/CGetResponse.h"
+#include "odil/DataSet.h"
+#include "odil/message/Message.h"
+#include "odil/registry.h"
+
+#include "../../MessageFixtureBase.h"
+
+struct Fixture: public MessageFixtureBase<odil::message::CGetResponse>
+{
+ odil::DataSet command_set;
+ odil::DataSet data_set;
+
+ Fixture()
+ {
+ this->command_set.add(
+ "CommandField", {odil::message::Message::Command::C_GET_RSP});
+ this->command_set.add("MessageIDBeingRespondedTo", {1234});
+ this->command_set.add("Status", {odil::message::Response::Success});
+
+ this->command_set.add("MessageID", {5678});
+ this->command_set.add("AffectedSOPClassUID",
+ {odil::registry::StudyRootQueryRetrieveInformationModelGET});
+ this->command_set.add(odil::registry::NumberOfRemainingSuboperations, {1});
+ this->command_set.add(odil::registry::NumberOfCompletedSuboperations, {2});
+ this->command_set.add(odil::registry::NumberOfFailedSuboperations, {3});
+ this->command_set.add(odil::registry::NumberOfWarningSuboperations, {4});
+
+ this->data_set.add("PatientName", {"Doe^John"});
+ this->data_set.add("PatientID", {"DJ123"});
+ this->data_set.add("StudyDescription", {"Brain"});
+ this->data_set.add("StudyInstanceUID", {"1.2.3"});
+ }
+
+ virtual void check(odil::message::CGetResponse const & message)
+ {
+ BOOST_CHECK_EQUAL(
+ message.get_command_field(),
+ odil::message::Message::Command::C_GET_RSP);
+ BOOST_CHECK_EQUAL(message.get_message_id_being_responded_to(), 1234);
+ BOOST_CHECK_EQUAL(
+ message.get_status(), odil::message::Response::Success);
+
+ BOOST_CHECK(message.has_message_id());
+ BOOST_CHECK_EQUAL(message.get_message_id(), 5678);
+
+ BOOST_CHECK(message.has_affected_sop_class_uid());
+ BOOST_CHECK_EQUAL(
+ message.get_affected_sop_class_uid(),
+ odil::registry::StudyRootQueryRetrieveInformationModelGET);
+
+ BOOST_CHECK(message.has_number_of_remaining_sub_operations());
+ BOOST_CHECK_EQUAL(message.get_number_of_remaining_sub_operations(), 1);
+
+ BOOST_CHECK(message.has_number_of_completed_sub_operations());
+ BOOST_CHECK_EQUAL(message.get_number_of_completed_sub_operations(), 2);
+
+ BOOST_CHECK(message.has_number_of_failed_sub_operations());
+ BOOST_CHECK_EQUAL(message.get_number_of_failed_sub_operations(), 3);
+
+ BOOST_CHECK(message.has_number_of_warning_sub_operations());
+ BOOST_CHECK_EQUAL(message.get_number_of_warning_sub_operations(), 4);
+
+ BOOST_CHECK(message.has_data_set());
+ BOOST_CHECK(message.get_data_set() == this->data_set);
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(Constructor, Fixture)
+{
+ odil::message::CGetResponse message(
+ 1234, odil::message::Response::Success, this->data_set);
+ message.set_message_id(5678);
+ message.set_affected_sop_class_uid(
+ odil::registry::StudyRootQueryRetrieveInformationModelGET);
+ message.set_number_of_remaining_sub_operations(1);
+ message.set_number_of_completed_sub_operations(2);
+ message.set_number_of_failed_sub_operations(3);
+ message.set_number_of_warning_sub_operations(4);
+
+ this->check(message);
+}
+
+BOOST_FIXTURE_TEST_CASE(MessageConstructor, Fixture)
+{
+ this->check_message_constructor(this->command_set, this->data_set);
+}
+
+BOOST_FIXTURE_TEST_CASE(MessageConstructorWrongCommandField, Fixture)
+{
+ this->command_set.as_int(odil::registry::CommandField) = {
+ odil::message::Message::Command::C_ECHO_RQ };
+ this->check_message_constructor_throw(this->command_set, this->data_set);
+}
+
+BOOST_AUTO_TEST_CASE(StatusWarning)
+{
+ std::vector<odil::Value::Integer> const statuses = {
+ odil::message::CGetResponse::SubOperationsCompleteOneOrMoreFailuresOrWarnings
+ };
+
+ for(auto const status:statuses)
+ {
+ odil::message::CGetResponse response(1234, status);
+ BOOST_REQUIRE(!response.is_pending());
+ BOOST_REQUIRE(response.is_warning());
+ BOOST_REQUIRE(!response.is_failure());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(StatusFailure)
+{
+ std::vector<odil::Value::Integer> const statuses = {
+ odil::message::CGetResponse::RefusedOutOfResourcesUnableToCalculateNumberOfMatches,
+ odil::message::CGetResponse::RefusedOutOfResourcesUnableToPerformSubOperations,
+ odil::message::CGetResponse::IdentifierDoesNotMatchSOPClass,
+ odil::message::CGetResponse::UnableToProcess
+ };
+
+ for(auto const status:statuses)
+ {
+ odil::message::CGetResponse response(1234, status);
+ BOOST_REQUIRE(!response.is_pending());
+ BOOST_REQUIRE(!response.is_warning());
+ BOOST_REQUIRE(response.is_failure());
+ }
+}
diff --git a/tests/code/CMoveRequest.cpp b/tests/code/message/CMoveRequest.cpp
similarity index 58%
rename from tests/code/CMoveRequest.cpp
rename to tests/code/message/CMoveRequest.cpp
index 5d9d10f..fc71965 100644
--- a/tests/code/CMoveRequest.cpp
+++ b/tests/code/message/CMoveRequest.cpp
@@ -1,28 +1,27 @@
#define BOOST_TEST_MODULE CMoveRequest
#include <boost/test/unit_test.hpp>
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/dimse.h>
+#include "odil/message/CMoveRequest.h"
+#include "odil/DataSet.h"
+#include "odil/message/Message.h"
+#include "odil/registry.h"
-#include "dcmtkpp/CMoveRequest.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Message.h"
-#include "dcmtkpp/registry.h"
+#include "../../MessageFixtureBase.h"
-#include "../MessageFixtureBase.h"
-
-struct Fixture: public MessageFixtureBase<dcmtkpp::CMoveRequest>
+struct Fixture: public MessageFixtureBase<odil::message::CMoveRequest>
{
- dcmtkpp::DataSet command_set;
- dcmtkpp::DataSet query;
+ odil::DataSet command_set;
+ odil::DataSet query;
Fixture()
{
- this->command_set.add("CommandField", {dcmtkpp::Message::Command::C_MOVE_RQ});
+ this->command_set.add(
+ "CommandField", {odil::message::Message::Command::C_MOVE_RQ});
this->command_set.add("MessageID", {1234});
this->command_set.add("AffectedSOPClassUID",
- {dcmtkpp::registry::PatientRootQueryRetrieveInformationModelMOVE});
- this->command_set.add("Priority", {DIMSE_PRIORITY_MEDIUM});
+ {odil::registry::PatientRootQueryRetrieveInformationModelMOVE});
+ this->command_set.add(
+ "Priority", {odil::message::Message::Priority::MEDIUM});
this->command_set.add("MoveDestination", {"destination"});
this->query.add("PatientName", {"Doe^John"});
@@ -30,13 +29,15 @@ struct Fixture: public MessageFixtureBase<dcmtkpp::CMoveRequest>
this->query.add("QueryRetrieveLevel", {"STUDY"});
}
- virtual void check(dcmtkpp::CMoveRequest const & message)
+ virtual void check(odil::message::CMoveRequest const & message)
{
- BOOST_CHECK_EQUAL(message.get_command_field(), dcmtkpp::Message::Command::C_MOVE_RQ);
+ BOOST_CHECK_EQUAL(
+ message.get_command_field(),
+ odil::message::Message::Command::C_MOVE_RQ);
BOOST_CHECK_EQUAL(message.get_message_id(), 1234);
BOOST_CHECK_EQUAL(
message.get_affected_sop_class_uid(),
- dcmtkpp::registry::PatientRootQueryRetrieveInformationModelMOVE);
+ odil::registry::PatientRootQueryRetrieveInformationModelMOVE);
BOOST_CHECK_EQUAL(message.get_move_destination(), "destination");
BOOST_CHECK(message.has_data_set());
BOOST_CHECK(message.get_data_set() == this->query);
@@ -45,9 +46,10 @@ struct Fixture: public MessageFixtureBase<dcmtkpp::CMoveRequest>
BOOST_FIXTURE_TEST_CASE(Constructor, Fixture)
{
- dcmtkpp::CMoveRequest const message(
- 1234, dcmtkpp::registry::PatientRootQueryRetrieveInformationModelMOVE,
- DIMSE_PRIORITY_MEDIUM, "destination", this->query);
+ odil::message::CMoveRequest const message(
+ 1234, odil::registry::PatientRootQueryRetrieveInformationModelMOVE,
+ odil::message::Message::Priority::MEDIUM, "destination",
+ this->query);
this->check(message);
}
@@ -58,7 +60,8 @@ BOOST_FIXTURE_TEST_CASE(MessageConstructor, Fixture)
BOOST_FIXTURE_TEST_CASE(MessageConstructorWrongCommandField, Fixture)
{
- this->command_set.as_int("CommandField") = {dcmtkpp::Message::Command::C_ECHO_RQ};
+ this->command_set.as_int("CommandField") = {
+ odil::message::Message::Command::C_ECHO_RQ};
this->check_message_constructor_throw(this->command_set, this->query);
}
@@ -76,6 +79,6 @@ BOOST_FIXTURE_TEST_CASE(MessageConstructorMissingPriority, Fixture)
BOOST_FIXTURE_TEST_CASE(MessageConstructorEmptyQuery, Fixture)
{
- dcmtkpp::DataSet empty;
+ odil::DataSet empty;
this->check_message_constructor_throw(this->command_set, empty);
}
diff --git a/tests/code/message/CMoveResponse.cpp b/tests/code/message/CMoveResponse.cpp
new file mode 100644
index 0000000..593aa6e
--- /dev/null
+++ b/tests/code/message/CMoveResponse.cpp
@@ -0,0 +1,130 @@
+#define BOOST_TEST_MODULE CMoveResponse
+#include <boost/test/unit_test.hpp>
+
+#include "odil/message/CMoveResponse.h"
+#include "odil/DataSet.h"
+#include "odil/message/Message.h"
+#include "odil/registry.h"
+
+#include "../../MessageFixtureBase.h"
+
+struct Fixture: public MessageFixtureBase<odil::message::CMoveResponse>
+{
+ odil::DataSet command_set;
+ odil::DataSet data_set;
+
+ Fixture()
+ {
+ this->command_set.add(
+ "CommandField", {odil::message::Message::Command::C_MOVE_RSP});
+ this->command_set.add("MessageIDBeingRespondedTo", {1234});
+ this->command_set.add("Status", {odil::message::Response::Success});
+
+ this->command_set.add("MessageID", {5678});
+ this->command_set.add("AffectedSOPClassUID",
+ {odil::registry::StudyRootQueryRetrieveInformationModelMOVE});
+ this->command_set.add(odil::registry::NumberOfRemainingSuboperations, {1});
+ this->command_set.add(odil::registry::NumberOfCompletedSuboperations, {2});
+ this->command_set.add(odil::registry::NumberOfFailedSuboperations, {3});
+ this->command_set.add(odil::registry::NumberOfWarningSuboperations, {4});
+
+ this->data_set.add("PatientName", {"Doe^John"});
+ this->data_set.add("PatientID", {"DJ123"});
+ this->data_set.add("StudyDescription", {"Brain"});
+ this->data_set.add("StudyInstanceUID", {"1.2.3"});
+ }
+
+ virtual void check(odil::message::CMoveResponse const & message)
+ {
+ BOOST_CHECK_EQUAL(
+ message.get_command_field(),
+ odil::message::Message::Command::C_MOVE_RSP);
+ BOOST_CHECK_EQUAL(message.get_message_id_being_responded_to(), 1234);
+ BOOST_CHECK_EQUAL(
+ message.get_status(), odil::message::Response::Success);
+
+ BOOST_CHECK(message.has_message_id());
+ BOOST_CHECK_EQUAL(message.get_message_id(), 5678);
+
+ BOOST_CHECK(message.has_affected_sop_class_uid());
+ BOOST_CHECK_EQUAL(
+ message.get_affected_sop_class_uid(),
+ odil::registry::StudyRootQueryRetrieveInformationModelMOVE);
+
+ BOOST_CHECK(message.has_number_of_remaining_sub_operations());
+ BOOST_CHECK_EQUAL(message.get_number_of_remaining_sub_operations(), 1);
+
+ BOOST_CHECK(message.has_number_of_completed_sub_operations());
+ BOOST_CHECK_EQUAL(message.get_number_of_completed_sub_operations(), 2);
+
+ BOOST_CHECK(message.has_number_of_failed_sub_operations());
+ BOOST_CHECK_EQUAL(message.get_number_of_failed_sub_operations(), 3);
+
+ BOOST_CHECK(message.has_number_of_warning_sub_operations());
+ BOOST_CHECK_EQUAL(message.get_number_of_warning_sub_operations(), 4);
+
+ BOOST_CHECK(message.has_data_set());
+ BOOST_CHECK(message.get_data_set() == this->data_set);
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(Constructor, Fixture)
+{
+ odil::message::CMoveResponse message(
+ 1234, odil::message::Response::Success, this->data_set);
+ message.set_message_id(5678);
+ message.set_affected_sop_class_uid(
+ odil::registry::StudyRootQueryRetrieveInformationModelMOVE);
+ message.set_number_of_remaining_sub_operations(1);
+ message.set_number_of_completed_sub_operations(2);
+ message.set_number_of_failed_sub_operations(3);
+ message.set_number_of_warning_sub_operations(4);
+
+ this->check(message);
+}
+
+BOOST_FIXTURE_TEST_CASE(MessageConstructor, Fixture)
+{
+ this->check_message_constructor(this->command_set, this->data_set);
+}
+
+BOOST_FIXTURE_TEST_CASE(MessageConstructorWrongCommandField, Fixture)
+{
+ this->command_set.as_int("CommandField") = {
+ odil::message::Message::Command::C_ECHO_RQ};
+ this->check_message_constructor_throw(this->command_set, this->data_set);
+}
+
+BOOST_AUTO_TEST_CASE(StatusWarning)
+{
+ std::vector<odil::Value::Integer> const statuses = {
+ odil::message::CMoveResponse::SubOperationsCompleteOneOrMoreFailuresOrWarnings
+ };
+
+ for(auto const status:statuses)
+ {
+ odil::message::CMoveResponse response(1234, status);
+ BOOST_REQUIRE(!response.is_pending());
+ BOOST_REQUIRE(response.is_warning());
+ BOOST_REQUIRE(!response.is_failure());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(StatusFailure)
+{
+ std::vector<odil::Value::Integer> const statuses = {
+ odil::message::CMoveResponse::RefusedOutOfResourcesUnableToCalculateNumberOfMatches,
+ odil::message::CMoveResponse::RefusedOutOfResourcesUnableToPerformSubOperations,
+ odil::message::CMoveResponse::RefusedMoveDestinationUnknown,
+ odil::message::CMoveResponse::IdentifierDoesNotMatchSOPClass,
+ odil::message::CMoveResponse::UnableToProcess
+ };
+
+ for(auto const status:statuses)
+ {
+ odil::message::CMoveResponse response(1234, status);
+ BOOST_REQUIRE(!response.is_pending());
+ BOOST_REQUIRE(!response.is_warning());
+ BOOST_REQUIRE(response.is_failure());
+ }
+}
diff --git a/tests/code/CStoreRequest.cpp b/tests/code/message/CStoreRequest.cpp
similarity index 65%
rename from tests/code/CStoreRequest.cpp
rename to tests/code/message/CStoreRequest.cpp
index fd926db..9d0162f 100644
--- a/tests/code/CStoreRequest.cpp
+++ b/tests/code/message/CStoreRequest.cpp
@@ -1,28 +1,28 @@
#define BOOST_TEST_MODULE CStoreRequest
#include <boost/test/unit_test.hpp>
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmnet/dimse.h>
+#include "odil/message/CStoreRequest.h"
+#include "odil/DataSet.h"
+#include "odil/message/Message.h"
+#include "odil/registry.h"
-#include "dcmtkpp/CStoreRequest.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Message.h"
-#include "dcmtkpp/registry.h"
+#include "../../MessageFixtureBase.h"
-#include "../MessageFixtureBase.h"
-
-struct Fixture: public MessageFixtureBase<dcmtkpp::CStoreRequest>
+struct Fixture: public MessageFixtureBase<odil::message::CStoreRequest>
{
- dcmtkpp::DataSet command_set;
- dcmtkpp::DataSet data_set;
+ odil::DataSet command_set;
+ odil::DataSet data_set;
Fixture()
{
- this->command_set.add("CommandField", {dcmtkpp::Message::Command::C_STORE_RQ});
+ this->command_set.add(
+ "CommandField", {odil::message::Message::Command::C_STORE_RQ});
this->command_set.add("MessageID", {1234});
- this->command_set.add("AffectedSOPClassUID", {dcmtkpp::registry::MRImageStorage});
+ this->command_set.add(
+ "AffectedSOPClassUID", {odil::registry::MRImageStorage});
this->command_set.add("AffectedSOPInstanceUID", {"1.2.3.4"});
- this->command_set.add("Priority", {DIMSE_PRIORITY_MEDIUM});
+ this->command_set.add(
+ "Priority", {odil::message::Message::Priority::MEDIUM});
this->command_set.add("MoveOriginatorApplicationEntityTitle", {"origin"});
this->command_set.add("MoveOriginatorMessageID", {5678});
@@ -33,12 +33,14 @@ struct Fixture: public MessageFixtureBase<dcmtkpp::CStoreRequest>
this->data_set.add("StudyInstanceUID", {"1.2.3"});
}
- void check(dcmtkpp::CStoreRequest const & message)
+ void check(odil::message::CStoreRequest const & message)
{
- BOOST_CHECK_EQUAL(message.get_command_field(), dcmtkpp::Message::Command::C_STORE_RQ);
+ BOOST_CHECK_EQUAL(
+ message.get_command_field(),
+ odil::message::Message::Command::C_STORE_RQ);
BOOST_CHECK_EQUAL(message.get_message_id(), 1234);
BOOST_CHECK_EQUAL(
- message.get_affected_sop_class_uid(), dcmtkpp::registry::MRImageStorage);
+ message.get_affected_sop_class_uid(), odil::registry::MRImageStorage);
BOOST_CHECK_EQUAL(
message.get_affected_sop_instance_uid(), "1.2.3.4");
@@ -55,8 +57,9 @@ struct Fixture: public MessageFixtureBase<dcmtkpp::CStoreRequest>
BOOST_FIXTURE_TEST_CASE(Constructor, Fixture)
{
- dcmtkpp::CStoreRequest message(
- 1234, dcmtkpp::registry::MRImageStorage, "1.2.3.4", DIMSE_PRIORITY_MEDIUM,
+ odil::message::CStoreRequest message(
+ 1234, odil::registry::MRImageStorage, "1.2.3.4",
+ odil::message::Message::Priority::MEDIUM,
this->data_set);
message.set_move_originator_ae_title("origin");
message.set_move_originator_message_id(5678);
@@ -71,7 +74,8 @@ BOOST_FIXTURE_TEST_CASE(MessageConstructor, Fixture)
BOOST_FIXTURE_TEST_CASE(MessageConstructorWrongCommandField, Fixture)
{
- this->command_set.as_int("CommandField") = {dcmtkpp::Message::Command::C_ECHO_RSP};
+ this->command_set.as_int("CommandField") = {
+ odil::message::Message::Command::C_ECHO_RSP};
this->check_message_constructor_throw(this->command_set, this->data_set);
}
diff --git a/tests/code/message/CStoreResponse.cpp b/tests/code/message/CStoreResponse.cpp
new file mode 100644
index 0000000..369e1e7
--- /dev/null
+++ b/tests/code/message/CStoreResponse.cpp
@@ -0,0 +1,106 @@
+#define BOOST_TEST_MODULE CStoreResponse
+#include <boost/test/unit_test.hpp>
+
+#include "odil/message/CStoreResponse.h"
+#include "odil/DataSet.h"
+#include "odil/message/Message.h"
+#include "odil/registry.h"
+
+#include "../../MessageFixtureBase.h"
+
+struct Fixture: public MessageFixtureBase<odil::message::CStoreResponse>
+{
+ odil::DataSet command_set;
+
+ Fixture()
+ {
+ this->command_set.add(
+ "CommandField", {odil::message::Message::Command::C_STORE_RSP});
+ this->command_set.add("MessageIDBeingRespondedTo", {1234});
+ this->command_set.add("Status", {odil::message::Response::Success});
+
+ this->command_set.add("MessageID", {5678});
+ this->command_set.add(
+ "AffectedSOPClassUID", {odil::registry::MRImageStorage});
+ this->command_set.add("AffectedSOPInstanceUID", {"1.2.3.4"});
+ }
+
+ void check(odil::message::CStoreResponse const & message)
+ {
+ BOOST_CHECK_EQUAL(
+ message.get_command_field(),
+ odil::message::Message::Command::C_STORE_RSP);
+ BOOST_CHECK_EQUAL(message.get_message_id_being_responded_to(), 1234);
+ BOOST_CHECK_EQUAL(
+ message.get_status(), odil::message::Response::Success);
+ BOOST_CHECK(!message.has_data_set());
+
+ BOOST_CHECK(message.has_message_id());
+ BOOST_CHECK_EQUAL(message.get_message_id(), 5678);
+
+ BOOST_CHECK(message.has_affected_sop_class_uid());
+ BOOST_CHECK_EQUAL(
+ message.get_affected_sop_class_uid(), odil::registry::MRImageStorage);
+
+ BOOST_CHECK(message.has_affected_sop_instance_uid());
+ BOOST_CHECK_EQUAL(message.get_affected_sop_instance_uid(), "1.2.3.4");
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(Constructor, Fixture)
+{
+ odil::message::CStoreResponse message(
+ 1234, odil::message::Response::Success);
+ message.set_message_id(5678);
+ message.set_affected_sop_class_uid(odil::registry::MRImageStorage);
+ message.set_affected_sop_instance_uid("1.2.3.4");
+
+ this->check(message);
+}
+
+BOOST_FIXTURE_TEST_CASE(MessageConstructor, Fixture)
+{
+ this->check_message_constructor(this->command_set);
+}
+
+BOOST_FIXTURE_TEST_CASE(MessageConstructorWrongCommandField, Fixture)
+{
+ this->command_set.add(
+ "CommandField", {odil::message::Message::Command::C_ECHO_RQ});
+ this->check_message_constructor_throw(this->command_set);
+}
+
+BOOST_AUTO_TEST_CASE(StatusWarning)
+{
+ std::vector<odil::Value::Integer> const statuses = {
+ odil::message::CStoreResponse::CoercionOfDataElements,
+ odil::message::CStoreResponse::DataSetDoesNotMatchSOPClass,
+ odil::message::CStoreResponse::ElementsDiscarded
+ };
+
+ for(auto const status:statuses)
+ {
+ odil::message::CStoreResponse response(1234, status);
+ BOOST_REQUIRE(!response.is_pending());
+ BOOST_REQUIRE(response.is_warning());
+ BOOST_REQUIRE(!response.is_failure());
+ }
+}
+
+
+BOOST_AUTO_TEST_CASE(StatusFailure)
+{
+ std::vector<odil::Value::Integer> const statuses = {
+ odil::message::CStoreResponse::RefusedOutOfResources,
+ odil::message::CStoreResponse::ErrorDataSetDoesNotMatchSOPClass,
+ odil::message::CStoreResponse::ErrorCannotUnderstand
+ };
+
+ for(auto const status:statuses)
+ {
+ odil::message::CStoreResponse response(1234, status);
+ BOOST_REQUIRE(!response.is_pending());
+ BOOST_REQUIRE(!response.is_warning());
+ BOOST_REQUIRE(response.is_failure());
+ }
+}
diff --git a/tests/code/message/Cancellation.cpp b/tests/code/message/Cancellation.cpp
new file mode 100644
index 0000000..c193924
--- /dev/null
+++ b/tests/code/message/Cancellation.cpp
@@ -0,0 +1,44 @@
+#define BOOST_TEST_MODULE Cancellation
+#include <boost/test/unit_test.hpp>
+
+#include "odil/DataSet.h"
+#include "odil/message/Cancellation.h"
+
+#include "../../MessageFixtureBase.h"
+
+struct Fixture: public MessageFixtureBase<odil::message::Cancellation>
+{
+ odil::DataSet command_set;
+ Fixture()
+ {
+ this->command_set.add(
+ "CommandField", {odil::message::Message::Command::C_CANCEL_RQ});
+ this->command_set.add("MessageIDBeingRespondedTo", {1234});
+ }
+
+ void check(odil::message::Cancellation const & message)
+ {
+ BOOST_CHECK_EQUAL(
+ message.get_command_field(),
+ odil::message::Message::Command::C_CANCEL_RQ);
+ BOOST_CHECK_EQUAL(message.get_message_id_being_responded_to(), 1234);
+ }
+};
+
+BOOST_FIXTURE_TEST_CASE(Constructor, Fixture)
+{
+ odil::message::Cancellation const message(1234);
+ this->check(message);
+}
+
+BOOST_FIXTURE_TEST_CASE(MessageConstructor, Fixture)
+{
+ this->check_message_constructor(this->command_set);
+}
+
+BOOST_FIXTURE_TEST_CASE(MessageConstructorWrongCommandField, Fixture)
+{
+ this->command_set.as_int("CommandField") = {
+ odil::message::Message::Command::C_ECHO_RQ};
+ this->check_message_constructor_throw(this->command_set);
+}
diff --git a/tests/code/message/Message.cpp b/tests/code/message/Message.cpp
new file mode 100644
index 0000000..08f9172
--- /dev/null
+++ b/tests/code/message/Message.cpp
@@ -0,0 +1,63 @@
+#define BOOST_TEST_MODULE Message
+#include <boost/test/unit_test.hpp>
+
+#include "odil/DataSet.h"
+#include "odil/message/Message.h"
+
+BOOST_AUTO_TEST_CASE(DefaultConstructor)
+{
+ odil::message::Message const message;
+
+ // Command Set might not be empty (Command Group Length, Data Set Type)
+ BOOST_CHECK(!message.has_data_set());
+}
+
+BOOST_AUTO_TEST_CASE(Constructor)
+{
+ odil::DataSet command_set;
+ command_set.add(
+ "CommandField", {odil::message::Message::Command::C_ECHO_RQ});
+
+ odil::DataSet data_set;
+
+ odil::message::Message const message(command_set, data_set);
+
+ BOOST_CHECK_EQUAL(
+ message.get_command_set().as_int("CommandField", 0),
+ odil::message::Message::Command::C_ECHO_RQ);
+
+ BOOST_CHECK(message.has_data_set());
+ BOOST_CHECK(message.get_data_set().empty());
+
+ BOOST_CHECK_EQUAL(
+ message.get_command_field(),
+ odil::message::Message::Command::C_ECHO_RQ);
+}
+
+BOOST_AUTO_TEST_CASE(CommandField)
+{
+ odil::message::Message message;
+ message.set_command_field(odil::message::Message::Command::C_FIND_RSP);
+
+ BOOST_CHECK(
+ message.get_command_set().as_int("CommandField") ==
+ odil::Value::Integers(
+ {odil::message::Message::Command::C_FIND_RSP}));
+ BOOST_CHECK_EQUAL(
+ message.get_command_field(),
+ odil::message::Message::Command::C_FIND_RSP);
+}
+
+BOOST_AUTO_TEST_CASE(DeleteDataSet)
+{
+ odil::DataSet command_set;
+ odil::DataSet data_set;
+
+ odil::message::Message message(command_set, data_set);
+
+ BOOST_CHECK(message.has_data_set());
+
+ message.delete_data_set();
+
+ BOOST_CHECK(!message.has_data_set());
+}
diff --git a/tests/code/Request.cpp b/tests/code/message/Request.cpp
similarity index 53%
rename from tests/code/Request.cpp
rename to tests/code/message/Request.cpp
index c040689..b17af64 100644
--- a/tests/code/Request.cpp
+++ b/tests/code/message/Request.cpp
@@ -1,26 +1,24 @@
#define BOOST_TEST_MODULE Request
#include <boost/test/unit_test.hpp>
-#include <dcmtk/config/osconfig.h>
-
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/Message.h"
-#include "dcmtkpp/Request.h"
+#include "odil/DataSet.h"
+#include "odil/message/Message.h"
+#include "odil/message/Request.h"
BOOST_AUTO_TEST_CASE(Constructor)
{
- dcmtkpp::Request const message(1234);
+ odil::message::Request const message(1234);
BOOST_CHECK_EQUAL(message.get_message_id(), 1234);
}
BOOST_AUTO_TEST_CASE(MessageConstructor)
{
- dcmtkpp::DataSet command_set;
+ odil::DataSet command_set;
command_set.add("MessageID", {1234});
- dcmtkpp::Message const generic_message(command_set);
+ odil::message::Message const generic_message(command_set);
- dcmtkpp::Request const message(generic_message);
+ odil::message::Request const message(generic_message);
BOOST_CHECK_EQUAL(message.get_message_id(), 1234);
BOOST_CHECK(!message.has_data_set());
diff --git a/tests/code/message/Response.cpp b/tests/code/message/Response.cpp
new file mode 100644
index 0000000..616772c
--- /dev/null
+++ b/tests/code/message/Response.cpp
@@ -0,0 +1,112 @@
+#define BOOST_TEST_MODULE Response
+#include <boost/test/unit_test.hpp>
+
+#include <vector>
+
+#include "odil/DataSet.h"
+#include "odil/message/Message.h"
+#include "odil/message/Response.h"
+#include "odil/Value.h"
+
+BOOST_AUTO_TEST_CASE(Constructor)
+{
+ odil::message::Response const message(
+ 1234, odil::message::Response::Pending);
+
+ BOOST_CHECK_EQUAL(message.get_message_id_being_responded_to(), 1234);
+ BOOST_CHECK_EQUAL(message.get_status(),
+ odil::message::Response::Pending);
+}
+
+BOOST_AUTO_TEST_CASE(MessageConstructor)
+{
+ odil::DataSet command_set;
+ command_set.add("MessageIDBeingRespondedTo", {1234});
+ command_set.add("Status", {odil::message::Response::Pending});
+ odil::message::Message const generic_message(command_set);
+
+ odil::message::Response const message(generic_message);
+
+ BOOST_CHECK_EQUAL(message.get_message_id_being_responded_to(), 1234);
+ BOOST_CHECK_EQUAL(
+ message.get_status(), odil::message::Response::Pending);
+ BOOST_CHECK(!message.has_data_set());
+}
+
+BOOST_AUTO_TEST_CASE(StatusPending)
+{
+ std::vector<odil::Value::Integer> const statuses = {
+ odil::message::Response::Pending,
+ };
+
+ for(auto const status:statuses)
+ {
+ BOOST_REQUIRE(odil::message::Response::is_pending(status));
+ BOOST_REQUIRE(!odil::message::Response::is_warning(status));
+ BOOST_REQUIRE(!odil::message::Response::is_failure(status));
+
+ odil::message::Response response(1234, status);
+ BOOST_REQUIRE(response.is_pending());
+ BOOST_REQUIRE(!response.is_warning());
+ BOOST_REQUIRE(!response.is_failure());
+ }
+}
+
+
+BOOST_AUTO_TEST_CASE(StatusWarning)
+{
+ std::vector<odil::Value::Integer> const statuses = {
+ odil::message::Response::AttributeListError,
+ odil::message::Response::AttributeValueOutOfRange
+ };
+
+ for(auto const status:statuses)
+ {
+ BOOST_REQUIRE(!odil::message::Response::is_pending(status));
+ BOOST_REQUIRE(odil::message::Response::is_warning(status));
+ BOOST_REQUIRE(!odil::message::Response::is_failure(status));
+
+ odil::message::Response response(1234, status);
+ BOOST_REQUIRE(!response.is_pending());
+ BOOST_REQUIRE(response.is_warning());
+ BOOST_REQUIRE(!response.is_failure());
+ }
+}
+
+BOOST_AUTO_TEST_CASE(StatusFailure)
+{
+ std::vector<odil::Value::Integer> const statuses = {
+ odil::message::Response::SOPClassNotSupported,
+ odil::message::Response::ClassInstanceConflict,
+ odil::message::Response::DuplicateSOPInstance,
+ odil::message::Response::DuplicateInvocation,
+ odil::message::Response::InvalidArgumentValue,
+ odil::message::Response::InvalidAttributeValue,
+ odil::message::Response::InvalidObjectInstance,
+ odil::message::Response::MissingAttribute,
+ odil::message::Response::MissingAttributeValue,
+ odil::message::Response::MistypedArgument,
+ odil::message::Response::NoSuchArgument,
+ odil::message::Response::NoSuchAttribute,
+ odil::message::Response::NoSuchEventType,
+ odil::message::Response::NoSuchSOPInstance,
+ odil::message::Response::NoSuchSOPClass,
+ odil::message::Response::ProcessingFailure,
+ odil::message::Response::ResourceLimitation,
+ odil::message::Response::UnrecognizedOperation,
+ odil::message::Response::NoSuchActionType,
+ odil::message::Response::RefusedNotAuthorized
+ };
+
+ for(auto const status:statuses)
+ {
+ BOOST_REQUIRE(!odil::message::Response::is_pending(status));
+ BOOST_REQUIRE(!odil::message::Response::is_warning(status));
+ BOOST_REQUIRE(odil::message::Response::is_failure(status));
+
+ odil::message::Response response(1234, status);
+ BOOST_REQUIRE(!response.is_pending());
+ BOOST_REQUIRE(!response.is_warning());
+ BOOST_REQUIRE(response.is_failure());
+ }
+}
diff --git a/tests/code/pdu/AAbort.cpp b/tests/code/pdu/AAbort.cpp
new file mode 100644
index 0000000..bbe1765
--- /dev/null
+++ b/tests/code/pdu/AAbort.cpp
@@ -0,0 +1,51 @@
+#define BOOST_TEST_MODULE pdu::AAbort
+#include <boost/test/unit_test.hpp>
+
+#include <istream>
+#include <memory>
+
+#include "odil/pdu/AAbort.h"
+#include "odil/Exception.h"
+
+std::string const data = {
+ 0x07, 0x00,
+ 0x00, 0x00, 0x00, 0x04,
+ 0x00, 0x00,
+ 0x01, 0x02
+};
+
+BOOST_AUTO_TEST_CASE(ConstructorFields)
+{
+ odil::pdu::AAbort const pdu(1, 2);
+ BOOST_REQUIRE_EQUAL(pdu.get_source(), 1);
+ BOOST_REQUIRE_EQUAL(pdu.get_reason(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(ConstructorStream)
+{
+ std::istringstream stream(data);
+ odil::pdu::AAbort const pdu(stream);
+ BOOST_REQUIRE_EQUAL(pdu.get_source(), 1);
+ BOOST_REQUIRE_EQUAL(pdu.get_reason(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(Write)
+{
+ odil::pdu::AAbort const pdu(1, 2);
+ std::ostringstream stream;
+ stream << pdu;
+
+ BOOST_REQUIRE(stream.str() == data);
+}
+
+BOOST_AUTO_TEST_CASE(WrongSource)
+{
+ odil::pdu::AAbort pdu(1, 2);
+ BOOST_REQUIRE_THROW(pdu.set_source(3), odil::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(WrongReason)
+{
+ odil::pdu::AAbort pdu(1, 2);
+ BOOST_REQUIRE_THROW(pdu.set_reason(9), odil::Exception);
+}
diff --git a/tests/code/pdu/AAssociateAC.cpp b/tests/code/pdu/AAssociateAC.cpp
new file mode 100644
index 0000000..2e44a82
--- /dev/null
+++ b/tests/code/pdu/AAssociateAC.cpp
@@ -0,0 +1,279 @@
+#define BOOST_TEST_MODULE AAssociateAC
+#include <boost/test/unit_test.hpp>
+
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "odil/pdu/AAssociateAC.h"
+#include "odil/pdu/ApplicationContext.h"
+#include "odil/pdu/PresentationContextAC.h"
+#include "odil/pdu/UserIdentityAC.h"
+#include "odil/pdu/UserInformation.h"
+#include "odil/Exception.h"
+
+struct Fixture
+{
+public:
+ static std::string const read_data;
+ static std::string const write_data;
+
+ odil::pdu::ApplicationContext application_context;
+ std::vector<odil::pdu::PresentationContextAC> presentation_contexts;
+ odil::pdu::UserInformation user_information;
+
+ Fixture()
+ : application_context("foo")
+ {
+ odil::pdu::PresentationContextAC pc1(3, "transfer_syntax", 1);
+ odil::pdu::PresentationContextAC pc2(5, "transfer_syntax_2", 2);
+ this->presentation_contexts = {pc1, pc2};
+
+ this->user_information.set_sub_items<odil::pdu::MaximumLength>(
+ { { 0x12345678 } });
+ this->user_information.set_sub_items<odil::pdu::UserIdentityAC>(
+ { { "bar" } });
+ }
+
+ void check_application_context(
+ odil::pdu::ApplicationContext const & context) const
+ {
+ BOOST_REQUIRE_EQUAL(
+ context.get_name(), this->application_context.get_name());
+ }
+
+ void check_presentation_contexts(
+ std::vector<odil::pdu::PresentationContextAC> const & contexts)
+ {
+ BOOST_REQUIRE_EQUAL(contexts.size(), presentation_contexts.size());
+ for(int i=0; i<contexts.size(); ++i)
+ {
+ BOOST_REQUIRE_EQUAL(
+ contexts[i].get_item_type(),
+ presentation_contexts[i].get_item_type());
+ BOOST_REQUIRE_EQUAL(
+ contexts[i].get_id(),
+ presentation_contexts[i].get_id());
+ BOOST_REQUIRE_EQUAL(
+ contexts[i].get_result_reason(),
+ presentation_contexts[i].get_result_reason());
+ }
+ }
+
+ void check_user_information(
+ odil::pdu::UserInformation const & user_information) const
+ {
+ BOOST_REQUIRE(
+ !user_information.get_sub_items<odil::pdu::MaximumLength>().empty());
+ BOOST_REQUIRE_EQUAL(
+ user_information.get_sub_items<odil::pdu::MaximumLength>()[0].get_maximum_length(),
+ 0x12345678);
+
+ BOOST_REQUIRE(
+ !user_information.get_sub_items<odil::pdu::UserIdentityAC>().empty());
+ BOOST_REQUIRE_EQUAL(
+ user_information.get_sub_items<odil::pdu::UserIdentityAC>()[0].get_server_response(),
+ "bar");
+ }
+};
+
+std::string const
+Fixture
+::read_data(
+ // Header, 6 bytes
+
+ "\x02\x00"
+ "\x00\x00\x00\x98"
+
+ // Items: 68 bytes
+
+ "\x00\x01\x00\x00"
+ " CALLED_AE"
+ "CALLING_AE "
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+
+ // Application Context, 7 bytes
+
+ "\x10\x00\x00\x03"
+ "foo"
+
+ // Presentation Contexts, 27+29 bytes
+
+ "\x21\x00\x00\x17"
+ "\x03\x00\x01\x00"
+ "\x40\x00\x00\x0f""transfer_syntax"
+
+ "\x21\x00\x00\x19"
+ "\x05\x00\x02\x00"
+ "\x40\x00\x00\x11""transfer_syntax_2"
+
+ // User Information, 21 bytes
+
+ "\x50\x00\x00\x11"
+ "\x51\x00\x00\x04"
+ "\x12\x34\x56\x78"
+ "\x59\x00\x00\x05"
+ "\x00\x03" "bar"
+ ,
+ 158
+);
+
+std::string const
+Fixture
+::write_data(
+ // Header, 6 bytes
+
+ "\x02\x00"
+ "\x00\x00\x00\x98"
+
+ // Items: 68 bytes
+
+ "\x00\x01\x00\x00"
+ "CALLED_AE "
+ "CALLING_AE "
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+
+ // Application Context, 7 bytes
+
+ "\x10\x00\x00\x03"
+ "foo"
+
+ // Presentation Contexts, 27+29 bytes
+
+ "\x21\x00\x00\x17"
+ "\x03\x00\x01\x00"
+ "\x40\x00\x00\x0f""transfer_syntax"
+
+ "\x21\x00\x00\x19"
+ "\x05\x00\x02\x00"
+ "\x40\x00\x00\x11""transfer_syntax_2"
+
+ // User Information, 21 bytes
+
+ "\x50\x00\x00\x11"
+ "\x51\x00\x00\x04"
+ "\x12\x34\x56\x78"
+ "\x59\x00\x00\x05"
+ "\x00\x03" "bar"
+ ,
+ 158
+);
+
+BOOST_AUTO_TEST_CASE(Constructor)
+{
+ odil::pdu::AAssociateAC const pdu;
+
+ BOOST_REQUIRE_EQUAL(pdu.get_called_ae_title(), "");
+ BOOST_REQUIRE_EQUAL(pdu.get_calling_ae_title(), "");
+ BOOST_REQUIRE_EQUAL(pdu.get_protocol_version(), 0);
+ BOOST_REQUIRE_THROW(pdu.get_application_context(), odil::Exception);
+ BOOST_REQUIRE(pdu.get_presentation_contexts().empty());
+ BOOST_REQUIRE_THROW(pdu.get_user_information(), odil::Exception);
+}
+
+BOOST_FIXTURE_TEST_CASE(ConstructorStream, Fixture)
+{
+ std::istringstream stream(read_data);
+ odil::pdu::AAssociateAC const pdu(stream);
+
+ BOOST_REQUIRE_EQUAL(pdu.get_called_ae_title(), "CALLED_AE");
+ BOOST_REQUIRE_EQUAL(pdu.get_calling_ae_title(), "CALLING_AE");
+ BOOST_REQUIRE_EQUAL(pdu.get_protocol_version(), 1);
+ this->check_application_context(pdu.get_application_context());
+ this->check_presentation_contexts(pdu.get_presentation_contexts());
+ this->check_user_information(pdu.get_user_information());
+}
+
+BOOST_AUTO_TEST_CASE(ProtocolVersion)
+{
+ odil::pdu::AAssociateAC pdu;
+ BOOST_REQUIRE_EQUAL(pdu.get_protocol_version(), 0);
+ pdu.set_protocol_version(2);
+ BOOST_REQUIRE_EQUAL(pdu.get_protocol_version(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(CalledAETitle)
+{
+ odil::pdu::AAssociateAC pdu;
+ BOOST_REQUIRE_EQUAL(pdu.get_called_ae_title(), "");
+ pdu.set_called_ae_title("called");
+ BOOST_REQUIRE_EQUAL(pdu.get_called_ae_title(), "called");
+}
+
+BOOST_AUTO_TEST_CASE(CallingAETitle)
+{
+ odil::pdu::AAssociateAC pdu;
+ BOOST_REQUIRE_EQUAL(pdu.get_calling_ae_title(), "");
+ pdu.set_calling_ae_title("calling");
+ BOOST_REQUIRE_EQUAL(pdu.get_calling_ae_title(), "calling");
+}
+
+BOOST_FIXTURE_TEST_CASE(ApplicationContext, Fixture)
+{
+ odil::pdu::AAssociateAC pdu;
+ BOOST_REQUIRE_THROW(pdu.get_application_context(), odil::Exception);
+ pdu.set_application_context(application_context);
+ this->check_application_context(pdu.get_application_context());
+}
+
+BOOST_FIXTURE_TEST_CASE(PresentationContexts, Fixture)
+{
+ odil::pdu::AAssociateAC pdu;
+ BOOST_REQUIRE(pdu.get_presentation_contexts().empty());
+ pdu.set_presentation_contexts(presentation_contexts);
+ this->check_presentation_contexts(pdu.get_presentation_contexts());
+}
+
+BOOST_FIXTURE_TEST_CASE(UserInformation, Fixture)
+{
+ odil::pdu::AAssociateAC pdu;
+ BOOST_REQUIRE_THROW(pdu.get_user_information(), odil::Exception);
+ pdu.set_user_information(user_information);
+ this->check_user_information(pdu.get_user_information());
+}
+
+BOOST_FIXTURE_TEST_CASE(Write, Fixture)
+{
+ odil::pdu::AAssociateAC pdu;
+ pdu.set_protocol_version(1);
+ pdu.set_called_ae_title("CALLED_AE");
+ pdu.set_calling_ae_title("CALLING_AE");
+
+ pdu.set_application_context(application_context);
+ pdu.set_presentation_contexts(presentation_contexts);
+ pdu.set_user_information(user_information);
+
+ std::ostringstream stream;
+ stream << pdu;
+
+ BOOST_REQUIRE(stream.str() == write_data);
+}
+
+BOOST_AUTO_TEST_CASE(CalledAETitleEmpty)
+{
+ odil::pdu::AAssociateAC pdu;
+ BOOST_REQUIRE_THROW(pdu.set_called_ae_title(""), odil::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(CalledAETitleTooLong)
+{
+ odil::pdu::AAssociateAC pdu;
+ BOOST_REQUIRE_THROW(
+ pdu.set_called_ae_title("123456789abcdef01"), odil::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(CallingAETitleEmpty)
+{
+ odil::pdu::AAssociateAC pdu;
+ BOOST_REQUIRE_THROW(pdu.set_calling_ae_title(""), odil::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(CallingAETitleTooLong)
+{
+ odil::pdu::AAssociateAC pdu;
+ BOOST_REQUIRE_THROW(
+ pdu.set_calling_ae_title("123456789abcdef01"), odil::Exception);
+}
diff --git a/tests/code/pdu/AAssociateRJ.cpp b/tests/code/pdu/AAssociateRJ.cpp
new file mode 100644
index 0000000..b857ee0
--- /dev/null
+++ b/tests/code/pdu/AAssociateRJ.cpp
@@ -0,0 +1,59 @@
+#define BOOST_TEST_MODULE pdu::AssociateRJ
+#include <boost/test/unit_test.hpp>
+
+#include <sstream>
+
+#include "odil/pdu/AAssociateRJ.h"
+#include "odil/Exception.h"
+
+std::string const data = {
+ 0x03, 0x00,
+ 0x00, 0x00, 0x00, 0x04,
+ 0x00, 0x01,
+ 0x02, 0x03
+};
+
+BOOST_AUTO_TEST_CASE(ConstructorFields)
+{
+ odil::pdu::AAssociateRJ const pdu(1, 2, 3);
+ BOOST_REQUIRE_EQUAL(pdu.get_result(), 1);
+ BOOST_REQUIRE_EQUAL(pdu.get_source(), 2);
+ BOOST_REQUIRE_EQUAL(pdu.get_reason(), 3);
+}
+
+BOOST_AUTO_TEST_CASE(ConstructorStream)
+{
+ std::istringstream stream(data);
+ odil::pdu::AAssociateRJ const pdu(stream);
+ BOOST_REQUIRE_EQUAL(pdu.get_result(), 1);
+ BOOST_REQUIRE_EQUAL(pdu.get_source(), 2);
+ BOOST_REQUIRE_EQUAL(pdu.get_reason(), 3);
+}
+
+BOOST_AUTO_TEST_CASE(Write)
+{
+ odil::pdu::AAssociateRJ const pdu(1, 2, 3);
+ std::ostringstream stream;
+ stream << pdu;
+
+ BOOST_REQUIRE(stream.str() == data);
+}
+
+BOOST_AUTO_TEST_CASE(WrongResult)
+{
+ odil::pdu::AAssociateRJ pdu(1, 2, 3);
+ BOOST_REQUIRE_THROW(pdu.set_result(4), odil::Exception);
+}
+
+
+BOOST_AUTO_TEST_CASE(WrongSource)
+{
+ odil::pdu::AAssociateRJ pdu(1, 2, 3);
+ BOOST_REQUIRE_THROW(pdu.set_source(4), odil::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(WrongReason)
+{
+ odil::pdu::AAssociateRJ pdu(1, 2, 3);
+ BOOST_REQUIRE_THROW(pdu.set_reason(25), odil::Exception);
+}
diff --git a/tests/code/pdu/AAssociateRQ.cpp b/tests/code/pdu/AAssociateRQ.cpp
new file mode 100644
index 0000000..3fc2156
--- /dev/null
+++ b/tests/code/pdu/AAssociateRQ.cpp
@@ -0,0 +1,290 @@
+#define BOOST_TEST_MODULE AAssociateRQ
+#include <boost/test/unit_test.hpp>
+
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "odil/pdu/AAssociateRQ.h"
+#include "odil/pdu/ApplicationContext.h"
+#include "odil/pdu/PresentationContextRQ.h"
+#include "odil/pdu/UserIdentityRQ.h"
+#include "odil/pdu/UserInformation.h"
+#include "odil/Exception.h"
+
+struct Fixture
+{
+public:
+ static std::string const read_data;
+ static std::string const write_data;
+
+ odil::pdu::ApplicationContext application_context;
+ std::vector<odil::pdu::PresentationContextRQ> presentation_contexts;
+ odil::pdu::UserInformation user_information;
+
+ Fixture()
+ : application_context("foo")
+ {
+ odil::pdu::PresentationContextRQ pc1(3, "abstract_syntax", {"ts1", "ts2"});
+ this->presentation_contexts = {pc1};
+
+ this->user_information.set_sub_items<odil::pdu::MaximumLength>(
+ { { 0x12345678 } });
+ odil::pdu::UserIdentityRQ user_identity;
+ user_identity.set_username_and_passcode("foo", "bar");
+ user_identity.set_positive_response_requested(true);
+ this->user_information.set_sub_items<odil::pdu::UserIdentityRQ>(
+ { user_identity });
+ }
+
+ void check_application_context(
+ odil::pdu::ApplicationContext const & context) const
+ {
+ BOOST_REQUIRE_EQUAL(
+ context.get_name(), this->application_context.get_name());
+ }
+
+ void check_presentation_contexts(
+ std::vector<odil::pdu::PresentationContextRQ> const & contexts)
+ {
+ BOOST_REQUIRE_EQUAL(contexts.size(), presentation_contexts.size());
+ for(int i=0; i<contexts.size(); ++i)
+ {
+ BOOST_REQUIRE_EQUAL(
+ contexts[i].get_item_type(),
+ presentation_contexts[i].get_item_type());
+ BOOST_REQUIRE_EQUAL(
+ contexts[i].get_id(),
+ presentation_contexts[i].get_id());
+ BOOST_REQUIRE_EQUAL(
+ contexts[i].get_abstract_syntax(),
+ presentation_contexts[i].get_abstract_syntax());
+ }
+ }
+
+ void check_user_information(
+ odil::pdu::UserInformation const & user_information) const
+ {
+ BOOST_REQUIRE(
+ !user_information.get_sub_items<odil::pdu::MaximumLength>().empty());
+ BOOST_REQUIRE_EQUAL(
+ user_information.get_sub_items<odil::pdu::MaximumLength>()[0].get_maximum_length(),
+ 0x12345678);
+
+ BOOST_REQUIRE(
+ !user_information.get_sub_items<odil::pdu::UserIdentityRQ>().empty());
+ BOOST_REQUIRE_EQUAL(
+ user_information.get_sub_items<odil::pdu::UserIdentityRQ>()[0].get_type(),
+ 2);
+ BOOST_REQUIRE_EQUAL(
+ user_information.get_sub_items<odil::pdu::UserIdentityRQ>()[0].get_positive_response_requested(),
+ true);
+ BOOST_REQUIRE_EQUAL(
+ user_information.get_sub_items<odil::pdu::UserIdentityRQ>()[0].get_primary_field(),
+ "foo");
+ BOOST_REQUIRE_EQUAL(
+ user_information.get_sub_items<odil::pdu::UserIdentityRQ>()[0].get_secondary_field(),
+ "bar");
+ }
+};
+
+std::string const
+Fixture
+::read_data(
+ // Header, 6 bytes
+
+ "\x01\x00"
+ "\x00\x00\x00\x90"
+
+ // Items: 68 bytes
+
+ "\x00\x01\x00\x00"
+ " CALLED_AE"
+ "CALLING_AE "
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+
+ // Application Context, 7 bytes
+
+ "\x10\x00\x00\x03"
+ "foo"
+
+ // Presentation Contexts, 41 bytes
+
+ "\x20\x00\x00\x25"
+ "\x03\x00\x00\x00"
+ "\x30\x00\x00\x0f""abstract_syntax"
+ "\x40\x00\x00\x03""ts1"
+ "\x40\x00\x00\x03""ts2"
+
+ // User Information, 28 bytes
+
+ "\x50\x00\x00\x18"
+ "\x51\x00\x00\x04"
+ "\x12\x34\x56\x78"
+ "\x58\x00\x00\x0c"
+ "\x02\x01"
+ "\x00\x03" "foo"
+ "\x00\x03" "bar"
+ ,
+ 150
+);
+
+std::string const
+Fixture
+::write_data(
+ // Header, 6 bytes
+
+ "\x01\x00"
+ "\x00\x00\x00\x90"
+
+ // Items: 68 bytes
+
+ "\x00\x01\x00\x00"
+ "CALLED_AE "
+ "CALLING_AE "
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+
+ // Application Context, 7 bytes
+
+ "\x10\x00\x00\x03"
+ "foo"
+
+ // Presentation Contexts, 41 bytes
+
+ "\x20\x00\x00\x25"
+ "\x03\x00\x00\x00"
+ "\x30\x00\x00\x0f""abstract_syntax"
+ "\x40\x00\x00\x03""ts1"
+ "\x40\x00\x00\x03""ts2"
+
+ // User Information, 28 bytes
+
+ "\x50\x00\x00\x18"
+ "\x51\x00\x00\x04"
+ "\x12\x34\x56\x78"
+ "\x58\x00\x00\x0c"
+ "\x02\x01"
+ "\x00\x03" "foo"
+ "\x00\x03" "bar"
+ ,
+ 150
+);
+
+BOOST_AUTO_TEST_CASE(Constructor)
+{
+ odil::pdu::AAssociateRQ const pdu;
+
+ BOOST_REQUIRE_EQUAL(pdu.get_called_ae_title(), "");
+ BOOST_REQUIRE_EQUAL(pdu.get_calling_ae_title(), "");
+ BOOST_REQUIRE_EQUAL(pdu.get_protocol_version(), 0);
+ BOOST_REQUIRE_THROW(pdu.get_application_context(), odil::Exception);
+ BOOST_REQUIRE(pdu.get_presentation_contexts().empty());
+ BOOST_REQUIRE_THROW(pdu.get_user_information(), odil::Exception);
+}
+
+BOOST_FIXTURE_TEST_CASE(ConstructorStream, Fixture)
+{
+ std::istringstream stream(read_data);
+ odil::pdu::AAssociateRQ const pdu(stream);
+
+ BOOST_REQUIRE_EQUAL(pdu.get_called_ae_title(), "CALLED_AE");
+ BOOST_REQUIRE_EQUAL(pdu.get_calling_ae_title(), "CALLING_AE");
+ BOOST_REQUIRE_EQUAL(pdu.get_protocol_version(), 1);
+ this->check_application_context(pdu.get_application_context());
+ this->check_presentation_contexts(pdu.get_presentation_contexts());
+ this->check_user_information(pdu.get_user_information());
+}
+
+BOOST_AUTO_TEST_CASE(ProtocolVersion)
+{
+ odil::pdu::AAssociateRQ pdu;
+ BOOST_REQUIRE_EQUAL(pdu.get_protocol_version(), 0);
+ pdu.set_protocol_version(2);
+ BOOST_REQUIRE_EQUAL(pdu.get_protocol_version(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(CalledAETitle)
+{
+ odil::pdu::AAssociateRQ pdu;
+ BOOST_REQUIRE_EQUAL(pdu.get_called_ae_title(), "");
+ pdu.set_called_ae_title("called");
+ BOOST_REQUIRE_EQUAL(pdu.get_called_ae_title(), "called");
+}
+
+BOOST_AUTO_TEST_CASE(CallingAETitle)
+{
+ odil::pdu::AAssociateRQ pdu;
+ BOOST_REQUIRE_EQUAL(pdu.get_calling_ae_title(), "");
+ pdu.set_calling_ae_title("calling");
+ BOOST_REQUIRE_EQUAL(pdu.get_calling_ae_title(), "calling");
+}
+
+BOOST_FIXTURE_TEST_CASE(ApplicationContext, Fixture)
+{
+ odil::pdu::AAssociateRQ pdu;
+ BOOST_REQUIRE_THROW(pdu.get_application_context(), odil::Exception);
+ pdu.set_application_context(application_context);
+ this->check_application_context(pdu.get_application_context());
+}
+
+BOOST_FIXTURE_TEST_CASE(PresentationContexts, Fixture)
+{
+ odil::pdu::AAssociateRQ pdu;
+ BOOST_REQUIRE(pdu.get_presentation_contexts().empty());
+ pdu.set_presentation_contexts(presentation_contexts);
+ this->check_presentation_contexts(pdu.get_presentation_contexts());
+}
+
+BOOST_FIXTURE_TEST_CASE(UserInformation, Fixture)
+{
+ odil::pdu::AAssociateRQ pdu;
+ BOOST_REQUIRE_THROW(pdu.get_user_information(), odil::Exception);
+ pdu.set_user_information(user_information);
+ this->check_user_information(pdu.get_user_information());
+}
+
+BOOST_FIXTURE_TEST_CASE(Write, Fixture)
+{
+ odil::pdu::AAssociateRQ pdu;
+ pdu.set_protocol_version(1);
+ pdu.set_called_ae_title("CALLED_AE");
+ pdu.set_calling_ae_title("CALLING_AE");
+
+ pdu.set_application_context(application_context);
+ pdu.set_presentation_contexts(presentation_contexts);
+ pdu.set_user_information(user_information);
+
+ std::ostringstream stream;
+ stream << pdu;
+
+ BOOST_REQUIRE(stream.str() == write_data);
+}
+
+BOOST_AUTO_TEST_CASE(CalledAETitleEmpty)
+{
+ odil::pdu::AAssociateRQ pdu;
+ BOOST_REQUIRE_THROW(pdu.set_called_ae_title(""), odil::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(CalledAETitleTooLong)
+{
+ odil::pdu::AAssociateRQ pdu;
+ BOOST_REQUIRE_THROW(
+ pdu.set_called_ae_title("123456789abcdef01"), odil::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(CallingAETitleEmpty)
+{
+ odil::pdu::AAssociateRQ pdu;
+ BOOST_REQUIRE_THROW(pdu.set_calling_ae_title(""), odil::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(CallingAETitleTooLong)
+{
+ odil::pdu::AAssociateRQ pdu;
+ BOOST_REQUIRE_THROW(
+ pdu.set_calling_ae_title("123456789abcdef01"), odil::Exception);
+}
diff --git a/tests/code/pdu/AReleaseRP.cpp b/tests/code/pdu/AReleaseRP.cpp
new file mode 100644
index 0000000..c1df04f
--- /dev/null
+++ b/tests/code/pdu/AReleaseRP.cpp
@@ -0,0 +1,34 @@
+#define BOOST_TEST_MODULE AReleaseRPPDU
+#include <boost/test/unit_test.hpp>
+
+#include <sstream>
+#include <string>
+
+#include "odil/pdu/AReleaseRP.h"
+
+std::string const data = {
+ 0x06, 0x00,
+ 0x00, 0x00, 0x00, 0x04,
+ 0x00, 0x00,
+ 0x00, 0x00
+};
+
+BOOST_AUTO_TEST_CASE(ConstructorFields)
+{
+ odil::pdu::AReleaseRP const pdu;
+}
+
+BOOST_AUTO_TEST_CASE(ConstructorStream)
+{
+ std::istringstream stream(data);
+ odil::pdu::AReleaseRP const pdu(stream);
+}
+
+
+BOOST_AUTO_TEST_CASE(Write)
+{
+ odil::pdu::AReleaseRP const pdu;
+ std::ostringstream stream;
+ stream << pdu;
+ BOOST_REQUIRE(stream.str() == data);
+}
diff --git a/tests/code/pdu/AReleaseRQ.cpp b/tests/code/pdu/AReleaseRQ.cpp
new file mode 100644
index 0000000..44cb275
--- /dev/null
+++ b/tests/code/pdu/AReleaseRQ.cpp
@@ -0,0 +1,34 @@
+#define BOOST_TEST_MODULE AReleaseRPPDU
+#include <boost/test/unit_test.hpp>
+
+#include <sstream>
+#include <string>
+
+#include "odil/pdu/AReleaseRQ.h"
+
+std::string const data = {
+ 0x05, 0x00,
+ 0x00, 0x00, 0x00, 0x04,
+ 0x00, 0x00,
+ 0x00, 0x00
+};
+
+BOOST_AUTO_TEST_CASE(ConstructorFields)
+{
+ odil::pdu::AReleaseRQ const pdu;
+}
+
+BOOST_AUTO_TEST_CASE(ConstructorStream)
+{
+ std::istringstream stream(data);
+ odil::pdu::AReleaseRQ const pdu(stream);
+}
+
+
+BOOST_AUTO_TEST_CASE(Write)
+{
+ odil::pdu::AReleaseRQ const pdu;
+ std::ostringstream stream;
+ stream << pdu;
+ BOOST_REQUIRE(stream.str() == data);
+}
diff --git a/tests/code/pdu/ApplicationContext.cpp b/tests/code/pdu/ApplicationContext.cpp
new file mode 100644
index 0000000..2001189
--- /dev/null
+++ b/tests/code/pdu/ApplicationContext.cpp
@@ -0,0 +1,44 @@
+#define BOOST_TEST_MODULE ApplicationContext
+#include <boost/test/unit_test.hpp>
+
+#include <sstream>
+#include <string>
+
+#include "odil/Exception.h"
+#include "odil/pdu/ApplicationContext.h"
+
+std::string const data(
+ "\x10\x00\x00\x03"
+ "foo",
+ 7
+);
+
+BOOST_AUTO_TEST_CASE(FromString)
+{
+ odil::pdu::ApplicationContext const context("foo");
+ BOOST_REQUIRE_EQUAL(context.get_name(), "foo");
+}
+
+BOOST_AUTO_TEST_CASE(FromStream)
+{
+ std::istringstream stream(data);
+
+ odil::pdu::ApplicationContext const context(stream);
+ BOOST_REQUIRE_EQUAL(context.get_name(), "foo");
+}
+
+BOOST_AUTO_TEST_CASE(Name)
+{
+ odil::pdu::ApplicationContext context("foo");
+ context.set_name("other");
+ BOOST_REQUIRE_EQUAL(context.get_name(), "other");
+}
+
+BOOST_AUTO_TEST_CASE(Write)
+{
+ odil::pdu::ApplicationContext const context("foo");
+ std::ostringstream stream;
+ stream << context;
+
+ BOOST_REQUIRE_EQUAL(stream.str(), data);
+}
diff --git a/tests/code/pdu/ImplementationClassUID.cpp b/tests/code/pdu/ImplementationClassUID.cpp
new file mode 100644
index 0000000..8492159
--- /dev/null
+++ b/tests/code/pdu/ImplementationClassUID.cpp
@@ -0,0 +1,43 @@
+#define BOOST_TEST_MODULE ImplementationClassUID
+#include <boost/test/unit_test.hpp>
+
+#include <sstream>
+#include <string>
+
+#include "odil/pdu/ImplementationClassUID.h"
+
+std::string const data(
+ "\x52\x00\x00\x03"
+ "foo",
+ 7
+);
+
+BOOST_AUTO_TEST_CASE(Constructor)
+{
+ odil::pdu::ImplementationClassUID const item("foo");
+ BOOST_REQUIRE_EQUAL(item.get_implementation_class_uid(), "foo");
+}
+
+BOOST_AUTO_TEST_CASE(FromStream)
+{
+ std::istringstream stream(data);
+ odil::pdu::ImplementationClassUID const item(stream);
+
+ BOOST_REQUIRE_EQUAL(item.get_implementation_class_uid(), "foo");
+}
+
+BOOST_AUTO_TEST_CASE(ImplementationClassUID)
+{
+ odil::pdu::ImplementationClassUID item("foo");
+ item.set_implementation_class_uid("bar");
+ BOOST_REQUIRE_EQUAL(item.get_implementation_class_uid(), "bar");
+}
+
+BOOST_AUTO_TEST_CASE(Write)
+{
+ odil::pdu::ImplementationClassUID const item("foo");
+ std::ostringstream stream;
+ stream << item;
+
+ BOOST_REQUIRE_EQUAL(stream.str(), data);
+}
diff --git a/tests/code/pdu/ImplementationVersionName.cpp b/tests/code/pdu/ImplementationVersionName.cpp
new file mode 100644
index 0000000..a9f71c9
--- /dev/null
+++ b/tests/code/pdu/ImplementationVersionName.cpp
@@ -0,0 +1,59 @@
+#define BOOST_TEST_MODULE ImplementationVersionName
+#include <boost/test/unit_test.hpp>
+
+#include <sstream>
+#include <string>
+
+#include "odil/Exception.h"
+#include "odil/pdu/ImplementationVersionName.h"
+
+std::string const data(
+ "\x55\x00\x00\x03"
+ "foo",
+ 7
+);
+
+BOOST_AUTO_TEST_CASE(Constructor)
+{
+ odil::pdu::ImplementationVersionName const item("foo");
+ BOOST_REQUIRE_EQUAL(item.get_implementation_version_name(), "foo");
+}
+
+BOOST_AUTO_TEST_CASE(FromStream)
+{
+ std::istringstream stream(data);
+ odil::pdu::ImplementationVersionName const item(stream);
+
+ BOOST_REQUIRE_EQUAL(item.get_implementation_version_name(), "foo");
+}
+
+BOOST_AUTO_TEST_CASE(VersionName)
+{
+ odil::pdu::ImplementationVersionName item("foo");
+ item.set_implementation_version_name("bar");
+ BOOST_REQUIRE_EQUAL(item.get_implementation_version_name(), "bar");
+}
+
+BOOST_AUTO_TEST_CASE(Write)
+{
+ odil::pdu::ImplementationVersionName const item("foo");
+ std::ostringstream stream;
+ stream << item;
+
+ BOOST_REQUIRE_EQUAL(stream.str(), data);
+}
+
+BOOST_AUTO_TEST_CASE(Empty)
+{
+ odil::pdu::ImplementationVersionName item("foo");
+ BOOST_REQUIRE_THROW(
+ item.set_implementation_version_name(""), odil::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(TooLong)
+{
+ odil::pdu::ImplementationVersionName item("foo");
+ BOOST_REQUIRE_THROW(
+ item.set_implementation_version_name("1234567890abcdef01"),
+ odil::Exception);
+}
diff --git a/tests/code/pdu/Item.cpp b/tests/code/pdu/Item.cpp
new file mode 100644
index 0000000..70ee680
--- /dev/null
+++ b/tests/code/pdu/Item.cpp
@@ -0,0 +1,128 @@
+#define BOOST_TEST_MODULE pdu::Item
+#include <boost/test/unit_test.hpp>
+
+#include <cstdint>
+
+#include "odil/pdu/Item.h"
+#include "odil/Exception.h"
+
+BOOST_AUTO_TEST_CASE(FieldUInt8)
+{
+ odil::pdu::Item::Field const field(uint8_t(123));
+
+ BOOST_REQUIRE(
+ field.get_type() == odil::pdu::Item::Field::Type::unsigned_int_8);
+ BOOST_REQUIRE_EQUAL(field.as_unsigned_int_8(), 123);
+}
+
+BOOST_AUTO_TEST_CASE(FieldUInt16)
+{
+ odil::pdu::Item::Field const field(uint16_t(1234));
+
+ BOOST_REQUIRE(
+ field.get_type() == odil::pdu::Item::Field::Type::unsigned_int_16);
+ BOOST_REQUIRE_EQUAL(field.as_unsigned_int_16(), 1234);
+}
+
+BOOST_AUTO_TEST_CASE(FieldUInt32)
+{
+ odil::pdu::Item::Field const field(uint32_t(123456));
+
+ BOOST_REQUIRE(
+ field.get_type() == odil::pdu::Item::Field::Type::unsigned_int_32);
+ BOOST_REQUIRE_EQUAL(field.as_unsigned_int_32(), 123456);
+}
+
+BOOST_AUTO_TEST_CASE(FieldString)
+{
+ odil::pdu::Item::Field const field("abcdef");
+
+ BOOST_REQUIRE(field.get_type() == odil::pdu::Item::Field::Type::string);
+ BOOST_REQUIRE_EQUAL(field.as_string(), "abcdef");
+}
+
+BOOST_AUTO_TEST_CASE(FieldItems)
+{
+ odil::pdu::Item const item_1({{"foo", uint8_t(123)}});
+ odil::pdu::Item const item_2({{"bar", std::string("abcdef")}});
+ odil::pdu::Item::Field const field(
+ std::vector<odil::pdu::Item>({item_1, item_2}));
+
+ BOOST_REQUIRE(field.get_type() == odil::pdu::Item::Field::Type::items);
+ BOOST_REQUIRE_EQUAL(field.as_items().size(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(DefaultConstructor)
+{
+ odil::pdu::Item const item;
+ BOOST_REQUIRE(item.empty());
+ BOOST_REQUIRE_EQUAL(item.size(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(FieldsConstructor)
+{
+ odil::pdu::Item const item({
+ {std::string("foo"), uint8_t(123)},
+ {std::string("bar"), std::string("abcd")}
+ });
+ BOOST_REQUIRE_EQUAL(item.size(), 2);
+ BOOST_REQUIRE_EQUAL(item.as_unsigned_int_8("foo"), 123);
+ BOOST_REQUIRE_EQUAL(item.as_string("bar"), "abcd");
+}
+
+BOOST_AUTO_TEST_CASE(UInt8)
+{
+ odil::pdu::Item item;
+ item.add("foo", uint8_t(123));
+ BOOST_REQUIRE_EQUAL(item.size(), 1);
+ BOOST_REQUIRE_EQUAL(item.has_field("foo"), true);
+ BOOST_REQUIRE_EQUAL(item.as_unsigned_int_8("foo"), 123);
+ BOOST_REQUIRE_THROW(item.as_unsigned_int_16("foo"), odil::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(UInt16)
+{
+ odil::pdu::Item item;
+ item.add("foo", uint16_t(1234));
+ BOOST_REQUIRE_EQUAL(item.size(), 1);
+ BOOST_REQUIRE_EQUAL(item.as_unsigned_int_16("foo"), 1234);
+ BOOST_REQUIRE_THROW(item.as_unsigned_int_32("foo"), odil::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(UInt32)
+{
+ odil::pdu::Item item;
+ item.add("foo", uint32_t(123456));
+ BOOST_REQUIRE_EQUAL(item.size(), 1);
+ BOOST_REQUIRE_EQUAL(item.as_unsigned_int_32("foo"), 123456);
+ BOOST_REQUIRE_THROW(item.as_string("foo"), odil::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(String)
+{
+ odil::pdu::Item item;
+ item.add("foo", std::string("abcd"));
+ BOOST_REQUIRE_EQUAL(item.size(), 1);
+ BOOST_REQUIRE_EQUAL(item.as_string("foo"), "abcd");
+ BOOST_REQUIRE_THROW(item.as_items("foo"), odil::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(Items)
+{
+ odil::pdu::Item const sub_item(
+ {{"bar", std::string("abcdef")}});
+ odil::pdu::Item item;
+ item.add("foo", std::vector<odil::pdu::Item>({sub_item}));
+ BOOST_REQUIRE_EQUAL(item.size(), 1);
+ BOOST_REQUIRE_EQUAL(item.as_items("foo").size(), 1);
+ BOOST_REQUIRE_THROW(item.as_unsigned_int_8("foo"), odil::Exception);
+}
+
+BOOST_AUTO_TEST_CASE(FieldAccessor)
+{
+ odil::pdu::Item item;
+ item.add("foo", std::string("abcd"));
+ BOOST_REQUIRE_NO_THROW(item["foo"]);
+ BOOST_REQUIRE_THROW(item["bar"], odil::Exception);
+
+}
diff --git a/tests/code/pdu/MaximumLength.cpp b/tests/code/pdu/MaximumLength.cpp
new file mode 100644
index 0000000..5eb274b
--- /dev/null
+++ b/tests/code/pdu/MaximumLength.cpp
@@ -0,0 +1,56 @@
+#define BOOST_TEST_MODULE MaximumLength
+#include <boost/test/unit_test.hpp>
+
+#include <sstream>
+#include <string>
+
+#include "odil/Exception.h"
+#include "odil/pdu/MaximumLength.h"
+
+BOOST_AUTO_TEST_CASE(ConstructorDefault)
+{
+ odil::pdu::MaximumLength const maximum_length;
+ BOOST_REQUIRE_EQUAL(maximum_length.get_maximum_length(), 0);
+}
+
+BOOST_AUTO_TEST_CASE(ConstructorInt)
+{
+ odil::pdu::MaximumLength const maximum_length(123);
+ BOOST_REQUIRE_EQUAL(maximum_length.get_maximum_length(), 123);
+}
+
+BOOST_AUTO_TEST_CASE(FromStream)
+{
+ std::string const data(
+ "\x51\x00\x00\x04"
+ "\x12\x34\x56\x78",
+ 8
+ );
+ std::istringstream stream(data);
+
+ odil::pdu::MaximumLength const maximum_length(stream);
+
+ BOOST_REQUIRE_EQUAL(maximum_length.get_maximum_length(), 0x12345678);
+}
+
+BOOST_AUTO_TEST_CASE(MaximumLength)
+{
+ odil::pdu::MaximumLength maximum_length;
+ maximum_length.set_maximum_length(123);
+ BOOST_REQUIRE_EQUAL(maximum_length.get_maximum_length(), 123);
+}
+
+BOOST_AUTO_TEST_CASE(Write)
+{
+ odil::pdu::MaximumLength const maximum_length(0x12345678);
+ std::ostringstream data;
+ data << maximum_length;
+
+ std::string const expected(
+ "\x51\x00\x00\x04"
+ "\x12\x34\x56\x78",
+ 8
+ );
+
+ BOOST_REQUIRE_EQUAL(data.str(), expected);
+}
diff --git a/tests/code/pdu/PDataTF.cpp b/tests/code/pdu/PDataTF.cpp
new file mode 100644
index 0000000..c66bed3
--- /dev/null
+++ b/tests/code/pdu/PDataTF.cpp
@@ -0,0 +1,74 @@
+#define BOOST_TEST_MODULE pdu::PDataTF
+#include <boost/test/unit_test.hpp>
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "odil/Exception.h"
+#include "odil/pdu/PDataTF.h"
+
+std::string const data = {
+ 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x1e,
+
+ 0x00, 0x00, 0x00, 0x0a,
+ 0x01, 0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+
+ 0x00, 0x00, 0x00, 0x0c,
+ 0x03, 0x02, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12
+};
+
+std::vector<odil::pdu::PDataTF::PresentationDataValueItem> const pdv_items = {
+ { 1, 0x01, "\x01\x02\x03\x04\x05\x06\x07\x08" },
+ { 3, 0x02, "\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12" },
+};
+
+namespace odil
+{
+
+namespace pdu
+{
+
+bool operator==(
+ PDataTF::PresentationDataValueItem const & lhs,
+ PDataTF::PresentationDataValueItem const & rhs)
+{
+ return (
+ lhs.get_presentation_context_id() == rhs.get_presentation_context_id() &&
+ lhs.get_control_header() == rhs.get_control_header() &&
+ lhs.get_fragment() == rhs.get_fragment());
+}
+
+}
+
+}
+
+BOOST_AUTO_TEST_CASE(ConstructorPDV)
+{
+ odil::pdu::PDataTF const pdu(pdv_items);
+ BOOST_REQUIRE(pdu.get_pdv_items() == pdv_items);
+}
+
+BOOST_AUTO_TEST_CASE(ConstructorStream)
+{
+ std::istringstream stream(data);
+ odil::pdu::PDataTF const pdu(stream);
+ BOOST_REQUIRE(pdu.get_pdv_items() == pdv_items);
+}
+
+BOOST_AUTO_TEST_CASE(Write)
+{
+ odil::pdu::PDataTF const pdu(pdv_items);
+ std::ostringstream stream;
+ stream << pdu;
+ BOOST_REQUIRE(stream.str() == data);
+}
+
+BOOST_AUTO_TEST_CASE(WrongPresentationContextID)
+{
+ BOOST_REQUIRE_THROW(
+ odil::pdu::PDataTF::PresentationDataValueItem(
+ 2, pdv_items[0].get_control_header(), pdv_items[0].get_fragment()),
+ odil::Exception);
+}
diff --git a/tests/code/pdu/PresentationContextAC.cpp b/tests/code/pdu/PresentationContextAC.cpp
new file mode 100644
index 0000000..6c77224
--- /dev/null
+++ b/tests/code/pdu/PresentationContextAC.cpp
@@ -0,0 +1,62 @@
+#define BOOST_TEST_MODULE PresentationContextAC
+#include <boost/test/unit_test.hpp>
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "odil/Exception.h"
+#include "odil/pdu/PresentationContextAC.h"
+
+std::string const data(
+ "\x21\x00\x00\x17"
+ "\x03\x00\x01\x00"
+ "\x40\x00\x00\x0f""transfer_syntax",
+ 27
+);
+
+BOOST_AUTO_TEST_CASE(Constructor)
+{
+ odil::pdu::PresentationContextAC const context(1, "transfer_syntax", 2);
+
+ BOOST_REQUIRE_EQUAL(context.get_item_type(), 0x21);
+ BOOST_REQUIRE_EQUAL(context.get_id(), 0x1);
+ BOOST_REQUIRE_EQUAL(context.get_result_reason(), 0x2);
+ BOOST_REQUIRE_EQUAL(context.get_transfer_syntax(), "transfer_syntax");
+}
+
+BOOST_AUTO_TEST_CASE(Id)
+{
+ odil::pdu::PresentationContextAC context(1, "transfer_syntax", 1);
+ context.set_id(123);
+ BOOST_REQUIRE_EQUAL(context.get_id(), 123);
+}
+
+BOOST_AUTO_TEST_CASE(TransferSyntax)
+{
+ odil::pdu::PresentationContextAC context(1, "transfer_syntax", 1);
+ context.set_transfer_syntax("foo");
+ BOOST_REQUIRE_EQUAL(context.get_transfer_syntax(), "foo");
+}
+
+BOOST_AUTO_TEST_CASE(Write)
+{
+ odil::pdu::PresentationContextAC context(3, "transfer_syntax", 1);
+
+ std::ostringstream stream;
+ stream << context;
+
+ BOOST_REQUIRE_EQUAL(stream.str(), data);
+}
+
+BOOST_AUTO_TEST_CASE(Read)
+{
+ std::istringstream stream(data);
+
+ odil::pdu::PresentationContextAC const context(stream);
+
+ BOOST_REQUIRE_EQUAL(context.get_item_type(), 0x21);
+ BOOST_REQUIRE_EQUAL(context.get_id(), 3);
+ BOOST_REQUIRE_EQUAL(context.get_result_reason(), 1);
+ BOOST_REQUIRE_EQUAL(context.get_transfer_syntax(), "transfer_syntax");
+}
diff --git a/tests/code/pdu/PresentationContextRQ.cpp b/tests/code/pdu/PresentationContextRQ.cpp
new file mode 100644
index 0000000..9954401
--- /dev/null
+++ b/tests/code/pdu/PresentationContextRQ.cpp
@@ -0,0 +1,82 @@
+#define BOOST_TEST_MODULE PresentationContextRQ
+#include <boost/test/unit_test.hpp>
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "odil/Exception.h"
+#include "odil/pdu/PresentationContextRQ.h"
+
+std::string const data(
+ "\x20\x00\x00\x25"
+ "\x03\x00\x00\x00"
+ "\x30\x00\x00\x0f""abstract_syntax"
+ "\x40\x00\x00\x03""ts1"
+ "\x40\x00\x00\x03""ts2",
+ 41
+);
+
+BOOST_AUTO_TEST_CASE(Constructor)
+{
+ odil::pdu::PresentationContextRQ const context(
+ 1, "abstract_syntax", {"ts1", "ts2"});
+
+ BOOST_REQUIRE_EQUAL(context.get_item_type(), 0x20);
+ BOOST_REQUIRE_EQUAL(context.get_id(), 1);
+ BOOST_REQUIRE_EQUAL(context.get_abstract_syntax(), "abstract_syntax");
+ BOOST_REQUIRE(
+ context.get_transfer_syntaxes() ==
+ std::vector<std::string>({"ts1", "ts2"}));
+}
+
+BOOST_AUTO_TEST_CASE(Id)
+{
+ odil::pdu::PresentationContextRQ context(
+ 1, "abstract_syntax", {"ts1", "ts2"});
+ context.set_id(123);
+ BOOST_REQUIRE_EQUAL(context.get_id(), 123);
+}
+
+BOOST_AUTO_TEST_CASE(AbstractSyntax)
+{
+ odil::pdu::PresentationContextRQ context(
+ 1, "abstract_syntax", {"ts1", "ts2"});
+ context.set_abstract_syntax("foo");
+ BOOST_REQUIRE_EQUAL(context.get_abstract_syntax(), "foo");
+}
+
+BOOST_AUTO_TEST_CASE(TransferSyntaxes)
+{
+ odil::pdu::PresentationContextRQ context(
+ 1, "abstract_syntax", {"ts1", "ts2"});
+ context.set_transfer_syntaxes({"foo", "bar"});
+ BOOST_REQUIRE(
+ context.get_transfer_syntaxes() ==
+ std::vector<std::string>({"foo", "bar"}));
+}
+
+BOOST_AUTO_TEST_CASE(Write)
+{
+ odil::pdu::PresentationContextRQ context(
+ 3, "abstract_syntax", {"ts1", "ts2"});
+
+ std::ostringstream stream;
+ stream << context;
+
+ BOOST_REQUIRE_EQUAL(stream.str(), data);
+}
+
+BOOST_AUTO_TEST_CASE(Read)
+{
+ std::istringstream stream(data);
+
+ odil::pdu::PresentationContextRQ const context(stream);
+
+ BOOST_REQUIRE_EQUAL(context.get_item_type(), 0x20);
+ BOOST_REQUIRE_EQUAL(context.get_id(), 3);
+ BOOST_REQUIRE_EQUAL(context.get_abstract_syntax(), "abstract_syntax");
+ BOOST_REQUIRE(
+ context.get_transfer_syntaxes() ==
+ std::vector<std::string>({"ts1", "ts2"}));
+}
diff --git a/tests/code/pdu/RoleSelection.cpp b/tests/code/pdu/RoleSelection.cpp
new file mode 100644
index 0000000..986d402
--- /dev/null
+++ b/tests/code/pdu/RoleSelection.cpp
@@ -0,0 +1,74 @@
+#define BOOST_TEST_MODULE RoleSelection
+#include <boost/test/unit_test.hpp>
+
+#include <sstream>
+#include <string>
+
+#include "odil/pdu/RoleSelection.h"
+
+#include <iostream>
+
+std::string const data(
+ "\x54\x00\x00\x0b"
+ "\x00\x07" "1.2.3.4"
+ "\x01\x01",
+ 15
+);
+
+BOOST_AUTO_TEST_CASE(DefaultConstructor)
+{
+ odil::pdu::RoleSelection const item;
+ BOOST_REQUIRE_EQUAL(item.get_sop_class_uid(), "");
+ BOOST_REQUIRE_EQUAL(item.get_scu_role_support(), false);
+ BOOST_REQUIRE_EQUAL(item.get_scp_role_support(), false);
+}
+
+BOOST_AUTO_TEST_CASE(Constructor)
+{
+ odil::pdu::RoleSelection const item("1.2.3.4", true, true);
+ BOOST_REQUIRE_EQUAL(item.get_sop_class_uid(), "1.2.3.4");
+ BOOST_REQUIRE_EQUAL(item.get_scu_role_support(), true);
+ BOOST_REQUIRE_EQUAL(item.get_scp_role_support(), true);
+}
+
+BOOST_AUTO_TEST_CASE(FromStream)
+{
+ std::istringstream stream(data);
+ odil::pdu::RoleSelection const item(stream);
+
+ BOOST_REQUIRE_EQUAL(item.get_sop_class_uid(), "1.2.3.4");
+ BOOST_REQUIRE_EQUAL(item.get_scu_role_support(), true);
+ BOOST_REQUIRE_EQUAL(item.get_scp_role_support(), true);
+}
+
+BOOST_AUTO_TEST_CASE(SOPClassUID)
+{
+ odil::pdu::RoleSelection item;
+ item.set_sop_class_uid("1.2.3.4");
+ BOOST_REQUIRE_EQUAL(item.get_sop_class_uid(), "1.2.3.4");
+}
+
+BOOST_AUTO_TEST_CASE(SCURoleSupport)
+{
+ odil::pdu::RoleSelection item;
+ item.set_scu_role_support(true);
+ BOOST_REQUIRE_EQUAL(item.get_scu_role_support(), true);
+}
+
+BOOST_AUTO_TEST_CASE(SCPRoleSupport)
+{
+ odil::pdu::RoleSelection item;
+ item.set_scp_role_support(true);
+ BOOST_REQUIRE_EQUAL(item.get_scp_role_support(), true);
+}
+
+
+BOOST_AUTO_TEST_CASE(Write)
+{
+ odil::pdu::RoleSelection item("1.2.3.4", true, true);
+
+ std::ostringstream stream;
+ stream << item;
+
+ BOOST_REQUIRE_EQUAL(stream.str(), data);
+}
diff --git a/tests/code/pdu/UserIdentityAC.cpp b/tests/code/pdu/UserIdentityAC.cpp
new file mode 100644
index 0000000..22d4efa
--- /dev/null
+++ b/tests/code/pdu/UserIdentityAC.cpp
@@ -0,0 +1,58 @@
+#define BOOST_TEST_MODULE UserIdentityAC
+#include <boost/test/unit_test.hpp>
+
+#include <sstream>
+#include <string>
+
+#include "odil/Exception.h"
+#include "odil/pdu/UserIdentityAC.h"
+
+BOOST_AUTO_TEST_CASE(ConstructorDefault)
+{
+ odil::pdu::UserIdentityAC const user_identity;
+ BOOST_REQUIRE_EQUAL(user_identity.get_server_response(), "");
+}
+
+BOOST_AUTO_TEST_CASE(ConstructorString)
+{
+ odil::pdu::UserIdentityAC const user_identity("foo");
+ BOOST_REQUIRE_EQUAL(user_identity.get_server_response(), "foo");
+}
+
+BOOST_AUTO_TEST_CASE(FromStream)
+{
+ std::string const data(
+ "\x59\x00\x00\x05"
+ "\x00\x03" "foo",
+ 9
+ );
+ std::istringstream stream(data);
+
+ odil::pdu::UserIdentityAC const user_identity(stream);
+
+ BOOST_REQUIRE_EQUAL(user_identity.get_server_response(), "foo");
+}
+
+BOOST_AUTO_TEST_CASE(Type)
+{
+ odil::pdu::UserIdentityAC user_identity;
+ user_identity.set_server_response("foo");
+ BOOST_REQUIRE_EQUAL(user_identity.get_server_response(), "foo");
+}
+
+BOOST_AUTO_TEST_CASE(Write)
+{
+ odil::pdu::UserIdentityAC user_identity;
+ user_identity.set_server_response("foo");
+
+ std::ostringstream data;
+ data << user_identity;
+
+ std::string const expected(
+ "\x59\x00\x00\x05"
+ "\x00\x03" "foo",
+ 9
+ );
+
+ BOOST_REQUIRE_EQUAL(data.str(), expected);
+}
diff --git a/tests/code/pdu/UserIdentityRQ.cpp b/tests/code/pdu/UserIdentityRQ.cpp
new file mode 100644
index 0000000..be34f8e
--- /dev/null
+++ b/tests/code/pdu/UserIdentityRQ.cpp
@@ -0,0 +1,124 @@
+#define BOOST_TEST_MODULE UserIdentityRQ
+#include <boost/test/unit_test.hpp>
+
+#include <sstream>
+#include <string>
+
+#include "odil/Exception.h"
+#include "odil/pdu/UserIdentityRQ.h"
+
+BOOST_AUTO_TEST_CASE(Constructor)
+{
+ odil::pdu::UserIdentityRQ const user_identity;
+ BOOST_REQUIRE_EQUAL(user_identity.get_type(), 1);
+ BOOST_REQUIRE_EQUAL(user_identity.get_positive_response_requested(), false);
+ BOOST_REQUIRE_EQUAL(user_identity.get_primary_field(), "");
+ BOOST_REQUIRE_EQUAL(user_identity.get_secondary_field(), "");
+}
+
+BOOST_AUTO_TEST_CASE(FromStream)
+{
+ std::string const data(
+ "\x58\x00\x00\x0c"
+ "\x02\x01"
+ "\x00\x03" "foo"
+ "\x00\x03" "bar",
+ 16
+ );
+ std::istringstream stream(data);
+
+ odil::pdu::UserIdentityRQ const user_identity(stream);
+
+ BOOST_REQUIRE_EQUAL(user_identity.get_type(), 2);
+ BOOST_REQUIRE_EQUAL(user_identity.get_positive_response_requested(), true);
+ BOOST_REQUIRE_EQUAL(user_identity.get_primary_field(), "foo");
+ BOOST_REQUIRE_EQUAL(user_identity.get_secondary_field(), "bar");
+}
+
+BOOST_AUTO_TEST_CASE(Type)
+{
+ odil::pdu::UserIdentityRQ user_identity;
+ user_identity.set_type(2);
+ BOOST_REQUIRE_EQUAL(user_identity.get_type(), 2);
+}
+
+BOOST_AUTO_TEST_CASE(PositiveResponseRequested)
+{
+ odil::pdu::UserIdentityRQ user_identity;
+ user_identity.set_positive_response_requested(true);
+ BOOST_REQUIRE_EQUAL(user_identity.get_positive_response_requested(), true);
+}
+
+BOOST_AUTO_TEST_CASE(PrimaryField)
+{
+ odil::pdu::UserIdentityRQ user_identity;
+ user_identity.set_primary_field("foo");
+ BOOST_REQUIRE_EQUAL(user_identity.get_primary_field(), "foo");
+}
+
+BOOST_AUTO_TEST_CASE(SecondaryField)
+{
+ odil::pdu::UserIdentityRQ user_identity;
+ user_identity.set_secondary_field("foo");
+ BOOST_REQUIRE_EQUAL(user_identity.get_secondary_field(), "foo");
+}
+
+BOOST_AUTO_TEST_CASE(Username)
+{
+ odil::pdu::UserIdentityRQ user_identity;
+ user_identity.set_username("foo");
+
+ BOOST_REQUIRE_EQUAL(user_identity.get_type(), 1);
+ BOOST_REQUIRE_EQUAL(user_identity.get_primary_field(), "foo");
+ BOOST_REQUIRE_EQUAL(user_identity.get_secondary_field(), "");
+}
+
+BOOST_AUTO_TEST_CASE(UsernameAndPasscode)
+{
+ odil::pdu::UserIdentityRQ user_identity;
+ user_identity.set_username_and_passcode("foo", "bar");
+
+ BOOST_REQUIRE_EQUAL(user_identity.get_type(), 2);
+ BOOST_REQUIRE_EQUAL(user_identity.get_primary_field(), "foo");
+ BOOST_REQUIRE_EQUAL(user_identity.get_secondary_field(), "bar");
+}
+
+BOOST_AUTO_TEST_CASE(KerberosTicket)
+{
+ odil::pdu::UserIdentityRQ user_identity;
+ user_identity.set_kerberos_service_ticket("1234");
+
+ BOOST_REQUIRE_EQUAL(user_identity.get_type(), 3);
+ BOOST_REQUIRE_EQUAL(user_identity.get_primary_field(), "1234");
+ BOOST_REQUIRE_EQUAL(user_identity.get_secondary_field(), "");
+}
+
+BOOST_AUTO_TEST_CASE(SAMLAssertion)
+{
+ odil::pdu::UserIdentityRQ user_identity;
+ user_identity.set_saml_assertion("1234");
+
+ BOOST_REQUIRE_EQUAL(user_identity.get_type(), 4);
+ BOOST_REQUIRE_EQUAL(user_identity.get_primary_field(), "1234");
+ BOOST_REQUIRE_EQUAL(user_identity.get_secondary_field(), "");
+}
+
+BOOST_AUTO_TEST_CASE(Write)
+{
+ odil::pdu::UserIdentityRQ user_identity;
+ user_identity.set_username_and_passcode("foo", "bar");
+ user_identity.set_positive_response_requested(true);
+
+ std::ostringstream data;
+ data << user_identity;
+
+ std::string const expected(
+ "\x58\x00\x00\x0c"
+ "\x02\x01"
+ "\x00\x03" "foo"
+ "\x00\x03" "bar",
+ 16
+ );
+
+ BOOST_REQUIRE_EQUAL(data.str(), expected);
+}
diff --git a/tests/code/pdu/UserInformation.cpp b/tests/code/pdu/UserInformation.cpp
new file mode 100644
index 0000000..a9bec4e
--- /dev/null
+++ b/tests/code/pdu/UserInformation.cpp
@@ -0,0 +1,160 @@
+#define BOOST_TEST_MODULE UserInformation
+#include <boost/test/unit_test.hpp>
+
+#include <sstream>
+
+#include "odil/Exception.h"
+#include "odil/pdu/ImplementationClassUID.h"
+#include "odil/pdu/ImplementationVersionName.h"
+#include "odil/pdu/MaximumLength.h"
+#include "odil/pdu/RoleSelection.h"
+#include "odil/pdu/UserInformation.h"
+#include "odil/pdu/UserIdentityAC.h"
+#include "odil/pdu/UserIdentityRQ.h"
+
+BOOST_AUTO_TEST_CASE(ConstructorDefault)
+{
+ odil::pdu::UserInformation const item;
+ BOOST_REQUIRE(item.get_sub_items<odil::pdu::MaximumLength>().empty());
+ BOOST_REQUIRE(item.get_sub_items<odil::pdu::UserIdentityRQ>().empty());
+}
+
+BOOST_AUTO_TEST_CASE(ConstructorStreamMaximumLength)
+{
+ std::string const data(
+ "\x50\x00\x00\x08"
+ "\x51\x00\x00\x04"
+ "\x12\x34\x56\x78",
+ 12
+ );
+ std::istringstream stream(data);
+
+ odil::pdu::UserInformation const item(stream);
+ BOOST_REQUIRE_EQUAL(
+ item.get_sub_items<odil::pdu::MaximumLength>().size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(ConstructorStreamImplementationClassUID)
+{
+ std::string const data(
+ "\x50\x00\x00\x07"
+ "\x52\x00\x00\x03" "foo",
+ 11
+ );
+ std::istringstream stream(data);
+
+ odil::pdu::UserInformation const item(stream);
+ BOOST_REQUIRE_EQUAL(
+ item.get_sub_items<odil::pdu::ImplementationClassUID>().size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(ConstructorStreamRoleSelection)
+{
+ std::string const data(
+ "\x50\x00\x00\x0f"
+ "\x54\x00\x00\x0b"
+ "\x00\x07" "1.2.3.4"
+ "\x01\x01",
+ 19
+ );
+ std::istringstream stream(data);
+
+ odil::pdu::UserInformation const item(stream);
+ BOOST_REQUIRE_EQUAL(
+ item.get_sub_items<odil::pdu::RoleSelection>().size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(ConstructorStreamImplementationVersionName)
+{
+ std::string const data(
+ "\x50\x00\x00\x07"
+ "\x55\x00\x00\x03" "foo",
+ 11
+ );
+ std::istringstream stream(data);
+
+ odil::pdu::UserInformation const item(stream);
+ BOOST_REQUIRE_EQUAL(
+ item.get_sub_items<odil::pdu::ImplementationVersionName>().size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(ConstructorStreamUserIdentityRQ)
+{
+ std::string const data(
+ "\x50\x00\x00\x10"
+
+ "\x58\x00\x00\x0c"
+ "\x02\x01"
+ "\x00\x03" "foo"
+ "\x00\x03" "bar",
+ 20
+ );
+ std::istringstream stream(data);
+
+ odil::pdu::UserInformation const item(stream);
+ BOOST_REQUIRE_EQUAL(
+ item.get_sub_items<odil::pdu::UserIdentityRQ>().size(), 1);
+
+}
+
+BOOST_AUTO_TEST_CASE(ConstructorStreamUserIdentityAC)
+{
+ std::string const data(
+ "\x50\x00\x00\x0a"
+
+ "\x59\x00\x00\x06"
+ "\x00\x04" "abcd",
+ 14
+ );
+ std::istringstream stream(data);
+
+ odil::pdu::UserInformation const item(stream);
+ BOOST_REQUIRE_EQUAL(
+ item.get_sub_items<odil::pdu::UserIdentityAC>().size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(SetSubItems)
+{
+ odil::pdu::UserInformation item;
+ BOOST_REQUIRE(item.get_sub_items<odil::pdu::MaximumLength>().empty());
+
+ item.set_sub_items<odil::pdu::MaximumLength>({{0x12345678}});
+
+ BOOST_REQUIRE_EQUAL(
+ item.get_sub_items<odil::pdu::MaximumLength>().size(), 1);
+}
+
+BOOST_AUTO_TEST_CASE(DeleteSubItems)
+{
+ odil::pdu::UserInformation item;
+ BOOST_REQUIRE(item.get_sub_items<odil::pdu::MaximumLength>().empty());
+
+ item.set_sub_items<odil::pdu::MaximumLength>({{0x12345678}});
+
+ BOOST_REQUIRE_EQUAL(
+ item.get_sub_items<odil::pdu::MaximumLength>().size(), 1);
+
+ item.delete_sub_items<odil::pdu::MaximumLength>();
+
+ BOOST_REQUIRE(item.get_sub_items<odil::pdu::MaximumLength>().empty());
+}
+
+BOOST_AUTO_TEST_CASE(Write)
+{
+ odil::pdu::UserInformation item;
+ item.set_sub_items<odil::pdu::MaximumLength>({{0x12345678}});
+
+ std::ostringstream data;
+ data << item;
+
+ std::string const expected(
+ "\x50\x00\x00\x08"
+
+ "\x51\x00\x00\x04"
+ "\x12\x34\x56\x78",
+ 12
+ );
+
+ BOOST_REQUIRE_EQUAL(data.str(), expected);
+}
+
diff --git a/tests/code/registry.cpp b/tests/code/registry.cpp
index 12e7b25..aef0026 100644
--- a/tests/code/registry.cpp
+++ b/tests/code/registry.cpp
@@ -1,14 +1,14 @@
#define BOOST_TEST_MODULE registry
#include <boost/test/unit_test.hpp>
-#include "dcmtkpp/registry.h"
-#include "dcmtkpp/Tag.h"
+#include "odil/registry.h"
+#include "odil/Tag.h"
BOOST_AUTO_TEST_CASE(PublicDictionary)
{
- auto const iterator = dcmtkpp::registry::public_dictionary.find(
- dcmtkpp::registry::PatientName);
- BOOST_REQUIRE(iterator != dcmtkpp::registry::public_dictionary.end());
+ auto const iterator = odil::registry::public_dictionary.find(
+ odil::registry::PatientName);
+ BOOST_REQUIRE(iterator != odil::registry::public_dictionary.end());
auto const & entry = iterator->second;
BOOST_REQUIRE_EQUAL(entry.name, "Patient's Name");
BOOST_REQUIRE_EQUAL(entry.keyword, "PatientName");
@@ -18,9 +18,9 @@ BOOST_AUTO_TEST_CASE(PublicDictionary)
BOOST_AUTO_TEST_CASE(UIDsDictionary)
{
- auto const iterator = dcmtkpp::registry::uids_dictionary.find(
- dcmtkpp::registry::MRImageStorage);
- BOOST_REQUIRE(iterator != dcmtkpp::registry::uids_dictionary.end());
+ auto const iterator = odil::registry::uids_dictionary.find(
+ odil::registry::MRImageStorage);
+ BOOST_REQUIRE(iterator != odil::registry::uids_dictionary.end());
auto const & entry = iterator->second;
BOOST_REQUIRE_EQUAL(entry.name, "MR Image Storage");
BOOST_REQUIRE_EQUAL(entry.keyword, "MRImageStorage");
diff --git a/tests/code/uid.cpp b/tests/code/uid.cpp
index 517b76d..7c729be 100644
--- a/tests/code/uid.cpp
+++ b/tests/code/uid.cpp
@@ -1,11 +1,11 @@
#define BOOST_TEST_MODULE UID
#include <boost/test/unit_test.hpp>
-#include "dcmtkpp/uid.h"
+#include "odil/uid.h"
BOOST_AUTO_TEST_CASE(generate)
{
- std::string const uid = dcmtkpp::generate_uid();
+ std::string const uid = odil::generate_uid();
BOOST_REQUIRE_LE(uid.size(), 64);
for(auto const & c: uid)
{
diff --git a/tests/code/unicode.cpp b/tests/code/unicode.cpp
index 5d23016..5b540e2 100644
--- a/tests/code/unicode.cpp
+++ b/tests/code/unicode.cpp
@@ -1,16 +1,13 @@
#define BOOST_TEST_MODULE unicode
#include <boost/test/unit_test.hpp>
-#include <dcmtk/config/osconfig.h>
-#include <dcmtk/dcmdata/dctk.h>
-
-#include "dcmtkpp/conversion.h"
-#include "dcmtkpp/DataSet.h"
-#include "dcmtkpp/unicode.h"
+#include "odil/DataSet.h"
+#include "odil/unicode.h"
+#include "odil/dcmtk/conversion.h"
BOOST_AUTO_TEST_CASE(SCSARAB)
{
- dcmtkpp::Value::Strings const specific_character_set = { "ISO_IR 127" };
+ odil::Value::Strings const specific_character_set = { "ISO_IR 127" };
std::string const source =
"\xe2\xc8\xc7\xe6\xea" "^" "\xe4\xe6\xd2\xc7\xd1";
std::string const expected =
@@ -18,51 +15,51 @@ BOOST_AUTO_TEST_CASE(SCSARAB)
"^"
"\xd9\x84\xd9\x86\xd8\xb2\xd8\xa7\xd8\xb1";
- std::string const utf8 = dcmtkpp::as_utf8(
+ std::string const utf8 = odil::as_utf8(
source, specific_character_set, true);
BOOST_REQUIRE_EQUAL(utf8, expected);
}
BOOST_AUTO_TEST_CASE(SCSFREN)
{
- dcmtkpp::Value::Strings const specific_character_set = { "ISO_IR 100" };
+ odil::Value::Strings const specific_character_set = { "ISO_IR 100" };
std::string const source = "Buc" "^" "J\xe9r\xf4me";
std::string const expected =
"Buc" "^" "J\xc3\xa9r\xc3\xb4me";
- std::string const utf8 = dcmtkpp::as_utf8(
+ std::string const utf8 = odil::as_utf8(
source, specific_character_set, true);
BOOST_REQUIRE_EQUAL(utf8, expected);
}
BOOST_AUTO_TEST_CASE(SCSGERM)
{
- dcmtkpp::Value::Strings const specific_character_set = { "ISO_IR 100" };
+ odil::Value::Strings const specific_character_set = { "ISO_IR 100" };
std::string const source =
"\xc4neas" "^" "R\xfc" "diger";
std::string const expected =
"\xc3\x84neas" "^" "R\xc3\xbc" "diger";
- std::string const utf8 = dcmtkpp::as_utf8(
+ std::string const utf8 = odil::as_utf8(
source, specific_character_set, true);
BOOST_REQUIRE_EQUAL(utf8, expected);
}
BOOST_AUTO_TEST_CASE(SCSGREEK)
{
- dcmtkpp::Value::Strings const specific_character_set = { "ISO_IR 126" };
+ odil::Value::Strings const specific_character_set = { "ISO_IR 126" };
std::string const source = "\xc4\xe9\xef\xed\xf5\xf3\xe9\xef\xf2";
std::string const expected =
"\xce\x94\xce\xb9\xce\xbf\xce\xbd\xcf\x85\xcf\x83\xce\xb9\xce\xbf\xcf\x82";
- std::string const utf8 = dcmtkpp::as_utf8(
+ std::string const utf8 = odil::as_utf8(
source, specific_character_set, true);
BOOST_REQUIRE_EQUAL(utf8, expected);
}
BOOST_AUTO_TEST_CASE(SCSH31)
{
- dcmtkpp::Value::Strings const specific_character_set =
+ odil::Value::Strings const specific_character_set =
{ "", "ISO 2022 IR 87" };
std::string const source =
"Yamada" "^" "Tarou"
@@ -80,14 +77,14 @@ BOOST_AUTO_TEST_CASE(SCSH31)
"\xe3\x82\x84\xe3\x81\xbe\xe3\x81\xa0"
"^" "\xe3\x81\x9f\xe3\x82\x8d\xe3\x81\x86";
- std::string const utf8 = dcmtkpp::as_utf8(
+ std::string const utf8 = odil::as_utf8(
source, specific_character_set, true);
BOOST_REQUIRE_EQUAL(utf8, expected);
}
BOOST_AUTO_TEST_CASE(SCSH32)
{
- dcmtkpp::Value::Strings const specific_character_set =
+ odil::Value::Strings const specific_character_set =
{ "ISO 2022 IR 13", "ISO 2022 IR 87" };
std::string const source =
"\xd4\xcf\xc0\xde" "^" "\xc0\xdb\xb3"
@@ -107,27 +104,27 @@ BOOST_AUTO_TEST_CASE(SCSH32)
"\xe3\x82\x84\xe3\x81\xbe\xe3\x81\xa0"
"^" "\xe3\x81\x9f\xe3\x82\x8d\xe3\x81\x86";
- std::string const utf8 = dcmtkpp::as_utf8(
+ std::string const utf8 = odil::as_utf8(
source, specific_character_set, true);
BOOST_REQUIRE_EQUAL(utf8, expected);
}
BOOST_AUTO_TEST_CASE(SCSHBRW)
{
- dcmtkpp::Value::Strings const specific_character_set = { "ISO_IR 138" };
+ odil::Value::Strings const specific_character_set = { "ISO_IR 138" };
std::string const source = "\xf9\xf8\xe5\xef" "^" "\xe3\xe1\xe5\xf8\xe4";
std::string const expected =
"\xd7\xa9\xd7\xa8\xd7\x95\xd7\x9f"
"^" "\xd7\x93\xd7\x91\xd7\x95\xd7\xa8\xd7\x94";
- std::string const utf8 = dcmtkpp::as_utf8(
+ std::string const utf8 = odil::as_utf8(
source, specific_character_set, true);
BOOST_REQUIRE_EQUAL(utf8, expected);
}
BOOST_AUTO_TEST_CASE(SCSI2)
{
- dcmtkpp::Value::Strings const specific_character_set =
+ odil::Value::Strings const specific_character_set =
{ "", "ISO 2022 IR 149" };
std::string const source =
@@ -143,26 +140,26 @@ BOOST_AUTO_TEST_CASE(SCSI2)
"="
"\xed\x99\x8d" "^" "\xea\xb8\xb8\xeb\x8f\x99";
- std::string const utf8 = dcmtkpp::as_utf8(
+ std::string const utf8 = odil::as_utf8(
source, specific_character_set, true);
BOOST_REQUIRE_EQUAL(utf8, expected);
}
BOOST_AUTO_TEST_CASE(SCSRUSS)
{
- dcmtkpp::Value::Strings const specific_character_set = { "ISO_IR 144" };
+ odil::Value::Strings const specific_character_set = { "ISO_IR 144" };
std::string const source = "\xbb\xee\xda\x63\x65\xdc\xd1\x79\x70\xd3";
std::string const expected =
"\xd0\x9b\xd1\x8e\xd0\xba\x63\x65\xd0\xbc\xd0\xb1\x79\x70\xd0\xb3";
- std::string const utf8 = dcmtkpp::as_utf8(
+ std::string const utf8 = odil::as_utf8(
source, specific_character_set, true);
BOOST_REQUIRE_EQUAL(utf8, expected);
}
BOOST_AUTO_TEST_CASE(SCSX1)
{
- dcmtkpp::Value::Strings const specific_character_set = { "ISO_IR 192" };
+ odil::Value::Strings const specific_character_set = { "ISO_IR 192" };
std::string const source =
"Wang" "^" "XiaoDong"
"="
@@ -173,14 +170,14 @@ BOOST_AUTO_TEST_CASE(SCSX1)
"\x3d\xe7\x8e\x8b" "^" "\xe5\xb0\x8f\xe6\x9d\xb1"
"=";
- std::string const utf8 = dcmtkpp::as_utf8(
+ std::string const utf8 = odil::as_utf8(
source, specific_character_set, true);
BOOST_REQUIRE_EQUAL(utf8, expected);
}
BOOST_AUTO_TEST_CASE(SCSX2)
{
- dcmtkpp::Value::Strings const specific_character_set = { "GB18030" };
+ odil::Value::Strings const specific_character_set = { "GB18030" };
std::string const source =
"Wang" "^" "XiaoDong"
"=" ""
@@ -192,67 +189,7 @@ BOOST_AUTO_TEST_CASE(SCSX2)
"\xe7\x8e\x8b" "^" "\xe5\xb0\x8f\xe4\xb8\x9c"
"=";
- std::string const utf8 = dcmtkpp::as_utf8(
+ std::string const utf8 = odil::as_utf8(
source, specific_character_set, true);
BOOST_REQUIRE_EQUAL(utf8, expected);
}
-
-/*
-dcmtkpp::DataSet read(std::string const & filename)
-{
- DcmFileFormat format;
-
- OFCondition const condition = format.loadFile(filename.c_str());
- if(condition.bad())
- {
- throw dcmtkpp::Exception(condition);
- }
-
- dcmtkpp::DataSet const result = dcmtkpp::convert(format.getDataset());
-
- return result;
-}
-
-
-int main(int argc, char ** argv)
-{
- for(char** argument=argv+1; argument != argv+argc; ++argument)
- {
- std::cout << "Reading " << *argument << std::endl;
-
- dcmtkpp::DataSet data_set;
- try
- {
- data_set = read(*argument);
- }
- catch(dcmtkpp::Exception const & e)
- {
- std::cerr << "Could not read " << *argument << ": " << e.what() << "\n";
- continue;
- }
-
- std::string patient_name;
- try
- {
- patient_name = dcmtkpp::as_utf8(
- data_set.as_string("PatientName")[0],
- data_set.as_string("SpecificCharacterSet"), true);
- }
- catch(dcmtkpp::Exception const & e)
- {
- std::cerr << "Could not convert PatientName to UTF-8: " << e.what() << "\n";
- continue;
- }
-
- std::cout << " dcmtkpp::Value::Strings const specific_character_set = {\n";
- for(auto const & c: data_set.as_string("SpecificCharacterSet"))
- {
- std::cout << " " << "\"" << c << "\",\n";
- }
- std::cout << " };\n";
- std::cout << "Patient Name: \"" << patient_name << "\"\n";
- }
-
- return 0;
-}
-*/
diff --git a/tests/code/xml_converter.cpp b/tests/code/xml_converter.cpp
index 259837a..840a7a7 100644
--- a/tests/code/xml_converter.cpp
+++ b/tests/code/xml_converter.cpp
@@ -1,9 +1,7 @@
#define BOOST_TEST_MODULE xml_converter
#include <boost/test/unit_test.hpp>
-#include "dcmtkpp/xml_converter.h"
-
-#include <boost/property_tree/xml_parser.hpp>
+#include "odil/xml_converter.h"
void check_attributes(boost::property_tree::ptree const & xml,
std::string const & tag,
@@ -99,8 +97,8 @@ void check_response(boost::property_tree::ptree const & xml,
/******************************* TEST Nominal **********************************/
BOOST_AUTO_TEST_CASE(AsXMLEmpty)
{
- dcmtkpp::DataSet data_set;
- auto const xml = dcmtkpp::as_xml(data_set);
+ odil::DataSet data_set;
+ auto const xml = odil::as_xml(data_set);
// Expected result:
// <NativeDicomModel />
@@ -113,10 +111,10 @@ BOOST_AUTO_TEST_CASE(AsXMLEmpty)
/******************************* TEST Nominal **********************************/
BOOST_AUTO_TEST_CASE(AsXMLIntegers)
{
- dcmtkpp::DataSet data_set;
+ odil::DataSet data_set;
data_set.add(0x00280010,
- dcmtkpp::Element(dcmtkpp::Value::Integers({1, 2}), dcmtkpp::VR::US));
- auto const xml = dcmtkpp::as_xml(data_set);
+ odil::Element(odil::Value::Integers({1, 2}), odil::VR::US));
+ auto const xml = odil::as_xml(data_set);
// Expected result:
// <NativeDicomModel>
@@ -127,16 +125,16 @@ BOOST_AUTO_TEST_CASE(AsXMLIntegers)
// </NativeDicomModel>
check_response(xml, "00280010", "US", "Rows",
- dcmtkpp::Value::Integers({1, 2}));
+ odil::Value::Integers({1, 2}));
}
/******************************* TEST Nominal **********************************/
BOOST_AUTO_TEST_CASE(AsXMLReals)
{
- dcmtkpp::DataSet data_set;
+ odil::DataSet data_set;
data_set.add(0x00089459,
- dcmtkpp::Element(dcmtkpp::Value::Reals({1.2, 3.4}), dcmtkpp::VR::FL));
- auto const xml = dcmtkpp::as_xml(data_set);
+ odil::Element(odil::Value::Reals({1.2, 3.4}), odil::VR::FL));
+ auto const xml = odil::as_xml(data_set);
// Expected result:
// <NativeDicomModel>
@@ -148,18 +146,18 @@ BOOST_AUTO_TEST_CASE(AsXMLReals)
// </NativeDicomModel>
check_response(xml, "00089459", "FL", "RecommendedDisplayFrameRateInFloat",
- dcmtkpp::Value::Reals({1.2, 3.4}));
+ odil::Value::Reals({1.2, 3.4}));
}
/******************************* TEST Nominal **********************************/
BOOST_AUTO_TEST_CASE(AsXMLStrings)
{
- dcmtkpp::DataSet data_set;
+ odil::DataSet data_set;
data_set.add(0x00080060,
- dcmtkpp::Element(
- dcmtkpp::Value::Strings({"FOO", "BAR"}),
- dcmtkpp::VR::CS));
- auto const xml = dcmtkpp::as_xml(data_set);
+ odil::Element(
+ odil::Value::Strings({"FOO", "BAR"}),
+ odil::VR::CS));
+ auto const xml = odil::as_xml(data_set);
// Expected result:
// <NativeDicomModel>
@@ -170,19 +168,19 @@ BOOST_AUTO_TEST_CASE(AsXMLStrings)
// </NativeDicomModel>
check_response(xml, "00080060", "CS", "Modality",
- dcmtkpp::Value::Strings({"FOO", "BAR"}));
+ odil::Value::Strings({"FOO", "BAR"}));
}
/******************************* TEST Nominal **********************************/
BOOST_AUTO_TEST_CASE(AsXMLPersonName)
{
- dcmtkpp::DataSet data_set;
+ odil::DataSet data_set;
data_set.add(0x00100010,
- dcmtkpp::Element(
- dcmtkpp::Value::Strings({"Alpha^Betic=Ideo^Graphic=Pho^Netic",
+ odil::Element(
+ odil::Value::Strings({"Alpha^Betic=Ideo^Graphic=Pho^Netic",
"family^given^middle^prefix^suffix"}),
- dcmtkpp::VR::PN));
- auto const xml = dcmtkpp::as_xml(data_set);
+ odil::VR::PN));
+ auto const xml = odil::as_xml(data_set);
// Expected result:
// <NativeDicomModel>
@@ -367,24 +365,24 @@ BOOST_AUTO_TEST_CASE(AsXMLPersonName)
/******************************* TEST Nominal **********************************/
BOOST_AUTO_TEST_CASE(AsXMLDataSets)
{
- dcmtkpp::DataSet item1;
+ odil::DataSet item1;
item1.add(0x00100020,
- dcmtkpp::Element(dcmtkpp::Value::Strings({"valueLO1"}),
- dcmtkpp::VR::LO));
+ odil::Element(odil::Value::Strings({"valueLO1"}),
+ odil::VR::LO));
item1.add(0x00100022,
- dcmtkpp::Element(dcmtkpp::Value::Strings({"valueCS1"}),
- dcmtkpp::VR::CS));
- dcmtkpp::DataSet item2;
+ odil::Element(odil::Value::Strings({"valueCS1"}),
+ odil::VR::CS));
+ odil::DataSet item2;
item2.add(0x00100022,
- dcmtkpp::Element(dcmtkpp::Value::Strings({"valueCS2"}),
- dcmtkpp::VR::CS));
- dcmtkpp::DataSet data_set;
+ odil::Element(odil::Value::Strings({"valueCS2"}),
+ odil::VR::CS));
+ odil::DataSet data_set;
data_set.add(0x00101002,
- dcmtkpp::Element(
- dcmtkpp::Value::DataSets({item1, item2}),
- dcmtkpp::VR::SQ));
+ odil::Element(
+ odil::Value::DataSets({item1, item2}),
+ odil::VR::SQ));
- auto const xml = dcmtkpp::as_xml(data_set);
+ auto const xml = odil::as_xml(data_set);
// Expected result:
// <NativeDicomModel>
@@ -417,7 +415,7 @@ BOOST_AUTO_TEST_CASE(AsXMLDataSets)
BOOST_CHECK_EQUAL(native_dicom_model.front().first, "DicomAttribute");
BOOST_REQUIRE_EQUAL(native_dicom_model.front().second.size(), 3);
- dcmtkpp::Value::DataSets expected_values({item1, item2});
+ odil::Value::DataSets expected_values({item1, item2});
int count = 0;
for (auto it = native_dicom_model.front().second.begin();
@@ -471,7 +469,7 @@ BOOST_AUTO_TEST_CASE(AsXMLDataSets)
{
check_attributes((*it_dicomattr).second,
std::string(current_value->first),
- dcmtkpp::as_string(
+ odil::as_string(
current_value->second.vr),
current_value->first.get_name());
}
@@ -514,13 +512,13 @@ BOOST_AUTO_TEST_CASE(AsXMLDataSets)
/******************************* TEST Nominal **********************************/
BOOST_AUTO_TEST_CASE(AsXMLBinary)
{
- dcmtkpp::DataSet data_set;
+ odil::DataSet data_set;
data_set.add(0x00660023,
- dcmtkpp::Element(
- dcmtkpp::Value::Binary({0x1, 0x2, 0x3, 0x4, 0x5}),
- dcmtkpp::VR::OW));
+ odil::Element(
+ odil::Value::Binary({0x1, 0x2, 0x3, 0x4, 0x5}),
+ odil::VR::OW));
- auto const xml = dcmtkpp::as_xml(data_set);
+ auto const xml = odil::as_xml(data_set);
// Expected result:
// <NativeDicomModel>
@@ -573,12 +571,12 @@ BOOST_AUTO_TEST_CASE(AsXMLBinary)
/******************************* TEST Nominal **********************************/
BOOST_AUTO_TEST_CASE(AsXMLEmptyElement)
{
- dcmtkpp::DataSet data_set;
+ odil::DataSet data_set;
data_set.add(0x00080060,
- dcmtkpp::Element(
- dcmtkpp::Value::Strings({}),
- dcmtkpp::VR::CS));
- auto const xml = dcmtkpp::as_xml(data_set);
+ odil::Element(
+ odil::Value::Strings({}),
+ odil::VR::CS));
+ auto const xml = odil::as_xml(data_set);
// Expected result:
// <NativeDicomModel>
@@ -586,7 +584,7 @@ BOOST_AUTO_TEST_CASE(AsXMLEmptyElement)
// </NativeDicomModel>
check_response(xml, "00080060", "CS", "Modality",
- dcmtkpp::Value::Strings({}));
+ odil::Value::Strings({}));
}
/******************************* TEST Error ************************************/
@@ -594,50 +592,50 @@ BOOST_AUTO_TEST_CASE(AsXMLInvalidPersonName)
{
// Too many values separate by '^'
{
- dcmtkpp::DataSet data_set;
+ odil::DataSet data_set;
data_set.add(0x00100010,
- dcmtkpp::Element(
- dcmtkpp::Value::Strings({"Alpha^Betic^Ideo^Graphic^Pho^Netic"}),
- dcmtkpp::VR::PN));
- BOOST_REQUIRE_THROW(dcmtkpp::as_xml(data_set), dcmtkpp::Exception);
+ odil::Element(
+ odil::Value::Strings({"Alpha^Betic^Ideo^Graphic^Pho^Netic"}),
+ odil::VR::PN));
+ BOOST_REQUIRE_THROW(odil::as_xml(data_set), odil::Exception);
}
// Too many values separate by '='
{
- dcmtkpp::DataSet data_set;
+ odil::DataSet data_set;
data_set.add(0x00100010,
- dcmtkpp::Element(
- dcmtkpp::Value::Strings(
+ odil::Element(
+ odil::Value::Strings(
{"Alpha^Betic=Ideo^Graphic=Pho^Netic=Bad^Value"}),
- dcmtkpp::VR::PN));
- BOOST_REQUIRE_THROW(dcmtkpp::as_xml(data_set), dcmtkpp::Exception);
+ odil::VR::PN));
+ BOOST_REQUIRE_THROW(odil::as_xml(data_set), odil::Exception);
}
}
/******************************* TEST Error ************************************/
BOOST_AUTO_TEST_CASE(AsXMLInvalidDICOMTag)
{
- dcmtkpp::DataSet data_set;
+ odil::DataSet data_set;
data_set.add(0xbad00bad,
- dcmtkpp::Element(
- dcmtkpp::Value::Strings({"value"}),
- dcmtkpp::VR::CS));
- BOOST_REQUIRE_THROW(dcmtkpp::as_xml(data_set), dcmtkpp::Exception);
+ odil::Element(
+ odil::Value::Strings({"value"}),
+ odil::VR::CS));
+ BOOST_REQUIRE_THROW(odil::as_xml(data_set), odil::Exception);
}
/******************************* TEST Error ************************************/
BOOST_AUTO_TEST_CASE(AsXMLBadVR)
{
- dcmtkpp::DataSet data_set;
+ odil::DataSet data_set;
data_set.add(0x00080060,
- dcmtkpp::Element(
- dcmtkpp::Value::Strings({"value"}),
- dcmtkpp::VR::UNKNOWN));
- BOOST_REQUIRE_THROW(dcmtkpp::as_xml(data_set), dcmtkpp::Exception);
+ odil::Element(
+ odil::Value::Strings({"value"}),
+ odil::VR::UNKNOWN));
+ BOOST_REQUIRE_THROW(odil::as_xml(data_set), odil::Exception);
}
template<typename TValueType>
-dcmtkpp::DataSet create_dataset(std::string const & tag, std::string const & vr,
+odil::DataSet create_dataset(std::string const & tag, std::string const & vr,
std::string const & keyword,
TValueType const & values,
std::vector<int> const & order)
@@ -661,7 +659,7 @@ dcmtkpp::DataSet create_dataset(std::string const & tag, std::string const & vr,
boost::property_tree::ptree dataset_xml;
dataset_xml.add_child("NativeDicomModel", nativedicommodel);
- return dcmtkpp::as_dataset(dataset_xml);
+ return odil::as_dataset(dataset_xml);
}
template<typename T>
@@ -686,7 +684,7 @@ void create_dataset_error(std::string const & tag, std::string const & vr,
dataset_xml.add_child("NativeDicomModel", nativedicommodel);
// throw Exception
- dcmtkpp::as_dataset(dataset_xml);
+ odil::as_dataset(dataset_xml);
}
/******************************* TEST Nominal **********************************/
@@ -695,14 +693,14 @@ BOOST_AUTO_TEST_CASE(AsDataSetEmpty)
boost::property_tree::ptree dataset_xml;
dataset_xml.add_child("NativeDicomModel", boost::property_tree::ptree());
- dcmtkpp::DataSet const data_set = dcmtkpp::as_dataset(dataset_xml);
+ odil::DataSet const data_set = odil::as_dataset(dataset_xml);
BOOST_REQUIRE(data_set.empty());
}
/******************************* TEST Nominal **********************************/
BOOST_AUTO_TEST_CASE(AsDataSetIntegers)
{
- dcmtkpp::Value::Integers values({128, 256});
+ odil::Value::Integers values({128, 256});
// Tag Value sorted by number and not sorted
std::vector<std::vector<int> > orders = {{0, 1}, {1, 0}};
@@ -712,7 +710,7 @@ BOOST_AUTO_TEST_CASE(AsDataSetIntegers)
values, order);
BOOST_REQUIRE_EQUAL(data_set.size(), 1);
BOOST_REQUIRE(data_set.has("00280010"));
- BOOST_REQUIRE(data_set.get_vr("00280010") == dcmtkpp::VR::US);
+ BOOST_REQUIRE(data_set.get_vr("00280010") == odil::VR::US);
BOOST_REQUIRE(data_set.is_int("00280010"));
BOOST_REQUIRE(data_set.as_int("00280010") == values);
}
@@ -721,7 +719,7 @@ BOOST_AUTO_TEST_CASE(AsDataSetIntegers)
/******************************* TEST Nominal **********************************/
BOOST_AUTO_TEST_CASE(AsDataSetReals)
{
- dcmtkpp::Value::Reals values({1.2, 3.4});
+ odil::Value::Reals values({1.2, 3.4});
// Tag Value sorted by number and not sorted
std::vector<std::vector<int> > orders = {{0, 1}, {1, 0}};
@@ -732,7 +730,7 @@ BOOST_AUTO_TEST_CASE(AsDataSetReals)
values, order);
BOOST_REQUIRE_EQUAL(data_set.size(), 1);
BOOST_REQUIRE(data_set.has("00089459"));
- BOOST_REQUIRE(data_set.get_vr("00089459") == dcmtkpp::VR::FL);
+ BOOST_REQUIRE(data_set.get_vr("00089459") == odil::VR::FL);
BOOST_REQUIRE(data_set.is_real("00089459"));
BOOST_REQUIRE(data_set.as_real("00089459") == values);
}
@@ -741,7 +739,7 @@ BOOST_AUTO_TEST_CASE(AsDataSetReals)
/******************************* TEST Nominal **********************************/
BOOST_AUTO_TEST_CASE(AsDataSetStrings)
{
- dcmtkpp::Value::Strings values({"FOO", "BAR"});
+ odil::Value::Strings values({"FOO", "BAR"});
// Tag Value sorted by number and not sorted
std::vector<std::vector<int> > orders = {{0, 1}, {1, 0}};
@@ -751,7 +749,7 @@ BOOST_AUTO_TEST_CASE(AsDataSetStrings)
values, order);
BOOST_REQUIRE_EQUAL(data_set.size(), 1);
BOOST_REQUIRE(data_set.has("00080060"));
- BOOST_REQUIRE(data_set.get_vr("00080060") == dcmtkpp::VR::CS);
+ BOOST_REQUIRE(data_set.get_vr("00080060") == odil::VR::CS);
BOOST_REQUIRE(data_set.is_string("00080060"));
BOOST_REQUIRE(data_set.as_string("00080060") == values);
}
@@ -829,12 +827,12 @@ BOOST_AUTO_TEST_CASE(AsDataSetPersonName)
boost::property_tree::ptree dataset_xml;
dataset_xml.add_child("NativeDicomModel", nativedicommodel);
- dcmtkpp::DataSet const data_set = dcmtkpp::as_dataset(dataset_xml);
+ odil::DataSet const data_set = odil::as_dataset(dataset_xml);
BOOST_REQUIRE_EQUAL(data_set.size(), 1);
BOOST_REQUIRE(data_set.has("00100010"));
- BOOST_REQUIRE(data_set.get_vr("00100010") == dcmtkpp::VR::PN);
+ BOOST_REQUIRE(data_set.get_vr("00100010") == odil::VR::PN);
BOOST_REQUIRE(data_set.is_string("00100010"));
- BOOST_REQUIRE(data_set.as_string("00100010") == dcmtkpp::Value::Strings(
+ BOOST_REQUIRE(data_set.as_string("00100010") == odil::Value::Strings(
{"family^given^middle^prefix^suffix", "family=family=family"}));
}
}
@@ -842,16 +840,16 @@ BOOST_AUTO_TEST_CASE(AsDataSetPersonName)
/******************************* TEST Nominal **********************************/
BOOST_AUTO_TEST_CASE(AsDataSetDataSets)
{
- dcmtkpp::DataSet item1;
+ odil::DataSet item1;
item1.add(0x00100020,
- dcmtkpp::Element(dcmtkpp::Value::Strings({"FOO"}), dcmtkpp::VR::LO));
+ odil::Element(odil::Value::Strings({"FOO"}), odil::VR::LO));
item1.add(0x00100022,
- dcmtkpp::Element(dcmtkpp::Value::Strings({"BAR"}), dcmtkpp::VR::CS));
- dcmtkpp::DataSet item2;
+ odil::Element(odil::Value::Strings({"BAR"}), odil::VR::CS));
+ odil::DataSet item2;
item2.add(0x00100020,
- dcmtkpp::Element(dcmtkpp::Value::Strings({"OTHER"}), dcmtkpp::VR::LO));
+ odil::Element(odil::Value::Strings({"OTHER"}), odil::VR::LO));
- dcmtkpp::Value::DataSets expected_result({item1, item2});
+ odil::Value::DataSets expected_result({item1, item2});
std::vector<std::vector<int> > orders = {{0, 1}, {1, 0}};
for (auto order : orders)
@@ -871,7 +869,7 @@ BOOST_AUTO_TEST_CASE(AsDataSetDataSets)
{
boost::property_tree::ptree subdicomattribute;
subdicomattribute.put("<xmlattr>.vr",
- dcmtkpp::as_string(it->second.vr));
+ odil::as_string(it->second.vr));
subdicomattribute.put("<xmlattr>.tag", std::string(it->first));
subdicomattribute.put("<xmlattr>.keyword", it->first.get_name());
@@ -896,15 +894,10 @@ BOOST_AUTO_TEST_CASE(AsDataSetDataSets)
boost::property_tree::ptree dataset_xml;
dataset_xml.add_child("NativeDicomModel", nativedicommodel);
- std::stringstream xmldataset;
- boost::property_tree::xml_writer_settings<char> settings(' ', 4);
- boost::property_tree::write_xml(xmldataset, dataset_xml, settings);
-
- std::cout << xmldataset.str() << std::endl;
- dcmtkpp::DataSet const data_set = dcmtkpp::as_dataset(dataset_xml);
+ odil::DataSet const data_set = odil::as_dataset(dataset_xml);
BOOST_REQUIRE_EQUAL(data_set.size(), 1);
BOOST_REQUIRE(data_set.has("00101002"));
- BOOST_REQUIRE(data_set.get_vr("00101002") == dcmtkpp::VR::SQ);
+ BOOST_REQUIRE(data_set.get_vr("00101002") == odil::VR::SQ);
BOOST_REQUIRE(data_set.is_data_set("00101002"));
BOOST_REQUIRE(data_set.as_data_set("00101002") == expected_result);
@@ -930,20 +923,20 @@ BOOST_AUTO_TEST_CASE(AsDataSetBinary)
boost::property_tree::ptree dataset_xml;
dataset_xml.add_child("NativeDicomModel", nativedicommodel);
- dcmtkpp::DataSet const data_set = dcmtkpp::as_dataset(dataset_xml);
+ odil::DataSet const data_set = odil::as_dataset(dataset_xml);
BOOST_REQUIRE_EQUAL(data_set.size(), 1);
BOOST_REQUIRE(data_set.has("00660023"));
- BOOST_REQUIRE(data_set.get_vr("00660023") == dcmtkpp::VR::OW);
+ BOOST_REQUIRE(data_set.get_vr("00660023") == odil::VR::OW);
BOOST_REQUIRE(data_set.is_binary("00660023"));
- BOOST_REQUIRE(data_set.as_binary("00660023") == dcmtkpp::Value::Binary(
+ BOOST_REQUIRE(data_set.as_binary("00660023") == odil::Value::Binary(
{0x1, 0x2, 0x3, 0x4, 0x5}));
}
/******************************* TEST Error ************************************/
BOOST_AUTO_TEST_CASE(AsDataSetMissingRootNode)
{
- BOOST_REQUIRE_THROW(dcmtkpp::as_dataset(boost::property_tree::ptree()),
- dcmtkpp::Exception);
+ BOOST_REQUIRE_THROW(odil::as_dataset(boost::property_tree::ptree()),
+ odil::Exception);
}
/******************************* TEST Error ************************************/
@@ -955,7 +948,7 @@ BOOST_AUTO_TEST_CASE(AsDataSetBadDICOMNode)
boost::property_tree::ptree dataset_xml;
dataset_xml.add_child("NativeDicomModel", nativedicommodel);
- BOOST_REQUIRE_THROW(dcmtkpp::as_dataset(dataset_xml), dcmtkpp::Exception);
+ BOOST_REQUIRE_THROW(odil::as_dataset(dataset_xml), odil::Exception);
}
/******************************* TEST Error ************************************/
@@ -964,7 +957,7 @@ BOOST_AUTO_TEST_CASE(AsDataSetBadDicomAttributeSubNode)
// String value
BOOST_REQUIRE_THROW(create_dataset_error("00080060", "CS",
"Modality", "FOO"),
- dcmtkpp::Exception);
+ odil::Exception);
{ // Person Name
boost::property_tree::ptree dicomattribute;
@@ -987,18 +980,18 @@ BOOST_AUTO_TEST_CASE(AsDataSetBadDicomAttributeSubNode)
boost::property_tree::ptree dataset_xml;
dataset_xml.add_child("NativeDicomModel", nativedicommodel);
- BOOST_REQUIRE_THROW(dcmtkpp::as_dataset(dataset_xml), dcmtkpp::Exception);
+ BOOST_REQUIRE_THROW(odil::as_dataset(dataset_xml), odil::Exception);
}
// Real value
BOOST_REQUIRE_THROW(create_dataset_error(
"00089459", "FL",
"RecommendedDisplayFrameRateInFloat", 1.2),
- dcmtkpp::Exception);
+ odil::Exception);
// Integer value
BOOST_REQUIRE_THROW(create_dataset_error("00280010", "US", "Rows", 1),
- dcmtkpp::Exception);
+ odil::Exception);
{ // Sequence
boost::property_tree::ptree dicomattribute;
@@ -1016,14 +1009,14 @@ BOOST_AUTO_TEST_CASE(AsDataSetBadDicomAttributeSubNode)
boost::property_tree::ptree dataset_xml;
dataset_xml.add_child("NativeDicomModel", nativedicommodel);
- BOOST_REQUIRE_THROW(dcmtkpp::as_dataset(dataset_xml), dcmtkpp::Exception);
+ BOOST_REQUIRE_THROW(odil::as_dataset(dataset_xml), odil::Exception);
}
// Binary
BOOST_REQUIRE_THROW(create_dataset_error(
"00660023", "OW",
"TrianglePointIndexList", "AQIDBAU="),
- dcmtkpp::Exception);
+ odil::Exception);
}
/******************************* TEST Error ************************************/
@@ -1031,7 +1024,7 @@ BOOST_AUTO_TEST_CASE(AsDataSetUnknownVR)
{
BOOST_REQUIRE_THROW(create_dataset_error("00080060", "UR",
"Modality", "FOO"),
- dcmtkpp::Exception);
+ odil::Exception);
}
/******************************* TEST Error ************************************/
@@ -1057,7 +1050,7 @@ BOOST_AUTO_TEST_CASE(AsDataSetBadPersonNameSubNode)
boost::property_tree::ptree dataset_xml;
dataset_xml.add_child("NativeDicomModel", nativedicommodel);
- BOOST_REQUIRE_THROW(dcmtkpp::as_dataset(dataset_xml), dcmtkpp::Exception);
+ BOOST_REQUIRE_THROW(odil::as_dataset(dataset_xml), odil::Exception);
}
/******************************* TEST Error ************************************/
@@ -1083,7 +1076,7 @@ BOOST_AUTO_TEST_CASE(AsDataSetBadAlphabeticSubNode)
boost::property_tree::ptree dataset_xml;
dataset_xml.add_child("NativeDicomModel", nativedicommodel);
- BOOST_REQUIRE_THROW(dcmtkpp::as_dataset(dataset_xml), dcmtkpp::Exception);
+ BOOST_REQUIRE_THROW(odil::as_dataset(dataset_xml), odil::Exception);
}
/******************************* TEST Error ************************************/
@@ -1110,5 +1103,5 @@ BOOST_AUTO_TEST_CASE(AsDataSetTooManyInlineBinaryNode)
boost::property_tree::ptree dataset_xml;
dataset_xml.add_child("NativeDicomModel", nativedicommodel);
- BOOST_REQUIRE_THROW(dcmtkpp::as_dataset(dataset_xml), dcmtkpp::Exception);
+ BOOST_REQUIRE_THROW(odil::as_dataset(dataset_xml), odil::Exception);
}
diff --git a/tests/run.sh b/tests/run.sh
index fa8a490..1177ea8 100755
--- a/tests/run.sh
+++ b/tests/run.sh
@@ -40,17 +40,17 @@ clean() {
rm -rf ${directory}
}
-directory=$(mktemp -d dcmtkpp.XXX)
+directory=$(mktemp -d odil.XXX)
configure
start_scp
add_data
-export DCMTKPP_OWN_AET=LOCAL
-export DCMTKPP_PEER_HOST_NAME=localhost
-export DCMTKPP_PEER_PORT=11112
-export DCMTKPP_PEER_AET=REMOTE
+export ODIL_OWN_AET=LOCAL
+export ODIL_PEER_HOST_NAME=127.0.0.1
+export ODIL_PEER_PORT=11112
+export ODIL_PEER_AET=REMOTE
-ctest --no-compress-output -T Test || true
+ctest --no-compress-output -T Test $@ || true
stop_scp
clean
diff --git a/tests/tools/CMakeLists.txt b/tests/tools/CMakeLists.txt
new file mode 100644
index 0000000..9895042
--- /dev/null
+++ b/tests/tools/CMakeLists.txt
@@ -0,0 +1,11 @@
+find_package(DCMTK REQUIRED)
+
+add_definitions(${DCMTK_DEFINITIONS})
+include_directories(${DCMTK_INCLUDE_DIRS})
+link_directories(${DCMTK_LIBRARY_DIRS})
+
+file(GLOB headers *.h)
+file(GLOB files "*.cc")
+
+add_executable(getscu ${files} ${headers})
+target_link_libraries(getscu ${DCMTK_LIBRARIES})
diff --git a/tests/tools/dndefine.h b/tests/tools/dndefine.h
new file mode 100644
index 0000000..136535a
--- /dev/null
+++ b/tests/tools/dndefine.h
@@ -0,0 +1,80 @@
+/*
+ *
+ * Copyright (C) 2011, OFFIS e.V.
+ * All rights reserved. See COPYRIGHT file for details.
+ *
+ * This software and supporting documentation were developed by
+ *
+ * OFFIS e.V.
+ * R&D Division Health
+ * Escherweg 2
+ * D-26121 Oldenburg, Germany
+ *
+ *
+ * Module: dcnmnet
+ *
+ * Author: Uli Schlachter
+ *
+ * Purpose: Contains preprocessor definitions
+ *
+ * Last Update: $Author: uli $
+ * Update Date: $Date: 2011-12-14 11:45:14 $
+ * CVS/RCS Revision: $Revision: 1.1 $
+ * Status: $State: Exp $
+ *
+ * CVS/RCS Log at end of file
+ *
+ */
+
+
+#ifndef DNDEFINE_H
+#define DNDEFINE_H
+
+#include "dcmtk/config/osconfig.h"
+
+#include "dcmtk/ofstd/ofdefine.h"
+
+// MODIFICATION: Add definitions
+#ifdef DCMTK_SHARED
+#ifdef _WIN32
+/* Defines needed for building DLLs on windows */
+#define DCMTK_DECL_EXPORT __declspec(dllexport)
+#define DCMTK_DECL_IMPORT __declspec(dllimport)
+#elif defined(HAVE_HIDDEN_VISIBILITY)
+/* GCC hides everything when given -fvisibility=hidden. The symbols which
+ * should be visible have to get a default visibility again.
+ */
+#define DCMTK_DECL_EXPORT __attribute__ ((visibility("default")))
+#endif
+#endif
+
+#ifndef DCMTK_DECL_EXPORT
+#define DCMTK_DECL_EXPORT
+#endif
+
+#ifndef DCMTK_DECL_IMPORT
+#define DCMTK_DECL_IMPORT
+#endif
+// END Modification
+
+#ifdef dcmnet_EXPORTS
+#define DCMTK_DCMNET_EXPORT DCMTK_DECL_EXPORT
+#else
+#define DCMTK_DCMNET_EXPORT DCMTK_DECL_IMPORT
+#endif
+
+
+#endif
+
+
+/*
+ * CVS/RCS Log:
+ * $Log: dndefine.h,v $
+ * Revision 1.1 2011-12-14 11:45:14 uli
+ * Make it possible to perfectly build dcmnet and dcmtls a DLLs.
+ *
+ * Revision 1.1 2011-12-14 09:50:21 uli
+ * Make it possible to properly build dcmimgle as a DLL.
+ *
+ *
+ */
diff --git a/tests/tools/getscu.cc b/tests/tools/getscu.cc
new file mode 100644
index 0000000..88349a4
--- /dev/null
+++ b/tests/tools/getscu.cc
@@ -0,0 +1,657 @@
+/*
+ *
+ * Copyright (C) 2011, OFFIS e.V.
+ * All rights reserved. See COPYRIGHT file for details.
+ *
+ * This software and supporting documentation were developed by
+ *
+ * OFFIS e.V.
+ * R&D Division Health
+ * Escherweg 2
+ * D-26121 Oldenburg, Germany
+ *
+ *
+ * Module: dcmnet
+ *
+ * Author: Michael Onken
+ *
+ * Purpose: Query/Retrieve Service Class User (C-GET operation)
+ *
+ * Last Update: $Author: onken $
+ * Update Date: $Date: 2012-02-10 14:54:38 $
+ * CVS/RCS Revision: $Revision: 1.10 $
+ * Status: $State: Exp $
+ *
+ * CVS/RCS Log at end of file
+ *
+ */
+
+#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */
+
+#ifdef HAVE_GUSI_H
+#include <GUSI.h>
+#endif
+
+#include "dcmtk/ofstd/ofconapp.h"
+#include "dcmtk/ofstd/oflist.h"
+// MODIFICATION: Use local header file
+#include "scu.h"
+// END Modification
+#include "dcmtk/dcmdata/dcuid.h" /* for dcmtk version name */
+#include "dcmtk/dcmdata/dcostrmz.h" /* for dcmZlibCompressionLevel */
+#include "dcmtk/dcmdata/dcpath.h" /* for DcmPathProcessor */
+
+#ifdef WITH_ZLIB
+#include <zlib.h> /* for zlibVersion() */
+#endif
+
+#define OFFIS_CONSOLE_APPLICATION "getscu"
+
+static OFLogger getscuLogger = OFLog::getLogger("dcmtk.apps." OFFIS_CONSOLE_APPLICATION);
+
+static char rcsid[] = "$dcmtk: " OFFIS_CONSOLE_APPLICATION " v"
+ OFFIS_DCMTK_VERSION " " OFFIS_DCMTK_RELEASEDATE " $";
+
+/* default application titles */
+#define APPLICATIONTITLE "GETSCU"
+#define PEERAPPLICATIONTITLE "ANY-SCP"
+
+typedef enum {
+ QMPatientRoot = 0,
+ QMStudyRoot = 1,
+ QMPatientStudyOnly = 2
+} QueryModel;
+
+static const char* querySyntax[3] = {
+ UID_GETPatientRootQueryRetrieveInformationModel,
+ UID_GETStudyRootQueryRetrieveInformationModel,
+ UID_RETIRED_GETPatientStudyOnlyQueryRetrieveInformationModel
+};
+
+OFCmdUnsignedInt opt_maxPDU = ASC_DEFAULTMAXPDU;
+E_TransferSyntax opt_store_networkTransferSyntax = EXS_Unknown;
+E_TransferSyntax opt_get_networkTransferSyntax = EXS_Unknown;
+DcmStorageMode opt_storageMode = DCMSCU_STORAGE_DISK;
+OFBool opt_showPresentationContexts = OFFalse;
+OFBool opt_abortAssociation = OFFalse;
+OFCmdUnsignedInt opt_repeatCount = 1;
+QueryModel opt_queryModel = QMPatientRoot;
+T_DIMSE_BlockingMode opt_blockMode = DIMSE_BLOCKING;
+int opt_dimse_timeout = 0;
+int opt_acse_timeout = 30;
+OFString opt_outputDirectory = ".";
+static OFList<OFString> overrideKeys;
+
+static void prepareTS(E_TransferSyntax ts,
+ OFList<OFString>& syntaxes);
+
+static void applyOverrideKeys(DcmDataset *dataset);
+
+#define SHORTCOL 4
+#define LONGCOL 21
+
+int
+main(int argc, char *argv[])
+{
+ const char *opt_peer;
+ OFCmdUnsignedInt opt_port = 104;;
+ const char *opt_peerTitle = PEERAPPLICATIONTITLE;
+ const char *opt_ourTitle = APPLICATIONTITLE;
+ OFList<OFString> fileNameList;
+
+#ifdef HAVE_GUSI_H
+ /* needed for Macintosh */
+ GUSISetup(GUSIwithSIOUXSockets);
+ GUSISetup(GUSIwithInternetSockets);
+#endif
+
+ char tempstr[20];
+ OFString temp_str;
+ OFConsoleApplication app(OFFIS_CONSOLE_APPLICATION , "DICOM retrieve (C-GET) SCU", rcsid);
+ OFCommandLine cmd;
+
+ cmd.setParamColumn(LONGCOL + SHORTCOL + 4);
+ cmd.addParam("peer", "hostname of DICOM peer");
+ cmd.addParam("port", "tcp/ip port number of peer");
+ cmd.addParam("dcmfile-in", "DICOM query file(s)", OFCmdParam::PM_MultiOptional);
+
+ cmd.setOptionColumns(LONGCOL, SHORTCOL);
+ cmd.addGroup("general options:", LONGCOL, SHORTCOL + 2);
+ cmd.addOption("--help", "-h", "print this help text and exit", OFCommandLine::AF_Exclusive);
+ cmd.addOption("--version", "print version information and exit", OFCommandLine::AF_Exclusive);
+ OFLog::addOptions(cmd);
+ cmd.addOption("--verbose-pc", "+v", "show presentation contexts in verbose mode");
+
+ cmd.addGroup("network options:");
+ cmd.addSubGroup("override matching keys:");
+ cmd.addOption("--key", "-k", 1, "[k]ey: gggg,eeee=\"str\", path or dic. name=\"str\"",
+ "override matching key");
+ cmd.addSubGroup("retrieve information model:");
+ cmd.addOption("--patient", "-P", "use patient root information model (default)");
+ cmd.addOption("--study", "-S", "use study root information model");
+ cmd.addOption("--psonly", "-O", "use patient/study only information model");
+ cmd.addSubGroup("application entity titles:");
+ OFString opt1 = "set my calling AE title (default: ";
+ opt1 += APPLICATIONTITLE;
+ opt1 += ")";
+ cmd.addOption("--aetitle", "-aet", 1, "[a]etitle: string", opt1.c_str());
+ OFString opt2 = "set called AE title of peer (default: ";
+ opt2 += PEERAPPLICATIONTITLE;
+ opt2 += ")";
+ cmd.addOption("--call", "-aec", 1, "[a]etitle: string", opt2.c_str());
+ cmd.addSubGroup("preferred storage transfer syntaxes (incoming associations):");
+ cmd.addOption("--prefer-uncompr", "+x=", "prefer explicit VR local byte order (default)");
+ cmd.addOption("--prefer-little", "+xe", "prefer explicit VR little endian TS");
+ cmd.addOption("--prefer-big", "+xb", "prefer explicit VR big endian TS");
+ cmd.addOption("--prefer-lossless", "+xs", "prefer default JPEG lossless TS");
+ cmd.addOption("--prefer-jpeg8", "+xy", "prefer default JPEG lossy TS for 8 bit data");
+ cmd.addOption("--prefer-jpeg12", "+xx", "prefer default JPEG lossy TS for 12 bit data");
+ cmd.addOption("--prefer-j2k-lossless", "+xv", "prefer JPEG 2000 lossless TS");
+ cmd.addOption("--prefer-j2k-lossy", "+xw", "prefer JPEG 2000 lossy TS");
+ cmd.addOption("--prefer-jls-lossless", "+xt", "prefer JPEG-LS lossless TS");
+ cmd.addOption("--prefer-jls-lossy", "+xu", "prefer JPEG-LS lossy TS");
+ cmd.addOption("--prefer-mpeg2", "+xm", "prefer MPEG2 Main Profile @ Main Level TS");
+ cmd.addOption("--prefer-mpeg2-high", "+xh", "prefer MPEG2 Main Profile @ High Level TS");
+ cmd.addOption("--prefer-mpeg4", "+xn", "prefer MPEG4 AVC/H.264 HP / Level 4.1 TS");
+ cmd.addOption("--prefer-mpeg4-bd", "+xl", "prefer MPEG4 AVC/H.264 BD-compatible TS");
+ cmd.addOption("--prefer-rle", "+xr", "prefer RLE lossless TS");
+#ifdef WITH_ZLIB
+ cmd.addOption("--prefer-deflated", "+xd", "prefer deflated explicit VR little endian TS");
+#endif
+ cmd.addOption("--implicit", "+xi", "accept implicit VR little endian TS only");
+ cmd.addSubGroup("proposed retrieve transfer syntaxes (outgoing associations):");
+ cmd.addOption("--propose-uncompr", "-x=", "propose all uncompressed TS, explicit VR\nwith local byte ordering first (default)");
+ cmd.addOption("--propose-little", "-xe", "propose all uncompressed TS, explicit VR\nlittle endian first");
+ cmd.addOption("--propose-big", "-xb", "propose all uncompressed TS, explicit VR\nbig endian first");
+#ifdef WITH_ZLIB
+ cmd.addOption("--propose-deflated", "-xd", "propose deflated explicit VR little endian TS\nand all uncompressed transfer syntaxes");
+#endif
+ cmd.addOption("--propose-implicit", "-xi", "propose implicit VR little endian TS only");
+ cmd.addSubGroup("other network options:");
+ cmd.addOption("--timeout", "-to", 1, "[s]econds: integer (default: unlimited)", "timeout for connection requests");
+ cmd.addOption("--acse-timeout", "-ta", 1, "[s]econds: integer (default: 30)", "timeout for ACSE messages");
+ cmd.addOption("--dimse-timeout", "-td", 1, "[s]econds: integer (default: unlimited)", "timeout for DIMSE messages");
+
+ OFString opt3 = "set max receive pdu to n bytes (default: ";
+ sprintf(tempstr, "%ld", OFstatic_cast(long, ASC_DEFAULTMAXPDU));
+ opt3 += tempstr;
+ opt3 += ")";
+ OFString opt4 = "[n]umber of bytes: integer (";
+ sprintf(tempstr, "%ld", OFstatic_cast(long, ASC_MINIMUMPDUSIZE));
+ opt4 += tempstr;
+ opt4 += "..";
+ sprintf(tempstr, "%ld", OFstatic_cast(long, ASC_MAXIMUMPDUSIZE));
+ opt4 += tempstr;
+ opt4 += ")";
+ cmd.addOption("--max-pdu", "-pdu", 1, opt4.c_str(), opt3.c_str());
+ cmd.addOption("--repeat", 1, "[n]umber: integer", "repeat n times");
+ cmd.addOption("--abort", "abort association instead of releasing it");
+ cmd.addGroup("output options:");
+ cmd.addSubGroup("general:");
+ cmd.addOption("--output-directory", "-od", 1, "[d]irectory: string (default: \".\")", "write received objects to existing directory d");
+ cmd.addSubGroup("storage mode:");
+ cmd.addOption("--normal", "-B", "receive in memory, then write to disk (default)");
+ cmd.addOption("--bit-preserving", "+B", "receive directly to disk");
+ cmd.addOption("--ignore", "ignore store data, receive but do not store");
+
+ /* evaluate command line */
+ prepareCmdLineArgs(argc, argv, OFFIS_CONSOLE_APPLICATION);
+ if (!app.parseCommandLine(cmd, argc, argv, OFCommandLine::PF_ExpandWildcards))
+ {
+ exit(1);
+ }
+ /* check exclusive options first */
+ if (cmd.hasExclusiveOption())
+ {
+ if (cmd.findOption("--version"))
+ {
+ app.printHeader(OFTrue /*print host identifier*/);
+ COUT << OFendl << "External libraries used:";
+#ifdef WITH_ZLIB
+ COUT << OFendl << "- ZLIB, Version " << zlibVersion() << OFendl;
+#else
+ COUT << " none" << OFendl;
+#endif
+ return 0;
+ }
+ }
+
+ /* command line parameters */
+
+ cmd.getParam(1, opt_peer);
+ app.checkParam(cmd.getParamAndCheckMinMax(2, opt_port, 1, 65535));
+
+ OFLog::configureFromCommandLine(cmd, app);
+ if (cmd.findOption("--verbose-pc"))
+ {
+ app.checkDependence("--verbose-pc", "verbose mode", getscuLogger.isEnabledFor(OFLogger::INFO_LOG_LEVEL));
+ opt_showPresentationContexts = OFTrue;
+ }
+
+ if (cmd.findOption("--key", 0, OFCommandLine::FOM_FirstFromLeft))
+ {
+ const char *ovKey = NULL;
+ do
+ {
+ app.checkValue(cmd.getValue(ovKey));
+ overrideKeys.push_back(ovKey);
+ } while (cmd.findOption("--key", 0, OFCommandLine::FOM_NextFromLeft));
+ }
+
+ cmd.beginOptionBlock();
+ if (cmd.findOption("--patient")) opt_queryModel = QMPatientRoot;
+ if (cmd.findOption("--study")) opt_queryModel = QMStudyRoot;
+ if (cmd.findOption("--psonly")) opt_queryModel = QMPatientStudyOnly;
+ cmd.endOptionBlock();
+
+ if (cmd.findOption("--aetitle")) app.checkValue(cmd.getValue(opt_ourTitle));
+ if (cmd.findOption("--call")) app.checkValue(cmd.getValue(opt_peerTitle));
+
+ cmd.beginOptionBlock();
+ if (cmd.findOption("--prefer-uncompr"))
+ {
+ opt_store_networkTransferSyntax = EXS_Unknown;
+ }
+// MODIFICATION: Comment undefined TransferSyntax
+ if (cmd.findOption("--prefer-little")) opt_store_networkTransferSyntax = EXS_LittleEndianExplicit;
+ if (cmd.findOption("--prefer-big")) opt_store_networkTransferSyntax = EXS_BigEndianExplicit;
+// if (cmd.findOption("--prefer-lossless")) opt_store_networkTransferSyntax = EXS_JPEGProcess14SV1;
+// if (cmd.findOption("--prefer-jpeg8")) opt_store_networkTransferSyntax = EXS_JPEGProcess1;
+// if (cmd.findOption("--prefer-jpeg12")) opt_store_networkTransferSyntax = EXS_JPEGProcess2_4;
+ if (cmd.findOption("--prefer-j2k-lossless")) opt_store_networkTransferSyntax = EXS_JPEG2000LosslessOnly;
+ if (cmd.findOption("--prefer-j2k-lossy")) opt_store_networkTransferSyntax = EXS_JPEG2000;
+ if (cmd.findOption("--prefer-jls-lossless")) opt_store_networkTransferSyntax = EXS_JPEGLSLossless;
+ if (cmd.findOption("--prefer-jls-lossy")) opt_store_networkTransferSyntax = EXS_JPEGLSLossy;
+ if (cmd.findOption("--prefer-mpeg2")) opt_store_networkTransferSyntax = EXS_MPEG2MainProfileAtMainLevel;
+ if (cmd.findOption("--prefer-mpeg2-high")) opt_store_networkTransferSyntax = EXS_MPEG2MainProfileAtHighLevel;
+// if (cmd.findOption("--prefer-mpeg4")) opt_store_networkTransferSyntax = EXS_MPEG4HighProfileLevel4_1;
+// if (cmd.findOption("--prefer-mpeg4-bd")) opt_store_networkTransferSyntax = EXS_MPEG4BDcompatibleHighProfileLevel4_1;
+ if (cmd.findOption("--prefer-rle")) opt_store_networkTransferSyntax = EXS_RLELossless;
+// END Modification
+#ifdef WITH_ZLIB
+ if (cmd.findOption("--prefer-deflated")) opt_store_networkTransferSyntax = EXS_DeflatedLittleEndianExplicit;
+#endif
+ if (cmd.findOption("--implicit")) opt_store_networkTransferSyntax = EXS_LittleEndianImplicit;
+ cmd.endOptionBlock();
+
+ cmd.beginOptionBlock();
+ if (cmd.findOption("--propose-uncompr")) opt_get_networkTransferSyntax = EXS_Unknown;
+ if (cmd.findOption("--propose-little")) opt_get_networkTransferSyntax = EXS_LittleEndianExplicit;
+ if (cmd.findOption("--propose-big")) opt_get_networkTransferSyntax = EXS_BigEndianExplicit;
+ if (cmd.findOption("--propose-implicit")) opt_get_networkTransferSyntax = EXS_LittleEndianImplicit;
+#ifdef WITH_ZLIB
+ if (cmd.findOption("--propose-deflated")) opt_get_networkTransferSyntax = EXS_DeflatedLittleEndianExplicit;
+#endif
+ cmd.endOptionBlock();
+
+ if (cmd.findOption("--timeout"))
+ {
+ OFCmdSignedInt opt_timeout = 0;
+ app.checkValue(cmd.getValueAndCheckMin(opt_timeout, 1));
+ dcmConnectionTimeout.set(OFstatic_cast(Sint32, opt_timeout));
+ }
+
+ if (cmd.findOption("--acse-timeout"))
+ {
+ OFCmdSignedInt opt_timeout = 0;
+ app.checkValue(cmd.getValueAndCheckMin(opt_timeout, 1));
+ opt_acse_timeout = OFstatic_cast(int, opt_timeout);
+ }
+
+ if (cmd.findOption("--dimse-timeout"))
+ {
+ OFCmdSignedInt opt_timeout = 0;
+ app.checkValue(cmd.getValueAndCheckMin(opt_timeout, 1));
+ opt_dimse_timeout = OFstatic_cast(int, opt_timeout);
+ opt_blockMode = DIMSE_NONBLOCKING;
+ }
+
+ if (cmd.findOption("--max-pdu")) app.checkValue(cmd.getValueAndCheckMinMax(opt_maxPDU, ASC_MINIMUMPDUSIZE, ASC_MAXIMUMPDUSIZE));
+ if (cmd.findOption("--repeat")) app.checkValue(cmd.getValueAndCheckMin(opt_repeatCount, 1));
+ if (cmd.findOption("--abort")) opt_abortAssociation = OFTrue;
+ if (cmd.findOption("--ignore")) opt_storageMode = DCMSCU_STORAGE_IGNORE;
+
+ if (cmd.findOption("--output-directory"))
+ {
+ app.checkValue(cmd.getValue(opt_outputDirectory));
+ app.checkConflict("--output-directory", "--ignore", opt_storageMode == DCMSCU_STORAGE_IGNORE);
+ }
+
+ cmd.beginOptionBlock();
+ if (cmd.findOption("--bit-preserving"))
+ {
+ app.checkConflict("--bit-preserving", "--ignore", opt_storageMode == DCMSCU_STORAGE_IGNORE);
+ opt_storageMode = DCMSCU_STORAGE_BIT_PRESERVING;
+ }
+ if (cmd.findOption("--normal"))
+ {
+ app.checkConflict("--normal", "--bit-preserving", opt_storageMode == DCMSCU_STORAGE_BIT_PRESERVING);
+ app.checkConflict("--normal", "--ignore", opt_storageMode == DCMSCU_STORAGE_IGNORE);
+ opt_storageMode = DCMSCU_STORAGE_DISK;
+ }
+ cmd.endOptionBlock();
+
+ /* finally parse filenames */
+ int paramCount = cmd.getParamCount();
+ const char *currentFilename = NULL;
+ OFString errormsg;
+
+ for (int i=3; i <= paramCount; i++)
+ {
+ cmd.getParam(i, currentFilename);
+ if (access(currentFilename, R_OK) < 0)
+ {
+ errormsg = "cannot access file: ";
+ errormsg += currentFilename;
+ app.printError(errormsg.c_str());
+ }
+ fileNameList.push_back(currentFilename);
+ }
+
+ if (fileNameList.empty() && overrideKeys.empty())
+ {
+ app.printError("either query file or override keys (or both) must be specified");
+ }
+
+ /* print resource identifier */
+ OFLOG_DEBUG(getscuLogger, rcsid << OFendl);
+
+ /* make sure data dictionary is loaded */
+ if (!dcmDataDict.isDictionaryLoaded())
+ {
+ OFLOG_WARN(getscuLogger, "no data dictionary loaded, check environment variable: "
+ << DCM_DICT_ENVIRONMENT_VARIABLE);
+ }
+
+ /* make sure output directory exists and is writeable */
+
+ if (!OFStandard::dirExists(opt_outputDirectory))
+ {
+ OFLOG_FATAL(getscuLogger, "specified output directory does not exist");
+ return 1;
+ }
+ else if (!OFStandard::isWriteable(opt_outputDirectory))
+ {
+ OFLOG_FATAL(getscuLogger, "specified output directory is not writeable");
+ return 1;
+ }
+
+ // Setup SCU
+ OFList<OFString> syntaxes;
+ prepareTS(opt_get_networkTransferSyntax, syntaxes);
+ DcmSCU scu;
+ scu.setMaxReceivePDULength(opt_maxPDU);
+ scu.setACSETimeout(opt_acse_timeout);
+ scu.setDIMSEBlockingMode(opt_blockMode);
+ scu.setDIMSETimeout(opt_dimse_timeout);
+ scu.setAETitle(opt_ourTitle);
+ scu.setPeerHostName(opt_peer);
+ scu.setPeerPort(OFstatic_cast(Uint16, opt_port));
+ scu.setPeerAETitle(opt_peerTitle);
+ scu.setVerbosePCMode(opt_showPresentationContexts);
+
+ // Add presentation contexts for get and find (we do not actually need find...)
+ // (only uncompressed)
+ scu.addPresentationContext(querySyntax[opt_queryModel], syntaxes);
+
+ // Add storage presentation contexts (long list of storage SOP classes, uncompressed)
+ syntaxes.clear();
+ prepareTS(opt_store_networkTransferSyntax, syntaxes);
+ for (Uint16 j = 0; j < numberOfDcmLongSCUStorageSOPClassUIDs; j++)
+ {
+ scu.addPresentationContext(dcmLongSCUStorageSOPClassUIDs[j], syntaxes, ASC_SC_ROLE_SCP);
+ }
+
+ // Set the storage mode
+ scu.setStorageMode(opt_storageMode);
+ if (opt_storageMode != DCMSCU_STORAGE_IGNORE)
+ {
+ scu.setStorageDir(opt_outputDirectory);
+ }
+
+ // Initialize network and negotiate association
+ OFCondition cond = scu.initNetwork();
+ if (cond.bad())
+ {
+ OFLOG_FATAL(getscuLogger, DimseCondition::dump(temp_str, cond));
+ exit(1);
+ }
+ cond = scu.negotiateAssociation();
+ if (cond.bad())
+ {
+ OFLOG_FATAL(getscuLogger, "No Acceptable Presentation Contexts");
+ exit(1);
+ }
+ cond = EC_Normal;
+ T_ASC_PresentationContextID pcid = scu.findPresentationContextID(querySyntax[opt_queryModel], "");
+ if (pcid == 0)
+ {
+ OFLOG_FATAL(getscuLogger, "No adequate Presentation Contexts for sending C-GET");
+ exit(1);
+ }
+
+ /* Do the real work, i.e. send C-GET requests and receive objects */
+ for (Uint16 repeat = 0; repeat < opt_repeatCount; repeat++)
+ {
+ Uint16 numRuns = 1;
+ DcmFileFormat dcmff;
+ DcmDataset *dset = dcmff.getDataset();
+ OFListConstIterator(OFString) it;
+ /* Load first file, if there is one */
+ if (!fileNameList.empty())
+ {
+ numRuns = fileNameList.size();
+ it = fileNameList.begin();
+ cond = dcmff.loadFile((*it).c_str());
+ if (cond.bad())
+ {
+ OFLOG_FATAL(getscuLogger, DimseCondition::dump(temp_str, cond));
+ exit(1);
+ }
+ dset = dcmff.getDataset();
+ }
+ OFList<RetrieveResponse*> responses;
+ /* For all files (or at least one run from override keys) */
+ for (Uint16 i = 0; i < numRuns; i++)
+ {
+ applyOverrideKeys(dset);
+ cond = scu.sendCGETRequest(pcid, dset, &responses);
+ if (cond.bad())
+ {
+ exit(1);
+ }
+ /* Load next file if there is one */
+ if (numRuns > 1)
+ {
+ it++;
+ cond = dcmff.loadFile((*it).c_str());
+ if (cond.bad())
+ {
+ OFLOG_FATAL(getscuLogger, DimseCondition::dump(temp_str, cond));
+ exit(1);
+ }
+ dset = dcmff.getDataset();
+ }
+ }
+ if (!responses.empty())
+ {
+ /* Output final status report */
+ OFLOG_INFO(getscuLogger, "Final status report from last C-GET message:");
+ (*(--responses.end()))->print();
+ /* Delete responses */
+ OFListIterator(RetrieveResponse*) iter = responses.begin();
+ OFListConstIterator(RetrieveResponse*) last = responses.end();
+ while (iter != last)
+ {
+ delete (*iter);
+ iter = responses.erase(iter);
+ }
+ }
+ }
+
+ /* tear down association */
+ if (cond == EC_Normal)
+ {
+ if (opt_abortAssociation)
+ {
+ OFLOG_INFO(getscuLogger, "Aborting Association");
+ scu.closeAssociation(DCMSCU_ABORT_ASSOCIATION);
+ exit(0);
+ } else
+ {
+ /* release association */
+ scu.closeAssociation(DCMSCU_RELEASE_ASSOCIATION);
+ exit(0);
+ }
+ }
+ else if (cond == DUL_PEERREQUESTEDRELEASE)
+ {
+ OFLOG_ERROR(getscuLogger, "Protocol Error: Peer requested release (Aborting)");
+ scu.closeAssociation(DCMSCU_ABORT_ASSOCIATION);
+ exit(1);
+ }
+ else if (cond == DUL_PEERABORTEDASSOCIATION)
+ {
+ OFLOG_INFO(getscuLogger, "Peer Aborted Association");
+ }
+ else
+ {
+ OFLOG_ERROR(getscuLogger, "GET SCU Failed: " << DimseCondition::dump(temp_str, cond));
+ OFLOG_INFO(getscuLogger, "Aborting Association");
+ scu.closeAssociation(DCMSCU_ABORT_ASSOCIATION);
+ exit(1);
+ }
+
+#ifdef HAVE_WINSOCK_H
+ WSACleanup();
+#endif
+
+ return 0;
+}
+
+static void applyOverrideKeys(DcmDataset *dataset)
+{
+ /* replace specific keys by those in overrideKeys */
+ OFListConstIterator(OFString) path = overrideKeys.begin();
+ OFListConstIterator(OFString) endOfList = overrideKeys.end();
+ DcmPathProcessor proc;
+ proc.setItemWildcardSupport(OFFalse);
+ proc.checkPrivateReservations(OFFalse);
+ OFCondition cond;
+ while (path != endOfList)
+ {
+ cond = proc.applyPathWithValue(dataset, *path);
+ if (cond.bad())
+ {
+ OFLOG_ERROR(getscuLogger, "Bad override key/path: " << *path << ": " << cond.text());
+ }
+ path++;
+ }
+}
+
+static void prepareTS(E_TransferSyntax ts,
+ OFList<OFString>& syntaxes)
+{
+ /*
+ ** We prefer to use Explicitly encoded transfer syntaxes.
+ ** If we are running on a Little Endian machine we prefer
+ ** LittleEndianExplicitTransferSyntax to BigEndianTransferSyntax.
+ ** Some SCP implementations will just select the first transfer
+ ** syntax they support (this is not part of the standard) so
+ ** organise the proposed transfer syntaxes to take advantage
+ ** of such behaviour.
+ **
+ ** The presentation contexts proposed here are only used for
+ ** C-FIND and C-MOVE, so there is no need to support compressed
+ ** transmission.
+ */
+
+ switch (ts)
+ {
+ case EXS_LittleEndianImplicit:
+ /* we only support Little Endian Implicit */
+ syntaxes.push_back(UID_LittleEndianExplicitTransferSyntax);
+ break;
+ case EXS_LittleEndianExplicit:
+ /* we prefer Little Endian Explicit */
+ syntaxes.push_back(UID_LittleEndianExplicitTransferSyntax);
+ syntaxes.push_back(UID_BigEndianExplicitTransferSyntax);
+ syntaxes.push_back(UID_LittleEndianImplicitTransferSyntax);
+ break;
+ case EXS_BigEndianExplicit:
+ /* we prefer Big Endian Explicit */
+ syntaxes.push_back(UID_BigEndianExplicitTransferSyntax);
+ syntaxes.push_back(UID_LittleEndianExplicitTransferSyntax);
+ syntaxes.push_back(UID_LittleEndianImplicitTransferSyntax);
+ break;
+#ifdef WITH_ZLIB
+ case EXS_DeflatedLittleEndianExplicit:
+ /* we prefer Deflated Little Endian Explicit */
+ syntaxes.push_back(UID_DeflatedExplicitVRLittleEndianTransferSyntax);
+ syntaxes.push_back(UID_LittleEndianExplicitTransferSyntax);
+ syntaxes.push_back(UID_BigEndianExplicitTransferSyntax);
+ syntaxes.push_back(UID_LittleEndianImplicitTransferSyntax);
+ break;
+#endif
+ default:
+ DcmXfer xfer(ts);
+ if (xfer.isEncapsulated())
+ {
+ syntaxes.push_back(xfer.getXferID());
+ }
+ /* We prefer explicit transfer syntaxes.
+ * If we are running on a Little Endian machine we prefer
+ * LittleEndianExplicitTransferSyntax to BigEndianTransferSyntax.
+ */
+ if (gLocalByteOrder == EBO_LittleEndian) /* defined in dcxfer.h */
+ {
+ syntaxes.push_back(UID_LittleEndianExplicitTransferSyntax);
+ syntaxes.push_back(UID_BigEndianExplicitTransferSyntax);
+ } else
+ {
+ syntaxes.push_back(UID_BigEndianExplicitTransferSyntax);
+ syntaxes.push_back(UID_LittleEndianExplicitTransferSyntax);
+ }
+ syntaxes.push_back(UID_LittleEndianImplicitTransferSyntax);
+ break;
+ }
+}
+
+
+/*
+** CVS Log
+** $Log: getscu.cc,v $
+** Revision 1.10 2012-02-10 14:54:38 onken
+** Removed duplicate log message when closing an association.
+**
+** Revision 1.9 2011-12-16 16:36:44 meichel
+** Minor changes for MSVC6 compatibility
+**
+** Revision 1.8 2011-09-26 08:13:53 joergr
+** Moved --verbose-pc option to the end of the "general options" section.
+**
+** Revision 1.7 2011-09-21 12:57:47 joergr
+** Removed TCP wrapper support (libwrap) which is not really useful for an SCU.
+**
+** Revision 1.6 2011-09-21 11:06:28 joergr
+** Removed option --disable-host-lookup which is not really useful for an SCU.
+**
+** Revision 1.5 2011-09-09 08:58:11 joergr
+** Replaced local list of supported storage SOP classes by the global variable
+** dcmLongSCUStorageSOPClassUIDs, which is maintained at a central location.
+**
+** Revision 1.4 2011-09-07 12:40:45 joergr
+** Made more clear that option --key also supports path expressions.
+**
+** Revision 1.3 2011-08-25 15:05:04 joergr
+** Changed data structure for Q/R responses from OFVector to OFList. Also fixed
+** some possible memory leaks and made the FIND/MOVE/GET code more consistent.
+**
+** Revision 1.2 2011-08-25 12:51:22 joergr
+** Added "verbose-pc" mode that shows the presentation contexts in verbose mode.
+**
+** Revision 1.1 2011-08-25 09:31:32 onken
+** Added C-GET functionality to DcmSCU class and accompanying getscu
+** commandline application.
+**
+*/
diff --git a/tests/tools/scu.cc b/tests/tools/scu.cc
new file mode 100644
index 0000000..6c45384
--- /dev/null
+++ b/tests/tools/scu.cc
@@ -0,0 +1,2590 @@
+/*
+ *
+ * Copyright (C) 2008-2012, OFFIS e.V.
+ * All rights reserved. See COPYRIGHT file for details.
+ *
+ * This software and supporting documentation were developed by
+ *
+ * OFFIS e.V.
+ * R&D Division Health
+ * Escherweg 2
+ * D-26121 Oldenburg, Germany
+ *
+ *
+ * Module: dcmnet
+ *
+ * Author: Michael Onken
+ *
+ * Purpose: Base class for Service Class Users (SCUs)
+ *
+ * Last Update: $Author: onken $
+ * Update Date: $Date: 2012-05-14 10:42:54 $
+ * CVS/RCS Revision: $Revision: 1.60 $
+ * Status: $State: Exp $
+ *
+ * CVS/RCS Log at end of file
+ *
+ */
+
+#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */
+
+// MODIFICATION: Take the local scu.h
+#include "scu.h"
+// END Modification
+#include "dcmtk/dcmnet/diutil.h" /* for dcmnet logger */
+#include "dcmtk/dcmdata/dcuid.h" /* for dcmFindUIDName() */
+#include "dcmtk/dcmdata/dcostrmf.h" /* for class DcmOutputFileStream */
+
+
+#ifdef WITH_ZLIB
+#include <zlib.h> /* for zlibVersion() */
+#endif
+
+#if OFFIS_DCMTK_VERSION_NUMBER <= 360
+
+// MODIFICATION: Add missing Errors
+const OFConditionConst NET_EC_InvalidSOPClassUID(OFM_dcmnet, 1000, OF_error, "Invalid SOP Class UID");
+const OFConditionConst NET_EC_UnknownStorageSOPClass(OFM_dcmnet, 1001, OF_error, "Unknown Storage SOP Class");
+const OFConditionConst NET_EC_InvalidSOPInstanceUID(OFM_dcmnet, 1002, OF_error, "Invalid SOP Instance UID");
+const OFConditionConst NET_EC_InvalidTransferSyntaxUID(OFM_dcmnet, 1003, OF_error, "Invalid Transfer Syntax UID");
+const OFConditionConst NET_EC_UnknownTransferSyntax(OFM_dcmnet, 1004, OF_error, "Unknown Transfer Syntax");
+const OFConditionConst NET_EC_NoPresentationContextsDefined(OFM_dcmnet, 1005, OF_error, "No Presentation Contexts defined");
+const OFConditionConst NET_EC_NoAcceptablePresentationContexts(OFM_dcmnet, 1006, OF_error, "No acceptable Presentation Contexts");
+const OFConditionConst NET_EC_NoSOPInstancesToSend(OFM_dcmnet, 1007, OF_error, "No SOP instances to send");
+const OFConditionConst NET_EC_NoSuchSOPInstance(OFM_dcmnet, 1008, OF_error, "No such SOP instance");
+const OFConditionConst NET_EC_InvalidDatasetPointer(OFM_dcmnet, 1009, OF_error, "Invalid dataset pointer");
+const OFConditionConst NET_EC_AlreadyConnected(OFM_dcmnet, 1010, OF_error, "Already connected");
+const OFConditionConst NET_EC_InsufficientPortPrivileges(OFM_dcmnet, 1023, OF_error, "Insufficient port privileges");
+// END Modification
+
+#endif
+
+DcmSCU::DcmSCU() :
+ m_assoc(NULL),
+ m_net(NULL),
+ m_params(NULL),
+ m_assocConfigFilename(),
+ m_assocConfigProfile(),
+ m_presContexts(),
+ m_assocConfigFile(),
+ m_openDIMSERequest(NULL),
+ m_maxReceivePDULength(ASC_DEFAULTMAXPDU),
+ m_blockMode(DIMSE_BLOCKING),
+ m_ourAETitle("ANY-SCU"),
+ m_peer(),
+ m_peerAETitle("ANY-SCP"),
+ m_peerPort(104),
+ m_dimseTimeout(0),
+ m_acseTimeout(30),
+ m_storageDir(),
+ m_storageMode(DCMSCU_STORAGE_DISK),
+ m_verbosePCMode(OFFalse),
+ m_datasetConversionMode(OFFalse),
+ m_progressNotificationMode(OFTrue)
+{
+
+#ifdef HAVE_GUSI_H
+ GUSISetup(GUSIwithSIOUXSockets);
+ GUSISetup(GUSIwithInternetSockets);
+#endif
+
+#ifdef HAVE_WINSOCK_H
+ WSAData winSockData;
+ /* we need at least version 1.1 */
+ WORD winSockVersionNeeded = MAKEWORD( 1, 1 );
+ WSAStartup(winSockVersionNeeded, &winSockData); // TODO: check with multiple SCU instances whether this is harmful
+#endif
+}
+
+void DcmSCU::freeNetwork()
+{
+ if ((m_assoc != NULL) || (m_net != NULL) || (m_params != NULL))
+ DCMNET_DEBUG("Cleaning up internal association and network structures");
+ /* destroy association parameters, i.e. free memory of T_ASC_Parameters.
+ Usually this is done in ASC_destroyAssociation; however, if we already
+ have association parameters but not yet an association (e.g. after calling
+ initNetwork() and negotiateAssociation()), the latter approach may fail.
+ */
+ if (m_params)
+ {
+ ASC_destroyAssociationParameters(&m_params);
+ m_params = NULL;
+ // make sure destroyAssocation does not try to free params a second time
+ // (happens in case we have already have an association structure)
+ if (m_assoc)
+ m_assoc->params = NULL;
+ }
+ // destroy the association, i.e. free memory of T_ASC_Association* structure.
+ ASC_destroyAssociation(&m_assoc);
+ // drop the network, i.e. free memory of T_ASC_Network* structure.
+ ASC_dropNetwork(&m_net);
+ // Cleanup old DIMSE request if any
+ delete m_openDIMSERequest;
+ m_openDIMSERequest = NULL;
+}
+
+
+DcmSCU::~DcmSCU()
+{
+ // abort association (if any) and destroy dcmnet data structures
+ if (isConnected())
+ {
+ closeAssociation(DCMSCU_ABORT_ASSOCIATION); // also frees network
+ } else {
+ freeNetwork();
+ }
+
+#ifdef HAVE_WINSOCK_H
+ WSACleanup(); // TODO: check with multiple SCU instances whether this is harmful
+#endif
+}
+
+
+OFCondition DcmSCU::initNetwork()
+{
+ /* Return if SCU is already connected */
+ if (isConnected())
+ return NET_EC_AlreadyConnected;
+
+ /* Be sure internal network structures are clean (delete old) */
+ freeNetwork();
+
+ OFString tempStr;
+ /* initialize network, i.e. create an instance of T_ASC_Network*. */
+ OFCondition cond = ASC_initializeNetwork(NET_REQUESTOR, 0, m_acseTimeout, &m_net);
+ if (cond.bad())
+ {
+ DimseCondition::dump(tempStr, cond);
+ DCMNET_ERROR(tempStr);
+ return cond;
+ }
+
+ /* initialize asscociation parameters, i.e. create an instance of T_ASC_Parameters*. */
+ cond = ASC_createAssociationParameters(&m_params, m_maxReceivePDULength);
+ if (cond.bad())
+ {
+ DCMNET_ERROR(DimseCondition::dump(tempStr, cond));
+ return cond;
+ }
+
+ /* sets this application's title and the called application's title in the params */
+ /* structure. The default values are "ANY-SCU" and "ANY-SCP". */
+ ASC_setAPTitles(m_params, m_ourAETitle.c_str(), m_peerAETitle.c_str(), NULL);
+
+ /* Figure out the presentation addresses and copy the */
+ /* corresponding values into the association parameters.*/
+ DIC_NODENAME localHost;
+ DIC_NODENAME peerHost;
+ gethostname(localHost, sizeof(localHost) - 1);
+ /* Since the underlying dcmnet structures reserve only 64 bytes for peer
+ as well as local host name, we check here for buffer overflow.
+ */
+ if ((m_peer.length() + 5 /* max 65535 */) + 1 /* for ":" */ > 63)
+ {
+ DCMNET_ERROR("Maximum length of peer host name '" << m_peer << "' is longer than maximum of 57 characters");
+ return EC_IllegalCall; // TODO: need to find better error code
+ }
+ if (strlen(localHost) + 1 > 63)
+ {
+ DCMNET_ERROR("Maximum length of local host name '" << localHost << "' is longer than maximum of 62 characters");
+ return EC_IllegalCall; // TODO: need to find better error code
+ }
+ sprintf(peerHost, "%s:%d", m_peer.c_str(), OFstatic_cast(int, m_peerPort));
+ ASC_setPresentationAddresses(m_params, localHost, peerHost);
+
+ /* Add presentation contexts */
+
+ // First, import from config file, if specified
+ OFCondition result;
+ if (!m_assocConfigFilename.empty())
+ {
+ DcmAssociationConfiguration assocConfig;
+ result = DcmAssociationConfigurationFile::initialize(assocConfig, m_assocConfigFilename.c_str());
+ if (result.bad())
+ {
+ DCMNET_WARN("Unable to parse association configuration file " << m_assocConfigFilename
+ << " (ignored): " << result.text());
+ return result;
+ }
+ else
+ {
+ /* perform name mangling for config file key */
+ OFString profileName;
+ const unsigned char *c = OFreinterpret_cast(const unsigned char *, m_assocConfigProfile.c_str());
+ while (*c)
+ {
+ if (! isspace(*c)) profileName += OFstatic_cast(char, toupper(*c));
+ ++c;
+ }
+
+ result = assocConfig.setAssociationParameters(profileName.c_str(), *m_params);
+ if (result.bad())
+ {
+ DCMNET_WARN("Unable to apply association configuration file " << m_assocConfigFilename
+ <<" (ignored): " << result.text());
+ return result;
+ }
+ }
+ }
+
+ // Adapt presentation context ID to existing presentation contexts.
+ // It's important that presentation context IDs are numerated 1,3,5,7...!
+ Uint32 nextFreePresID = 257;
+ Uint32 numContexts = ASC_countPresentationContexts(m_params);
+ if (numContexts <= 127)
+ {
+ // Need Uint16 to avoid overflow in currPresID (unsigned char)
+ nextFreePresID = 2 * numContexts + 1; /* add 1 to point to the next free ID*/
+ }
+ // Print warning if number of overall presentation contexts exceeds 128
+ if ((numContexts + m_presContexts.size()) > 128)
+ {
+ DCMNET_WARN("Number of presentation contexts exceeds 128 (" << numContexts + m_presContexts.size()
+ << "). Some contexts will not be negotiated");
+ }
+ else
+ {
+ DCMNET_TRACE("Configured " << numContexts << " presentation contexts from config file");
+ if (m_presContexts.size() > 0)
+ DCMNET_TRACE("Adding another " << m_presContexts.size() << " presentation contexts configured for SCU");
+ }
+
+ // Add presentation contexts not originating from config file
+ OFListIterator(DcmSCUPresContext) contIt = m_presContexts.begin();
+ OFListConstIterator(DcmSCUPresContext) endOfContList = m_presContexts.end();
+ while ((contIt != endOfContList) && (nextFreePresID <= 255))
+ {
+ const Uint16 numTransferSyntaxes = OFstatic_cast(Uint16, (*contIt).transferSyntaxes.size());
+ const char** transferSyntaxes = new const char*[numTransferSyntaxes];
+
+ // Iterate over transfer syntaxes within one presentation context
+ OFListIterator(OFString) syntaxIt = (*contIt).transferSyntaxes.begin();
+ OFListIterator(OFString) endOfSyntaxList = (*contIt).transferSyntaxes.end();
+ Uint16 sNum = 0;
+ // copy all transfer syntaxes to array
+ while (syntaxIt != endOfSyntaxList)
+ {
+ transferSyntaxes[sNum] = (*syntaxIt).c_str();
+ ++syntaxIt;
+ ++sNum;
+ }
+
+ // add the presentation context
+ cond = ASC_addPresentationContext(m_params, OFstatic_cast(Uint8, nextFreePresID),
+ (*contIt).abstractSyntaxName.c_str(), transferSyntaxes, numTransferSyntaxes,(*contIt).roleSelect);
+ // if adding was successfull, prepare presentation context ID for next addition
+ delete[] transferSyntaxes;
+ transferSyntaxes = NULL;
+ if (cond.bad())
+ return cond;
+ contIt++;
+ // goto next free number, only odd presentation context IDs permitted
+ nextFreePresID += 2;
+ }
+
+ numContexts = ASC_countPresentationContexts(m_params);
+ if (numContexts == 0)
+ {
+ DCMNET_ERROR("Cannot initialize network: No presentation contexts defined");
+ return NET_EC_NoPresentationContextsDefined;
+ }
+ DCMNET_DEBUG("Configured a total of " << numContexts << " presentation contexts for SCU");
+
+ return cond;
+}
+
+
+OFCondition DcmSCU::negotiateAssociation()
+{
+ /* Return error if SCU is already connected */
+ if (isConnected())
+ return NET_EC_AlreadyConnected;
+
+ /* dump presentation contexts if required */
+ OFString tempStr;
+ if (m_verbosePCMode)
+ DCMNET_INFO("Request Parameters:" << OFendl << ASC_dumpParameters(tempStr, m_params, ASC_ASSOC_RQ));
+ else
+ DCMNET_DEBUG("Request Parameters:" << OFendl << ASC_dumpParameters(tempStr, m_params, ASC_ASSOC_RQ));
+
+ /* create association, i.e. try to establish a network connection to another */
+ /* DICOM application. This call creates an instance of T_ASC_Association*. */
+ DCMNET_INFO("Requesting Association");
+ OFCondition cond = ASC_requestAssociation(m_net, m_params, &m_assoc);
+ if (cond.bad())
+ {
+ if (cond == DUL_ASSOCIATIONREJECTED)
+ {
+ T_ASC_RejectParameters rej;
+ ASC_getRejectParameters(m_params, &rej);
+ DCMNET_DEBUG("Association Rejected:" << OFendl << ASC_printRejectParameters(tempStr, &rej));
+ return cond;
+ }
+ else
+ {
+ DCMNET_DEBUG("Association Request Failed: " << DimseCondition::dump(tempStr, cond));
+ return cond;
+ }
+ }
+
+ /* dump the presentation contexts which have been accepted/refused */
+ if (m_verbosePCMode)
+ DCMNET_INFO("Association Parameters Negotiated:" << OFendl << ASC_dumpParameters(tempStr, m_params, ASC_ASSOC_AC));
+ else
+ DCMNET_DEBUG("Association Parameters Negotiated:" << OFendl << ASC_dumpParameters(tempStr, m_params, ASC_ASSOC_AC));
+
+ /* count the presentation contexts which have been accepted by the SCP */
+ /* If there are none, finish the execution */
+ if (ASC_countAcceptedPresentationContexts(m_params) == 0)
+ {
+ DCMNET_ERROR("No Acceptable Presentation Contexts");
+ return NET_EC_NoAcceptablePresentationContexts;
+ }
+
+ /* dump general information concerning the establishment of the network connection if required */
+ DCMNET_INFO("Association Accepted (Max Send PDV: " << OFstatic_cast(unsigned long, m_assoc->sendPDVLength) << ")");
+ return EC_Normal;
+}
+
+
+OFCondition DcmSCU::addPresentationContext(const OFString &abstractSyntax,
+ const OFList<OFString> &xferSyntaxes,
+ const T_ASC_SC_ROLE role)
+
+{
+
+ DcmSCUPresContext presContext;
+ presContext.abstractSyntaxName = abstractSyntax;
+ OFListConstIterator(OFString) it = xferSyntaxes.begin();
+ OFListConstIterator(OFString) endOfList = xferSyntaxes.end();
+ while (it != endOfList)
+ {
+ presContext.transferSyntaxes.push_back(*it);
+ it++;
+ }
+ presContext.roleSelect = role;
+ m_presContexts.push_back(presContext);
+ return EC_Normal;
+}
+
+
+OFCondition DcmSCU::useSecureConnection(DcmTransportLayer *tlayer)
+{
+ OFCondition cond = ASC_setTransportLayer(m_net, tlayer, OFFalse /* do not take over ownership */);
+ if (cond.good())
+ cond = ASC_setTransportLayerType(m_params, OFTrue /* use TLS */);
+ return cond;
+}
+
+
+void DcmSCU::clearPresentationContexts()
+{
+ m_presContexts.clear();
+ m_assocConfigFilename.clear();
+ m_assocConfigProfile.clear();
+}
+
+
+// Returns usable presentation context ID for a given abstract syntax UID and
+// transfer syntax UID. 0 if none matches.
+T_ASC_PresentationContextID DcmSCU::findPresentationContextID(const OFString &abstractSyntax,
+ const OFString &transferSyntax)
+{
+ if (!isConnected())
+ return 0;
+
+ DUL_PRESENTATIONCONTEXT *pc;
+ LST_HEAD **l;
+ OFBool found = OFFalse;
+
+ if (abstractSyntax.empty()) return 0;
+
+ /* first of all we look for a presentation context
+ * matching both abstract and transfer syntax
+ */
+ l = &m_assoc->params->DULparams.acceptedPresentationContext;
+ pc = (DUL_PRESENTATIONCONTEXT*) LST_Head(l);
+ (void)LST_Position(l, (LST_NODE*)pc);
+ while (pc && !found)
+ {
+ found = (strcmp(pc->abstractSyntax, abstractSyntax.c_str()) == 0);
+ found &= (pc->result == ASC_P_ACCEPTANCE);
+ if (!transferSyntax.empty()) // ignore transfer syntax if not specified
+ found &= (strcmp(pc->acceptedTransferSyntax, transferSyntax.c_str()) == 0);
+ if (!found) pc = (DUL_PRESENTATIONCONTEXT*) LST_Next(l);
+ }
+ if (found)
+ return pc->presentationContextID;
+
+ return 0; /* not found */
+}
+
+// Returns the presentation context ID that best matches the given abstract syntax UID and
+// transfer syntax UID.
+T_ASC_PresentationContextID DcmSCU::findAnyPresentationContextID(const OFString &abstractSyntax,
+ const OFString &transferSyntax)
+{
+ if (m_assoc == NULL)
+ return 0;
+
+ DUL_PRESENTATIONCONTEXT *pc;
+ LST_HEAD **l;
+ OFBool found = OFFalse;
+
+ if (abstractSyntax.empty()) return 0;
+
+ /* first of all we look for a presentation context
+ * matching both abstract and transfer syntax
+ */
+ l = &m_assoc->params->DULparams.acceptedPresentationContext;
+ pc = (DUL_PRESENTATIONCONTEXT*) LST_Head(l);
+ (void)LST_Position(l, (LST_NODE*)pc);
+ while (pc && !found)
+ {
+ found = (strcmp(pc->abstractSyntax, abstractSyntax.c_str()) == 0);
+ found &= (pc->result == ASC_P_ACCEPTANCE);
+ if (!transferSyntax.empty()) // ignore transfer syntax if not specified
+ found &= (strcmp(pc->acceptedTransferSyntax, transferSyntax.c_str()) == 0);
+ if (!found) pc = (DUL_PRESENTATIONCONTEXT*) LST_Next(l);
+ }
+ if (found) return pc->presentationContextID;
+
+ /* now we look for an explicit VR uncompressed PC. */
+ l = &m_assoc->params->DULparams.acceptedPresentationContext;
+ pc = (DUL_PRESENTATIONCONTEXT*) LST_Head(l);
+ (void)LST_Position(l, (LST_NODE*)pc);
+ while (pc && !found)
+ {
+ found = (strcmp(pc->abstractSyntax, abstractSyntax.c_str()) == 0)
+ && (pc->result == ASC_P_ACCEPTANCE)
+ && ((strcmp(pc->acceptedTransferSyntax, UID_LittleEndianExplicitTransferSyntax) == 0)
+ || (strcmp(pc->acceptedTransferSyntax, UID_BigEndianExplicitTransferSyntax) == 0));
+ if (!found) pc = (DUL_PRESENTATIONCONTEXT*) LST_Next(l);
+ }
+ if (found) return pc->presentationContextID;
+
+ /* now we look for an implicit VR uncompressed PC. */
+ l = &m_assoc->params->DULparams.acceptedPresentationContext;
+ pc = (DUL_PRESENTATIONCONTEXT*) LST_Head(l);
+ (void)LST_Position(l, (LST_NODE*)pc);
+ while (pc && !found)
+ {
+ found = (strcmp(pc->abstractSyntax, abstractSyntax.c_str()) == 0)
+ && (pc->result == ASC_P_ACCEPTANCE)
+ && (strcmp(pc->acceptedTransferSyntax, UID_LittleEndianImplicitTransferSyntax) == 0);
+ if (!found) pc = (DUL_PRESENTATIONCONTEXT*) LST_Next(l);
+ }
+ if (found) return pc->presentationContextID;
+
+ /* finally we accept everything we get.
+ returns 0 if abstract syntax is not supported
+ */
+ return ASC_findAcceptedPresentationContextID(m_assoc, abstractSyntax.c_str());
+}
+
+void DcmSCU::findPresentationContext(const T_ASC_PresentationContextID presID,
+ OFString &abstractSyntax,
+ OFString &transferSyntax)
+{
+ transferSyntax.clear();
+ abstractSyntax.clear();
+ if (m_assoc == NULL)
+ return;
+
+ DUL_PRESENTATIONCONTEXT *pc;
+ LST_HEAD **l;
+
+ /* we look for a presentation context matching
+ * both abstract and transfer syntax
+ */
+ l = &m_assoc->params->DULparams.acceptedPresentationContext;
+ pc = (DUL_PRESENTATIONCONTEXT*) LST_Head(l);
+ (void)LST_Position(l, (LST_NODE*)pc);
+ while (pc)
+ {
+ if (presID == pc->presentationContextID)
+ {
+ if (pc->result == ASC_P_ACCEPTANCE)
+ {
+ // found a match
+ transferSyntax = pc->acceptedTransferSyntax;
+ abstractSyntax = pc->abstractSyntax;
+ }
+ break;
+ }
+ pc = (DUL_PRESENTATIONCONTEXT*) LST_Next(l);
+ }
+}
+
+
+Uint16 DcmSCU::nextMessageID()
+{
+ if (!isConnected())
+ return 0;
+ else
+ return m_assoc->nextMsgID++;
+}
+
+
+void DcmSCU::closeAssociation(const DcmCloseAssociationType closeType)
+{
+ if (!isConnected())
+ {
+ DCMNET_WARN("Closing of association request but no association active (ignored)");
+ return;
+ }
+
+ OFCondition cond;
+ OFString tempStr;
+
+ /* tear down association, i.e. terminate network connection to SCP */
+ switch (closeType)
+ {
+ case DCMSCU_RELEASE_ASSOCIATION:
+ /* release association */
+ DCMNET_INFO("Releasing Association");
+ cond = ASC_releaseAssociation(m_assoc);
+ if (cond.bad())
+ {
+ DCMNET_ERROR("Association Release Failed: " << DimseCondition::dump(tempStr, cond));
+ return; // TODO: do we really need this?
+ }
+ break;
+ case DCMSCU_ABORT_ASSOCIATION:
+ /* abort association */
+ DCMNET_INFO("Aborting Association");
+ cond = ASC_abortAssociation(m_assoc);
+ if (cond.bad())
+ {
+ DCMNET_ERROR("Association Abort Failed: " << DimseCondition::dump(tempStr, cond));
+ }
+ break;
+ case DCMSCU_PEER_REQUESTED_RELEASE:
+ /* peer requested release */
+ DCMNET_ERROR("Protocol Error: Peer requested release (Aborting)");
+ DCMNET_INFO("Aborting Association");
+ cond = ASC_abortAssociation(m_assoc);
+ if (cond.bad())
+ {
+ DCMNET_ERROR("Association Abort Failed: " << DimseCondition::dump(tempStr, cond));
+ }
+ break;
+ case DCMSCU_PEER_ABORTED_ASSOCIATION:
+ /* peer aborted association */
+ DCMNET_INFO("Peer Aborted Association");
+ break;
+ }
+
+ // destroy and free memory of internal association and network structures
+ freeNetwork();
+}
+
+
+/* ************************************************************************* */
+/* C-ECHO functionality */
+/* ************************************************************************* */
+
+// Sends C-ECHO request to another DICOM application
+OFCondition DcmSCU::sendECHORequest(const T_ASC_PresentationContextID presID)
+{
+ if (!isConnected())
+ return DIMSE_ILLEGALASSOCIATION;
+
+ OFCondition cond;
+ T_ASC_PresentationContextID pcid = presID;
+
+ /* If necessary, find appropriate presentation context */
+ if (pcid == 0)
+ pcid = findPresentationContextID(UID_VerificationSOPClass, UID_LittleEndianExplicitTransferSyntax);
+ if (pcid == 0)
+ pcid = findPresentationContextID(UID_VerificationSOPClass, UID_BigEndianExplicitTransferSyntax);
+ if (pcid == 0)
+ pcid = findPresentationContextID(UID_VerificationSOPClass, UID_LittleEndianImplicitTransferSyntax);
+ if (pcid == 0)
+ {
+ DCMNET_ERROR("No presentation context found for sending C-ECHO with SOP Class / Transfer Syntax: "
+ << dcmFindNameOfUID(UID_VerificationSOPClass, "") << " / "
+ << DcmXfer(UID_LittleEndianImplicitTransferSyntax).getXferName());
+ return DIMSE_NOVALIDPRESENTATIONCONTEXTID;
+ }
+
+ /* Now, assemble DIMSE message */
+ T_DIMSE_Message msg;
+ T_DIMSE_C_EchoRQ* req = &(msg.msg.CEchoRQ);
+ // Set type of message
+ msg.CommandField = DIMSE_C_ECHO_RQ;
+ // Set message ID
+ req->MessageID = nextMessageID();
+ // Announce no dataset
+ req->DataSetType = DIMSE_DATASET_NULL;
+ // Set affected SOP Class UID (always Verification SOP Class)
+ OFStandard::strlcpy(req->AffectedSOPClassUID, UID_VerificationSOPClass, sizeof(req->AffectedSOPClassUID));
+
+ /* Send request */
+ OFString tempStr;
+ DCMNET_INFO("Sending C-ECHO Request (MsgID " << req->MessageID << ")");
+
+ cond = sendDIMSEMessage(pcid, &msg, NULL /*dataObject*/);
+ if (cond.bad())
+ {
+ DCMNET_ERROR("Failed sending C-ECHO request: " << DimseCondition::dump(tempStr, cond));
+ return cond;
+ }
+
+ /* Receive response */
+ T_DIMSE_Message rsp;
+ DcmDataset* statusDetail = NULL;
+ cond = receiveDIMSECommand(&pcid, &rsp, &statusDetail, NULL /* not interested in the command set */);
+ if (cond.bad())
+ {
+ DCMNET_ERROR("Failed receiving DIMSE response: " << DimseCondition::dump(tempStr, cond));
+ return cond;
+ }
+ /* Check whether we received C-ECHO response, otherwise print error */
+ if (rsp.CommandField == DIMSE_C_ECHO_RSP)
+ {
+ DCMNET_INFO("Received C-ECHO Response (" << rsp.msg.CEchoRSP.DimseStatus << ")");
+
+ } else {
+ DCMNET_ERROR("Expected C-ECHO response but received DIMSE command 0x"
+ << STD_NAMESPACE hex << STD_NAMESPACE setfill('0') << STD_NAMESPACE setw(4)
+ << OFstatic_cast(unsigned int, rsp.CommandField));
+ DCMNET_DEBUG(DIMSE_dumpMessage(tempStr, rsp, DIMSE_INCOMING, NULL, pcid));
+ delete statusDetail;
+ return DIMSE_BADCOMMANDTYPE;
+ }
+ /* Print status detail if it was received */
+ if (statusDetail != NULL)
+ {
+ DCMNET_DEBUG("Response has status detail:" << OFendl << DcmObject::PrintHelper(*statusDetail));
+ delete statusDetail;
+ }
+ return EC_Normal;
+}
+
+/* ************************************************************************* */
+/* C-STORE functionality */
+/* ************************************************************************* */
+
+// Sends C-STORE request to another DICOM application
+OFCondition DcmSCU::sendSTORERequest(const T_ASC_PresentationContextID presID,
+ const OFString &dicomFile,
+ DcmDataset *dataset,
+ Uint16 &rspStatusCode)
+{
+ // Do some basic validity checks
+ if (!isConnected())
+ return DIMSE_ILLEGALASSOCIATION;
+
+ OFCondition cond;
+ OFString tempStr;
+ T_ASC_PresentationContextID pcid = presID;
+ DcmDataset* statusDetail = NULL;
+ T_DIMSE_Message msg;
+ T_DIMSE_C_StoreRQ* req = &(msg.msg.CStoreRQ);
+
+ // Set type of message
+ msg.CommandField = DIMSE_C_STORE_RQ;
+ /* Set message ID */
+ req->MessageID = nextMessageID();
+ /* Load file if necessary */
+ DcmFileFormat *fileformat = NULL;
+ if (!dicomFile.empty())
+ {
+ fileformat = new DcmFileFormat();
+ if (fileformat == NULL)
+ return EC_MemoryExhausted;
+ cond = fileformat->loadFile(dicomFile.c_str());
+ if (cond.bad())
+ {
+ delete fileformat;
+ return cond;
+ }
+ dataset = fileformat->getDataset();
+ }
+
+ /* Fill message according to dataset to be sent */
+ OFString sopClassUID;
+ OFString sopInstanceUID;
+ E_TransferSyntax xferSyntax = EXS_Unknown;
+ cond = getDatasetInfo(dataset, sopClassUID, sopInstanceUID, xferSyntax);
+ DcmXfer xfer(xferSyntax);
+ /* Check whether the information is sufficient */
+ if (sopClassUID.empty() || sopInstanceUID.empty() || ((pcid == 0) && (xferSyntax == EXS_Unknown)))
+ {
+ DCMNET_ERROR("Cannot send SOP instance, missing information:");
+ if (!dicomFile.empty())
+ DCMNET_ERROR(" DICOM Filename : " << dicomFile);
+ DCMNET_ERROR(" SOP Class UID : " << sopClassUID);
+ DCMNET_ERROR(" SOP Instance UID : " << sopInstanceUID);
+ DCMNET_ERROR(" Transfer Syntax : " << xfer.getXferName());
+ if (pcid == 0)
+ DCMNET_ERROR(" Pres. Context ID : 0 (find via SOP Class and Transfer Syntax)");
+ else
+ DCMNET_ERROR(" Pres. Context ID : " << OFstatic_cast(unsigned int, pcid));
+ delete fileformat;
+ return cond;
+ }
+ OFStandard::strlcpy(req->AffectedSOPClassUID, sopClassUID.c_str(), sizeof(req->AffectedSOPClassUID));
+ OFStandard::strlcpy(req->AffectedSOPInstanceUID, sopInstanceUID.c_str(), sizeof(req->AffectedSOPInstanceUID));
+ req->DataSetType = DIMSE_DATASET_PRESENT;
+ req->Priority = DIMSE_PRIORITY_LOW;
+
+ /* If no presentation context is specified by the caller ... */
+ if (pcid == 0)
+ {
+ /* ... try to find an appropriate presentation context automatically */
+ pcid = findPresentationContextID(sopClassUID, xfer.getXferID());
+ }
+ else if (m_datasetConversionMode)
+ {
+ /* Convert dataset to network transfer syntax (if required) */
+ OFString abstractSyntax, transferSyntax;
+ findPresentationContext(pcid, abstractSyntax, transferSyntax);
+ /* Check whether given presentation context was accepted by the peer */
+ if (abstractSyntax.empty() || transferSyntax.empty())
+ {
+ /* Mark presentation context as invalid */
+ pcid = 0;
+ } else {
+ if (abstractSyntax != sopClassUID)
+ {
+ DCMNET_WARN("Inappropriate presentation context with ID " << OFstatic_cast(unsigned int, pcid)
+ << ": abstract syntax does not match SOP class UID");
+ }
+ /* Try to convert to the negotiated transfer syntax */
+ DcmXfer netXfer = DcmXfer(transferSyntax.c_str()).getXfer();
+ if (netXfer.getXfer() != xferSyntax)
+ {
+ DCMNET_INFO("Converting transfer syntax: " << xfer.getXferName() << " -> "
+ << netXfer.getXferName());
+ dataset->chooseRepresentation(netXfer.getXfer(), NULL);
+ }
+ }
+ }
+ /* No appropriate presentation context for sending */
+ if (pcid == 0)
+ {
+ OFString sopClassName = dcmFindNameOfUID(sopClassUID.c_str(), sopClassUID.c_str());
+ OFString xferName = xfer.getXferName();
+ DCMNET_ERROR("No presentation context found for sending C-STORE with SOP Class / Transfer Syntax: "
+ << sopClassName << " / " << xferName);
+ return DIMSE_NOVALIDPRESENTATIONCONTEXTID;
+ }
+
+ /* Send request */
+ DCMNET_INFO("Sending C-STORE Request (MsgID " << req->MessageID << ", "
+ << dcmSOPClassUIDToModality(sopClassUID.c_str(), "OT") << ")");
+
+ cond = sendDIMSEMessage(pcid, &msg, dataset);
+ delete fileformat;
+ fileformat = NULL;
+ if (cond.bad())
+ {
+ DCMNET_ERROR("Failed sending C-STORE request: " << DimseCondition::dump(tempStr, cond));
+ return cond;
+ }
+
+ /* Receive response */
+ T_DIMSE_Message rsp;
+ cond = receiveDIMSECommand(&pcid, &rsp, &statusDetail, NULL /* not interested in the command set */);
+ if (cond.bad())
+ {
+ DCMNET_ERROR("Failed receiving DIMSE response: " << DimseCondition::dump(tempStr, cond));
+ return cond;
+ }
+
+ if (rsp.CommandField == DIMSE_C_STORE_RSP)
+ {
+ DCMNET_INFO("Received C-STORE Response (" << DU_cstoreStatusString(rsp.msg.CStoreRSP.DimseStatus) << ")");
+ } else {
+ DCMNET_ERROR("Expected C-STORE response but received DIMSE command 0x"
+ << STD_NAMESPACE hex << STD_NAMESPACE setfill('0') << STD_NAMESPACE setw(4)
+ << OFstatic_cast(unsigned int, rsp.CommandField));
+ DCMNET_DEBUG(DIMSE_dumpMessage(tempStr, rsp, DIMSE_INCOMING, NULL, pcid));
+ delete statusDetail;
+ return DIMSE_BADCOMMANDTYPE;
+ }
+ T_DIMSE_C_StoreRSP storeRsp = rsp.msg.CStoreRSP;
+ rspStatusCode = storeRsp.DimseStatus;
+ if (statusDetail != NULL)
+ {
+ DCMNET_DEBUG("Response has status detail:" << OFendl << DcmObject::PrintHelper(*statusDetail));
+ delete statusDetail;
+ }
+
+ return cond;
+}
+
+
+/* ************************************************************************* */
+/* C-MOVE functionality */
+/* ************************************************************************* */
+
+// Sends a C-MOVE Request on given presentation context
+OFCondition DcmSCU::sendMOVERequest(const T_ASC_PresentationContextID presID,
+ const OFString &moveDestinationAETitle,
+ DcmDataset *dataset,
+ OFList<RetrieveResponse*> *responses)
+{
+ // Do some basic validity checks
+ if (!isConnected())
+ return DIMSE_ILLEGALASSOCIATION;
+ if (dataset == NULL)
+ return DIMSE_NULLKEY;
+
+ /* Prepare DIMSE data structures for issuing request */
+ OFCondition cond;
+ OFString tempStr;
+ T_ASC_PresentationContextID pcid = presID;
+ T_DIMSE_Message msg;
+ DcmDataset* statusDetail = NULL;
+ T_DIMSE_C_MoveRQ* req = &(msg.msg.CMoveRQ);
+ // Set type of message
+ msg.CommandField = DIMSE_C_MOVE_RQ;
+ // Set message ID
+ req->MessageID = nextMessageID();
+ // Announce dataset
+ req->DataSetType = DIMSE_DATASET_PRESENT;
+ // Set target for embedded C-Store's
+ OFStandard::strlcpy(req->MoveDestination, moveDestinationAETitle.c_str(), sizeof(req->MoveDestination));
+ // Set priority (mandatory)
+ req->Priority = DIMSE_PRIORITY_LOW;
+
+ /* Determine SOP Class from presentation context */
+ OFString abstractSyntax, transferSyntax;
+ findPresentationContext(pcid, abstractSyntax, transferSyntax);
+ if (abstractSyntax.empty() || transferSyntax.empty())
+ return DIMSE_NOVALIDPRESENTATIONCONTEXTID;
+ OFStandard::strlcpy(req->AffectedSOPClassUID, abstractSyntax.c_str(), sizeof(req->AffectedSOPClassUID));
+
+ /* Send request */
+ DCMNET_INFO("Sending C-MOVE Request (MsgID " << req->MessageID << ")");
+
+ cond = sendDIMSEMessage(pcid, &msg, dataset);
+ if (cond.bad())
+ {
+ DCMNET_ERROR("Failed sending C-MOVE request: " << DimseCondition::dump(tempStr, cond));
+ return cond;
+ }
+
+ /* Receive and handle C-MOVE response messages */
+ OFBool waitForNextResponse = OFTrue;
+ while (waitForNextResponse)
+ {
+ T_DIMSE_Message rsp;
+ statusDetail = NULL;
+
+ // Receive command set
+ cond = receiveDIMSECommand(&pcid, &rsp, &statusDetail, NULL /* not interested in the command set */);
+ if (cond.bad())
+ {
+ DCMNET_ERROR("Failed receiving DIMSE response: " << DimseCondition::dump(tempStr, cond));
+ delete statusDetail;
+ break;
+ }
+
+ if (rsp.CommandField == DIMSE_C_MOVE_RSP)
+ {
+ DCMNET_INFO("Received C-MOVE Response (" << DU_cmoveStatusString(rsp.msg.CMoveRSP.DimseStatus) << ")");
+
+ } else {
+ DCMNET_ERROR("Expected C-MOVE response but received DIMSE command 0x"
+ << STD_NAMESPACE hex << STD_NAMESPACE setfill('0') << STD_NAMESPACE setw(4)
+ << OFstatic_cast(unsigned int, rsp.CommandField));
+ DCMNET_DEBUG(DIMSE_dumpMessage(tempStr, rsp, DIMSE_INCOMING, NULL, pcid));
+ delete statusDetail;
+ cond = DIMSE_BADCOMMANDTYPE;
+ break;
+ }
+
+ // Prepare response package for response handler
+ RetrieveResponse *moveRSP = new RetrieveResponse();
+ moveRSP->m_affectedSOPClassUID = rsp.msg.CMoveRSP.AffectedSOPClassUID;
+ moveRSP->m_messageIDRespondedTo = rsp.msg.CMoveRSP.MessageIDBeingRespondedTo;
+ moveRSP->m_status = rsp.msg.CMoveRSP.DimseStatus;
+ moveRSP->m_numberOfRemainingSubops = rsp.msg.CMoveRSP.NumberOfRemainingSubOperations;
+ moveRSP->m_numberOfCompletedSubops = rsp.msg.CMoveRSP.NumberOfCompletedSubOperations;
+ moveRSP->m_numberOfFailedSubops = rsp.msg.CMoveRSP.NumberOfFailedSubOperations;
+ moveRSP->m_numberOfWarningSubops = rsp.msg.CMoveRSP.NumberOfWarningSubOperations;
+ moveRSP->m_statusDetail = statusDetail;
+ //DCMNET_DEBUG("C-MOVE response has status 0x"
+ // << STD_NAMESPACE hex << STD_NAMESPACE setfill('0') << STD_NAMESPACE setw(4)
+ // << moveRSP->m_status);
+ if (statusDetail != NULL)
+ {
+ DCMNET_DEBUG("Response has status detail:" << OFendl << DcmObject::PrintHelper(*statusDetail));
+ }
+
+ // Receive dataset if there is one (status PENDING)
+ DcmDataset *rspDataset = NULL;
+ // Check if dataset is announced correctly
+ if (rsp.msg.CMoveRSP.DataSetType != DIMSE_DATASET_NULL) // Some of the sub operations have failed, thus a dataset with a list of them is attached
+ {
+ // Receive dataset
+ cond = receiveDIMSEDataset(&pcid, &rspDataset);
+ if (cond.bad())
+ {
+ DCMNET_ERROR("Unable to receive C-MOVE dataset on presentation context "
+ << OFstatic_cast(unsigned int, pcid) << ": " << DimseCondition::dump(tempStr, cond));
+ delete moveRSP; // includes statusDetail
+ break;
+ }
+ moveRSP->m_dataset = rspDataset;
+ }
+
+ // Handle C-MOVE response (has to handle all possible status flags)
+ cond = handleMOVEResponse(pcid, moveRSP, waitForNextResponse);
+ if (cond.bad())
+ {
+ DCMNET_WARN("Unable to handle C-MOVE response correctly: " << cond.text() << " (ignored)");
+ delete moveRSP; // includes statusDetail
+ // don't return here but trust the "waitForNextResponse" variable
+ }
+ // if response could be handled successfully, add it to response list
+ else
+ {
+ if (responses != NULL) // only add if desired by caller
+ responses->push_back(moveRSP);
+ else
+ delete moveRSP; // includes statusDetail
+ }
+ }
+ /* All responses received or break signal occured */
+ return cond;
+}
+
+
+// Standard handler for C-MOVE message responses
+OFCondition DcmSCU::handleMOVEResponse( const T_ASC_PresentationContextID /* presID */,
+ RetrieveResponse *response,
+ OFBool &waitForNextResponse )
+{
+ // Do some basic validity checks
+ if (!isConnected())
+ return DIMSE_ILLEGALASSOCIATION;
+ if (response == NULL)
+ return DIMSE_NULLKEY;
+
+ DCMNET_DEBUG("Handling C-MOVE Response");
+ switch (response->m_status) {
+ case STATUS_MOVE_Failed_IdentifierDoesNotMatchSOPClass:
+ waitForNextResponse = OFFalse;
+ DCMNET_ERROR("Identifier does not match SOP class in C-MOVE response");
+ break;
+ case STATUS_MOVE_Failed_MoveDestinationUnknown:
+ waitForNextResponse = OFFalse;
+ DCMNET_ERROR("Move destination unknown");
+ break;
+ case STATUS_MOVE_Failed_UnableToProcess:
+ waitForNextResponse = OFFalse;
+ DCMNET_ERROR("Unable to process C-Move response");
+ break;
+ case STATUS_MOVE_Cancel_SubOperationsTerminatedDueToCancelIndication:
+ waitForNextResponse = OFFalse;
+ DCMNET_DEBUG("Suboperations canceled by server due to CANCEL indication");
+ break;
+ case STATUS_MOVE_Warning_SubOperationsCompleteOneOrMoreFailures:
+ waitForNextResponse = OFFalse;
+ DCMNET_WARN("Suboperations of C-MOVE completed with one or more failures");
+ break;
+ case STATUS_Pending:
+ /* in this case the current C-MOVE-RSP indicates that */
+ /* there will be some more results */
+ waitForNextResponse = OFTrue;
+ DCMNET_DEBUG("One or more pending C-MOVE responses");
+ break;
+ case STATUS_Success:
+ /* in this case, we received the last C-MOVE-RSP so there */
+ /* will be no other responses we have to wait for. */
+ waitForNextResponse = OFFalse;
+ DCMNET_DEBUG("Received final C-MOVE response, no more C-MOVE responses expected");
+ break;
+ default:
+ /* in all other cases, don't expect further responses to come */
+ waitForNextResponse = OFFalse;
+ DCMNET_WARN("Status is 0x"
+ << STD_NAMESPACE hex << STD_NAMESPACE setfill('0') << STD_NAMESPACE setw(4)
+ << response->m_status << " (unknown)");
+ DCMNET_WARN("Will not wait for further C-MOVE responses");
+ break;
+ } //switch
+
+ return EC_Normal;
+}
+
+
+/* ************************************************************************* */
+/* C-GET and acommpanying C-STORE functionality */
+/* ************************************************************************* */
+
+// Sends a C-GET Request on given presentation context
+OFCondition DcmSCU::sendCGETRequest(const T_ASC_PresentationContextID presID,
+ DcmDataset *dataset,
+ OFList<RetrieveResponse*> *responses)
+{
+ // Do some basic validity checks
+ if (!isConnected())
+ return DIMSE_ILLEGALASSOCIATION;
+ if (dataset == NULL)
+ return DIMSE_NULLKEY;
+
+ /* Prepare DIMSE data structures for issuing request */
+ OFCondition cond;
+ OFString tempStr;
+ T_ASC_PresentationContextID pcid = presID;
+ T_DIMSE_Message msg;
+ T_DIMSE_C_GetRQ* req = &(msg.msg.CGetRQ);
+ // Set type of message
+ msg.CommandField = DIMSE_C_GET_RQ;
+ // Set message ID
+ req->MessageID = nextMessageID();
+ // Announce dataset
+ req->DataSetType = DIMSE_DATASET_PRESENT;
+ // Specify priority
+ req->Priority = DIMSE_PRIORITY_LOW;
+
+ // Determine SOP Class from presentation context
+ OFString abstractSyntax, transferSyntax;
+ findPresentationContext(pcid, abstractSyntax, transferSyntax);
+ if (abstractSyntax.empty() || transferSyntax.empty())
+ return DIMSE_NOVALIDPRESENTATIONCONTEXTID;
+ OFStandard::strlcpy(req->AffectedSOPClassUID, abstractSyntax.c_str(), sizeof(req->AffectedSOPClassUID));
+
+ /* Send request */
+ DCMNET_INFO("Sending C-GET Request (MsgID " << req->MessageID << ")");
+
+ cond = sendDIMSEMessage(pcid, &msg, dataset);
+ if (cond.bad())
+ {
+ DCMNET_ERROR("Failed sending C-GET request: " << DimseCondition::dump(tempStr, cond));
+ return cond;
+ }
+
+ cond = handleCGETSession(pcid, dataset, responses);
+ return cond;
+}
+
+
+// Does the logic for switching between C-GET Response and C-STORE Requests
+OFCondition DcmSCU::handleCGETSession(const T_ASC_PresentationContextID /* presID */,
+ DcmDataset * /* dataset */,
+ OFList<RetrieveResponse*> *responses)
+{
+ OFCondition result;
+ OFBool continueSession = OFTrue;
+ OFString tempStr;
+
+ // As long we want to continue (usually, as long as we receive more objects,
+ // i.e. the final C-GET reponse has not arrived yet)
+ while (continueSession)
+ {
+ T_DIMSE_Message rsp;
+ DcmDataset *statusDetail = NULL;
+ T_ASC_PresentationContextID pcid = 0;
+
+ // Receive command set
+ result = receiveDIMSECommand(&pcid, &rsp, &statusDetail, NULL /* not interested in the command set */);
+ if (result.bad())
+ {
+ DCMNET_ERROR("Failed receiving DIMSE command: " << DimseCondition::dump(tempStr, result));
+ delete statusDetail;
+ break;
+ }
+ // Handle C-GET Response
+ if (rsp.CommandField == DIMSE_C_GET_RSP)
+ {
+ DCMNET_INFO("Received C-GET Response (" << DU_cgetStatusString(rsp.msg.CGetRSP.DimseStatus) << ")");
+
+ // Prepare response package for response handler
+ RetrieveResponse *getRSP = new RetrieveResponse();
+ getRSP->m_affectedSOPClassUID = rsp.msg.CGetRSP.AffectedSOPClassUID;
+ getRSP->m_messageIDRespondedTo = rsp.msg.CGetRSP.MessageIDBeingRespondedTo;
+ getRSP->m_status = rsp.msg.CGetRSP.DimseStatus;
+ getRSP->m_numberOfRemainingSubops = rsp.msg.CGetRSP.NumberOfRemainingSubOperations;
+ getRSP->m_numberOfCompletedSubops = rsp.msg.CGetRSP.NumberOfCompletedSubOperations;
+ getRSP->m_numberOfFailedSubops = rsp.msg.CGetRSP.NumberOfFailedSubOperations;
+ getRSP->m_numberOfWarningSubops = rsp.msg.CGetRSP.NumberOfWarningSubOperations;
+ getRSP->m_statusDetail = statusDetail;
+ if (statusDetail != NULL)
+ {
+ DCMNET_DEBUG("Response has status detail:" << OFendl << DcmObject::PrintHelper(*statusDetail));
+ statusDetail = NULL; // forget reference to status detail, will be deleted with getRSP
+ }
+ result = handleCGETResponse(pcid, getRSP, continueSession);
+ if (result.bad())
+ {
+ DCMNET_WARN("Unable to handle C-GET response correctly: " << result.text() << " (ignored)");
+ delete getRSP; // includes statusDetail
+ // don't return here but trust the "continueSession" variable
+ }
+ // if response could be handled successfully, add it to response list
+ else {
+ if (responses != NULL) // only add if desired by caller
+ responses->push_back(getRSP);
+ else
+ delete getRSP; // includes statusDetail
+ }
+ }
+
+ // Handle C-STORE Request
+ else if (rsp.CommandField == DIMSE_C_STORE_RQ)
+ {
+ DCMNET_INFO("Received C-STORE Request (MsgID " << rsp.msg.CStoreRQ.MessageID << ")");
+
+ // Receive dataset if there is one (status PENDING)
+ DcmDataset *rspDataset = NULL;
+ // Check if dataset is announced correctly
+ if (rsp.msg.CStoreRQ.DataSetType == DIMSE_DATASET_NULL)
+ {
+ DCMNET_WARN("Incoming C-STORE with no dataset, trying to receive one anyway");
+ }
+ Uint16 desiredCStoreReturnStatus = 0;
+ // handle normal storage mode, i.e. receive in memory and store to disk
+ if (m_storageMode == DCMSCU_STORAGE_DISK)
+ {
+ // Receive dataset
+ result = receiveDIMSEDataset(&pcid, &rspDataset);
+ if (result.bad())
+ {
+ result = DIMSE_NULLKEY;
+ desiredCStoreReturnStatus = STATUS_STORE_Error_CannotUnderstand;
+ } else {
+ result = handleSTORERequest(pcid, rspDataset, continueSession, desiredCStoreReturnStatus);
+ }
+ }
+ // handle bit preserving storage mode, i.e. receive directly to disk
+ else if (m_storageMode == DCMSCU_STORAGE_BIT_PRESERVING)
+ {
+ OFString storageFilename;
+ OFStandard::combineDirAndFilename(storageFilename, m_storageDir, rsp.msg.CStoreRQ.AffectedSOPInstanceUID, OFTrue);
+ result = handleSTORERequestFile(&pcid, storageFilename, &(rsp.msg.CStoreRQ));
+ if (result.good())
+ {
+ notifyInstanceStored(storageFilename, rsp.msg.CStoreRQ.AffectedSOPClassUID, rsp.msg.CStoreRQ.AffectedSOPInstanceUID);
+ }
+ }
+ // handle ignore storage mode, i.e. ignore received dataset and do not store at all
+ else
+ {
+ result = ignoreSTORERequest(pcid, rsp.msg.CStoreRQ);
+ }
+
+ // Evaluate result from C-STORE request handling and send response
+ if (result.bad())
+ {
+ desiredCStoreReturnStatus = STATUS_STORE_Error_CannotUnderstand;
+ continueSession = OFFalse;
+ }
+ result = sendSTOREResponse(pcid, desiredCStoreReturnStatus, rsp.msg.CStoreRQ);
+ if (result.bad())
+ {
+ continueSession = OFFalse;
+ }
+ delete rspDataset; // should be NULL if not existing
+ }
+
+ // Handle other DIMSE command (error since other command than GET/STORE not expected)
+ else
+ {
+ DCMNET_ERROR("Expected C-GET response or C-STORE request but received DIMSE command 0x"
+ << STD_NAMESPACE hex << STD_NAMESPACE setfill('0') << STD_NAMESPACE setw(4)
+ << OFstatic_cast(unsigned int, rsp.CommandField));
+ DCMNET_DEBUG(DIMSE_dumpMessage(tempStr, rsp, DIMSE_INCOMING, NULL, pcid));
+ result = DIMSE_BADCOMMANDTYPE;
+ continueSession = OFFalse;
+ }
+
+ delete statusDetail; // should be NULL if not existing or added to response list
+ statusDetail = NULL;
+ }
+ /* All responses received or break signal occured */
+
+ return result;
+
+}
+
+
+// Handles single C-GET Response
+OFCondition DcmSCU::handleCGETResponse(const T_ASC_PresentationContextID /* presID */,
+ RetrieveResponse* response,
+ OFBool& continueCGETSession)
+{
+ // Do some basic validity checks
+ if (!isConnected())
+ return DIMSE_ILLEGALASSOCIATION;
+ if (response == NULL)
+ return DIMSE_NULLKEY;
+
+ DCMNET_DEBUG("Handling C-GET Response");
+
+ /* First, perform separate check for 0xCxxx error codes */
+ Uint16 highNibble = response->m_status & 0xf000;
+ if (highNibble == STATUS_GET_Failed_UnableToProcess)
+ {
+ continueCGETSession = OFFalse;
+ DCMNET_ERROR("Unable to Process");
+ DCMNET_WARN("Full status is 0x"
+ << STD_NAMESPACE hex << STD_NAMESPACE setfill('0') << STD_NAMESPACE setw(4)
+ << response->m_status);
+ return EC_Normal;
+ }
+
+ /* Check for other error codes */
+ switch (response->m_status) {
+ case STATUS_GET_Refused_OutOfResourcesNumberOfMatches:
+ continueCGETSession = OFFalse;
+ DCMNET_ERROR("Out of Resouces - Unable to calculate number of matches");
+ break;
+ case STATUS_GET_Refused_OutOfResourcesSubOperations:
+ continueCGETSession = OFFalse;
+ DCMNET_ERROR("Out of Resouces - Unable to perform sub-operations");
+ break;
+ case STATUS_GET_Failed_IdentifierDoesNotMatchSOPClass:
+ continueCGETSession = OFFalse;
+ DCMNET_ERROR("Identifier does not match SOP class");
+ break;
+ case STATUS_GET_Cancel_SubOperationsTerminatedDueToCancelIndication:
+ continueCGETSession = OFFalse;
+ DCMNET_DEBUG("Suboperations canceled by server due to CANCEL indication");
+ break;
+ case STATUS_GET_Warning_SubOperationsCompleteOneOrMoreFailures:
+ continueCGETSession = OFFalse;
+ DCMNET_WARN("Suboperations of C-GET completed with one or more failures");
+ break;
+ case STATUS_Pending:
+ /* in this case the current C-MOVE-RSP indicates that */
+ /* there will be some more results */
+ continueCGETSession = OFTrue;
+ DCMNET_DEBUG("One or more pending C-GET responses");
+ break;
+ case STATUS_Success:
+ /* in this case, we received the last C-MOVE-RSP so there */
+ /* will be no other responses we have to wait for. */
+ continueCGETSession = OFFalse;
+ DCMNET_DEBUG("Received final C-GET response, no more C-GET responses expected");
+ break;
+ default:
+ /* in all other cases, don't expect further responses to come */
+ continueCGETSession = OFFalse;
+ DCMNET_WARN("Status is 0x"
+ << STD_NAMESPACE hex << STD_NAMESPACE setfill('0') << STD_NAMESPACE setw(4)
+ << response->m_status << " (unknown)");
+ DCMNET_WARN("Will not wait for further C-GET responses");
+ break;
+ } //switch
+
+ return EC_Normal;
+}
+
+
+// Handles single C-STORE Request received during C-GET session
+OFCondition DcmSCU::handleSTORERequest(const T_ASC_PresentationContextID /* presID */,
+ DcmDataset *incomingObject,
+ OFBool& /* continueCGETSession */,
+ Uint16& cStoreReturnStatus)
+{
+ if (incomingObject == NULL)
+ return DIMSE_NULLKEY;
+
+ OFString sopClassUID;
+ OFString sopInstanceUID;
+ OFCondition result = incomingObject->findAndGetOFString(DCM_SOPClassUID, sopClassUID);
+ if (result.good())
+ result = incomingObject->findAndGetOFString(DCM_SOPInstanceUID, sopInstanceUID);
+ if (result.bad())
+ {
+ DCMNET_ERROR("Cannot store received object: either SOP Instance or SOP Class UID not present");
+ cStoreReturnStatus = STATUS_STORE_Error_DataSetDoesNotMatchSOPClass;
+ return EC_TagNotFound;
+ }
+
+ OFString filename = createStorageFilename(incomingObject);
+ result = incomingObject->saveFile(filename.c_str());
+ if (result.good())
+ {
+ E_TransferSyntax xferSyntax;
+ getDatasetInfo(incomingObject, sopClassUID, sopInstanceUID, xferSyntax);
+ notifyInstanceStored(filename, sopClassUID, sopInstanceUID);
+ cStoreReturnStatus = STATUS_Success;
+ }
+ else
+ {
+ cStoreReturnStatus = STATUS_STORE_Refused_OutOfResources;
+ }
+
+ return result;
+}
+
+OFCondition DcmSCU::handleSTORERequestFile(T_ASC_PresentationContextID *presID,
+ const OFString& filename,
+ T_DIMSE_C_StoreRQ* request)
+{
+ if (filename.empty())
+ return EC_IllegalParameter;
+
+ /* in the following, we want to receive data over the network and write it to a file */
+ /* exactly the way it was received over the network. Hence, a filestream will be created and the data */
+ /* set will be received and written to the file through the call to DIMSE_receiveDataSetInFile(...).*/
+ /* create filestream */
+ DcmOutputFileStream *filestream = NULL;
+ OFCondition cond = DIMSE_createFilestream(filename.c_str(), request, m_assoc, *presID, OFTrue, &filestream);
+ if (cond.good())
+ {
+ if (m_progressNotificationMode)
+ {
+ cond = DIMSE_receiveDataSetInFile(m_assoc, m_blockMode, m_dimseTimeout, presID, filestream,
+ callbackRECEIVEProgress, this /*callbackData*/);
+ } else {
+ cond = DIMSE_receiveDataSetInFile(m_assoc, m_blockMode, m_dimseTimeout, presID, filestream,
+ NULL /*callback*/, NULL /*callbackData*/);
+ }
+ delete filestream;
+ if (cond != EC_Normal)
+ {
+ unlink(filename.c_str());
+ }
+ DCMNET_DEBUG("Received dataset on presentation context " << OFstatic_cast(unsigned int, *presID));
+ }
+ else
+ {
+ OFString tempStr;
+ DCMNET_ERROR("Unable to receive and store dataset on presentation context "
+ << OFstatic_cast(unsigned int, *presID) << ": " << DimseCondition::dump(tempStr, cond));
+ }
+ return cond;
+}
+
+
+OFCondition DcmSCU::sendSTOREResponse(T_ASC_PresentationContextID presID,
+ Uint16 status,
+ const T_DIMSE_C_StoreRQ& request)
+{
+ // Send back response
+ T_DIMSE_Message response;
+ T_DIMSE_C_StoreRSP &storeRsp = response.msg.CStoreRSP;
+ response.CommandField = DIMSE_C_STORE_RSP;
+ storeRsp.MessageIDBeingRespondedTo = request.MessageID;
+ storeRsp.DimseStatus = status;
+ storeRsp.DataSetType = DIMSE_DATASET_NULL;
+ storeRsp.opts = 0;
+ /* Following information is optional and normally not sent by the underlying
+ * dcmnet routines. However, maybe this could be changed later, so insert it.
+ */
+ OFStandard::strlcpy(storeRsp.AffectedSOPClassUID, request.AffectedSOPClassUID, sizeof(storeRsp.AffectedSOPClassUID));
+ OFStandard::strlcpy(storeRsp.AffectedSOPInstanceUID, request.AffectedSOPInstanceUID, sizeof(storeRsp.AffectedSOPInstanceUID));
+
+ OFString tempStr;
+ DCMNET_INFO("Sending C-STORE Response (" << DU_cstoreStatusString(status) << ")");
+
+ OFCondition cond = sendDIMSEMessage(presID, &response, NULL /*dataObject*/);
+ if (cond.bad())
+ {
+ DCMNET_ERROR("Failed sending C-STORE response: " << DimseCondition::dump(tempStr, cond));
+ }
+ return cond;
+}
+
+
+OFString DcmSCU::createStorageFilename(DcmDataset *dataset)
+{
+ OFString sopClassUID, sopInstanceUID;
+ E_TransferSyntax dummy;
+ getDatasetInfo(dataset, sopClassUID, sopInstanceUID, dummy);
+ // Create unique filename
+ if (sopClassUID.empty() || sopInstanceUID.empty())
+ return "";
+ OFString name = dcmSOPClassUIDToModality(sopClassUID.c_str(), "UNKNOWN");
+ name += ".";
+ name += sopInstanceUID;
+ OFString returnStr;
+ OFStandard::combineDirAndFilename(returnStr, m_storageDir, name, OFTrue);
+ return returnStr;
+}
+
+
+OFCondition DcmSCU::ignoreSTORERequest(T_ASC_PresentationContextID presID,
+ const T_DIMSE_C_StoreRQ& request)
+{
+
+ /* We cannot create the filestream, so ignore the incoming dataset and return an out-of-resources error to the SCU */
+ DIC_UL bytesRead = 0;
+ DIC_UL pdvCount=0;
+ DCMNET_DEBUG("Ignoring incoming C-STORE dataset on presentation context "
+ << OFstatic_cast(unsigned int, presID)
+ << " with Affected SOP Instance UID: " << request.AffectedSOPInstanceUID );
+ OFCondition result = DIMSE_ignoreDataSet(m_assoc, m_blockMode, m_dimseTimeout, &bytesRead, &pdvCount);
+ if (result.good())
+ {
+ DCMNET_TRACE("Successfully skipped " << bytesRead << " bytes in " << pdvCount << " PDVs");
+ }
+ return result;
+}
+
+
+void DcmSCU::notifyInstanceStored(const OFString& filename,
+ const OFString& sopClassUID,
+ const OFString& sopInstanceUID) const
+{
+ DCMNET_DEBUG("Stored instance to disk:");
+ DCMNET_DEBUG(" Filename: " << filename);
+ DCMNET_DEBUG(" SOP Class UID: " << sopClassUID);
+ DCMNET_DEBUG(" SOP Instance UID: " << sopInstanceUID);
+}
+
+
+/* ************************************************************************* */
+/* C-FIND functionality */
+/* ************************************************************************* */
+
+// Sends a C-FIND Request on given presentation context
+OFCondition DcmSCU::sendFINDRequest(const T_ASC_PresentationContextID presID,
+ DcmDataset *queryKeys,
+ OFList<QRResponse*> *responses)
+{
+ // Do some basic validity checks
+ if (!isConnected())
+ return DIMSE_ILLEGALASSOCIATION;
+ if (queryKeys == NULL)
+ return DIMSE_NULLKEY;
+
+ /* Prepare DIMSE data structures for issuing request */
+ OFCondition cond;
+ OFString tempStr;
+ T_ASC_PresentationContextID pcid = presID;
+ T_DIMSE_Message msg;
+ DcmDataset* statusDetail = NULL;
+ T_DIMSE_C_FindRQ* req = &(msg.msg.CFindRQ);
+ // Set type of message
+ msg.CommandField = DIMSE_C_FIND_RQ;
+ // Set message ID
+ req->MessageID = nextMessageID();
+ // Announce dataset
+ req->DataSetType = DIMSE_DATASET_PRESENT;
+ // Specify priority
+ req->Priority = DIMSE_PRIORITY_LOW;
+
+ // Determine SOP Class from presentation context
+ OFString abstractSyntax, transferSyntax;
+ findPresentationContext(pcid, abstractSyntax, transferSyntax);
+ if (abstractSyntax.empty() || transferSyntax.empty())
+ return DIMSE_NOVALIDPRESENTATIONCONTEXTID;
+ OFStandard::strlcpy(req->AffectedSOPClassUID, abstractSyntax.c_str(), sizeof(req->AffectedSOPClassUID));
+
+ /* Send request */
+ DCMNET_INFO("Sending C-FIND Request (MsgID " << req->MessageID << ")");
+
+ cond = sendDIMSEMessage(pcid, &msg, queryKeys);
+ if (cond.bad())
+ {
+ DCMNET_ERROR("Failed sending C-FIND request: " << DimseCondition::dump(tempStr, cond));
+ return cond;
+ }
+
+ /* Receive and handle response */
+ OFBool waitForNextResponse = OFTrue;
+ while (waitForNextResponse)
+ {
+ T_DIMSE_Message rsp;
+ statusDetail = NULL;
+
+ // Receive command set
+ cond = receiveDIMSECommand(&pcid, &rsp, &statusDetail, NULL /* not interested in the command set */);
+ if (cond.bad())
+ {
+ DCMNET_ERROR("Failed receiving DIMSE response: " << DimseCondition::dump(tempStr, cond));
+ return cond;
+ }
+
+ if (rsp.CommandField == DIMSE_C_FIND_RSP)
+ {
+ DCMNET_INFO("Received C-FIND Response (" << DU_cfindStatusString(rsp.msg.CFindRSP.DimseStatus) << ")");
+
+ } else {
+ DCMNET_ERROR("Expected C-FIND response but received DIMSE command 0x"
+ << STD_NAMESPACE hex << STD_NAMESPACE setfill('0') << STD_NAMESPACE setw(4)
+ << OFstatic_cast(unsigned int, rsp.CommandField));
+ DCMNET_DEBUG(DIMSE_dumpMessage(tempStr, rsp, DIMSE_INCOMING, NULL, pcid));
+ delete statusDetail;
+ return DIMSE_BADCOMMANDTYPE;
+ }
+
+ // Prepare response package for response handler
+ QRResponse *findRSP = new QRResponse();
+ findRSP->m_affectedSOPClassUID = rsp.msg.CFindRSP.AffectedSOPClassUID;
+ findRSP->m_messageIDRespondedTo = rsp.msg.CFindRSP.MessageIDBeingRespondedTo;
+ findRSP->m_status = rsp.msg.CFindRSP.DimseStatus;
+ findRSP->m_statusDetail = statusDetail;
+
+ // Receive dataset if there is one (status PENDING)
+ DcmDataset *rspDataset = NULL;
+ if (DICOM_PENDING_STATUS(findRSP->m_status))
+ {
+ // Check if dataset is announced correctly
+ if (rsp.msg.CFindRSP.DataSetType == DIMSE_DATASET_NULL)
+ {
+ DCMNET_ERROR("Received C-FIND response with PENDING status but no dataset announced, aborting");
+ delete findRSP; // includes statusDetail
+ return DIMSE_BADMESSAGE;
+ }
+
+ // Receive dataset
+ cond = receiveDIMSEDataset(&pcid, &rspDataset);
+ if (cond.bad())
+ {
+ delete findRSP; // includes statusDetail
+ return DIMSE_BADDATA;
+ }
+ findRSP->m_dataset = rspDataset;
+ }
+
+ // Handle C-FIND response (has to handle all possible status flags)
+ cond = handleFINDResponse(pcid, findRSP, waitForNextResponse);
+ if (cond.bad())
+ {
+ DCMNET_WARN("Unable to handle C-FIND response correctly: " << cond.text() << " (ignored)");
+ delete findRSP; // includes statusDetail and rspDataset
+ // don't return here but trust the "waitForNextResponse" variable
+ }
+ // if response could be handled successfully, add it to response list
+ else
+ {
+ if (responses != NULL) // only add if desired by caller
+ responses->push_back(findRSP);
+ else
+ delete findRSP; // includes statusDetail and rspDataset
+ }
+ }
+ /* All responses received or break signal occured */
+ return EC_Normal;
+}
+
+
+// Standard handler for C-FIND message responses
+OFCondition DcmSCU::handleFINDResponse(const T_ASC_PresentationContextID /* presID */,
+ QRResponse *response,
+ OFBool &waitForNextResponse)
+{
+ if (!isConnected())
+ return DIMSE_ILLEGALASSOCIATION;
+ if (response == NULL)
+ return DIMSE_NULLKEY;
+
+ DCMNET_DEBUG("Handling C-FIND Response");
+ switch (response->m_status) {
+ case STATUS_Pending:
+ case STATUS_FIND_Pending_WarningUnsupportedOptionalKeys:
+ /* in this case the current C-FIND-RSP indicates that */
+ /* there will be some more results */
+ waitForNextResponse = OFTrue;
+ DCMNET_DEBUG("One or more pending C-FIND responses");
+ break;
+ case STATUS_Success:
+ /* in this case the current C-FIND-RSP indicates that */
+ /* there are no more records that match the search mask */
+ waitForNextResponse = OFFalse;
+ DCMNET_DEBUG("Received final C-FIND response, no more C-FIND responses expected");
+ break;
+ default:
+ /* in all other cases, don't expect further responses to come */
+ waitForNextResponse = OFFalse;
+ DCMNET_DEBUG("Status tells not to wait for further C-FIND responses");
+ break;
+ } //switch
+ return EC_Normal;
+}
+
+
+/* ************************************************************************* */
+/* C-CANCEL functionality */
+/* ************************************************************************* */
+
+// Send C-CANCEL-REQ and, therefore, ends current C-FIND, -MOVE or -GET session
+OFCondition DcmSCU::sendCANCELRequest(const T_ASC_PresentationContextID presID)
+{
+ if (!isConnected())
+ return DIMSE_ILLEGALASSOCIATION;
+
+ /* Prepare DIMSE data structures for issuing request */
+ OFCondition cond;
+ OFString tempStr;
+ T_ASC_PresentationContextID pcid = presID;
+ T_DIMSE_Message msg;
+ T_DIMSE_C_CancelRQ* req = &(msg.msg.CCancelRQ);
+ // Set type of message
+ msg.CommandField = DIMSE_C_CANCEL_RQ;
+ /* Set message ID responded to. A new message ID is _not_ needed so
+ we do not increment the message ID here but instead have to give the
+ message ID that was used last.
+ Note that that it is required to actually use the message ID of the last
+ C-FIND/GET/MOVE that was issued on this presentation context channel.
+ However, since we only support synchronous association mode so far,
+ it is enough to take the last message ID used at all.
+ For asynchronous operation, we would have to lookup the message ID
+ of the last C-FIND/GET/MOVE request issued and thus, store this
+ information after sending it.
+ */
+ req->MessageIDBeingRespondedTo = m_assoc->nextMsgID - 1;
+ // Announce dataset
+ req->DataSetType = DIMSE_DATASET_NULL;
+
+ /* We do not care about the transfer syntax since no
+ dataset is transported at all, i.e. we trust that the user provided
+ the correct presentation context ID (could be private one).
+ */
+ DCMNET_INFO("Sending C-CANCEL Request (MsgID " << req->MessageIDBeingRespondedTo
+ << ", PresID " << OFstatic_cast(unsigned int, pcid) << ")");
+
+ cond = sendDIMSEMessage(pcid, &msg, NULL /*dataObject*/);
+ if (cond.bad())
+ {
+ DCMNET_ERROR("Failed sending C-CANCEL request: " << DimseCondition::dump(tempStr, cond));
+ }
+
+ DCMNET_TRACE("There is no C-CANCEL response in DICOM, so none expected");
+ return cond;
+}
+
+
+/* ************************************************************************* */
+/* N-ACTION functionality */
+/* ************************************************************************* */
+
+// Sends N-ACTION request to another DICOM application
+OFCondition DcmSCU::sendACTIONRequest(const T_ASC_PresentationContextID presID,
+ const OFString &sopInstanceUID,
+ const Uint16 actionTypeID,
+ DcmDataset *reqDataset,
+ Uint16 &rspStatusCode)
+{
+ // Do some basic validity checks
+ if (!isConnected())
+ return DIMSE_ILLEGALASSOCIATION;
+ if (sopInstanceUID.empty() || (reqDataset == NULL))
+ return DIMSE_NULLKEY;
+
+ // Prepare DIMSE data structures for issuing request
+ OFCondition cond;
+ OFString tempStr;
+ T_ASC_PresentationContextID pcid = presID;
+ T_DIMSE_Message request;
+ T_DIMSE_N_ActionRQ &actionReq = request.msg.NActionRQ;
+ DcmDataset *statusDetail = NULL;
+
+ request.CommandField = DIMSE_N_ACTION_RQ;
+ actionReq.MessageID = nextMessageID();
+ actionReq.DataSetType = DIMSE_DATASET_PRESENT;
+ actionReq.ActionTypeID = actionTypeID;
+
+ // Determine SOP Class from presentation context
+ OFString abstractSyntax, transferSyntax;
+ findPresentationContext(pcid, abstractSyntax, transferSyntax);
+ if (abstractSyntax.empty() || transferSyntax.empty())
+ return DIMSE_NOVALIDPRESENTATIONCONTEXTID;
+ OFStandard::strlcpy(actionReq.RequestedSOPClassUID, abstractSyntax.c_str(), sizeof(actionReq.RequestedSOPClassUID));
+ OFStandard::strlcpy(actionReq.RequestedSOPInstanceUID, sopInstanceUID.c_str(), sizeof(actionReq.RequestedSOPInstanceUID));
+
+ // Send request
+ DCMNET_INFO("Sending N-ACTION Request (MsgID " << actionReq.MessageID << ")");
+
+ cond = sendDIMSEMessage(pcid, &request, reqDataset);
+ if (cond.bad())
+ {
+ DCMNET_ERROR("Failed sending N-ACTION request: " << DimseCondition::dump(tempStr, cond));
+ return cond;
+ }
+
+ // Receive response
+ T_DIMSE_Message response;
+ cond = receiveDIMSECommand(&pcid, &response, &statusDetail, NULL /* commandSet */);
+ if (cond.bad())
+ {
+ DCMNET_ERROR("Failed receiving DIMSE response: " << DimseCondition::dump(tempStr, cond));
+ return cond;
+ }
+
+ // Check command set
+ if (response.CommandField == DIMSE_N_ACTION_RSP)
+ {
+ DCMNET_INFO("Received N-ACTION Response (" << DU_nactionStatusString(response.msg.NActionRSP.DimseStatus) << ")");
+
+ } else {
+ DCMNET_ERROR("Expected N-ACTION response but received DIMSE command 0x"
+ << STD_NAMESPACE hex << STD_NAMESPACE setfill('0') << STD_NAMESPACE setw(4)
+ << OFstatic_cast(unsigned int, response.CommandField));
+ DCMNET_DEBUG(DIMSE_dumpMessage(tempStr, response, DIMSE_INCOMING, NULL, pcid));
+ delete statusDetail;
+ return DIMSE_BADCOMMANDTYPE;
+ }
+ if (statusDetail != NULL)
+ {
+ DCMNET_DEBUG("Response has status detail:" << OFendl << DcmObject::PrintHelper(*statusDetail));
+ delete statusDetail;
+ }
+
+ // Set return value
+ T_DIMSE_N_ActionRSP &actionRsp = response.msg.NActionRSP;
+ rspStatusCode = actionRsp.DimseStatus;
+
+ // Check whether there is a dataset to be received
+ if (actionRsp.DataSetType == DIMSE_DATASET_PRESENT)
+ {
+ // this should never happen
+ DcmDataset *tempDataset = NULL;
+ T_ASC_PresentationContextID tempID;
+ DCMNET_WARN("Trying to retrieve unexpected dataset in N-ACTION response");
+ cond = receiveDIMSEDataset(&tempID, &tempDataset);
+ if (cond.good())
+ {
+ DCMNET_WARN("Received unexpected dataset after N-ACTION response, ignoring");
+ delete tempDataset;
+ } else {
+ return DIMSE_BADDATA;
+ }
+ }
+ if (actionRsp.MessageIDBeingRespondedTo != actionReq.MessageID)
+ {
+ // since we only support synchronous communication, the message ID in the response
+ // should be identical to the one in the request
+ DCMNET_ERROR("Received response with wrong message ID (" << actionRsp.MessageIDBeingRespondedTo
+ << " instead of " << actionReq.MessageID << ")");
+ return DIMSE_BADMESSAGE;
+ }
+
+ return cond;
+}
+
+
+/* ************************************************************************* */
+/* N-EVENT REPORT functionality */
+/* ************************************************************************* */
+
+// Sends N-EVENT-REPORT request and receives N-EVENT-REPORT response
+OFCondition DcmSCU::sendEVENTREPORTRequest(const T_ASC_PresentationContextID presID,
+ const OFString &sopInstanceUID,
+ const Uint16 eventTypeID,
+ DcmDataset *reqDataset,
+ Uint16 &rspStatusCode)
+{
+ // Do some basic validity checks
+ if (!isConnected())
+ return DIMSE_ILLEGALASSOCIATION;
+ if (sopInstanceUID.empty() || (reqDataset == NULL))
+ return DIMSE_NULLKEY;
+
+ // Prepare DIMSE data structures for issuing request
+ OFCondition cond;
+ OFString tempStr;
+ T_ASC_PresentationContextID pcid = presID;
+ T_DIMSE_Message request;
+ T_DIMSE_N_EventReportRQ &eventReportReq = request.msg.NEventReportRQ;
+ DcmDataset *statusDetail = NULL;
+
+ request.CommandField = DIMSE_N_EVENT_REPORT_RQ;
+
+ // Generate a new message ID
+ eventReportReq.MessageID = nextMessageID();
+ eventReportReq.DataSetType = DIMSE_DATASET_PRESENT;
+ eventReportReq.EventTypeID = eventTypeID;
+
+ // Determine SOP Class from presentation context
+ OFString abstractSyntax, transferSyntax;
+ findPresentationContext(pcid, abstractSyntax, transferSyntax);
+ if (abstractSyntax.empty() || transferSyntax.empty())
+ return DIMSE_NOVALIDPRESENTATIONCONTEXTID;
+ OFStandard::strlcpy(eventReportReq.AffectedSOPClassUID, abstractSyntax.c_str(), sizeof(eventReportReq.AffectedSOPClassUID));
+ OFStandard::strlcpy(eventReportReq.AffectedSOPInstanceUID, sopInstanceUID.c_str(), sizeof(eventReportReq.AffectedSOPInstanceUID));
+
+ // Send request
+ DCMNET_INFO("Sending N-EVENT-REPORT Request (MsgID " << eventReportReq.MessageID << ")");
+
+ cond = sendDIMSEMessage(pcid, &request, reqDataset);
+ if (cond.bad())
+ {
+ DCMNET_ERROR("Failed sending N-EVENT-REPORT request: " << DimseCondition::dump(tempStr, cond));
+ return cond;
+ }
+ // Receive response
+ T_DIMSE_Message response;
+ cond = receiveDIMSECommand(&pcid, &response, &statusDetail, NULL /* commandSet */);
+ if (cond.bad())
+ {
+ DCMNET_ERROR("Failed receiving DIMSE response: " << DimseCondition::dump(tempStr, cond));
+ return cond;
+ }
+
+ // Check command set
+ if (response.CommandField == DIMSE_N_EVENT_REPORT_RSP)
+ {
+ DCMNET_INFO("Received N-EVENT-REPORT Response (" << DU_neventReportStatusString(response.msg.NEventReportRSP.DimseStatus) << ")");
+
+ } else {
+ DCMNET_ERROR("Expected N-EVENT-REPORT response but received DIMSE command 0x"
+ << STD_NAMESPACE hex << STD_NAMESPACE setfill('0') << STD_NAMESPACE setw(4)
+ << OFstatic_cast(unsigned int, response.CommandField));
+ DCMNET_DEBUG(DIMSE_dumpMessage(tempStr, response, DIMSE_INCOMING, NULL, pcid));
+ delete statusDetail;
+ return DIMSE_BADCOMMANDTYPE;
+ }
+ if (statusDetail != NULL)
+ {
+ DCMNET_DEBUG("Response has status detail:" << OFendl << DcmObject::PrintHelper(*statusDetail));
+ delete statusDetail;
+ }
+
+ // Set return value
+ T_DIMSE_N_EventReportRSP &eventReportRsp = response.msg.NEventReportRSP;
+ rspStatusCode = eventReportRsp.DimseStatus;
+
+ // Check whether there is a dataset to be received
+ if (eventReportRsp.DataSetType == DIMSE_DATASET_PRESENT)
+ {
+ // this should never happen
+ DcmDataset *tempDataset = NULL;
+ T_ASC_PresentationContextID tempID;
+ cond = receiveDIMSEDataset(&tempID, &tempDataset);
+ if (cond.good())
+ {
+ DCMNET_WARN("Received unexpected dataset after N-EVENT-REPORT response, ignoring");
+ delete tempDataset;
+ } else {
+ DCMNET_ERROR("Failed receiving unexpected dataset after N-EVENT-REPORT response: "
+ << DimseCondition::dump(tempStr, cond));
+ return DIMSE_BADDATA;
+ }
+ }
+
+ // Check whether the message ID being responded to is equal to the message ID of the request
+ if (eventReportRsp.MessageIDBeingRespondedTo != eventReportReq.MessageID)
+ {
+ DCMNET_ERROR("Received response with wrong message ID (" << eventReportRsp.MessageIDBeingRespondedTo
+ << " instead of " << eventReportReq.MessageID << ")");
+ return DIMSE_BADMESSAGE;
+ }
+ return cond;
+}
+
+// Receives N-EVENT-REPORT request
+OFCondition DcmSCU::handleEVENTREPORTRequest(DcmDataset *&reqDataset,
+ Uint16 &eventTypeID,
+ const int timeout)
+{
+ // Do some basic validity checks
+ if (!isConnected())
+ return DIMSE_ILLEGALASSOCIATION;
+
+ OFCondition cond;
+ OFString tempStr;
+ T_ASC_PresentationContextID presID;
+ T_ASC_PresentationContextID presIDdset;
+ T_DIMSE_Message request;
+ T_DIMSE_N_EventReportRQ &eventReportReq = request.msg.NEventReportRQ;
+ DcmDataset *dataset = NULL;
+ DcmDataset *statusDetail = NULL;
+ Uint16 statusCode = 0;
+
+ if (timeout > 0)
+ DCMNET_DEBUG("Handle N-EVENT-REPORT request, waiting up to " << timeout << " seconds (only for N-EVENT-REPORT message)");
+ else if ((m_dimseTimeout > 0) && (m_blockMode == DIMSE_NONBLOCKING))
+ DCMNET_DEBUG("Handle N-EVENT-REPORT request, waiting up to " << m_dimseTimeout << " seconds (default for all DIMSE messages)");
+ else
+ DCMNET_DEBUG("Handle N-EVENT-REPORT request, waiting an unlimited period of time");
+
+ // Receive request, use specific timeout (if defined)
+ cond = receiveDIMSECommand(&presID, &request, &statusDetail, NULL /* commandSet */, timeout);
+ if (cond.bad())
+ {
+ if (cond != DIMSE_NODATAAVAILABLE)
+ DCMNET_ERROR("Failed receiving DIMSE request: " << DimseCondition::dump(tempStr, cond));
+ return cond;
+ }
+
+ // Check command set
+ if (request.CommandField == DIMSE_N_EVENT_REPORT_RQ)
+ {
+ DCMNET_INFO("Received N-EVENT-REPORT Request (MsgID " << eventReportReq.MessageID << ")");
+ } else {
+ DCMNET_DEBUG(DIMSE_dumpMessage(tempStr, request, DIMSE_INCOMING, NULL, presID));
+ DCMNET_ERROR("Expected N-EVENT-REPORT request but received DIMSE command 0x"
+ << STD_NAMESPACE hex << STD_NAMESPACE setfill('0') << STD_NAMESPACE setw(4)
+ << OFstatic_cast(unsigned int, request.CommandField));
+ delete statusDetail;
+ return DIMSE_BADCOMMANDTYPE;
+ }
+ if (statusDetail != NULL)
+ {
+ DCMNET_DEBUG("Request has status detail:" << OFendl << DcmObject::PrintHelper(*statusDetail));
+ delete statusDetail;
+ }
+
+ // Check if dataset is announced correctly
+ if (eventReportReq.DataSetType == DIMSE_DATASET_NULL)
+ {
+ DCMNET_DEBUG(DIMSE_dumpMessage(tempStr, request, DIMSE_INCOMING, NULL, presID));
+ DCMNET_ERROR("Received N-EVENT-REPORT request but no dataset announced, aborting");
+ return DIMSE_BADMESSAGE;
+ }
+
+ // Receive dataset
+ cond = receiveDIMSEDataset(&presIDdset, &dataset);
+ if (cond.bad())
+ {
+ DCMNET_DEBUG(DIMSE_dumpMessage(tempStr, request, DIMSE_INCOMING, NULL, presID));
+ return DIMSE_BADDATA;
+ }
+
+ // Output dataset only if trace level is enabled
+ DCMNET_DEBUG(DIMSE_dumpMessage(tempStr, request, DIMSE_INCOMING, NULL, presID));
+
+ // Compare presentation context ID of command and data set
+ if (presIDdset != presID)
+ {
+ DCMNET_ERROR("Presentation Context ID of command (" << OFstatic_cast(unsigned int, presID)
+ << ") and data set (" << OFstatic_cast(unsigned int, presIDdset) << ") differ");
+ delete dataset;
+ return makeDcmnetCondition(DIMSEC_INVALIDPRESENTATIONCONTEXTID, OF_error,
+ "DIMSE: Presentation Contexts of Command and Data Set differ");
+ }
+
+ // Check the request dataset and return the DIMSE status code to be used
+ statusCode = checkEVENTREPORTRequest(eventReportReq, dataset);
+
+ // Send back response
+ T_DIMSE_Message response;
+ T_DIMSE_N_EventReportRSP &eventReportRsp = response.msg.NEventReportRSP;
+ response.CommandField = DIMSE_N_EVENT_REPORT_RSP;
+ eventReportRsp.MessageIDBeingRespondedTo = eventReportReq.MessageID;
+ eventReportRsp.DimseStatus = statusCode;
+ eventReportRsp.DataSetType = DIMSE_DATASET_NULL;
+ eventReportRsp.opts = 0;
+ eventReportRsp.AffectedSOPClassUID[0] = 0;
+ eventReportRsp.AffectedSOPInstanceUID[0] = 0;
+
+ DCMNET_INFO("Sending N-EVENT-REPORT Response (" << DU_neventReportStatusString(statusCode) << ")");
+
+ cond = sendDIMSEMessage(presID, &response, NULL /*dataObject*/);
+ if (cond.bad())
+ {
+ DCMNET_ERROR("Failed sending N-EVENT-REPORT response: " << DimseCondition::dump(tempStr, cond));
+ delete dataset;
+ return cond;
+ }
+
+ // Set return values
+ reqDataset = dataset;
+ eventTypeID = eventReportReq.EventTypeID;
+
+ return cond;
+}
+
+
+Uint16 DcmSCU::checkEVENTREPORTRequest(T_DIMSE_N_EventReportRQ & /*eventReportReq*/,
+ DcmDataset * /*reqDataset*/)
+{
+ // we default to success
+ return STATUS_Success;
+}
+
+
+/* ************************************************************************* */
+/* General message handling */
+/* ************************************************************************* */
+
+void DcmSCU::notifySENDProgress(const unsigned long byteCount)
+{
+ DCMNET_TRACE("Bytes sent: " << byteCount);
+}
+
+
+void DcmSCU::notifyRECEIVEProgress(const unsigned long byteCount)
+{
+ DCMNET_TRACE("Bytes received: " << byteCount);
+}
+
+
+/* ************************************************************************* */
+/* Various helpers */
+/* ************************************************************************* */
+
+// Sends a DIMSE command and possibly also instance data to the configured peer DICOM application
+OFCondition DcmSCU::sendDIMSEMessage(const T_ASC_PresentationContextID presID,
+ T_DIMSE_Message *msg,
+ DcmDataset *dataObject,
+ DcmDataset **commandSet)
+{
+ if (!isConnected())
+ return DIMSE_ILLEGALASSOCIATION;
+ if (msg == NULL)
+ return DIMSE_NULLKEY;
+
+ OFCondition cond;
+ /* call the corresponding DIMSE function to send the message */
+ if (m_progressNotificationMode)
+ {
+ cond = DIMSE_sendMessageUsingMemoryData(m_assoc, presID, msg, NULL /*statusDetail*/, dataObject,
+ callbackSENDProgress, this /*callbackData*/, commandSet);
+ } else {
+ cond = DIMSE_sendMessageUsingMemoryData(m_assoc, presID, msg, NULL /*statusDetail*/, dataObject,
+ NULL /*callback*/, NULL /*callbackData*/, commandSet);
+ }
+
+#if 0
+ // currently disabled because it is not (yet) needed
+ if (cond.good())
+ {
+ /* create a copy of the current DIMSE command message */
+ delete m_openDIMSERequest;
+ m_openDIMSERequest = new T_DIMSE_Message;
+ memcpy((char*)m_openDIMSERequest, msg, sizeof(*m_openDIMSERequest));
+ }
+#endif
+
+ return cond;
+}
+
+
+// Receive DIMSE command (excluding dataset!) over the currently open association
+OFCondition DcmSCU::receiveDIMSECommand(T_ASC_PresentationContextID *presID,
+ T_DIMSE_Message *msg,
+ DcmDataset **statusDetail,
+ DcmDataset **commandSet,
+ const Uint32 timeout)
+{
+ if (!isConnected())
+ return DIMSE_ILLEGALASSOCIATION;
+
+ OFCondition cond;
+ if (timeout > 0)
+ {
+ /* call the corresponding DIMSE function to receive the command (use specified timeout)*/
+ cond = DIMSE_receiveCommand(m_assoc, DIMSE_NONBLOCKING, timeout, presID,
+ msg, statusDetail, commandSet);
+ } else {
+ /* call the corresponding DIMSE function to receive the command (use default timeout) */
+ cond = DIMSE_receiveCommand(m_assoc, m_blockMode, m_dimseTimeout, presID,
+ msg, statusDetail, commandSet);
+ }
+ return cond;
+}
+
+// Receives one dataset (of instance data) via network from another DICOM application
+OFCondition DcmSCU::receiveDIMSEDataset(T_ASC_PresentationContextID *presID,
+ DcmDataset **dataObject)
+{
+ if (!isConnected())
+ return DIMSE_ILLEGALASSOCIATION;
+
+ OFCondition cond;
+ /* call the corresponding DIMSE function to receive the dataset */
+ if (m_progressNotificationMode)
+ {
+ cond = DIMSE_receiveDataSetInMemory(m_assoc, m_blockMode, m_dimseTimeout, presID, dataObject,
+ callbackRECEIVEProgress, this /*callbackData*/);
+ } else {
+ cond = DIMSE_receiveDataSetInMemory(m_assoc, m_blockMode, m_dimseTimeout, presID, dataObject,
+ NULL /*callback*/, NULL /*callbackData*/);
+ }
+
+ if (cond.good())
+ {
+ DCMNET_DEBUG("Received dataset on presentation context " << OFstatic_cast(unsigned int, *presID));
+ }
+ else
+ {
+ OFString tempStr;
+ DCMNET_ERROR("Unable to receive dataset on presentation context "
+ << OFstatic_cast(unsigned int, *presID) << ": " << DimseCondition::dump(tempStr, cond));
+ }
+ return cond;
+}
+
+
+void DcmSCU::setMaxReceivePDULength(const unsigned long maxRecPDU)
+{
+ m_maxReceivePDULength = maxRecPDU;
+}
+
+
+void DcmSCU::setDIMSEBlockingMode(const T_DIMSE_BlockingMode blockingMode)
+{
+ m_blockMode = blockingMode;
+}
+
+
+void DcmSCU::setAETitle(const OFString &myAETtitle)
+{
+ m_ourAETitle = myAETtitle;
+}
+
+
+void DcmSCU::setPeerHostName(const OFString &peerHostName)
+{
+ m_peer = peerHostName;
+}
+
+
+void DcmSCU::setPeerAETitle(const OFString &peerAETitle)
+{
+ m_peerAETitle = peerAETitle;
+}
+
+
+void DcmSCU::setPeerPort(const Uint16 peerPort)
+{
+ m_peerPort = peerPort;
+}
+
+
+void DcmSCU::setDIMSETimeout(const Uint32 dimseTimeout)
+{
+ m_dimseTimeout = dimseTimeout;
+}
+
+
+void DcmSCU::setACSETimeout(const Uint32 acseTimeout)
+{
+ m_acseTimeout = acseTimeout;
+}
+
+
+void DcmSCU::setAssocConfigFileAndProfile(const OFString &filename,
+ const OFString &profile)
+{
+ m_assocConfigFilename = filename;
+ m_assocConfigProfile = profile;
+}
+
+
+void DcmSCU::setStorageDir(const OFString& storeDir)
+{
+ m_storageDir = storeDir;
+}
+
+
+void DcmSCU::setStorageMode(const DcmStorageMode storageMode)
+{
+ m_storageMode = storageMode;
+}
+
+
+void DcmSCU::setVerbosePCMode(const OFBool mode)
+{
+ m_verbosePCMode = mode;
+}
+
+
+void DcmSCU::setDatasetConversionMode(const OFBool mode)
+{
+ m_datasetConversionMode = mode;
+}
+
+
+void DcmSCU::setProgressNotificationMode(const OFBool mode)
+{
+ m_progressNotificationMode = mode;
+}
+
+
+/* Get methods */
+
+OFBool DcmSCU::isConnected() const
+{
+ return (m_assoc != NULL) && (m_assoc->DULassociation != NULL);
+}
+
+Uint32 DcmSCU::getMaxReceivePDULength() const
+{
+ return m_maxReceivePDULength;
+}
+
+
+OFBool DcmSCU::getTLSEnabled() const
+{
+ return OFFalse;
+}
+
+
+T_DIMSE_BlockingMode DcmSCU::getDIMSEBlockingMode() const
+{
+ return m_blockMode;
+}
+
+
+const OFString &DcmSCU::getAETitle() const
+{
+ return m_ourAETitle;
+}
+
+
+const OFString &DcmSCU::getPeerHostName() const
+{
+ return m_peer;
+}
+
+
+const OFString &DcmSCU::getPeerAETitle() const
+{
+ return m_peerAETitle;
+}
+
+
+Uint16 DcmSCU::getPeerPort() const
+{
+ return m_peerPort;
+}
+
+
+Uint32 DcmSCU::getDIMSETimeout() const
+{
+ return m_dimseTimeout;
+}
+
+
+Uint32 DcmSCU::getACSETimeout() const
+{
+ return m_acseTimeout;
+}
+
+
+OFString DcmSCU::getStorageDir() const
+{
+ return m_storageDir;
+}
+
+
+DcmStorageMode DcmSCU::getStorageMode() const
+{
+ return m_storageMode;
+}
+
+
+OFBool DcmSCU::getVerbosePCMode() const
+{
+ return m_verbosePCMode;
+}
+
+
+OFBool DcmSCU::getDatasetConversionMode() const
+{
+ return m_datasetConversionMode;
+}
+
+
+OFBool DcmSCU::getProgressNotificationMode() const
+{
+ return m_progressNotificationMode;
+}
+
+
+OFCondition DcmSCU::getDatasetInfo(DcmDataset *dataset,
+ OFString &sopClassUID,
+ OFString &sopInstanceUID,
+ E_TransferSyntax &transferSyntax)
+{
+ OFCondition status = EC_IllegalParameter;
+ sopClassUID.clear();
+ sopInstanceUID.clear();
+ transferSyntax = EXS_Unknown;
+ if (dataset != NULL)
+ {
+ // ignore returned condition codes (e.g. EC_TagNotFound)
+ dataset->findAndGetOFString(DCM_SOPClassUID, sopClassUID);
+ dataset->findAndGetOFString(DCM_SOPInstanceUID, sopInstanceUID);
+ transferSyntax = dataset->getOriginalXfer();
+ // check return values for validity
+ if (sopClassUID.empty())
+ status = NET_EC_InvalidSOPClassUID;
+ else if (sopInstanceUID.empty())
+ status = NET_EC_InvalidSOPInstanceUID;
+ else if (transferSyntax == EXS_Unknown)
+ status = NET_EC_UnknownTransferSyntax;
+ else
+ status = EC_Normal;
+ }
+ return status;
+}
+
+
+/* ************************************************************************* */
+/* Callback functions */
+/* ************************************************************************* */
+
+void DcmSCU::callbackSENDProgress(void *callbackContext,
+ const unsigned long byteCount)
+{
+ if (callbackContext != NULL)
+ OFreinterpret_cast(DcmSCU *, callbackContext)->notifySENDProgress(byteCount);
+}
+
+
+void DcmSCU::callbackRECEIVEProgress(void *callbackContext,
+ const unsigned long byteCount)
+{
+ if (callbackContext != NULL)
+ OFreinterpret_cast(DcmSCU *, callbackContext)->notifyRECEIVEProgress(byteCount);
+}
+
+
+/* ************************************************************************* */
+/* class RetrieveResponse */
+/* ************************************************************************* */
+
+void RetrieveResponse::print()
+{
+ DCMNET_INFO(" Number of Remaining Suboperations : " << m_numberOfRemainingSubops);
+ DCMNET_INFO(" Number of Completed Suboperations : " << m_numberOfCompletedSubops);
+ DCMNET_INFO(" Number of Failed Suboperations : " << m_numberOfFailedSubops);
+ DCMNET_INFO(" Number of Warning Suboperations : " << m_numberOfWarningSubops);
+}
+
+
+/*
+** CVS Log
+** $Log: scu.cc,v $
+** Revision 1.60 2012-05-14 10:42:54 onken
+** Enhanced handling of error codes for C-GET (added explicit support for
+** missing code Ax702 and enhanced recognition of customized 0xCxxx codes.)
+**
+** Revision 1.59 2012-02-21 08:48:54 joergr
+** Added support for progress notifications while sending and receiving DICOM
+** datasets.
+**
+** Revision 1.58 2011-10-10 14:01:29 uli
+** Moved SCU-specific error condition to the correct place.
+**
+** Revision 1.57 2011-10-04 08:58:16 joergr
+** Added flag that allows for specifying whether to convert a dataset to be
+** transferred to the network transfer syntax. Also removed unused parameters
+** "rspCommandSet" and "rspStatusDetail" from method sendSTORERequest().
+**
+** Revision 1.56 2011-09-29 17:12:03 joergr
+** Fixed memory leak in sendSTORERequest(), a DICOM dataset was not deleted.
+**
+** Revision 1.55 2011-09-29 13:12:01 joergr
+** Introduced new network-related error codes, e.g. in case that none of the
+** proposed presentation contexts were accepted by the association acceptor.
+**
+** Revision 1.54 2011-09-29 13:04:09 joergr
+** Added check whether the presentation context specified by the caller of the
+** method was really accepted before sending a C-STORE request.
+**
+** Revision 1.53 2011-09-29 12:56:21 joergr
+** Enhanced implementation of the function that retrieves the abstract syntax
+** and transfer syntax of a particular presentation context (using the ID).
+**
+** Revision 1.52 2011-09-29 09:04:26 joergr
+** Output message ID of request and DIMSE status of response messages to the
+** INFO logger (if DEBUG level is not enabled). All tools and classes in the
+** "dcmnet" module now use (more or less) the same output in verbose mode.
+**
+** Revision 1.51 2011-09-28 16:28:18 joergr
+** Added general support for transfer syntax conversions to sendSTORERequest().
+**
+** Revision 1.50 2011-09-28 15:25:36 joergr
+** Return a more appropriate error code in case the dataset to be sent is
+** invalid. This also required to introduce a return value for getDatasetInfo().
+**
+** Revision 1.49 2011-09-28 14:37:01 joergr
+** Output the DIMSE status in verbose mode (if debug mode is not enabled).
+**
+** Revision 1.48 2011-09-28 13:31:54 joergr
+** Added method that allows for clearing the list of presentation contexts.
+**
+** Revision 1.47 2011-09-23 15:27:02 joergr
+** Removed needless deletion of the "statusDetail" variable.
+**
+** Revision 1.46 2011-09-16 09:38:40 joergr
+** Fixed some typos and other small inconsistencies.
+**
+** Revision 1.45 2011-09-06 16:12:53 ogazzar
+** Fixed typos in a log commit message.
+**
+** Revision 1.44 2011-09-06 14:15:10 ogazzar
+** Fixed wrong logger name which caused compiler error.
+**
+** Revision 1.43 2011-09-06 12:58:35 ogazzar
+** Added a function to send N-EVENT-REPORT request and to receive a response.
+**
+** Revision 1.42 2011-08-25 15:46:20 joergr
+** Further cleanup of minor inconsistencies regarding documentation, parameter
+** names, log output and handling of status details information.
+**
+** Revision 1.41 2011-08-25 15:05:09 joergr
+** Changed data structure for Q/R responses from OFVector to OFList. Also fixed
+** some possible memory leaks and made the FIND/MOVE/GET code more consistent.
+**
+** Revision 1.40 2011-08-25 13:49:31 joergr
+** Fixed minor issues in the documentation, parameter and method names. Output
+** retrieve responses to main dcmnet logger instead of response logger.
+**
+** Revision 1.39 2011-08-25 09:31:35 onken
+** Added C-GET functionality to DcmSCU class and accompanying getscu
+** commandline application.
+**
+** Revision 1.38 2011-08-24 11:50:48 joergr
+** Uncommented name of unused method parameter that caused a compiler warning.
+**
+** Revision 1.37 2011-07-06 11:08:48 uli
+** Fixed various compiler warnings.
+**
+** Revision 1.36 2011-06-29 16:33:45 joergr
+** Fixed various issues that are reported when compiled with "gcc -Weffc++".
+**
+** Revision 1.35 2011-06-01 15:04:29 onken
+** Removed unused status variable from C-ECHO code.
+**
+** Revision 1.34 2011-05-30 20:10:44 onken
+** Added dump of query/move keys in debug mode when sending C-FIND or C-MOVE.
+**
+** Revision 1.33 2011-05-27 10:12:18 joergr
+** Fixed typos and source code formatting.
+**
+** Revision 1.32 2011-05-25 09:56:52 ogazzar
+** Renamed a function name.
+**
+** Revision 1.31 2011-05-25 09:31:53 ogazzar
+** Added a function to look for a presentation context ID that best matches the
+** abstract syntax UID and the transfer syntax UID.
+**
+** Revision 1.30 2011-05-24 08:38:39 ogazzar
+** Added role selection negotiation while adding a presenation context.
+**
+** Revision 1.29 2011-05-19 17:19:52 onken
+** Fixed some documentation. Added some extra checks for NULL when handling MOVE
+** and FIND responses. Simplified destructors for FIND and MOVEResponses.
+**
+** Revision 1.28 2011-05-19 10:51:20 onken
+** Simplified C string copy by using OFStandard::strlcpy and removed debugging
+** code introduced with last comit.
+**
+** Revision 1.27 2011-05-19 10:37:44 onken
+** Removed unused variable that caused compiler warning. Fixed typo.
+**
+** Revision 1.26 2011-05-19 09:57:24 onken
+** Fixed message ID field in C-CANCEL request (should be the one of last
+** request). In case of error status codes in C-MOVE responses, the default
+** behaviour is now to not wait for further responses. Fixed log output level
+** to better fit the messages while receiveing C-MOVE responses. Minor
+** code and comment cleanups. Renamed function parameter in sendMOVEREquest
+** to better reflect the standard.
+**
+** Revision 1.25 2011-05-19 08:08:30 onken
+** Fixed wrong usage of strlcpy in new C-CANCEL function.
+**
+** Revision 1.24 2011-05-17 14:26:19 onken
+** Implemented C-CANCEL message. Fixed some minor formatting issues.
+** Changed C-ECHO implementation to rely on sendDIMSEMesage as the other
+** DIMSE functions do. Changed some public function arguments to const to be
+** more correct. Fixed CVS log at the end of the scu.cc file.
+**
+** Revision 1.23 2011-04-28 17:50:05 onken
+** Re-sorted header list to make scu.h come first (after osconfig.h).
+** Protected public networking functions for creating an association
+** from being called twice. Enhanced protection of DIMSE messaging
+** functions from being called without being connected. Introduced
+** status detail into C-FIND responses (and C-MOVE responses). Was
+** not accessible to the caller before. Minor code cleanups. Added
+** C-MOVE code for retrieving DICOM objects. So far only retrieving
+** on a separate connection is supported. Added function for cleaning
+** up internal memory from destructor. This function also fixes a
+** memory leak in case users call initNetwork more than one time.
+** Added error code returned by functions if SCU is already connected.
+**
+** Revision 1.22 2011-04-18 07:01:03 uli
+** Use global variables for the logger objects. This removes the thread-unsafe
+** static local variables which were used before.
+**
+** Revision 1.21 2011-04-05 11:16:13 joergr
+** Output DIMSE status code in hexadecimal format to the logger. Removed unused
+** code (local half-implemented function). Added more comments.
+**
+** Revision 1.20 2011-03-09 11:13:28 onken
+** Enhanced error message for missing data in store request.
+**
+** Revision 1.19 2011-02-23 08:11:51 joergr
+** Fixed issue with undefined priority field in C-STORE and C-FIND request.
+**
+** Revision 1.18 2011-02-16 08:55:17 joergr
+** Fixed issue in sendSTORERequest() when sending a dataset that was created
+** in memory (and which has, therefore, an original transfer of EXS_Unknown).
+**
+** Revision 1.17 2011-02-04 12:57:40 uli
+** Made sure all members are initialized in the constructor (-Weffc++).
+**
+** Revision 1.16 2010-12-21 09:37:36 onken
+** Fixed wrong response assignment in DcmSCU's C-STORE code. Thanks to
+** forum user "takeos" for the hint and fix.
+**
+** Revision 1.15 2010-10-20 07:41:36 uli
+** Made sure isalpha() & friends are only called with valid arguments.
+**
+** Revision 1.14 2010-10-14 13:14:29 joergr
+** Updated copyright header. Added reference to COPYRIGHT file.
+**
+** Revision 1.13 2010-10-01 12:25:29 uli
+** Fixed most compiler warnings in remaining modules.
+**
+** Revision 1.12 2010-08-10 11:59:32 uli
+** Fixed some cases where dcmFindNameOfUID() returning NULL could cause crashes.
+**
+** Revision 1.11 2010-06-24 09:26:57 joergr
+** Added check on whether the presentation context ID of command and data set are
+** identical. Made sure that received dataset is deleted when an error occurs.
+** Used more appropriate error conditions / return codes. Further code cleanup.
+**
+** Revision 1.10 2010-06-22 15:48:53 joergr
+** Introduced new enumeration type to be used for closeAssociation().
+** Further code cleanup. Renamed some methods, variables, types and so on.
+**
+** Revision 1.9 2010-06-18 14:58:01 joergr
+** Changed some error conditions / return codes to more appropriate values.
+** Further revised logging output. Use DimseCondition::dump() where appropriate.
+**
+** Revision 1.8 2010-06-17 17:13:06 joergr
+** Added preliminary support for N-EVENT-REPORT to DcmSCU. Some further code
+** cleanups and enhancements. Renamed some methods. Revised documentation.
+**
+** Revision 1.7 2010-06-09 16:33:34 joergr
+** Added preliminary support for N-ACTION to DcmSCU. Some further code cleanups
+** and enhancements.
+**
+** Revision 1.6 2010-06-08 17:54:14 onken
+** Added C-FIND functionality to DcmSCU. Some code cleanups. Fixed
+** memory leak sometimes occuring during association configuration.
+**
+** Revision 1.5 2010-06-02 16:01:49 joergr
+** Slightly modified some log messages and levels for reasons of consistency.
+** Use type cast macros (e.g. OFstatic_cast) where appropriate.
+**
+** Revision 1.4 2010-04-29 16:13:25 onken
+** Made SCU class independent from dcmtls, i.e. outsourced TLS API. Added
+** direct API support for sending C-STORE requests. Further API changes and
+** some bugs fixed.
+**
+** Revision 1.3 2009-12-21 15:33:58 onken
+** Added documentation and refactored / enhanced some code.
+**
+** Revision 1.2 2009-12-17 09:12:27 onken
+** Fixed other scu and scp base class compile issues.
+**
+** Revision 1.1 2009-12-16 17:05:35 onken
+** Added base classes for SCU and SCP implementation.
+**
+** Revision 1.5 2009-12-02 14:26:05 uli
+** Stop including dcdebug.h which was removed.
+**
+** Revision 1.4 2009-11-18 12:37:28 uli
+** Fix compiler errors due to removal of DUL_Debug() and DIMSE_Debug().
+**
+** Revision 1.3 2009-01-08 18:25:34 joergr
+** Replaced further OFListIterator() by OFListConstIterator() in order to
+** compile when STL list classes are used.
+**
+** Revision 1.2 2009-01-08 13:33:31 joergr
+** Replaced OFListIterator() by OFListConstIterator() in order to compile when
+** STL list classes are used.
+**
+** Revision 1.1 2008-09-29 13:51:55 onken
+** Initial checkin of module dcmppscu implementing an MPPS commandline client.
+**
+*/
diff --git a/tests/tools/scu.h b/tests/tools/scu.h
new file mode 100644
index 0000000..fb3963d
--- /dev/null
+++ b/tests/tools/scu.h
@@ -0,0 +1,1197 @@
+/*
+ *
+ * Copyright (C) 2008-2012, OFFIS e.V.
+ * All rights reserved. See COPYRIGHT file for details.
+ *
+ * This software and supporting documentation were developed by
+ *
+ * OFFIS e.V.
+ * R&D Division Health
+ * Escherweg 2
+ * D-26121 Oldenburg, Germany
+ *
+ *
+ * Module: dcmnet
+ *
+ * Author: Michael Onken
+ *
+ * Purpose: Base class for Service Class Users (SCUs)
+ *
+ * Last Update: $Author: joergr $
+ * Update Date: $Date: 2012-02-21 08:48:51 $
+ * CVS/RCS Revision: $Revision: 1.41 $
+ * Status: $State: Exp $
+ *
+ * CVS/RCS Log at end of file
+ *
+ */
+
+#ifndef SCU_H
+#define SCU_H
+
+#include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */
+
+#include "dcmtk/dcmdata/dctk.h" /* Covers most common dcmdata classes */
+#include "dcmtk/dcmnet/dcompat.h"
+#include "dcmtk/dcmnet/dimse.h" /* DIMSE network layer */
+#include "dcmtk/dcmnet/dcasccff.h" /* For reading a association config file */
+#include "dcmtk/dcmnet/dcasccfg.h" /* For holding association config file infos */
+#include "dcmtk/ofstd/oflist.h"
+// MODIFICATION: Use local header file
+#include "dndefine.h"
+// END Modification
+
+
+/** Different types of closing an association
+ */
+enum DcmCloseAssociationType
+{
+ /// Release the current association
+ DCMSCU_RELEASE_ASSOCIATION,
+ /// Abort the current association
+ DCMSCU_ABORT_ASSOCIATION,
+ /// Peer requested release (Aborting)
+ DCMSCU_PEER_REQUESTED_RELEASE,
+ /// Peer aborted the association
+ DCMSCU_PEER_ABORTED_ASSOCIATION
+};
+
+/** Storage mode used for DICOM objects received via C-STORE
+ */
+enum DcmStorageMode
+{
+ /// Ignore any objects received via C-STORE
+ DCMSCU_STORAGE_IGNORE,
+ /// Try to store the objects to disk
+ DCMSCU_STORAGE_DISK,
+ /// Try to store to disk in bit-preserving mode. This is especially useful
+ /// for huge files that cannot fully be received in memory since the
+ /// data is directly streamed to disk. Originally, this was introduced for
+ /// DICOM signatures which can be kept valid this way.
+ DCMSCU_STORAGE_BIT_PRESERVING
+};
+
+
+/** Base class for C-FIND, C-MOVE and C-GET responses
+ */
+class DCMTK_DCMNET_EXPORT QRResponse
+{
+ public:
+
+ /** Standard constructor.
+ */
+ QRResponse() :
+ m_messageIDRespondedTo(0),
+ m_affectedSOPClassUID(),
+ m_dataset(NULL),
+ m_status(0),
+ m_statusDetail(NULL) {}
+
+ /** Destructor, cleans up internal memory (dataset if present).
+ */
+ virtual ~QRResponse() { delete m_dataset; delete m_statusDetail; }
+
+ /// The message ID responded to (mandatory response field,
+ /// equals message ID from request)
+ Uint16 m_messageIDRespondedTo;
+
+ /// Optional response field according to part 7 of the standard
+ /// If present, equals SOP Class UID from request.
+ OFString m_affectedSOPClassUID;
+
+ /// Conditional response field (NULL if absent). From the standard (2009,
+ /// part 4, C.4.2.1.4.2), for C-MOVE: In Q/R if no C-STORE sub-operation
+ /// failed, Failed SOP Instance UID List (0008,0058) is absent and
+ /// therefore no Data Set shall be sent in the C-MOVE response. Further
+ /// rules: Statuses of Canceled, Failure, Refused, or Warning shall
+ /// contain the Failed SOP Instance UID List Attribute; status of
+ /// Pending shall not.
+ DcmDataset *m_dataset;
+
+ /// The returned DIMSE status (mandatory Response Field)
+ Uint16 m_status;
+
+ /// Status detail (NULL if absent). For some DIMSE return status codes,
+ /// an additional dataset is sent which gives further information (i.e.
+ /// in case of warnings or errors).
+ DcmDataset *m_statusDetail;
+
+private:
+
+ /** Private undefined copy constructor.
+ * @param other The find response to copy from
+ */
+ QRResponse(const QRResponse& other);
+
+ /** Private undefined assignment operator.
+ * @param other The find response that should be assigned from
+ */
+ QRResponse& operator=(const QRResponse& other);
+};
+
+
+/// Base class representing for single C-GET or C-MOVE response
+class DCMTK_DCMNET_EXPORT RetrieveResponse : public QRResponse
+{
+public:
+ /** Standard constructor
+ */
+ RetrieveResponse() :
+ m_numberOfRemainingSubops(0),
+ m_numberOfCompletedSubops(0),
+ m_numberOfFailedSubops(0),
+ m_numberOfWarningSubops(0) {}
+
+ /** Destructor, cleans up internal memory
+ */
+ virtual ~RetrieveResponse() {}
+
+ /** Prints response to INFO log level.
+ */
+ void print();
+
+ /// Number of remaining sub operations (in Q/R: C-STORE calls).
+ /// For Q/R MOVE and GET, for status of pending this field shall be filled.
+ /// For others, the field may be filled.
+ Uint16 m_numberOfRemainingSubops;
+
+ /// Number of successfully completed sub operations (in Q/R: C-STORE calls).
+ /// For Q/R MOVE and GET, for status of pending this field shall be filled.
+ /// For others, the field may be filled.
+ Uint16 m_numberOfCompletedSubops;
+
+ /// Number of failed sub operations (in Q/R: C-STORE calls).
+ /// For Q/R MOVE and GET, for status of pending this field shall be filled.
+ /// For others, the field may be filled.
+ Uint16 m_numberOfFailedSubops;
+
+ /// Number generated warnings generated by sub operations (in Q/R: C-STORE calls).
+ /// For Q/R MOVE and GET, for status of pending this field shall be filled.
+ /// For others, the field may be filled.
+ Uint16 m_numberOfWarningSubops;
+
+private:
+
+ /** Private undefined copy constructor
+ * @param other Response to copy from
+ */
+ RetrieveResponse(const RetrieveResponse& other);
+
+ /** Private undefined assignment operator
+ * @param other Response that should be assigned from
+ */
+ RetrieveResponse& operator=(const RetrieveResponse& other);
+};
+
+
+/** Base class for implementing DICOM Service Class User functionality. The class offers
+ * support for negotiating associations and sending and receiving arbitrary DIMSE messages
+ * on that connection. DcmSCU has built-in C-ECHO support so derived classes do not have to
+ * implement that capability on their own.
+ * @warning This class is EXPERIMENTAL. Be careful to use it in production environment.
+ */
+class DCMTK_DCMNET_EXPORT DcmSCU
+{
+
+public:
+
+ /** Constructor, just initializes internal class members
+ */
+ DcmSCU();
+
+ /** Virtual destructor
+ */
+ virtual ~DcmSCU();
+
+ /** Add presentation context to be used for association negotiation
+ * @param abstractSyntax [in] Abstract syntax name in UID format
+ * @param xferSyntaxes [in] List of transfer syntaxes to be added for the given abstract
+ * syntax
+ * @param role [in] The role to be negotiated
+ * @return EC_Normal if adding was successful, otherwise error code
+ */
+ OFCondition addPresentationContext(const OFString &abstractSyntax,
+ const OFList<OFString> &xferSyntaxes,
+ const T_ASC_SC_ROLE role = ASC_SC_ROLE_DEFAULT);
+
+ /** Initialize network, i.e.\ prepare for association negotiation. If the SCU is already
+ * connected, the call will not be successful and the old connection keeps open.
+ * @return EC_Normal if initialization was successful, otherwise error code.
+ * NET_EC_AlreadyConnected if SCU is already connected.
+ */
+ virtual OFCondition initNetwork();
+
+ /** Negotiate association by using presentation contexts and parameters as defined by
+ * earlier function calls. If negotiation fails, there is no need to close the association
+ * or to do anything else with this class.
+ * @return EC_Normal if negotiation was successful, otherwise error code.
+ * NET_EC_AlreadyConnected if SCU is already connected.
+ */
+ virtual OFCondition negotiateAssociation();
+
+ /** After negotiation association, this call returns the first usable presentation context
+ * given the desired abstract syntax and transfer syntax
+ * @param abstractSyntax [in] The abstract syntax (UID) to look for
+ * @param transferSyntax [in] The transfer syntax (UID) to look for. If empty, the transfer
+ * syntax is not checked.
+ * @return Adequate Presentation context ID that can be used. 0 if none found.
+ */
+ T_ASC_PresentationContextID findPresentationContextID(const OFString &abstractSyntax,
+ const OFString &transferSyntax);
+
+ /** After a successful association negotiation, this function is called to return the
+ * presentation context ID that best matches the desired abstract syntax and transfer
+ * syntax (TS). The function tries to do the following:
+ * - If possible finds a presentation context with matching TS
+ * - Else then tries to find an explicit VR uncompressed TS presentation context
+ * - Else then tries to find an implicit VR uncompressed TS presentation context
+ * - Else finally accepts each matching presentation ctx independent of TS.
+ * @param abstractSyntax [in] The abstract syntax (UID) to look for
+ * @param transferSyntax [in] The transfer syntax (UID) to look for. If empty, the transfer
+ * syntax is not checked.
+ * @return Adequate Presentation context ID that can be used. 0 if no appropriate
+ * presentation context could be found at all.
+ */
+ T_ASC_PresentationContextID findAnyPresentationContextID(const OFString &abstractSyntax,
+ const OFString &transferSyntax);
+
+ /** This function sends a C-ECHO command via network to another DICOM application
+ * @param presID [in] Presentation context ID to use. A value of 0 lets SCP class tries
+ * to choose one on its own.
+ * @return EC_Normal if echo was successful, an error code otherwise
+ *
+ */
+ virtual OFCondition sendECHORequest(const T_ASC_PresentationContextID presID);
+
+ /** This function sends a C-STORE request on the currently opened association and receives
+ * the corresponding response then. If required and supported, the dataset of the SOP
+ * instance can be converted automatically to the network transfer syntax that was
+ * negotiated (and is specified by the parameter 'presID'). However, this feature is
+ * disabled by default. See setDatasetConversionMode() on how to enable it.
+ * @param presID [in] Contains in the end the ID of the presentation context which
+ * was specified in the DIMSE command. If 0 is given, the
+ * function tries to find an approriate presentation context
+ * itself (based on SOP class and original transfer syntax of
+ * the 'dicomFile' or 'dataset').
+ * @param dicomFile [in] The filename of the DICOM file to be sent. Alternatively, a
+ * dataset can be given in the next parameter. If both are given
+ * the dataset from the file name is used.
+ * @param dataset [in] The dataset to be sent. Alternatively, a filename can be
+ * specified in the previous parameter. If both are given the
+ * dataset from the filename is used.
+ * @param rspStatusCode [out] The response status code received. 0 means success, others
+ * can be found in the DICOM standard.
+ * @return EC_Normal if request could be issued and response was received successfully,
+ * error code otherwise. That means that if the receiver sends a response denoting
+ * failure of the storage request, EC_Normal will be returned.
+ */
+ virtual OFCondition sendSTORERequest(const T_ASC_PresentationContextID presID,
+ const OFString &dicomFile,
+ DcmDataset *dataset,
+ Uint16 &rspStatusCode);
+
+ /** Sends a C-MOVE Request on given presentation context and receives list of responses.
+ * The function receives the first response and then calls the function handleMOVEResponse()
+ * which gets the relevant presentation context together with the response dataset and
+ * status information. Then it waits again for the next response, if there are more to
+ * come (i.e. response status is PENDING). In the end, after receiving all responses, the
+ * full list of responses is returned to the caller. If he is not interested, he just sets
+ * responses=NULL when calling the function.
+ * This function can be overwritten by actual SCU implementations but just should work fine
+ * for most people.
+ * @param presID [in] The presentation context ID that should be used.
+ * Must be an odd number.
+ * @param moveDestinationAETitle [in] The move destination's AE title, i.e.\ the one that
+ * is used for connection to the storage server.
+ * @param dataset [in] The dataset containing the information about the
+ * object(s) to be retrieved.
+ * @param responses [out] The incoming C-MOVE responses for this request.
+ * The caller is responsible for providing a non-NULL
+ * pointer for this case. After receiving the results,
+ * the caller is responsible for freeing the memory of
+ * this variable. If NULL is specified, the responses
+ * will not bereturned to the caller.
+ * @return EC_Normal if everything went fine, i.e.\ if request could be send and responses
+ * (with whatever status) could be received.
+ */
+ virtual OFCondition sendMOVERequest(const T_ASC_PresentationContextID presID,
+ const OFString &moveDestinationAETitle,
+ DcmDataset *dataset,
+ OFList<RetrieveResponse*> *responses);
+
+ /** This is the standard handler for C-MOVE message responses: It just adds up all responses
+ * it receives and prints a DEBUG message. Therefore, it is called for each response
+ * received in sendMOVERequest(). The idea is of course to overwrite this function in a
+ * derived, actual SCU implementation if required. Thus, after each response, the caller of
+ * sendMOVERequest() can decide on its own whether he wants to cancel the C-MOVE session,
+ * terminate the association, do something useful or whatever. Thus this function is a more
+ * object-oriented kind of callback.
+ * @param presID [in] The presentation context ID where the response was
+ * received on.
+ * @param response [in] The C-MOVE response received.
+ * @param waitForNextResponse [out] Denotes whether SCU should try to receive another
+ * response. If set to OFTrue, then sendMOVERequest() will
+ * continue waiting for responses. The current
+ * implementation does that for all responses do not have
+ * status Failed, Warning, Success or unknown. If set to
+ * OFFalse, sendMOVERequest() will return control to the
+ * caller.
+ * @return EC_Normal, if response could be handled. Error code otherwise.
+ * The current implementation always returns EC_Normal.
+ */
+ virtual OFCondition handleMOVEResponse(const T_ASC_PresentationContextID presID,
+ RetrieveResponse *response,
+ OFBool &waitForNextResponse);
+
+ /** Sends a C-GET Request on given presentation context and receives list of responses. It
+ * then switches control to the function handleCGETSession().
+ * The full list of responses is returned to the caller. If he is not interested, he can
+ * set responses=NULL when calling the function.
+ * This function can be overwritten by actual SCU implementations but just should work fine
+ * for most people.
+ * @param presID [in] The presentation context ID that should be used. Must be an odd
+ * number.
+ * @param dataset [in] The dataset containing the information about the
+ * object(s) to be retrieved
+ * @param responses [out] The incoming C-GET responses for this request. If the caller
+ * specifies NULL, no responses will be returned; otherwise there
+ * should be at least one final C-GET response (mandatory). C-GET
+ * responses after each DICOM object received are optional and may
+ * have been ommitted by the server.
+ * @return EC_Normal if everything went fine, i.e.\ if request could be sent and expected
+ * responses (with whatever status) could be received.
+ */
+ virtual OFCondition sendCGETRequest(const T_ASC_PresentationContextID presID,
+ DcmDataset *dataset,
+ OFList<RetrieveResponse*> *responses);
+
+ /** Does the logic for switching between C-GET Response and C-STORE Requests. Sends a C-GET
+ * Request on given presentation context and receives list of responses. Ihe full list of
+ * responses is returned to the caller. If he is not interested, he can set responses=NULL
+ * when calling the function. After sending a C-GET Request, there might be two different
+ * responses coming in: C-GET-RSP (optional after each received object and mandatory after
+ * the last object) or a mandatory C-STORE for each incoming object that is received due to
+ * the request. This function therefore either calls handleCGETResponse() or
+ * handleSTORERequest() in order to deal with the incoming message. All other messages lead
+ * to an error within this handler.
+ * This function can be overwritten by actual SCU implementations but just should work fine
+ * for most people.
+ * @param presID [in] The presentation context ID that should be used. Must be an odd
+ * number.
+ * @param dataset [in] The dataset containing the information about the object(s) to be
+ * retrieved
+ * @param responses [out] The incoming C-GET responses for this request. If the caller
+ * specifies NULL, no responses will be returned; otherwise there
+ * should be at least one final C-GET response (mandatory). C-GET
+ * responses after each DICOM object received are optional and may
+ * have been ommitted by the server.
+ * @return EC_Normal if everything went fine, i.e.\ if request could be send
+ * and expected responses (with whatever status) could be received.
+ */
+ virtual OFCondition handleCGETSession(const T_ASC_PresentationContextID presID,
+ DcmDataset *dataset,
+ OFList<RetrieveResponse*> *responses);
+
+ /** Function handling a single C-GET Response. This standard handler reads the status of the
+ * response and decides whether to receive any further messages related to the original
+ * C-GET Request or whether the last response was received or an error occured.
+ * @param presID [in] The presentation context the C-GET Response was
+ * received on.
+ * @param response [in] The response received
+ * @param continueCGETSession [out] Defines whether it is decided to wait for further C-GET
+ * Responses/C-STORE Requests within this C-GET session
+ * @return If no errors occur (dataset response NULL, SCU not connected), this method will
+ * return EC_Normal, otherwise error code.
+ */
+ virtual OFCondition handleCGETResponse(const T_ASC_PresentationContextID presID,
+ RetrieveResponse* response,
+ OFBool& continueCGETSession);
+
+ /** Function handling a single C-STORE Request. If storage mode is set to disk (default),
+ * this function is called and the incoming object stored to disk.
+ * @param presID [in] The presentation context the C-STORE Response was
+ * received on.
+ * @param incomingObject [in] The dataset (the object) received
+ * @param continueCGETSession [out] Defines whether it is decided to wait for further
+ * C-GET Responses/C-STORE requests within this C-GET
+ * session.
+ * @param cStoreReturnStatus [out] Denotes the desired C-STORE return status.
+ * @return If errors occur (incomingObject NULL or SCU not connected or file could not be
+ * stored), this method will return an error code otherwise EC_Normal.
+ */
+ virtual OFCondition handleSTORERequest(const T_ASC_PresentationContextID presID,
+ DcmDataset *incomingObject,
+ OFBool& continueCGETSession,
+ Uint16& cStoreReturnStatus);
+
+ /** Function handling a single C-STORE Request. If storage mode is set to bit preserving,
+ * this function is called and the incoming object stored directly to disk, i.e. not stored
+ * fully in memory.
+ * @param presID [in] The presentation context the C-STORE Response was received on.
+ * @param filename [in] The filename to store to
+ * @param request [in] The incoming C-STORE request command set
+ * @return If errors occur (incomingObject NULL or SCU not connected filename not
+ * specified), this method will return an error code otherwise EC_Normal.
+ */
+ virtual OFCondition handleSTORERequestFile(T_ASC_PresentationContextID *presID,
+ const OFString& filename,
+ T_DIMSE_C_StoreRQ* request);
+
+ /** This function is called if an object was received due to a C-GET request and can be
+ * overwritten by a user in order to be informed about such an event. The default
+ * implementation just prints a DEBUG message. Note that this function is not called if
+ * the SCU is in storage mode DCMSCU_STORAGE_IGNORE.
+ * @param filename [in] The filename written
+ * @param sopClassUID [in] The SOP Class UID of the object written
+ * @param sopInstanceUID [in] The SOP Instance UID of the object written
+ */
+ virtual void notifyInstanceStored(const OFString& filename,
+ const OFString& sopClassUID,
+ const OFString& sopInstanceUID) const;
+
+ /** Sends a C-FIND Request on given presentation context and receives list of responses.
+ * The function receives the first response and then calls the function handleFINDResponse
+ * which gets the relevant presentation context together with the response dataset and
+ * status information. Then it waits again for the next response, if there are more to
+ * come (i.e. response status is PENDING). In the end, after receiving all responses, the
+ * full list of responses is returned to the caller. If he is not interested, he just sets
+ * responses=NULL when calling the function.
+ * This function can be overwritten by actual SCU implementations but just should work fine
+ * for most people.
+ * @param presID [in] The presentation context ID that should be used. Must be an odd
+ * number.
+ * @param queryKeys [in] The dataset containing the query keys to be searched for on the
+ * server (SCP).
+ * @param responses [out] The incoming C-FIND responses for this request. The caller is
+ * responsible for providing a non-NULL pointer for this case.
+ * After receiving the results, the caller is responsible for
+ * freeing the memory of this variable. If NULL is specified, the
+ * responses will be not returned to the caller.
+ * @return EC_Normal if everything went fine, i.e.\ if request could be send and responses
+ * (with whatever status) could be received.
+ */
+ virtual OFCondition sendFINDRequest(const T_ASC_PresentationContextID presID,
+ DcmDataset *queryKeys,
+ OFList<QRResponse*> *responses);
+
+ /** This is the standard handler for C-FIND message responses: It just adds up all responses
+ * it receives and prints a DEBUG message. Therefore, it is called for each response
+ * received in sendFINDRequest(). The idea is of course to overwrite this function in a
+ * derived, actual SCU implementation if required. Thus, after each response, the caller of
+ * sendFINDRequest() can decide on its own whether he wants to cancel the C-FIND session,
+ * terminate the association, do something useful or whatever. That way this is a more
+ * object-oriented kind of callback.
+ * @param presID [in] The presentation context ID where the response was
+ * received on.
+ * @param response [in] The C-FIND response received.
+ * @param waitForNextResponse [out] Denotes whether SCU should try to receive another
+ * response. If set to OFTrue, then sendFINDRequest()
+ * will continue waiting for responses. The current
+ * implementation does that for all responses do not have
+ * status SUCESSS. If set to OFFalse, sendFINDRequest()
+ * will return control to the caller.
+ * @return EC_Normal, if response could be handled. Error code otherwise.
+ * The current implementation always returns EC_Normal.
+ */
+ virtual OFCondition handleFINDResponse(const T_ASC_PresentationContextID presID,
+ QRResponse *response,
+ OFBool &waitForNextResponse);
+
+ /** Send C-CANCEL and, therefore, ends the C-FIND -GET or -MOVE session, i.e.\ no further
+ * responses will be handled. A call to this function only makes sense if an association
+ * is open, the given presentation context represents a valid C-FIND/GET/MOVE-enabled SOP
+ * class and usually only, if the last command send on that presentation context was a
+ * C-FIND message.
+ * @param presID [in] The presentation context ID where the C-CANCEL should be sent on.
+ * @return The current implementation always returns EC_Normal.
+ */
+ virtual OFCondition sendCANCELRequest(const T_ASC_PresentationContextID presID);
+
+ /** This function sends a N-ACTION request on the currently opened association and receives
+ * the corresponding response then
+ * @param presID [in] The ID of the presentation context to be used for sending
+ * the request message. Should not be 0.
+ * @param sopInstanceUID [in] The requested SOP Instance UID
+ * @param actionTypeID [in] The action type ID to be used
+ * @param reqDataset [in] The dataset to be sent
+ * @param rspStatusCode [out] The response status code received. 0 means success,
+ * others can be found in the DICOM standard.
+ * @return EC_Normal if request could be issued and response was received successfully,
+ * an error code otherwise
+ */
+ virtual OFCondition sendACTIONRequest(const T_ASC_PresentationContextID presID,
+ const OFString &sopInstanceUID,
+ const Uint16 actionTypeID,
+ DcmDataset *reqDataset,
+ Uint16 &rspStatusCode);
+
+ /** This function sends N-EVENT-REPORT request and receives the corresponding response
+ * @param presID [in] The ID of the presentation context to be used for sending
+ * the request message. Should not be 0.
+ * @param sopInstanceUID [in] The requested SOP Instance UID
+ * @param eventTypeID [in] The event type ID to be used
+ * @param reqDataset [in] The request dataset to be sent
+ * @param rspStatusCode [out] The response status code received. 0 means success,
+ * others can be found in the DICOM standard.
+ * @return EC_Normal if request could be issued and response was received successfully,
+ * an error code otherwise
+ */
+ virtual OFCondition sendEVENTREPORTRequest(const T_ASC_PresentationContextID presID,
+ const OFString &sopInstanceUID,
+ const Uint16 eventTypeID,
+ DcmDataset *reqDataset,
+ Uint16 &rspStatusCode);
+
+ /** Receives N-EVENT-REPORT request on the currently opened association and sends a
+ * corresponding response. Calls checkEVENTREPORTRequest() in order to determine the
+ * DIMSE status code to be used for the N-EVENT-REPORT response.
+ * @param reqDataset [out] Pointer to the dataset received
+ * @param eventTypeID [out] Event Type ID from the command set received
+ * @param timeout [in] Optional timeout in seconds for receiving data. This value
+ * (if not 0) overwrites the standard DIMSE timeout and also
+ * enables the non-blocking mode for receiving the request.
+ * @return status, EC_Normal if successful, an error code otherwise
+ */
+ virtual OFCondition handleEVENTREPORTRequest(DcmDataset *&reqDataset,
+ Uint16 &eventTypeID,
+ const int timeout = 0);
+
+ /** This function is called while sending DIMSE messages, i.e.\ on each PDV of a dataset.
+ * The default implementation just prints a TRACE message on the number of bytes sent so
+ * far. By overwriting this method, the progress of the send process can be shown to the
+ * user in a more appropriate way. The progress notification can also be disabled
+ * completely by calling setProgressNotificationMode().
+ * @param byteCount [in] Number of bytes sent so far
+ */
+ virtual void notifySENDProgress(const unsigned long byteCount);
+
+ /** This function is called while receiving DIMSE messages, i.e.\ on each PDV of a dataset.
+ * The default implementation just prints a TRACE message on the number of bytes received
+ * so far. By overwriting this method, the progress of the receive process can be shown to
+ * the user in a more appropriate way. The progress notification can also be disabled
+ * completely by calling setProgressNotificationMode().
+ * @param byteCount [in] Number of bytes received so far
+ */
+ virtual void notifyRECEIVEProgress(const unsigned long byteCount);
+
+ /** Closes the association created by this SCU. Also resets the current association.
+ * @param closeType [in] Define whether to release or abort the association
+ */
+ virtual void closeAssociation(const DcmCloseAssociationType closeType);
+
+ /* Set methods */
+
+ /** Set maximum PDU length (to be received by SCU)
+ * @param maxRecPDU [in] The maximum PDU size to use in bytes
+ */
+ void setMaxReceivePDULength(const unsigned long maxRecPDU);
+
+ /** Set whether to send in DIMSE blocking or non-blocking mode
+ * @param blockingMode [in] Either blocking or non-blocking mode
+ */
+ void setDIMSEBlockingMode(const T_DIMSE_BlockingMode blockingMode);
+
+ /** Set SCU's AETitle to be used in association negotiation
+ * @param myAETtitle [in] The SCU's AETitle to be used
+ */
+ void setAETitle(const OFString &myAETtitle);
+
+ /** Set SCP's host (hostname or IP address) to talk to in association negotiation
+ * @param peerHostName [in] The SCP's hostname or IP address to be used
+ */
+ void setPeerHostName(const OFString &peerHostName);
+
+ /** Set SCP's AETitle to talk to in association negotiation
+ * @param peerAETitle [in] The SCP's AETitle to be used
+ */
+ void setPeerAETitle(const OFString &peerAETitle);
+
+ /** Set SCP's port number to connect to for association negotiation
+ * @param peerPort [in] The SCP's port number
+ */
+ void setPeerPort(const Uint16 peerPort);
+
+ /** Set timeout for receiving DIMSE messages
+ * @param dimseTimeout [in] DIMSE Timeout in seconds for receiving data. If the blocking
+ * mode is DIMSE_NONBLOCKING and we are trying to read data from
+ * the incoming socket stream and no data has been received.
+ */
+ void setDIMSETimeout(const Uint32 dimseTimeout);
+
+ /** Set timeout for receiving ACSE messages
+ * @param acseTimeout [in] ACSE Timeout in seconds used by timer for message timeouts
+ * during association negotiation
+ */
+ void setACSETimeout(const Uint32 acseTimeout);
+
+ /** Set an association configuration file and profile to be used
+ * @param filename [in] File name of the association configuration file
+ * @param profile [in] Profile inside the association negotiation file
+ */
+ void setAssocConfigFileAndProfile(const OFString &filename,
+ const OFString &profile);
+
+ /** Set the directory that should be used by the standard C-GET handler to store objects
+ * that come in with the corresponding C-STORE rqeuests
+ * @param storeDir [in] The directory to store to. It is checked in handleSTORERequest()
+ * whether the directory is writeable and readable. Per default, the
+ * received objects are stored in the current working directory.
+ */
+ void setStorageDir(const OFString& storeDir);
+
+ /** Set the storage mode to be used. Default is DCMSCU_STORAGE_DISK.
+ * @param storageMode The storage mode to be used.
+ */
+ void setStorageMode(const DcmStorageMode storageMode);
+
+ /** Set whether to show presentation contexts in verbose or debug mode
+ * @param mode [in] Show presentation contexts in verbose mode if OFTrue. By default, the
+ * presentation contexts are shown in debug mode.
+ */
+ void setVerbosePCMode(const OFBool mode);
+
+ /** Set the mode that specifies whether the transfer syntax of the dataset can be changed
+ * for network transmission. This mainly covers the compression/decompression of datasets,
+ * which is disabled by default.
+ * @param mode [in] Allow dataset conversion if OFTrue
+ */
+ void setDatasetConversionMode(const OFBool mode);
+
+ /** Set the mode that specifies whether the progress of sending and receiving DIMSE
+ * messages is notified by calling notifySENDProgress() and notifyRECEIVEProgress(),
+ * respectively. The progress notification is enabled by default.
+ * @param mode [in] Disable progress notification if OFFalse
+ */
+ void setProgressNotificationMode(const OFBool mode);
+
+ /* Get methods */
+
+ /** Get current connection status
+ * @return OFTrue if SCU is currently connected, OFFalse otherwise
+ */
+ OFBool isConnected() const;
+
+ /** Returns maximum PDU length configured to be received by SCU
+ * @return Maximum PDU length in bytes
+ */
+ Uint32 getMaxReceivePDULength() const;
+
+ /** Returns whether DIMSE messaging is configured to be blocking or unblocking
+ * @return The blocking mode configured
+ */
+ T_DIMSE_BlockingMode getDIMSEBlockingMode() const;
+
+ /** Returns the SCU's own configured AETitle
+ * @return The AETitle configured for this SCU
+ */
+ const OFString &getAETitle() const;
+
+ /** Returns the SCP's (peer's) host name configured
+ * @return The hostname (or IP) configured to be talked to
+ */
+ const OFString &getPeerHostName() const;
+
+ /** Returns the SCP's (peer's) AETitle configured
+ * @return The AETitle configured to be talked to
+ */
+ const OFString &getPeerAETitle() const;
+
+ /** Returns the SCP's (peer's) TCP/IP port configured
+ * @return The port configured to talked to
+ */
+ Uint16 getPeerPort() const;
+
+ /** Returns the DIMSE timeout configured defining how long SCU will wait for DIMSE responses
+ * @return The DIMSE timeout configured
+ */
+ Uint32 getDIMSETimeout() const;
+
+ /** Returns the timeout configured defining how long SCU will wait for messages during ACSE
+ * messaging (association negotiation)
+ * @return The ACSE timeout configured
+ */
+ Uint32 getACSETimeout() const;
+
+ /** Returns the storage directory used for storing objects received with C-STORE requests
+ * in the context of C-GET sessions. Default is empty string which refers to the current
+ * working directory.
+ * @return The storage directory
+ */
+ OFString getStorageDir() const;
+
+ /** Returns the storage mode enabled
+ * @return The storage mode enabled
+ */
+ DcmStorageMode getStorageMode() const;
+
+ /** Returns the verbose presentation context mode configured specifying whether details
+ * on the presentation contexts (negotiated during association setup) should be shown in
+ * verbose or debug mode. The latter is the default.
+ * @return The current verbose presentation context mode. Show details on the
+ * presentation contexts on INFO log level (verbose) if OFTrue and on DEBUG
+ * level if OFFalse.
+ */
+ OFBool getVerbosePCMode() const;
+
+ /** Returns the mode that specifies whether the transfer syntax of the dataset can be
+ * changed for network transmission. This mainly covers the compression/decompression
+ * of datasets, which is disabled by default.
+ * @return The current dataset conversion mode, enabled if OFTrue
+ */
+ OFBool getDatasetConversionMode() const;
+
+ /** Returns the mode that specifies whether the progress of sending and receiving DIMSE
+ * messages is notified by calling notifySENDProgress() and notifyRECEIVEProgress(),
+ * respectively. The progress notification is enabled by default.
+ * @return The current progress notification mode, enabled if OFTrue
+ */
+ OFBool getProgressNotificationMode() const;
+
+ /** Returns whether SCU is configured to create a TLS connection with the SCP
+ * @return OFFalse for this class but may be overriden by derived classes
+ */
+ OFBool getTLSEnabled() const;
+
+ /** Deletes internal networking structures from memory */
+ void freeNetwork();
+
+protected:
+
+ /** Sends a DIMSE command and possibly also a dataset from a data object via network to
+ * another DICOM application
+ * @param presID [in] Presentation context ID to be used for message
+ * @param msg [in] Structure that represents a certain DIMSE command which
+ * shall be sent
+ * @param dataObject [in] The instance data which shall be sent to the other DICOM
+ * application; NULL, if there is none
+ * @param commandSet [out] If this parameter is not NULL it will return a copy of the
+ * DIMSE command which is sent to the other DICOM application
+ * @return EC_Normal if sending request was successful, an error code otherwise
+ */
+ OFCondition sendDIMSEMessage(const T_ASC_PresentationContextID presID,
+ T_DIMSE_Message *msg,
+ DcmDataset *dataObject,
+ DcmDataset **commandSet = NULL);
+
+ /** Returns SOP Class UID, SOP Instance UID and original transfer syntax for a given dataset.
+ * If the dataset is NULL, all returned values will be undefined (i.e. empty or EXS_Unknown).
+ * @param dataset [in] The dataset to read from
+ * @param sopClassUID [out] The value of attribute SOP Class UID if present
+ * @param sopInstanceUID [out] The value of attribute SOP Instance UID if present
+ * @param transferSyntax [out] The value of transfer syntax that originally was read from
+ * disk. Will be unknown if the dataset was created in memory.
+ * @return EC_Normal if all information could be retrieved and is valid, an error code
+ * otherwise
+ */
+ OFCondition getDatasetInfo(DcmDataset *dataset,
+ OFString &sopClassUID,
+ OFString &sopInstanceUID,
+ E_TransferSyntax &transferSyntax);
+
+ /** Tells DcmSCU to use a secure TLS connection described by the given TLS layer
+ * @param tlayer [in] The TLS transport layer including all TLS parameters
+ * @return EC_Normal if given transport layer is ok, an error code otherwise
+ */
+ OFCondition useSecureConnection(DcmTransportLayer *tlayer);
+
+ /** Receive DIMSE command (excluding dataset!) over the currently open association
+ * @param presID [out] Contains in the end the ID of the presentation context
+ * which was specified in the DIMSE command received
+ * @param msg [out] The message received
+ * @param statusDetail [out] If a non-NULL value is passed this variable will in the end
+ * contain detailed information with regard to the status
+ * information which is captured in the status element
+ * (0000,0900). Note that the value for element (0000,0900) is
+ * not contained in this return value but in internal msg. For
+ * details on the structure of this object, see DICOM standard
+ * part 7, annex C).
+ * @param commandSet [out] If this parameter is not NULL, it will return a copy of the
+ * DIMSE command which was received from the other DICOM
+ * application. The caller is responsible to de-allocate the
+ * returned object!
+ * @param timeout [in] If this parameter is not 0, it specifies the timeout (in
+ * seconds) to be used for receiving the DIMSE command.
+ * Otherwise, the default timeout value is used (see
+ * setDIMSETimeout()).
+ * @return EC_Normal if command could be received successfully, an error code otherwise
+ */
+ OFCondition receiveDIMSECommand(T_ASC_PresentationContextID *presID,
+ T_DIMSE_Message *msg,
+ DcmDataset **statusDetail,
+ DcmDataset **commandSet = NULL,
+ const Uint32 timeout = 0);
+
+ /** Receives one dataset (of instance data) via network from another DICOM application
+ * @param presID [out] Contains in the end the ID of the presentation context which
+ * was used in the PDVs that were received on the network. If the
+ * PDVs show different presentation context IDs, this function
+ * will return an error.
+ * @param dataObject [out] Contains in the end the information which was received over
+ * the network
+ * @return EC_Normal if dataset could be received successfully, an error code otherwise
+ */
+ OFCondition receiveDIMSEDataset(T_ASC_PresentationContextID *presID,
+ DcmDataset **dataObject);
+
+ /** clear list of presentation contexts. In addition, any currently selected association
+ * configuration file is disabled.
+ */
+ void clearPresentationContexts();
+
+ /** After negotiation association, this call returns the presentation context belonging
+ * to the given presentation context ID
+ * @param presID [in] The presentation context ID to look for
+ * @param abstractSyntax [out] The abstract syntax (UID) for that ID.
+ * Empty, if such a presentation context does not exist.
+ * @param transferSyntax [out] The transfer syntax (UID) for that ID.
+ * Empty, if such a presentation context does not exist.
+ */
+ void findPresentationContext(const T_ASC_PresentationContextID presID,
+ OFString &abstractSyntax,
+ OFString &transferSyntax);
+
+ /** Check given N-EVENT-REPORT request and dataset for validity. This method is called by
+ * handleEVENTREPORTRequest() before sending the response in order to determine the
+ * DIMSE status code to be used for the response message.
+ * @param request [in] The N-EVENT-REPORT request message data structure
+ * @param reqDataset [in] The N-EVENT-REPORT request dataset received. Might be NULL.
+ * @return DIMSE status code to be used for the N-EVENT-REPORT response.
+ * Always returns STATUS_Success (0). Derived classes should, therefore,
+ * overwrite this method and return a more appropriate value based on the
+ * result of the checks performed.
+ */
+ virtual Uint16 checkEVENTREPORTRequest(T_DIMSE_N_EventReportRQ &request,
+ DcmDataset *reqDataset);
+
+ /** Sends back a C-STORE response on the given presentation context, with the designated
+ * status, fitting the corresponding C-STORE request.
+ * @param presID [in] The presentation context ID to be used.
+ * @param status [in] The storage DIMSE status to be used.
+ * @param request [in] The C-STORE request that should be responded to.
+ * @result EC_Normal if the response could be sent, error otherwise.
+ */
+ virtual OFCondition sendSTOREResponse(T_ASC_PresentationContextID presID,
+ Uint16 status,
+ const T_DIMSE_C_StoreRQ& request);
+
+ /** Helper function that generates a storage filename by extracting SOP Class and SOP
+ * Instance UID from a dataset and combining that with the configured storage directory.
+ * The SOP class is used to create an initial two letter abbreviation for the
+ * corresponding modality, e.g. CT. An example for a storage filename created by this
+ * function is /storage_dir/CT.1.2.3.4.5 for a CT with SOP Instance UID 1.2.3.4.
+ * This function might be overwritten to change the filename behaviour completely. This
+ * function is only called if the SCU is in DCMSCU_STORAGE_DISK mode.
+ * @param dataset [in] The dataset that should be stored to disk
+ * @result Non-empty string if successful, otherwise empty string.
+ */
+ virtual OFString createStorageFilename(DcmDataset *dataset);
+
+ /** Receives a DICOM dataset on a given presentation context ID but does not store it in
+ * memory or disk, thus ignoring it.
+ * @param presID [in] The presentation context to be used
+ * @param request [in] The corresponding C-STORE request
+ * @return EC_Normal if ignoring worked, error code otherwise.
+ */
+ virtual OFCondition ignoreSTORERequest(T_ASC_PresentationContextID presID,
+ const T_DIMSE_C_StoreRQ& request);
+
+ /* Callback functions */
+
+ /** Callback function used for sending DIMSE messages.
+ * @param callbackContext [in] The desired user callback data
+ * @param byteCount [in] Progress bytes count
+ */
+ static void callbackSENDProgress(void *callbackContext,
+ unsigned long byteCount);
+
+ /** Callback function used for receiving DIMSE messages.
+ * @param callbackContext [in] The desired user callback data
+ * @param byteCount [in] Progress bytes count
+ */
+ static void callbackRECEIVEProgress(void *callbackContext,
+ unsigned long byteCount);
+
+private:
+
+ /** Private undefined copy-constructor. Shall never be called.
+ * @param src Source object
+ */
+ DcmSCU(const DcmSCU &src);
+
+ /** Private undefined operator=. Shall never be called.
+ * @param src Source object
+ * @return Reference to this
+ */
+ DcmSCU &operator=(const DcmSCU &src);
+
+ /// Associaton of this SCU. This class only handles 1 association at a time.
+ T_ASC_Association *m_assoc;
+
+ /// The DICOM network the association is based on
+ T_ASC_Network *m_net;
+
+ /// Association parameters
+ T_ASC_Parameters *m_params;
+
+ /// Configuration file for presentation contexts (optional)
+ OFString m_assocConfigFilename;
+
+ /// Profile in configuration file that should be used (optional)
+ OFString m_assocConfigProfile;
+
+ /// Defines presentation context, consisting of one abstract syntax name
+ /// and a list of transfer syntaxes for this abstract syntax
+ struct DCMTK_DCMNET_EXPORT DcmSCUPresContext {
+ /** Default constructor
+ */
+ DcmSCUPresContext()
+ : abstractSyntaxName()
+ , transferSyntaxes()
+ , roleSelect(ASC_SC_ROLE_DEFAULT)
+ {
+ }
+ /// Abstract Syntax Name of Presentation Context
+ OFString abstractSyntaxName;
+ /// List of Transfer Syntaxes for Presentation Context
+ OFList<OFString> transferSyntaxes;
+ /// Role Selection
+ T_ASC_SC_ROLE roleSelect;
+ };
+
+ /// List of presentation contexts that should be negotiated
+ OFList<DcmSCUPresContext> m_presContexts;
+
+ /// Configuration file containing association parameters
+ OFString m_assocConfigFile;
+
+ /// The last DIMSE successfully sent, unresponded DIMSE request
+ T_DIMSE_Message *m_openDIMSERequest;
+
+ /// Maximum PDU size
+ Uint32 m_maxReceivePDULength;
+
+ /// DIMSE blocking mode
+ T_DIMSE_BlockingMode m_blockMode;
+
+ /// AEtitle of this application
+ OFString m_ourAETitle;
+
+ /// Peer hostname
+ OFString m_peer;
+
+ /// AEtitle of remote application
+ OFString m_peerAETitle;
+
+ /// Port of remote application entity
+ Uint16 m_peerPort;
+
+ /// DIMSE timeout
+ Uint32 m_dimseTimeout;
+
+ /// ACSE timeout
+ Uint32 m_acseTimeout;
+
+ /// Storage directory for objects received with C-STORE due to a
+ /// running C-GET session. Per default, the received objects
+ /// are stored in the current working directory.
+ OFString m_storageDir;
+
+ /// Set whether bit preserving storage should be enabled, i.e.\ any objects
+ /// retrieved via C-STORE should be written directly to disk without
+ /// any data correction/re-computation (e.g.\ group length calculations,
+ /// padding, etc.). This is especially interesting for retaining valid
+ /// signatures, and also to receive huge files which cannot be fully received
+ /// in memory. Default is OFFalse (no bit preserving) storage.
+ DcmStorageMode m_storageMode;
+
+ /// Verbose PC mode
+ OFBool m_verbosePCMode;
+
+ /// Dataset conversion mode
+ OFBool m_datasetConversionMode;
+
+ /// Progress notification mode
+ OFBool m_progressNotificationMode;
+
+ /** Returns next available message ID free to be used by SCU
+ * @return Next free message ID
+ */
+ Uint16 nextMessageID();
+};
+
+#endif // SCU_H
+
+/*
+** CVS Log
+** $Log: scu.h,v $
+** Revision 1.41 2012-02-21 08:48:51 joergr
+** Added support for progress notifications while sending and receiving DICOM
+** datasets.
+**
+** Revision 1.40 2011-12-14 11:45:15 uli
+** Make it possible to perfectly build dcmnet and dcmtls a DLLs.
+**
+** Revision 1.39 2011-10-10 14:01:29 uli
+** Moved SCU-specific error condition to the correct place.
+**
+** Revision 1.38 2011-10-04 08:58:14 joergr
+** Added flag that allows for specifying whether to convert a dataset to be
+** transferred to the network transfer syntax. Also removed unused parameters
+** "rspCommandSet" and "rspStatusDetail" from method sendSTORERequest().
+**
+** Revision 1.37 2011-09-28 16:28:20 joergr
+** Added general support for transfer syntax conversions to sendSTORERequest().
+**
+** Revision 1.36 2011-09-28 15:25:34 joergr
+** Return a more appropriate error code in case the dataset to be sent is
+** invalid. This also required to introduce a return value for getDatasetInfo().
+**
+** Revision 1.35 2011-09-28 13:31:56 joergr
+** Added method that allows for clearing the list of presentation contexts.
+**
+** Revision 1.34 2011-09-22 13:49:03 joergr
+** Fixed incorrect comment on return code of some sendXXXRequest() methods.
+**
+** Revision 1.33 2011-09-06 16:11:06 ogazzar
+** Fixed typos in a log commit message.
+**
+** Revision 1.32 2011-09-06 12:57:36 ogazzar
+** Added a function to send N-EVENT-REPORT request and receive a reponse.
+**
+** Revision 1.31 2011-08-25 15:46:18 joergr
+** Further cleanup of minor inconsistencies regarding documentation, parameter
+** names, log output and handling of status details information.
+**
+** Revision 1.30 2011-08-25 15:05:06 joergr
+** Changed data structure for Q/R responses from OFVector to OFList. Also fixed
+** some possible memory leaks and made the FIND/MOVE/GET code more consistent.
+**
+** Revision 1.29 2011-08-25 13:46:28 joergr
+** Fixed minor issues in the API documentation, parameter and method names.
+**
+** Revision 1.28 2011-08-25 09:31:33 onken
+** Added C-GET functionality to DcmSCU class and accompanying getscu
+** commandline application.
+**
+** Revision 1.27 2011-05-25 09:57:54 ogazzar
+** Renamed a function name.
+**
+** Revision 1.26 2011-05-25 09:30:12 ogazzar
+** Added a function to look for a presentation context ID that best matches
+** the abstract syntax and the transfer syntax.
+**
+** Revision 1.25 2011-05-24 13:17:58 onken
+** Added missing default initializiation of role flag in presentation context
+** constructor.
+**
+** Revision 1.24 2011-05-24 08:28:08 ogazzar
+** Added support for role selection negotiation.
+**
+** Revision 1.23 2011-05-19 17:20:09 onken
+** Fixed some documentation.
+**
+** Revision 1.22 2011-05-19 09:57:26 onken
+** Fixed message ID field in C-CANCEL request (should be the one of last
+** request). In case of error status codes in C-MOVE responses, the default
+** behaviour is now to not wait for further responses. Fixed log output level
+** to better fit the messages while receiveing C-MOVE responses. Minor
+** code and comment cleanups. Renamed function parameter in sendMOVEREquest
+** to better reflect the standard.
+**
+** Revision 1.21 2011-05-17 14:43:55 onken
+** Removed some windows line endings.
+**
+** Revision 1.20 2011-05-17 14:34:32 onken
+** Completed doxygen documentation of DcmSCU class. Fixed some minor formatting
+** issues. Fixed CVS log at end of the file. Implemented C-CANCEL message.
+** Fixed some minor formatting issues. Changed C-ECHO implementation to rely
+** on sendDIMSEMesage as the other DIMSE functions do. Changed some public
+** function arguments to const to be more correct. Fixed CVS log at the end
+** of the file. Re-formatted CHANGES log entry from 2011-04-28.
+**
+** Revision 1.19 2011-05-17 14:26:19 onken
+** Implemented C-CANCEL message. Fixed some minor formatting issues.
+** Changed C-ECHO implementation to rely on sendDIMSEMesage as the other
+** DIMSE functions do. Changed some public function arguments to const to be
+** more correct. Fixed CVS log at the end of the scu.cc file.
+**
+** Revision 1.18 2011-04-28 17:50:05 onken
+** Protected public networking functions for creating an association
+** from being called twice. Enhanced protection of DIMSE messaging
+** functions from being called without being connected. Introduced
+** status detail into C-FIND responses (and C-MOVE responses). Was
+** not accessible to the caller before. Minor code cleanups. Added
+** C-MOVE code for retrieving DICOM objects. So far only retrieving
+** on a separate connection is supported. Added function for cleaning
+** up internal memory from destructor. This function also fixes a
+** memory leak in case users call initNetwork more than one time.
+** Added error code returned by functions if SCU is already connected.
+**
+** Revision 1.17 2011-02-16 08:55:13 joergr
+** Fixed issue in sendSTORERequest() when sending a dataset that was created
+** in memory (and which has, therefore, an original transfer of EXS_Unknown).
+**
+** Revision 1.16 2011-02-04 12:57:40 uli
+** Made sure all members are initialized in the constructor (-Weffc++).
+**
+** Revision 1.15 2011-02-04 11:24:40 uli
+** Added private undefined functions where gcc's -Weffc++ warns otherwise.
+**
+** Revision 1.14 2011-02-01 09:01:19 joergr
+** Added "const" specifier to parameter in order to be consistent with the
+** source file (caused warnings/errors with certain compilers).
+**
+** Revision 1.13 2010-10-14 13:17:22 joergr
+** Updated copyright header. Added reference to COPYRIGHT file.
+**
+** Revision 1.12 2010-10-07 12:54:07 joergr
+** Fixed minor Doxygen API documentation issues (added backslash in order to
+** avoid that the short description ends at the first period).
+**
+** Revision 1.11 2010-06-24 09:21:54 joergr
+** Revised comment to make clear that the parameter "presID" shall never be 0
+** for the sendACTIONRequest() method.
+**
+** Revision 1.10 2010-06-22 15:45:27 joergr
+** Introduced new enumeration type to be used for closeAssociation().
+** Further code cleanup. Renamed some methods, variables, types and so on.
+**
+** Revision 1.9 2010-06-17 17:11:27 joergr
+** Added preliminary support for N-EVENT-REPORT to DcmSCU. Some further code
+** cleanups and enhancements. Renamed some methods. Revised documentation.
+**
+** Revision 1.8 2010-06-09 16:09:01 joergr
+** Added preliminary support for N-ACTION to DcmSCU. Some further code cleanups
+** and enhancements.
+**
+** Revision 1.7 2010-06-08 17:54:12 onken
+** Added C-FIND functionality to DcmSCU. Some code cleanups. Fixed
+** memory leak sometimes occuring during association configuration.
+**
+** Revision 1.6 2010-04-29 16:13:28 onken
+** Made SCU class independent from dcmtls, i.e. outsourced TLS API. Added
+** direct API support for sending C-STORE requests. Further API changes and
+** some bugs fixed.
+**
+** Revision 1.5 2009-12-21 17:00:32 onken
+** Fixed API documentation to keep doxygen quiet.
+**
+** Revision 1.4 2009-12-21 15:33:55 onken
+** Added documentation and refactored / enhanced some code.
+**
+** Revision 1.3 2009-12-17 09:12:10 onken
+** Fixed other scu and scp base class compile issues.
+**
+** Revision 1.2 2009-12-17 09:05:15 onken
+** Fixed typo resulting in build failure.
+**
+** Revision 1.1 2009-12-17 09:02:44 onken
+** Added base classes for SCU and SCP implementations.
+**
+** Revision 1.2 2009-12-02 14:26:05 uli
+** Stop including dcdebug.h which was removed.
+**
+** Revision 1.1 2008-09-29 13:51:52 onken
+** Initial checkin of module dcmppscu implementing an MPPS commandline client.
+**
+*/
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/dcmtkpp.git
More information about the debian-med-commit
mailing list