go to post Jeffrey Drumm · Jan 11, 2020 The OBX segment is probably defined as repeating, so: Should work.
go to post Jeffrey Drumm · Jan 9, 2020 I've added a query to the HICG.HL7 class that lets you leverage Ensemble's HL7 Search Tables to select messages by the fields specified for indexing. The new version is available on the HL7 Spy website. An example, using the default search table to select messages by PatientID/MRN: -- region 8 - message rows by SearchTable SELECT tbl.BodyId, msg.TimeCreated, msg.Name, msg.DocType, HICG.GetMsg(msg.Id) As Message FROM HICG.TblSrch('EnsLib.HL7.SearchTable','PatientID','4444') tbl INNER JOIN EnsLib_HL7.Message msg ON tbl.BodyId = msg.Id -- endregion
go to post Jeffrey Drumm · Jan 9, 2020 From COS - $SYSTEM.Version.GetVersion() From SQL - SELECT $ZVERSION Not a .NET guy, sorry!
go to post Jeffrey Drumm · Jan 6, 2020 This works in a client also ... SELECT head.ID As HeadId, body.ID As BodyId, body.Name as BodyName FROM Ens.MessageHeader head INNER JOIN EnsLib_HL7.Message body ON head.MessageBodyId = body.%ID WHERE head.ID > 0 AND head.MessageBodyClassName = 'EnsLib.HL7.Message' AND body.Name = 'ADT_A04' There's something fundamental here I'm missing.
go to post Jeffrey Drumm · Jan 6, 2020 So to clarify ... the class method isn't so much a stored procedure as a custom function. It should return an HL7 message as a string, which will be represented as a column in the result set generated by a SELECT. So technically, the SQLCODE I'm getting back is correct for what I'm doing, which is calling a function against a column value in the query. If the function fails, the query fails, and the reason for the query failure is the function failure. That's how it's coming back to ADO/ODBC when I force a failure, and the reason I asked the question in the first place is that sometimes I'm just not too smart :D
go to post Jeffrey Drumm · Jan 5, 2020 Hi Robert, Thanks. I understand what's happening now ... and very much appreciate your research and input!
go to post Jeffrey Drumm · Jan 4, 2020 I had tried PublicList as well. See my comment on Eduard's answer ... I think it may not be possible to set the "top level" error code returned from the SELECT (which is what I was hoping to do).
go to post Jeffrey Drumm · Jan 4, 2020 Hi Eduard, and thanks! I had already tried that option, but discarded it because the SQL code returned is <-149>:<SQL Function encountered an error>, with my error code and text as a subordinate (child?) error. Is there any way to set the "parent" SQLCODE and associated error text? Or is that effectively the "query" error, with the child error generated by the SqlProc?
go to post Jeffrey Drumm · Jan 4, 2020 Thanks Robert, but setting ProcedureBlock = 0 had no effect. I'm not getting any errors when setting the properties for %sqlcontext, but its properties don't seem to be exposed to the caller in a way that either the SQL shell or the ODBC invocation see as an error. I've tried setting SQLCODE as well with no effect.
go to post Jeffrey Drumm · Dec 30, 2019 Execution details for routing rules are located in Ens.Rule.Log. The available values are ID, ActivityName, ConfigName, CurrentHeaderId, DebugId, EffectiveBegin, EffectiveEnd, ErrorMsg, IsError, Reason, ReturnValue, RuleName, RuleSet, SessionId, and TimeExecuted. In the SQL facility, you can query Ens_Rule.log: SELECT ID, ConfigName, CurrentHeaderId, RuleName, RuleSet, SessionId, TimeExecuted FROM Ens_Rule.Log
go to post Jeffrey Drumm · Dec 29, 2019 Since the class didn't exist before I wrote it earlier today, you can be forgiven for not knowing about it
go to post Jeffrey Drumm · Dec 29, 2019 Assuming the stream contains the normal $C(13) segment delimiters: Class User.HL7.Stream Extends %RegisteredObject { ClassMethod GetCounts(pStream As %Stream.FileCharacter, Output pMsgCount As %Numeric, Output pSegCounts As %ArrayOfDataTypes) As %Status { Do pStream.Rewind() Set pStream.LineTerminator = $C(13) Set pSegCounts = 0 While 'pStream.AtEnd { Set tLine = pStream.ReadLine() // Remove leading control characters Set tSeg = $ZSTRIP(tLine,"<C") // Get the segment name Set tSegName = $EXTRACT(tSeg,1,3) If tSegName '= "" { If '$DATA(pSegCounts(tSegName)) { // We have a new subscript Set pSegCounts = pSegCounts + 1 Set pSegCounts(tSegName) = 1 } Else { Set pSegCounts(tSegName) = pSegCounts(tSegName) + 1 } } } Set pMsgCount = pSegCounts("MSH") Return $$$OK } } Call the classmethod with the stream as follows: Do ##class(User.HL7.Stream).GetCounts(stream,.Msgs,.Counts) Counts will be subscripted by the segment names found in the message stream. In the above example, you'll find the occurrence count of FT1 segments in Counts("FT1") and the number of messages will be in Counts("MSH"). The value returned in Msgs is the same as Counts("MSH").
go to post Jeffrey Drumm · Dec 17, 2019 If you have access to Caché terminal, you can run run queries that won't time out: (the sample below assumes your namespace is "PROD"; just substitute whatever your production's namespace is for that). USER> zn "PROD" PROD> d $system.SQL.Shell() SQL Command Line Shell---------------------------------------------------- The command prefix is currently set to: <<nothing>>.Enter q to quit, ? for help.PROD>>SELECT COUNT(*) AS AlertCount FROM Ens.MessageHeader WHERE MessageBodyClassName = 'Ens.AlertRequest' AlertCount2205 PROD>>Q PROD> So ... if you don't have BodiesToo checked, you most likely have lots of orphaned message bodies taking up database space. And KeepIntegrity is probably retaining a lot of message headers (and associated bodies) that you don't care about anymore. There are reasons you would not want to turn KeepIntegrity off in earlier versions of Caché/Ensemble, like pre-2015 releases. If you're on a release more modern than that and you don't need to worry about messages with parent/child relationships (certain batch types, for example), you can probably turn that off. There are a couple of articles regarding the management of orphaned bodies here on DC. Might be worthwhile to peruse them :)
go to post Jeffrey Drumm · Dec 17, 2019 MAX(ID) isn't necessarily the record count. Try a "select count(*) from Ens.AlertRequest" query and see what you get. Compare that to "'select count(*) from Ens.MessageHeader where MessageBodyClassName = 'Ens.AlertRequest'" If the numbers are in line with the MAX(ID), then my suspicion is that you either don't have "BodiesToo" checked or do have "KeepIntegrity" checked in your purge process configuration. Either of those may be keeping old Ens.AlertRequest bodies around.
go to post Jeffrey Drumm · Dec 17, 2019 If you could provide a little more detail on your use case, there may be a solution that doesn't involve developing a custom service. For example, you could configure the service with a filename pattern that would retrieve all matching files in the target path, but discard, via a routing rule, any that do not meet the date criteria. The name of the file should be available in the Source property of the message object.
go to post Jeffrey Drumm · Dec 16, 2019 printf formatting tokens (%Y, %m, %d, etc.) are not wildcard characters. You'll be limited to legal file naming characters and OS/Shell specific wildcard characters such as * and ? for fetching files.
go to post Jeffrey Drumm · Dec 12, 2019 Select something for the "In files/file types:" field, then enter a search value. You can also do it in reverse order, but both fields are required
go to post Jeffrey Drumm · Dec 5, 2019 You haven't specified an iteration for ORCgrp(). If there's only one ORC group in your messages, you should use ORCgrp(1).
go to post Jeffrey Drumm · Dec 5, 2019 If the layout of each record is identical, a simple record map populated through a BPL/DTL combo would be the preferred solution.
go to post Jeffrey Drumm · Nov 27, 2019 Define them as properties in your task class: Class User.Task.MessageArchive Extends %SYS.Task.Definition { /// Base directory for the archived files Property BaseDir As %String [ InitialExpression = "/hsf/archive/" ]; /// The date of the 24 hour period from which the messages will be selected (midnight to midnight) Property DaysOld As %Integer [ InitialExpression = 23 ]; /// When selected, messages received from services will be archived Property MessagesInbound As %Boolean [ InitialExpression = 1 ]; /// When selected, messages sent to operations will be archived Property MessagesOutbound As %Boolean [ InitialExpression = 1 ]; /// Send Notification Email Property NotifyByEmail As %Boolean [ InitialExpression = 0 ]; These properties can then be referenced in your task's methods with the .. prefix notation.