Tuesday, June 29, 2010

Fixed bugs and new features in release 1.0.1.7

Fixed bugs and new features in release 1.0.1.7 address all kind of issues that were found lately.
The most important feature that was added this build is the ability to receive N-EVENT-REPORT commands in the Requester class DCXREQ that represents the SCU.
The methods new methods CommitFilesAndWaitForResult and CommitInstancesAndWaitForResult in the requester class enable us to wait for the commit result on the same association that sent the request.
The following code shows how to build a Storage Commitment SCU with the new methods.


        [Test]
        public void CommitFilesAndWaitForResultOnSameAssoc()
        {
            bool status;
            bool gotIt;
            String fullpath = "SCMTEST";
            Directory.CreateDirectory(fullpath);
            CommonTestUtilities.CreateDummyImages(fullpath, 1, 1);
            string succeededInstances;
            string failedInstances;
            string MyAETitle = System.Environment.GetEnvironmentVariable("COMPUTERNAME");
            DCXREQ r = new DCXREQ();
            //r.OnCommitResult += new IDCXREQEvents_OnCommitResultEventHandler(a1.accepter_OnCommitResult);
            r.OnFileSent += new IDCXREQEvents_OnFileSentEventHandler(OnFileSent);
            r.Send(MyAETitle, IS_AE, IS_Host, IS_port, fullpath + "\\SER1\\IMG1", out succeededInstances, out failedInstances);
            DCXOBJ obj = new DCXOBJ();
            obj.openFile(fullpath + "\\SER1\\IMG1");
            string sop_class_uid = obj.getElementByTag((int)DICOM_TAGS_ENUM.sopClassUid).Value.ToString();
            string instance_uid = obj.getElementByTag((int)DICOM_TAGS_ENUM.sopInstanceUID).Value.ToString();
            string transactionUID = r.CommitFilesAndWaitForResult(MyAETitle, IS_AE, IS_Host, IS_port, fullpath + "\\SER1\\IMG1",
                5, out gotIt, out status, out succeededInstances, out failedInstances);
            Directory.Delete(fullpath, true);


            Assert.True(status, "Commit result is not success");
            Assert.That(failedInstances.Length == 0);
            Assert.AreEqual(succeededInstances, sop_class_uid + ";" + instance_uid + ";");
        }



It's important to remember that the SCP may send the result on a separate association so unless you're absolutely sure about the way the SCP will behave, you should be prepared to receive the result with your accepter that can either run in a separate thread or you can set it up just for this purpose.
Here's a little code snippet that does exactly this. It prepares an accepter, then send the request and then wait for the result either to be send instantly in the same association or on a separate one. Note that this is a NUnit test function with a lot of verification code. To simply send the commit and wait for the result you should call the send and wait methods and if didn't get the result, wait on the accepter.


        class SyncAccepter
        {
            public bool _gotIt = false;
            public bool _status = false;
            public string _transaction_uid;
            public string _succeeded_instances;
            public string _failed_instances;
            public DCXACC accepter;
            public string MyAETitle;


            public void accepter_OnCommitResult(
                bool status,
                string transaction_uid,
                string succeeded_instances,
                string failed_instances)
            {
                _gotIt = true;
                _status = status;
                _transaction_uid = transaction_uid;
                _succeeded_instances = succeeded_instances;
                _failed_instances = failed_instances;
            }


            public SyncAccepter()
            {
                accepter = new DCXACC();
                accepter.OnCommitResult += new IDCXACCEvents_OnCommitResultEventHandler(accepter_OnCommitResult);
                MyAETitle = System.Environment.GetEnvironmentVariable("COMPUTERNAME");
                accepter.WaitForConnection(MyAETitle, 104, 0);
            }


            public bool WaitForIt(int timeout)
            {
                if (accepter.WaitForConnection(MyAETitle, 104, timeout))
                    return accepter.WaitForCommand(timeout);
                else
                    return false;
            }
        }


        [Test]
        public void CommitFilesSameThread()
        {


            // Create test files
            String fullpath = "SCMTEST";
            Directory.CreateDirectory(fullpath);
            CommonTestUtilities.CreateDummyImages(fullpath, 1, 1);


            // Send test files
            string MyAETitle = System.Environment.GetEnvironmentVariable("COMPUTERNAME");
            DCXREQ r = new DCXREQ();
            string succeededInstances;
            string failedInstances;
            r.Send(MyAETitle, IS_AE, IS_Host, IS_port, fullpath + "\\SER1\\IMG1", out succeededInstances, out failedInstances);
            Assert.That(failedInstances.Length == 0);
            Assert.That(succeededInstances.Length > 0);


            // Commit files and wait for result on separate association for 30 seconds
            SyncAccepter a1 = new SyncAccepter();
            r.CommitFiles(MyAETitle, IS_AE, IS_Host, IS_port, fullpath + "\\SER1\\IMG1");
            a1.WaitForIt(30);


            if (a1._gotIt)
            {
                // Check the result


                Assert.True(a1._status, "Commit result is not success");
                Assert.That(a1._failed_instances.Length == 0);


                DCXOBJ obj = new DCXOBJ();
                obj.openFile(fullpath + "\\SER1\\IMG1");
                string sop_class_uid = obj.getElementByTag((int)DICOM_TAGS_ENUM.sopClassUid).Value.ToString();
                string instance_uid = obj.getElementByTag((int)DICOM_TAGS_ENUM.sopInstanceUID).Value.ToString();
                Assert.AreEqual(a1._succeeded_instances, sop_class_uid + ";" + instance_uid + ";");
            }
            else
                Assert.Fail("Didn't get commit result");


            /// Cleanup
            Directory.Delete(fullpath, true);
        }
    }




195codefixed1.0.1.6N-EVENT-REPORT Response for Storage Commit always return error to SCPedit
Returns status 0x00fe always.
196codefixed1.0.1.6DCXOBJ TransferSyntax (set) method is reverted after saveFileedit
Do the following: 1) Change Transfer Syntax 2) Save file 3) Load the file 4) Check the meta header transfer syntax -> Same as before (1) Reason, xfer member of DCXOBJ doesn't change at (1) even that the object is changed.
197codefixed1.0.1.6Can't get printer info if printer is only coloredit
Reported by L&T (6.2) If printer negotiates only basic color print management, then GetPrinterInfo will not work.
198newfixed1.0.1.6Get Storage Commit result N-EVENT-REPORT on requester associationedit
SCM SCP may send N-EVENT-REPORT with SCM Result on same association that the SCM SCU initiated and sent the request N-ACTION. It may also send it on a new association that the SCP initiates. SCM SCU may disconnect association immediatly after recieving the N-ACTION Response from the SCP. This change allows both ways to be implemented in RZDCX by adding the CommitInstancesAndWaitForResult andCommitFilesAndWaitForResult. These methods send the request and then wait for the N-EVENT-REPORT
199codefixed1.0.1.6get property SamplesPrePixel in DCXPrintImageBox may return wrong valuedit
Casting between int, enum and ushort may cause wrong interpratation of get value. Resolve by explicit cast in method.
200codefixed1.0.1.6Get TM (Time) data element is not identical to setedit
Get time of 13:04:59.123 doesn't get expected result. See unit test.change control: return value is up to millisecond i.e. formated as hh:mm:dd.ttt

Monday, June 21, 2010

RZDCX DICOM Toolkit 1.0.1.6 Released

The new build of RZDCX addresses two bugs found in the Print module and a fix to the C# MWL test application.



#TypeStatusCreatedBySubsysChangedAssignedSvrPriTitle 
194codefixedJun 20zroniRZDCXJun 20zroni11SPS sequence is not added to query object in C# MWL Test applicationedit
193codefixedJun 20zroniRZDCXJun 20zroni11Pixel data size in color print is wrongedit
192codefixedJun 20zroniRZDCXJun 20zroni11LANDSACPE instead of LANDSCAPE in Print APIedit

Thursday, June 17, 2010

FIX Problem: RZDCX not showing in the Add reference tab in Visual Studio 2008

Unfortunately, a problem was found in the registration of the win32 build 1.0.1.4.
The problem seams to be related to win32/x64 registry entry mix-up in the type library registry entry leading to the component not showing when trying to add a reference in C# projects.
This problem is showing in build 1.0.1.4 and 1.0.1.3.

The problem is now fixed in build 1.0.1.5

Note that for using the win32 component on x64 machines you should use the SysWOW32o64 version of regsvr32.exe

Thursday, June 3, 2010

RZDCX DICOM Toolkit 1.0.1.4 Released

A new release of RZDCX - Fast Strike DICOM Toolkit is now available on RZ web site at http://www.roniza.com/products/downloads.

This version include some bug fixes, new features and new sample applications.

The complete documentation can be found online in the following link: http://www.ganilo.net/RZDCX/1.0.1.4/index.html

A table with a list of the changes and modifications, some of them were already available in release 1.0.1.3 can be found at the end of this post.

I'm going to take this opportunity to start a new thread in this blog that discusses professional aspects of software development in general and DICOM programming specifically.

Today I'm going to talk a bit about the DICOM File Meta Infomration AKA File Meta Header.

When saving a DICOM object to file, the application that saves the file should add to the DICOM object a header.
This header is comprised of:

  1. A preamble - 128 bytes that can be anything but are usually set to 0 (0x00),
  2. The DICOM magic number i.e. 4 bytes equals to DICM and
  3. A set of DICOM attributes of group 0002 (0x0002) encoded using Expilict Little Endian Transfer Syntax.
It is important to remember that the file meta information should be re-created by the saving application and that it is not part of the DICOM object and thus should be removed before sending the object by C-STORE for example.

The roles of the elements in group 0002 are: 
  1. To specify the transfer syntax that is used for the DICOM object itself that may be different then LEE,
  2. To identify the saving application.
The first role is obvious but why do we need #2? Well, suppose that you find out that you have a problem with files that come from a specific application, so you can add code to your application that checks the application UID and version in the file meta header and cope with the problem accordingly.

In RZDCX, you can get the elements of the header simply by writing the following code (C#):
DCXOBJ obj = new DCXOBJ();

// Read the file
obj.openFile("DCMFILE");
// Get the header
DCXOBJ meta = obj.FileMetaInfo;

Now you can read elements from the header using getElementByTag.

DCXELM transferSyntax = meta.getElementByTag((int)DICOM_TAG_ENUM.TransferSyntaxUID);

So now you're asking how do I change it and the answer is you don't. Why not? because if you change it and someone finds a problem with the DICOM objects you create how can he know that it was generated by RZDCX :)






186todoclosed1.0.1.2COLOR Print is not workingedit
Reported by XX: Color print session is not working with current RZ evaluation version. It throws an exception while Creating Page. Both Color and Gray Scale printing should be possible
185newclosedAdd support for PRINTER SOP Class as SCUedit
Requested by XX
184codeclosedMPPS Set Request returns success even if the MPPS is deletededit
Need to verify following report:RZ API IDCXREQ::SendMPPSNSetRquest is giving the status as success in case of updating the deleted instance in server. The expected behavior is it should throw an exception or else there should be some callback mechanism which tells us the status as failure. Our suggestion: RZ needs to provide an implementation to verify the existence of created instance in server. OR IDCXREQ::SendMPPSNSetRquest should throw an exception in case of trying to update the deleted instance OR RZ should provide call back mechanism which gives the status as FAILURE in above scenario.
183newclosedModify MWL SCU to include date range queryedit
Use for example e->Value = "20100101-20101231";
182codeclosedCreate single project example with SCM SCU and SCM SCPedit
make a single example program with both listener and a Storage Commit SCU that shows how to send and receive storage commit requests and results
181newclosedAdd license restricted API to modify META header elementsedit
Check special license flag to allow/block this option. Add parameter to special call of save to file
180codeclosed1.0.1.1getElementByTag does not return NULL if element not found in objectedit
Needs verification. Code review suggests this bug doesn't exist. Reported by XXXX.
179newclosedAdd example for private tags in RZDCX Sample Applicationsedit
A simple example, can be added to create DICOM object example
178todoclosedAdd example of Multi-Frame Image creationedit
For Bankuzi