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 | Expand
// 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()ENDCLASSMETHOD 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 selfMETHOD 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() ENDCLASSMETHOD New() CLASS TDicomVR RETURN selfMETHOD 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 nilMETHOD 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 nilMETHOD 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 nilMETHOD ByteStream() CLASS TDicomVR RETURN ::str_Group + ::str_Element + ::str_DataLength + ::str_DataMETHOD 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 )ENDCLASSMETHOD 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()ENDCLASSMETHOD 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 selfMETHOD 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()ENDCLASSMETHOD 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 selfMETHOD 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
Any comments / suggestions happily received.