Whilst I don't have access to a DICOM based camera just at the moment (but will have soon), I have started writing some code with a view to making a start on this project.
I have written a base class (TDicomVR) for a DICOM value representation (VR). I would intend deriving classes for the various types (27 ?) of value representations from this base class.
To date I have written three such derived classes: TDicomVRUI (unique identifier), TDicomVRUL (unsigned long) and TDicomVRUS (unsigned short). These were picked to enable me to build a C-Echo Request class (TEchoReqBody).
I have not yet written any code to actually send this request out on the LAN/WAN nor to capture and decode the expected response.
The following code includes the above classes and uses them to build a C-Echo Request as per the book referenced in above posts and the string it builds matches that given in a table in the book with the exception of 1 byte which I think represents an error in the book.
Code: Select all
// dicom.prg
// initial experimental code aiming for DICOM capabilities from software written in [x]Harbour.
#include "hbclass.ch"
FUNCTION Main()
obj_ThisBody := TCEchoReqBody():New( "0020" )
ShowByteStream( obj_ThisBody:ByteStream() )
InKey( 0 )
QUIT
/**************************************************************\
* *
* CLASS TCEchoReqBody - DICOM C-Echo Request (DICOM-ping) *
* *
* Used to verify one DICOM AE is connected to another *
* *
\**************************************************************/
CLASS TCEchoReqBody
DATA obj_GroupLength
DATA obj_AffServClassUID
DATA obj_CommandField
DATA obj_MessageID
DATA obj_DataSetType
METHOD New( var_ID )
METHOD ByteStream()
ENDCLASS
METHOD New( var_ID ) CLASS TCEchoReqBody
LOCAL int_GroupLength
::obj_AffServClassUID := TDicomVRUI():New( "0000", "0002", "1.2.840.10008.1.1" )
::obj_CommandField := TDicomVRUS():New( "0000", "0100", "0030" )
::obj_MessageID := TDicomVRUS():New( "0000", "0110", var_ID )
::obj_DataSetType := TDicomVRUS():New( "0000", "0800", "0101" )
int_GroupLength := ::obj_AffServClassUID:Length() + ::obj_CommandField:Length() + ::obj_MessageID:Length() + ::obj_DataSetType:Length()
::obj_GroupLength := TDicomVRUL():New( "0000", "0000", int_GroupLength )
RETURN self
METHOD ByteStream() CLASS TCEchoReqBody
RETURN ::obj_GroupLength:ByteStream() + ::obj_AffServClassUID:ByteStream() + ::obj_CommandField:ByteStream() + ;
::obj_MessageID:ByteStream() + ::obj_DataSetType:ByteStream()
/**************************************************************\
* *
* CLASS TDicomVR - DICOM Value Representation *
* *
* This is the base class from which the various DICOM value *
* representations (there are currently 27 defined) are *
* derived. *
* *
\**************************************************************/
CLASS TDicomVR
DATA str_Group
DATA str_Element
DATA str_DataLength
DATA str_Data
METHOD New() CONSTRUCTOR
METHOD SetGroupHex( str_HexGroup )
METHOD SetElementHex( str_HexElement )
METHOD SetLength( int_Length )
METHOD ByteStream()
METHOD Length()
ENDCLASS
METHOD New() CLASS TDicomVR
RETURN self
METHOD SetGroupHex( str_HexGroup ) CLASS TDicomVR
LOCAL int_Group
int_Group := HexToNum( str_HexGroup )
::str_Group := Chr( int_Group % 256 ) + Chr( Int( int_Group / 256 ) )
RETURN nil
METHOD SetElementHex( str_HexElement ) CLASS TDicomVR
LOCAL int_Element
int_Element := HexToNum( str_HexElement )
::str_Element := Chr( int_Element % 256 ) + Chr( Int( int_Element / 256 ) )
RETURN nil
METHOD SetLength( int_Length ) CLASS TDicomVR
LOCAL int_ThisByte
int_ThisByte := int_Length % 256
::str_DataLength := Chr( int_ThisByte )
int_Length := ( int_length - int_ThisByte ) / 256
int_ThisByte := int_Length % 256
::str_DataLength += Chr( int_ThisByte )
int_Length := ( int_length - int_ThisByte ) / 256
int_ThisByte := int_Length % 256
::str_DataLength += Chr( int_ThisByte )
int_Length := ( int_length - int_ThisByte ) / 256
int_ThisByte := int_Length % 256
::str_DataLength += Chr( int_ThisByte )
RETURN nil
METHOD ByteStream() CLASS TDicomVR
RETURN ::str_Group + ::str_Element + ::str_DataLength + ::str_Data
METHOD Length() CLASS TDicomVR
RETURN 8 + Len( ::str_Data )
/******************************************************************\
* *
* CLASS TDicomVRUI - DICOM Value Representation Unique Identifier *
* *
* A character string containing a UID that is used to uniquely *
* identify a wide variety of items. *
* *
\******************************************************************/
CLASS TDicomVRUI FROM TDicomVR
METHOD New( str_Data )
ENDCLASS
METHOD New( str_Group, str_Element, str_Data ) CLASS TDicomVRUI
LOCAL int_Length
::SetGroupHex( str_Group )
::SetElementHex( str_Element )
// maximum acceptable length for data in a UI is 64 characters
int_Length := Len( str_Data )
IF int_Length > 64
str_Data := SubStr( str_Data, 1, 64 )
int_Length := 64
ENDIF
// data must be an even number of bytes - if odd pad with 0x00
IF int_Length % 2 == 1
str_Data += Chr( 0 )
int_Length += 1
ENDIF
// set data and length
::str_Data := str_Data
::SetLength( int_Length )
RETURN self
/**************************************************************\
* *
* CLASS TDicomVRUL - DICOM Value Representation Unsigned Long *
* *
* Unsigned binary integer 32 bits (4 bytes) in length *
* *
\**************************************************************/
CLASS TDicomVRUL FROM TDicomVR
METHOD New( str_Group, str_Element, var_Data ) CONSTRUCTOR
METHOD Length()
ENDCLASS
METHOD New( str_Group, str_Element, var_Data ) CLASS TDicomVRUL
LOCAL int_Data
::SetGroupHex( str_Group )
::SetElementHex( str_Element )
::SetLength( 4 )
IF ValType( var_Data ) == "C"
int_Data := HexToNum( var_Data )
ELSE
int_Data := var_Data
ENDIF
int_ThisByte := int_Data % 256
::str_Data := Chr( int_ThisByte )
int_Data := ( int_Data - int_ThisByte ) / 256
int_ThisByte := int_Data % 256
::str_Data += Chr( int_ThisByte )
int_Data := ( int_Data - int_ThisByte ) / 256
int_ThisByte := int_Data % 256
::str_Data += Chr( int_ThisByte )
int_Data := ( int_Data - int_ThisByte ) / 256
int_ThisByte := int_Data % 256
::str_Data += Chr( int_ThisByte )
RETURN self
METHOD Length() CLASS TDicomVRUL
RETURN 12
/**************************************************************\
* *
* CLASS TDicomVRUS - DICOM Value Representation Unsigned Short *
* *
* Unsigned binary integer 16 bits (2 bytes) in length *
* *
\**************************************************************/
CLASS TDicomVRUS FROM TDicomVR
METHOD New( str_Group, str_Element, var_Data ) CONSTRUCTOR
METHOD Length()
ENDCLASS
METHOD New( str_Group, str_Element, var_Data ) CLASS TDicomVRUS
LOCAL int_Data
::SetGroupHex( str_Group )
::SetElementHex( str_Element )
::SetLength( 2 )
IF ValType( var_Data ) == "C"
int_Data := HexToNum( var_Data )
ELSE
int_Data := var_Data
ENDIF
::str_Data := Chr( int_Data % 256 ) + Chr( Int( int_Data / 256 ) )
RETURN self
METHOD Length() CLASS TDicomVRUS
RETURN 10
FUNCTION ShowByteStream( str_Stream )
LOCAL int_StreamLength
int_StreamLength := Len( str_Stream )
FOR ii = 1 TO int_StreamLength
? "Byte", Str( ii, 3, 0 ), "=>", Str( Asc( str_Stream[ii] ), 3, 0), "Decimal or", NumToHex( Asc( str_Stream[ii] ), 2 ), "hex"
NEXT