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
[update 24 March 2023: latest HRZ softwarecan be found on HRZ website - www.hrzkit.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.


Download the complete package with RZDCX.DLL, The manifest files, C++ Example and C# example.


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

[update 24 March 2023: Check HRZ website for the latest software versions]

A new release of RZDCX (now known as the MODALIZER-SDK™: Fast Strike DICOM Toolkit) -  is now available.

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

The complete documentation can be found online in the following link: XXX

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