Attribute Sets
The following class diagram shows the classes that are involved in the manipulation of datasets:
DICOM network services and file service each deal with information objects. These objects are sets of attributes that provide information about the real world entities being acted upon by the service. Such information objects are represented by the MCattributeSet class.
Message and file objects are related to specific DICOM service-command pairs (i.e. SOP classes). Normally you construct new instances of message objects by providing the service name that identifies the DICOM information object and the DIMSE command that will be used with the information object. The command can be any of the valid values defined by the MCcommand class. The service name must be one of the services defined in the Services Profile (normally mergecom.srv) file; if not, a warning message will be logged.
DICOM data elements are represented by the MCdataElement class and they are identified by a unique tag that is either represented as an integer for standard elements or as an instance of the MCtag class for private elements. A DICOM attribute contains the value of a DICOM data element for a particular DICOM object. An attribute has assigned to it a value representation ( MCvr class), a value multiplicity (n[-n]) and a value type (1, 1C, 2, 2C, 3). The attribute values can be handled in the context of an attribute set (message, file or sequence item) through various methods of the MCattributeSet class.
DICOM messages sent across a network connection on an association are represented by the MCdimseMessage class. A DIMSE message contains a command set and optionally, a data set. The command set consists of group 0 attributes containing header information used by the DICOM DIMSE service. The data set contains the DICOM data being exchanged. When receiving messages the MCassociation.readMessage() method, creates the newly created message object representing the received message. When sending message objects must be created by using one of the MCdimseMessage constructors.
The DICOM Value Representation SQ is used to indicate a DICOM attribute that contains a value that is a sequence of items. Each item in the sequence is an attribute set represented by the MCitem class. Each of the attribute sets can also contain attributes that have a VR of SQ. This capability allows the nesting of attribute sets, or the definition of 'container' objects (such as folders, film boxes, directories, etc.).
The DICOM media storage service deals with file objects that contain a set of file meta information and a data set. These file objects are represented by the MCfile class. The MCfile class together with MCdicomDir class provide access to file sets on DICOM media.
Once your application has a populated message object, either one that you have built or one that you have received and are about to parse, the toolkit supplies DICOM message validation functionality. The MCattributeSet class provides validation methods for both the entire attribute set or just one attribute, against the DICOM Standard's specification for its service and command pair.
One of the files supplied with the toolkit is the message.txt file. This file contains a listing of all the messages supported by the toolkit and the parameters they are validated against. message.txt is a useful guide in your application development because it specifies the attributes that can make up the object instance portion of each message type (service-command pair) and is often easier to use as a quick reference than paging through two or three parts of the DICOM Standard. message.txt also specifies the contents of items and files. Remember though that the DICOM Standard is the final word and that message.txt has its limitations as described further below.
The validate methods do not validate the attributes that make up the command portion of a DICOM message. Command set attributes (attributes with a group number less than 0008) are also not specified in message.txt. The Merge DICOM Toolkit Library sets as many of the command group attributes as possible automatically.
While Merge DICOM validation is not foolproof, it is very useful and will catch many standard violations. It validates the following conditions:
As mentioned, Merge DICOM Toolkit does not capture all standard violations, and the DICOM Standard itself should be considered the final word when validating a message. Important limitations of Merge DICOM validation include:
An example of the use of the validate method follows. The example assumes a MCdimseMessage
(msg) was just received and the intent is to validate the message.
MCvalidationError ve = msg.validate( MCvalidationLevel.Errors_Only ); while( ve != null ) { System.out.println( ve ); ve = msg.getNextValidationError(); }
In this example, the application validates the MCdimseMessage object msg at Error Validation Level hat reports only errors. Other validation levels may be used to report both warnings and errors or errors, warnings, and informational messages. If the attribute set validates, the validate()
method returns null
, otherwise the returned object represents the first validation error. After the validate()
method returned a non-null value, the application can call
getNextValidationError() to obtain subsequent validation errors.
Each MCvalidationError instance provides the following information:
String
describing the error
The toString()
method of the MCvalidationError class provides a convenient way to display a validation error, e.g:
Attribute tag: (00100010) Value Number: 1 Description: Invalid value for this tag�s VR Error Number: 28
It is on the initial call to the validate
method that all the validation takes place and that the results of the validation for the entire message are logged to the message log file. Subsequent calls to the getNextValidationError() method simply step through the results of the validation, passing additional errors found back to the application.
A sample log file validation report follows.
01-11 13:52:09.00 7919 MC3 T5: (0008,0005) VI: Unable to check condition 01-11 13:52:09.00 7919 MC3 T5: (0008,0023) VI: Unable to check condition 01-11 13:52:09.00 7919 MC3 T5: (0008,0033) VI: Unable to check condition 01-11 13:52:09.00 7919 MC3 T5: (0010,1010) VE: [41Y ] Invalid value for this tag�s VR 01-11 13:52:09.00 7919 MC3 T5: (0018,0010) VW: Invalid attribute for service 01-11 13:52:09.00 7919 MC3 T5: (0018,0015) VE: Required attribute has no value 01-11 13:52:09.00 7919 MC3 T5: (0018,0020) VW: Invalid attribute for service 01-11 13:52:09.00 7919 MC3 T5: (0018,0021) VW: Invalid attribute for service 01-11 13:52:09.00 7919 MC3 T5: (0018,0022) VW: Invalid attribute for service 01-11 13:52:09.00 7919 MC3 T5: (0018,0023) VW: Invalid attribute for service 01-11 13:52:09.00 7919 MC3 T5: (0018,0050) VW: Invalid attribute for service 01-11 13:52:09.00 7919 MC3 T5: (0018,0080) VW: Invalid attribute for service 01-11 13:52:09.00 7919 MC3 T5: (0018,0081) VW: Invalid attribute for service 01-11 13:52:09.00 7919 MC3 T5: (0018,0082) VW: Invalid attribute for service 01-11 13:52:09.00 7919 MC3 T5: (0018,0084) VW: Invalid attribute for service 01-11 13:52:09.00 7919 MC3 T5: (0018,0085) VW: Invalid attribute for service 01-11 13:52:09.00 7919 MC3 T5: (0018,0091) VW: Invalid attribute for service 01-11 13:52:09.00 7919 MC3 T5: (0018,1041) VW: Invalid attribute for service 01-11 13:52:09.00 7919 MC3 T5: (0018,1060) VW: Invalid attribute for service 01-11 13:52:09.00 7919 MC3 T5: (0018,1250) VW: Invalid attribute for service 01-11 13:52:09.00 7919 MC3 T5: (0018,5101) VE: Required attribute has no value 01-11 13:52:09.00 7919 MC3 T5: (0020,0032) VW: Invalid attribute for service 01-11 13:52:09.00 7919 MC3 T5: (0020,0037) VW: Invalid attribute for service 01-11 13:52:09.00 7919 MC3 T5: (0020,0052) VW: Invalid attribute for service 01-11 13:52:09.00 7919 MC3 T5: (0020,0060) VI: Unable to check condition 01-11 13:52:09.00 7919 MC3 T5: (0020,1040) VW: Invalid attribute for service 01-11 13:52:09.00 7919 MC3 T5: (0020,1041) VW: Invalid attribute for service 01-11 13:52:09.00 7919 MC3 T5: (0028,0006) VI: Unable to check condition 01-11 13:52:09.00 7919 MC3 T5: (0028,0030) VW: Invalid attribute for service 01-11 13:52:09.00 7919 MC3 T5: (0028,0034) VI: Unable to check condition 01-11 13:52:09.00 7919 MC3 T5: (0028,1101) VI: Unable to check condition 01-11 13:52:09.00 7919 MC3 T5: (0028,1102) VI: Unable to check condition 01-11 13:52:09.00 7919 MC3 T5: (0028,1103) VI: Unable to check condition 01-11 13:52:09.00 7919 MC3 T5: (0028,1201) VI: Unable to check condition
Notice in this log file that all warnings and informational messages are also logged. This is always the case, although the first violation returned to the application was an error because Validation Level 1 was specified. The message log agrees in that the first VE (Validation Error) logged is for the attribute Patient�s Age (0010,1010). The log states that the message contains '41Y' as the value for this attribute. Part 6 of DICOM clearly states that this attribute has a value representation of AS (Age String) and part 5 states that for this VR the value should have a leading zero and be represented as '041Y'. There is also one other error flagged in this message, the required attribute View Position (0018,5101) had no value.
If you wish to validate only a single attribute, you may use the validateAttribute() method of the MCattributeSet
class. The method works exactly as the validate
described above with the exception that you provide a tag parameter to identify the attribute to validate.
When DICOM messages are exchanged over a network, they are in an encoded format specified by the DICOM standard and the negotiated transfer syntax. The toolkit calls this encoded format a message stream and supplies methods that allow your applications to work directly with message streams.
When your application builds or parses attribute sets as described earlier, it works with the MCattributeSet objects. These MCattributeSet
objects abstract and encapsulate the DICOM message and hides its details from the developer. When sending a DICOM message over the network, the toolkit internally creates a DICOM message stream that is passed over the network. This message stream is an encoded stream of bytes that follows all the rules of DICOM.
The toolkit also supplies methods to generate and read DICOM message streams directly. The writeToStream() method creates a DICOM stream from the contents of an attribute set object (encoding), while the readFromStream() method populates an attribute set object from a message stream (decoding). It is important that the transfer syntax specified in this call is identical to that used to create the stream or the call will fail with an error.
Message streams can be very valuable to your application for debugging and validation purposes. By writing DICOM message streams out to a binary file, you have a compact and reproducible representation of a message. You can directly examine the binary message stream to see how the data would be sent over the network. Also, you can read this binary file in again later to reconstruct the original message object. Once you have the message object you can use the usual toolkit methods to examine or alter its contents.
Writing Raw Attribute Sets Received from Network to DICOM Files
A common usage of the DICOM toolkit is to save incoming (received from network) message as a DICOM Part 10 file format using MCfile class. When reading a DICOM message from network, attributes in a message are parsed, validated before storing them in memory, and then later written out from memory objects to a DICOM file. With message that has many level of nested items, the parsing/creating of DICOM attributes in memory have a significant impact in performance. Very often, the intention of the Storage SCP applciation is to write out the received mesage content to a DICOM file without the need to modify the attributes of the message. When such a case is needed, it is best to just save the raw streamed content as quickly and efficiently as possible. The following code snippet shows how to save an incoming message into a DICOM file without parsing: (For detail implementation, please refer to samples\mcwsamples\StorageSCP.java in the distribution folder.)
// To read message from the association and save the raw // content without parsing the message's dataset, use // MCassociation.readMessageToTag() to read only the "group 0" // part of the message instead of using // MCassociation.readMessage() to read the entire message content. MCreadStatus status = association.readMessageToTag( -1, 0x00010000 ); // (0001,000) tag is just after group 0 // create a dummy MCfile to store DICOM preamble and meta header MCfile file = new MCfile(); file.setTransferSyntax(status.message.getTransferSyntax()); file.setValue( MCdicom.FILE_META_INFORMATION_VERSION, new byte[]{ 0, 1 } ); file.setValue( MCdicom.MEDIA_STORAGE_SOP_CLASS_UID, status.message.getAffectedSopClassUid() ); file.setValue( MCdicom.MEDIA_STORAGE_SOP_INSTANCE_UID, status.message.getAffectedSopInstanceUid() ); file.setValue( MCdicom.IMPLEMENTATION_CLASS_UID, MCconfig.getStringConfigValue("IMPLEMENTATION_CLASS_UID")); file.setValue( MCdicom.IMPLEMENTATION_VERSION_NAME, MCconfig.getStringConfigValue("IMPLEMENTATION_VERSION")); // create a file based on SOP Instance UID of the instance String fname = status.message.getAffectedSopInstanceUid() + ".dcm"; // create file output channel WritableByteChannel sink = new FileOutputStream(fname).getChannel(); // stream out this dummy P10 file (only has DICOM preamble and meta header, no data set) file.writeP10File(sink); file.dispose(); // continue reading data set from original message and write directly to the same output stream association.continueReadToStream( status.message, sink ); sink.close(); status.message.dispose();
Note: due to the raw saving technique, non DICOM compliant message will be saved as is and no warning will be issued (due to no parsing of message).