Just as a little addition to this.

Be careful when implementing the ##Class(Backup.General).ExternalSetHistory() and then running the script independent to actually taking backups from your external system.

Default journal retention is usually 2 days OR 2 backups. If this is called a few times without a backup, you will have effectively deleted your journals past the point of your most recent backup.

I've cobbled the following together from other posts that come close to this (for example, here and here) and running each line in terminal should disable all the services from the production in the namespace you run it in:

zn "NAMEOFNAMESPACEHERE"

Set tRS = ##class(%ResultSet).%New("Ens.Config.Production:EnumerateConfigItems")

Set tStatus = tRS.%Execute("Production.Name.Here", 1)

While tRS.%Next(.tStatus) {set sc = ##class(Ens.Director).EnableConfigItem(tRS.%Get("ConfigName"), 0, 1)}

Line 1 sets your namespace, 2 and 3 bring back the list of services (the flag set to 1 on the third line specifies listing the services, setting this to 3 will bring all operations)

Line 4 is a while loop to iterate through the result set and to then use Ens.Director.EnableConfigItem to disable the config item by name (flag 1 is the enable/disable flag, and the second is to tell it to update the production).

This could probably be made nicer and more efficient (eg. disabling all of the config names without updating the production and then updating the production once using "##class(Ens.Director).UpdateProduction()" to avoid doing it once per entry) however I hope it works as a starting point.

Hey Scott.

If you were open to having a Service in your production which is what your function sends its two variables (and the service then passes it onto your Operation) you could have something like this:

ClassMethod SendPage(PagerNumber As %String, Message As %String) As %Status
{
    //The String passed to Ens.Director must match a service name within the active production
    set tsc = ##class(Ens.Director).CreateBusinessService("Pager From Function Service",.tService)
    
    if ($IsObject(tService))
    {
        set input = ##class(osuwmc.Page.DataStructures.Page).%New()
        set input.PagerNumber = PagerNumber
        Set input.Message = Message
        
        set tsc = tService.ProcessInput(input)
        Quit tsc
                
    }
    else
    { 
    Quit 0
    }
}

and then you have a custom service that looks a little like this:

Class osuwmc.Services.PageService Extends Ens.BusinessService
{
Property TargetConfigName As Ens.DataType.ConfigName;

Parameter SETTINGS = "TargetConfigName";

Method OnProcessInput(pRequest As osuwmc.Page.DataStructures.Page) As %Status
{
    set tsc=..SendRequestAsync(..TargetConfigName, pRequest)
    
    Quit tsc
}

}

Then when you add the service to your production (remembering to match it to the name declared in the service code), you can select your target operation as a config item, and when the function is triggered it should go Function -->Service-->Operation.

Edit: my Service Class example had an error in the SETTINGS parameter, I have corrected it. 

Hey Scott.

I think you can achieve this by setting the second parameter to a comma delimted list of the request names, and then pass each value afterwards (in the same order you have the names).

For example:

Method Sample(pReq As osuwmc.Page.DataStructures.Page, Output pResp As %Net.HttpResponse) As %Status
{

    Set FormItems = "PNo,PMsg"
    
    set tSC = ..Adapter.Post(.tResponse,FormItems,pReq.PagerNumber,pReq.Message)

    if ('tSC)

    {

        $$$LOGERROR(tSC)

    }

    quit tSC

}

Hey Mufsi, I had this happen to me and according to WRC this is a known issue fixed in 2020.1.1.

It is caused by an attempt to get an exclusive lock on a specific node for the mirrored database (which is read only) where the task is being scheduled.

There is an alternative workaround to the steps you took by using the ^TASKMGR interface from the %SYS namespace in a Terminal session as it doesn't try to perform any write operations on the read-only mirror databases.

Just to expand on Davids response - the File Outbound adapter will create a new file per message assuming the filename settings are configured so that the filenames it produces are unique per message.

If you were to set the Filename property to a specific value rather than use the timestamp specifiers (so for example set it so that the filename is output.txt) then each message should write the data at the end of the file, giving you a single file with all of the entries.

You won't be able to do this using the built in viewer, however you can query the sql tables directly and then interrogate the results using your preferred method.

For example, I had a spike in activity on the 20th of Dec which made a disk fill a lot more than usual, but the purges meant I couldn't just check the messages as they had since been deleted. So I ran the following in the SQL option in the management portal:

SELECT *
FROM Ens_Activity_Data.Days
Where TimeSlot = '2020-12-20 00:00:00'

I then used the print option to export to CSV to then use a simple Pivot table to work through each Host name to see what had a dramatically higher number of messages to what I would usually expect. (I actually exported a few days worth of data to compare between days, but hopefully you're getting the idea)

You could always explore using Grafana to produce a nice visual representation of the data that you've surfaced using a method like this.

Hey Werner.

I know I have ignored your request on how to call a class method (Jeffery has you covered by the looks of things), but you could use $PIECE to break the string apart and then insert what you need.

For example if "source.{PhoneNumberHome(1).Emailaddress}" is equal to "myemail@myemaildomain.co.uk" then 

"test"_$PIECE(source.{PhoneNumberHome(1).Emailaddress},".",1)_"test"_"."_$PIECE(source.{PhoneNumberHome(1).Emailaddress},".",2,*)

will return: 

"testmyemail@myemaildomaintest.co.uk"

The idea being that we

  • Start the new string with "test"
  • take everything before the first period with $P(source.{PhoneNumberHome(1).Emailaddress},"."1) 
  • Add "test" in again
  • Add a period that gets dropped from the $PIECE from using the period as the delimiter
  • Provide everything from the second period onwards with $P(source.{PhoneNumberHome(1).Emailaddress},".",2,*)

Hey ED Coder.

There are built in classes to manage this in a nicer way.

##class(Ens.Util.Time).ConvertDateTime() is a good starting point. For example:

Here is the filled in classmethod call for easy copy/pasting:

Set NewDate = ##class(Ens.Util.Time).ConvertDateTime(HL7Date,"%Y%m%d%H%M%S","%Y-%m-%d %H:%M:%S")

The values for each section of the date are defined by the following: https://docs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls...

Hey Ahmad.

If this is all happening in the one production, then you will have an inbound service for each port.

You could setup a router with a specific rule for each destination, and then use the rule constraint to be restricted on the source service. That way, if you point all three services at the one router, it will only use the rule within the router that has the service listed as a constraint.

For example:

I hope that helps!

Hey, this should be controllable using the Service setting of AckMode:

Control of ACK handling. The options are:
  • Never: Do not send back any ACK.
  • Immediate: Send back (commit) ACK reply message immediately upon receipt of the inbound message.
  • Application: If message passes validation, wait for ACK from target config item and forward it back when it arrives.
  • MSH-determined: Send back ACK reply messages as requested in the MSH header of the incoming message.
  • Byte: Send back an immediate single ACK-code byte instead of an ACK message. Byte ASCII code 6 = 'OK', ASCII code 21 = 'Error'

In your case, you'll be interested in the Application ACK.