検索

質問
· 2025年4月10日

HL7 control characters showing up - resolved

Business Process is adding control characters.

Input HL7 shows no control characters in Notepad ++

Output

Code:

Method FromCerner(pRequest As EnsLib.HL7.Message) As %Status
{
    #dim tStatus As %Status = $$$OK
    #dim eException As %Exception.AbstractException
    #dim tOBXText As %String
    #dim tItemNumberCount As %Integer = 0
    #dim tItemNumberProcessed As %Integer = 0
    #dim tTotalCostSegment As %Integer = 0
    #dim tOrderType As %String
    #dim As %Interger = 0
    #dim tOBXKey As %Integer = 0
    #dim tOBXTextChanged As %String
       ////
       Set pOutput = pRequest.%ConstructClone(1) // moved here because %ConstructClone() need only be done at the message object level.
       //$$$LOGINFO(pOutput_" ---- pOutput to see if clone is created****")
    // Set the message
    Set tMessageType = pOutput.GetValueAt("MSH:9.1")
    //$$$LOGINFO(tMessageType_" --- tMessageType check that pOutput works") ORM found
    //
    Try {
          // Message Subtype
          Set tMessageSubType = pOutput.GetValueAt("ORCgrp(1).ORC:1")
          //$$$LOGINFO(tMessageSubType_"----- tMessageSubType - = NW -- ") works
          // Check if OBR:19 contains "Implant Usage (PSAS)" OR "Issued in Clinic (PSAS)"
          Set tOrderType = pOutput.GetValueAt("ORCgrp(1).OBRuniongrp.OBRunion.OBR:19(1).1")
          //$$$LOGINFO(tOrderType_" -- pOutput ???? -- tOrderType Ordder Type = Implant Usage (PSAS) for first interation")
       If ((tOrderType["Implant Usage (PSAS)") || (tOrderType["Issued in Clinic (PSAS)")) {
          // First loop: Count occurrences of "Item Number:"
          Set = 1
          Set tItemNumberCount = 0
          //
       While (pOutput.GetValueAt("ORCgrp(1).OBRuniongrp.OBXgrp("_i_").OBX")'="") {
            // Find the number of segments that are Item Number
            Set tOBXText = pOutput.GetValueAt("ORCgrp(1).OBRuniongrp.OBXgrp("_i_").OBX:5", .tStatus)
            //$$$LOGINFO(tOBXText_"**** "_i_" ---- tOBXText OBX 5 text and i value") // working
            //
                If (tOBXText [ "Item Number:") {
                Set tItemNumberCount = tItemNumberCount + 1
                     //$$$LOGINFO(tItemNumberCount_" ----- tItemNumberCount")
                                                                    }
            Set = + 1
        }    //end While loop
        //
        // Second loop: Modify OBX segments to remove "REASON FOR REQUEST"
        //$$$LOGINFO("SECOND LOOP for remove")
        //$$$LOGINFO(tItemNumberCount_" ----- tItemNumberCount")
        Set = 1
        Set tItemNumberProcessed = 0
           While (pOutput.GetValueAt("ORCgrp(1).OBRuniongrp.OBXgrp("_i_").OBX")'="") {
               Set tOBXText = pOutput.GetValueAt("ORCgrp(1).OBRuniongrp.OBXgrp("_i_").OBX:5", .tStatus) // using pOutput instead of pRequest
               //$$$LOGINFO(tOBXText_" ---- tOBXText get OBX 5 text")
            If (tOBXText [ "Item Number:") {
               Set tItemNumberProcessed = tItemNumberProcessed + 1
                                           }
            If (tItemNumberCount >= 1) && (tOBXText [ "REASON FOR REQUEST:") {
                Set tOBXText=$p(tOBXText,":",2,5) /// OBX 5 text parsed to replace in OBX 5 later
                // Start replacement process
                Do pOutput.SetValueAt(tOBXText,"ORCgrp(1).OBRuniongrp.OBXgrp("_i_").OBX:5","set") /// Required
                //$$$LOGINFO(tOBXText_" ---- OBX 5 parsed to be saved in new OBX 5")
                //Set tOBXTextChanged = pOutput.GetValueAt("ORCgrp(1).OBRuniongrp.OBXgrp("_i_").OBX:5") // NOT required
                //$$$LOGINFO(i_" **** "_tOBXTextChanged_" --- i --- tOBXTextChanged, Is OBX 5 changed???")
                Set = i
                
             //OBX|1|TX|2000.02^REASON FOR REQUEST^AS4|19||
             //Do pOutput.SetValueAt(tOBXText_"~~","ORCgrp(1).OBRuniongrp.OBXgrp("_i_").OBX:5","set") //
                set segcnt = 0
             //
            If ((tOBXText[ "Total Cost:")) {
                Set tSegmentOBX = ##class(EnsLib.HL7.Segment).%New()
                Set tSegmentOBX.SegType = "2.3:OBX"
                Set tSC = tSegmentOBX.SetValueAt("OBX", 0, "set")
                Set tSC = tSegmentOBX.SetValueAt("1|TX|2000.02^REASON FOR REQUEST^AS4", 1, "set")
             //|1|TX|2000.02^REASON FOR REQUEST^AS4
                set count = 0
                set = +5
            If ('= (segcnt -4)) {
                Set tSC = pOutput.InsertSegmentAt(tSegmentOBX, z)
                                 }
                set segcnt = pOutput.SegCount
                set count = segcnt - 2
             //$$$LOGINFO(tOBXText_" *** "_z_" "_count_"***"_segcnt_" z ----count --- segcnt")
                Set tRemoveText = pOutput.GetValueAt("ORCgrp(1).OBRuniongrp.OBXgrp("_i_").OBX:5", .tStatus)
             //$$$LOGINFO(i_" **** "_tRemoveText_"***"_z_"***"_count_"***"_segcnt_" --- i --- tRemoveText_ --- z ---- count --- segcount")
            If (segcnt - = 3) {
             //$$$LOGINFO(i_" **** "_tRemoveText_"***"_z_"***"_count_"***"_segcnt_" **** segcnt - 3 *** --- i --- tRemoveText_ --- z ---- count --- segcount")
                set tSC = pOutput.RemoveSegmentAt(z)
                                }
             //
             elseif (tItemNumberCount = 1) && (tOBXText [ "REASON FOR REQUEST:")
                {
                Set tOBXText=$p(tOBXText,":",2,5) /// OBX 5 text parsed to replace in OBX 5 later
                // Start replacement process
                Do pOutput.SetValueAt(tOBXText,"ORCgrp(1).OBRuniongrp.OBXgrp("_i_").OBX:5","set") /// Required
                //$$$LOGINFO(tOBXText_" ---- OBX 5 parsed to be saved in new OBX 5")
                //Set tOBXTextChanged = pOutput.GetValueAt("ORCgrp(1).OBRuniongrp.OBXgrp("_i_").OBX:5") // NOT required
                //$$$LOGINFO(i_" **** "_tOBXTextChanged_" --- i --- tOBXTextChanged, Is OBX 5 changed???")
                }
             }
            }
            Set = + 1
        }
    }
            //
        // Process ORM messages with subtype RE
        If tMessageSubType = "RE" {
            Set tResultType = pRequest.GetValueAt("ORCgrp(1).OBRuniongrp.OBXgrp(1).OBX:11")
            If tResultType = "D" {
                $$$ThrowOnError(..ProcessPurgeDocumentToVista(pRequest))
            Else {
                $$$ThrowOnError(..ProcessNewDocumentToVista(pRequest))
            }
        }
        ElseIf (tMessageType="ORM") {
            //$$$ThrowOnError(..SendRequestAsync(..VistaTarget, pRequest, 1, "OutputCernerToVistaORM"))
            $$$ThrowOnError(..SendRequestAsync(..VistaTarget, pOutput, 1, "OutputCernerToVistaORM")) // Must use the object for the clone
        }
        ElseIf (tMessageType="ORR") || (tMessageType="ACK") {
            $$$ThrowOnError(..SendRequestAsync(..VistaTarget, pRequest, 1, "OutputCernerToVistaORR"))
        }
        Else {
            $$$ThrowOnError($$$ERROR($$$GeneralError, "Error: unable to process message from Cerner. Message type " _ tMessageType _ " received."))
        }
    }
    Catch eException {
        Set tStatus = eException.AsStatus()
        Set tStatus = $$$ADDSC(tStatus, ..CreateAndSendAlert(eException.DisplayString(), pRequest))
    }
    Quit tStatus
} /// /////////////////////////////////////////
/// ////////////////////////////////////////

5 Comments
ディスカッション (5)3
続けるにはログインするか新規登録を行ってください
記事
· 2025年4月10日 2m read

Webゲートウェイの設定値をプログラムで変更する

これは、InterSystems FAQ サイトの記事です。
 

※Caché/Ensembleの「CSPゲートウェイ」は、IRISでは「Webゲートウェイ」と名称が変更されています。
 これに伴い、サービス名も、「%Service_CSP」 ⇒ 「%Service_WebGateway」となっています。
 以下は、IRISベースの名称で記載しておりますので、適宜読み替えてご参照ください。

Webゲートウェイ・レジストリとその関連クラスを使用すると、Webゲートウェイのインストール環境の確認、および、設定値の変更が可能です。
関連するクラスは、以下になります。
%CSP.Mgr.GatewayRegistry
%CSP.Mgr.GatewayMgr

例えば、以下は、アクティブなWebゲートウェイのインストール環境(リストの1番目)の情報を書き出しています。

Set reqistry = $system.CSP.GetGatewayRegistry()
Set gateways = reqistry.GetGatewayMgrs()
Set gateway = gateways.GetAt(1)
Write gateway.IPAddress," : ",gateway.Port," : ",gateway.Version

      ↓(結果)

127.0.0.1 : 52777 : 661.2001.1740


サーバ・パラメータの変更は以下のように行います。

 set reqistry = $system.CSP.GetGatewayRegistry()
 set gateways = reqistry.GetGatewayMgrs()
 set gateway = gateways.GetAt(1)
 set newpars("Server_Response_Timeout")=30
 do gateway.SetDefaultParams(.newpars)


Webゲートウェイ・レジストリの詳細については、下記ドキュメントをご参照ください。
[ドキュメント] InterSystems IRIS の Web ゲートウェイ・レジストリ

ディスカッション (0)1
続けるにはログインするか新規登録を行ってください
記事
· 2025年4月10日 1m read

サーバのIPアドレスやマシン名をオブジェクトスクリプトで取得する

これはInterSystems FAQサイトの記事です。
 

サーバのIPアドレスやマシン名は、%SYSTEM.INetInfoクラスのメソッドで取得することが出来ます。

set hostname=$System.INetInfo.LocalHostName() //マシン(ホスト)名
set ip=$system.INetInfo.HostNameToAddr($system.INetInfo.LocalHostName())  //IPアドレス 
ディスカッション (0)1
続けるにはログインするか新規登録を行ってください
ディスカッション
· 2025年4月10日

Using AI to produce ObjectScript code

I ask ChatGPT periodically to produce ObjectScript or plain MUMPS code for string manipulation, or for implementing known algorithms etc. Occasionally, it does make mistakes or uses non-existing class members but generally not that bad. Is there any tutorial on the subject of using AI for coding, ideally specifically for ObjectScript/MUMPS? Any AI productivity advice, or tricks you are using, or another AI flavor?

Thanks in advance,
Anna

4 Comments
ディスカッション (4)3
続けるにはログインするか新規登録を行ってください
質問
· 2025年4月10日

Performant SQL For Paging Results (DataTables, Select2, etc.)

Hello,

Our software commonly returns a full result set to the client and we use the DataTables plugin to display table data.  This has worked well, but at datasets grow larger, we are trying to move some of these requests server-side so the server handles the bulk of the work rather than the client.  This has had me scratching my head in so many ways.  

I'm hoping I can get a mix of general best practice advice but also maybe some IRIS specific ideas.

Some background

- I would admit our tables aren't best optimized for SQL in the sense that we don't utilize parent-child relationship in tables rather we point to other tables as property object references and often index on those pointers. I don't think this is uncommon, yeah?  I think it becomes an issue when we are accessing a property by the pointer pointing to another pointer and so on until we make the object reference we want.  

- I assume this next concept is common: depending on the table, some of the data reported to the client is calculated rather than specifically derived from the table or pointers. Or the data itself may be a link so there's an <a>Data Name</a> around it. This means if we need to filter data based on the calculated data we can't rely on just the SQL.  We call a base query and then load the resulting object IDs into an array and then loop through the array and process, transform and filter the data in ObjectScript.  

- Maybe that previous point isn't so common?  Do I need to buckle down and learn how to get a lot fancier in SQL? We have methods that transform data in ObjectScript, but I don't think I can call those within ISC SQL.  But really, at the end of the day, the SQL translates to a bunch of $ORDER-ing through indexes and such to get the data we need, right?  So by calling a base query and then further processing in ObjectScript, I'm just doing what the SQL would have been doing in the guts.  I know I'm oversimplifying, but does that make sense?

- A use case for example is that we have a table of lets say 1 million patients.  A base SQL query can performantly get that down to let's say 100,000 patients that belong to a specific facility.  I only need the first 10 to show on my client side DataTables view, but since I'm processing it all server side and don't want to return 100,000 results to the client, just 10, I still have to return the count of 100,000 to client.  I think that's easily done with SQL COUNT, but then things start to get trickier when I add a filter or search text.  Now I have to tell DataTables on the client side how many filtered records I have.  So if someone searches for "Sally" the query will count that there's 100,000 records, return 50 of them that have "Sally" (another point on this later), and then only return the first 10.  Or 11-20 if it's page two.  And so on.  Ok, that's still possibly all in SQL.  But what if "Sally" is in the patient name? Or in the responsibly party name?  Or the street name ("Sally St.")? Or in the city name (ok, no US cities name Sally's Cove in Newfoundland).  Now my SQL query becomes more complex (WHERE FirstName Like %SALLY% or Street1 Like %SALLY% or Street2 Like %SALLY% . . .).  Start adding other optional filters as well as search text.  Now the filters are filtering on calculated data - now the SQL query can't determine the TOTAL count and the FILTERED count and my pagination breaks. Also, we have to consider the ordering of the results which can be done in the query if you can point to or calculate data in the query, but as soon as one this can't be determined from the query with ordering you either have to disable ordering on that column or write the simplest query and handle all the ordering, sorting, filtering, in ObjectScript.

I hope this help paint a picture and I hope folks have some ideas or experiences they can share.  Some other ideas swimming in my head:

- Maybe we need to create temp tables or derived tables so the data is there and waiting as we want it for the request - this seems like a big project though. How/when do the tables refresh/update.  Do they live in the same namespace? What if it's being updated and the user calls it are there locking issues?

- As I said before, maybe I just need to get better at SQL - I know people write way more complex and performant queries that I can even imagine.  

- Create better indexes and table relationships - if we know we are pointing to a pointer 4 or five tables deep and we somehow connect the source table to that table with a new object property and index it appropriately?  

- Get better at reexamine client versus server activities - in the case of Datatables, I'm trying to do all the table init config in the payload from the server so that I'm just passing in a block of JSON to the init and no having to fiddle with client side config. Same with formatting.  Maybe I need to be better at letting the client handle the things the client it better at handling (e.g. forming data into URLs) and let the server just focus on raw data.

I would LOVE to hear your thoughts, folks.  Thank you for any resources or best practices or real word experience or whims you may have!

6 Comments
ディスカッション (6)3
続けるにはログインするか新規登録を行ってください