Wednesday, September 1, 2010

HL7 Mapper may not save configuration due to missing access to INFORMATION_SCHEMA

Symptom: When trying to save configuration, an error message is shown with the following text:
"Object Reference not set to an instance of an object"
Reason: The HL7 Mapping application relies on SQL Server INFORMATION_SCHEMA to generate the database view dynamically. Apparently there has been some compatibility issues with this feature since SQL Server 2005 and sometimes there's no access to this view.
The view may be accessible on one catalog and not accessible on another even if they are on the same table.
If this

Solution: We are working on a fix to this problem.

Ticket 224: HL7 Mapper may not save configuration due to missing system tables



Sunday, August 29, 2010

Healthcare Software Integration Engines are grossly over priced


"Healthcare Software Integration Engines are grossly over priced"
These are not my words. This is an exact quote of a very well recognized healthcare IT professional.
And I fully agree. After all, what's the big deal, Aha? It's just moving fields and attributes from one place to another, change some formats maybe, some XML tricks, database. Really, no big deal, right?

You maybe think to yourself that I'm joking but I'm not. After all, I have developed an HL7 Integration Engine of my own that I'm very proud of, HL7Kit Pro. This little devil is not a show off. It's a tool designed to do one thing - build a two way road, a bridge, between your SQL Server database and HL7. 

In modern design, less is more. When going over the features list of some of these products, I can't avoid the comparison with a 'do it all 149 peaces hardware tool kit' perfect package on TV Shop America.

When shopping for an integration engine it's important to remember that there's always the other road. The one of developing custom interfaces. Good programmers nowadays are not that expensive to hire and the software tools we have today are efficient and easy to use. So when you are facing an integration challenge, you should compare the options and don't get tempted to purchase a multi K$ package bundled with a big unknown figure of expert consultation, maintenance and support fees to feed the monster.

Instead, take the right HL7 tool in your hands and finish the work in no time.

The new 1.3 release of HL7Kit Pro adds hot folders integration. The runtime service can be configured to digest HL7 messages from an inbox folder and write outgoing messages to an Out-box directory.

There's also better support for running multiple HL7 runtime services on a single computer, each using it's own queue.

... And there's many bug fixes and little improvements like the ability to read and write directly from views ...

All in all, the new release is a direct continuation of the old one and that's simply because there's no need for major changes when a tool is doing the work. Simple work, but still work.

Ask every professional: working with quality tools designed for the task is great fun.

To get you started, there are two example projects on the HL7 support page. One for incoming messages and one for outgoing messages that you can download and try with the evaluation copy.


Monday, August 23, 2010

Registry free COM using Side By Side Assemblies

Registry Free COM
Did you know that you don't have to register a COM object in order to use it?
Well, I didn't. Until recently, when I was searching for something in the internet and bumped into this article in the MSDN library.
What this means, to me at least, is that there's no need to register RZDCX and thus it can be used in many applications that require no installation, because, as you know, to register a COM object in the registry you need administrative rights but to run an application from a CD you don't have to.

So, this post is all about using RZDCX.DLL with no installation.
For this purpose, we'll use one of the sample applications, for example the C# example EchoSCUExample.
All the examples with their source code can be downloaded from the DICOM download page.




In order to eliminate the use of registry, we'll create a manifest file for EchoSCUExample.exe and name it EchoSCUExample.exe.manifest

Next, we need to create a manifest file for RZDCX.DLL as well, name it rzdcx.X.manifest and save it in the same folder. Note that this is the manifest of the comming version 2.0.0.0 of RZDCX so some of the interfaces in it are not yet available in version 1.0.1.7 that is currently available on the web site.

Copy all four files (rzdcx.dll, rzdcx.X.manifest, EchoSCUExample.exe and EchoSCUExample.exe.manifest)  to a single directory, unregister RZDCX.DLL (using regsvr32.exe -u rzdcx.dll) so that you are absolutely sure that no registry is involved and double click EchoSCUExample.exe.

Enjoy!

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

Thursday, May 13, 2010

RZDCX Release 1.0.1.3

The new release of RZDCX is now available on the download page of our web site.

Since the last release we've changed many things and improved the development process by using the latest tools and methodologies in order to monitor and manage the production environment ensuring that it's consistent, traceable and reproducible. Behind these big words hide even bigger words like extreme programming and continuous integration and these are backed up by great products like TeamCity for automatic building and unit testing facilities like NUnit and MSTest. All together there's now a set of systems and utilities working together to provide complete Configuration Management (CM) system:

Source Control - CVS
Issues tracking - CVSTrac
Automatic Builds - TeamCity
Unit Testing - NUnit (2.5.4), CPPUnit and MSTest
Automatic Documentation - Doxygen

    Latest Changes

    The major changes in this release are:
    • Add bare N-CREATE calls.
    • Add support for Printer SOP Class N-GET.
    • Fix Color Printing.
    • Add example for multi-frame image creation
    There are some more small fixes. Most important, there are no backwards compatibility issues and old tests are running as before. The ease of verifying this short sentence demonstrates the benefits of investing in CM.





    Print Managment


    Following a user report, we've discovered that the color printing methods doesn't work properly and fixed that. The proper way to create an Image Box is now to call GetImageBox method of the DCXPrintPage class (that represents the film box). Otherwise, you'll have to set the ColorSpace property of the Image Box before calling any other method or property.
    Additionally, there's now two new calls in the DCXREQ class that can create film sessions (color or grayscale) with additional paramteres, as for example to set number of pages or priority.
    Please check the relevant new version documentation pages for more information









    Thursday, February 18, 2010

    "Hacking" HL7Kit Pro Runtime Service to work with multiple HL7 applications

    I know that it's not very common to show how to hack your own application but since this issue has been circling for a while, I'd like to show a workaround that will allow you to use the kit to integrate with many HL7 applications simultaneously.

    The kit original design is to communicate with one remote application.


    The workaround is to have multiple instances of the HL7 service running. The services can connect to the same database but each mush have it's own queue table.
    Each will have it's own configuration file and it's own mapping rules file and mapping definitions but they will all write to the same log file and there can be only one active tray icon.

    Here's how to set this up step by step:

    1. Stop the HL7 Service.
    2. In windows explorer navigate to the installation folder (default is c:\program files\rz software services\hl7) and make a copy of the folder, so you have hl7 and hl72.
    3. Create a copy of the HL7_QUEUE table so you have two queue tables or if you prefer, use each service on a different database schema.
    4. Use the HL7ServiceConfiguration.exe application in each folder to configure the port settings, mapping files and queue table names. Alternatively you can edit the HL7Service.exe.config file manually.
    5. Test your setup by running "HL7Service.exe -run" from two separate command line windows.
    6. Configure the new service to run on system startup. This is a bit tricky but here's a way that works:
      1. Run regedit
      2. navigate to HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\HL7Service and export it.
      3. Edit the reg file and replace all "HL7Service" with "HL7Service2"
      4. Save it and double click it to import it to the registry.
      5. In regedit change the ImagePath entry of HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\HL7Service2 to the copied HL7 Folder (hl72)
      6. Restart the computer.
    Now you should have two services installed: HL7Service and HL7Service2. 
    Of course, you can duplicate them as well.

    I hope to have a new release soon that will allow multiple destinations and sources of messages.

    Roni

    Sunday, February 14, 2010

    DICOM JPEG 2000 Decoding in RZDCX Release 1.0.1.1

    The new preliminary release of the Fast Strike DICOM Toolkit, RZDCX, adds JPEG 2000 support for communication and decoding. The function Decode of the DCXOBJ class decodes JPEG  2000 DICOM images just like it does for jpeg, jpeg lossless and RLE.
    The function SaveBitmap of the DCXIMG class also handles jpeg 2000 in the same manner.

    The following sample code snippet shows how to decode a JPEG 2000 image using the Decode function and save it as a new decompressed DICOM file:


                DCXOBJ image = new DCXOBJ();
                String filename = "DCMJ2K";
                image.openFile(filename);
                image.Decode();
                image.saveFile("DCMLEE");

    Another little feature of this release is the TransferSyntax property (set only) that can be used to encode or decode objects or explicitly set the transfer syntax, for example to store objects as little endian implicit or big endian implicit. Up until now, created objects could have been stored either as little endian explicit or with one of the supported compressed formats (jpeg, jpeg lossles and rle).
    The following code shows how to save files as LEI:

                DCXOBJ image = new DCXOBJ();
                /// ... add elements in
                image.TransferSyntax = TS_LEI;


                image.saveFile("DCMLEI");

    The new release is on the downloads page at www.roniza.com/downloads.


    Friday, February 5, 2010

    XRFiles Howto Video

    Following the previous post about the launch of XRFiles, here's a short video that shows how to upload, view and share medical imaging exams using XRFiles web site.

    Thursday, January 28, 2010

    www.xrfiles.com

    XRFiles is an online personal archive for medical imaging exams.
    Using XRFiles anyone can upload, view and share their X-Rays, Ultrasounds, CT's and other medical images.
    You probably know that this kind of images don't just open and show when you click (or double click) them on your computer. Many times they are just too big to send by email. XRFiles provides a solution to all that with a simple Upload-Share-View cycle.
    Once uploaded, all DICOM Images are processed and listed in the exams page.
    The user can review them online using the browser embeded viewer and invite others to review them.
    While common web DICOM viewers either compromise quality by converting the images to jpeg or require software installation in the form of ActiveX components or stand alone applications, the XRFiles viewer doesn't require all that and still keep the full image quality including contrast/brightness settings for 16 bit grayscales, utilizing unique IP protected imaging technology. This eliminates all the administration and security hustle that usually involves web viewers distribution.
    Another interesting element of the XRFiles web site is the sleek upload process that let you upload simply by dragging and dropping your files on the Transfer Manager Java application.
    And in case you're about to ask, the service is free of charge.