To make things clear, the mentioned popup message should be seen by someone, who is (most of the time) working on the servers console. Is it so? Or you want to popup this message on an arbitrary desktop, where a user works?

If I need to send a message to a user, either I send an email or I activate a popup message in my client (I have a very special client-UI).

By the way, you wrote, I quote "an email is NOT a realistic expectation and far from reliable". Today, (almost) everybody has a smartphone and I think, if somebody does not read an email, he/she won't read thos popup messages either. Of course, you should send short plain text messages and not those fancy bloated colorful emails.

So, a way to a solution... you could write a  small program (C++, VB, Delphi, etc.) which is started after user login.  The program should listen on a TCP port for messages from an arbitrary Cache/Iris (possibly background) application. If such a message arrives, this helper program changes to foreground, displays the message with or without an OK button. That's it.

Store only hashed passwords... that's all.

Class DC.MyUsers Extends %Persistent
{ 
Property Name As %String;
Property Password As %String;
Property passHash As %String [ Internal, Private, ReadOnly ];
Property passSalt As %String [ Internal, Private, ReadOnly ];
Parameter ITER = 1024;
Parameter LENGTH = 20;
Method PassCheck(psw) As %Boolean
{
  set salt = $system.Encryption.Base64Decode(..passSalt) 
  set hash = $system.Encryption.Base64Decode(..passHash)
  quit $system.Encryption.PBKDF2(psw, ..#ITER, salt, ..#LENGTH)=hash
}
Method PasswordSet(psw) As %Status
{
  // optionally, quality/requirement-check
  if '..pswQuality(psw) quit $$Error^%apiOBJ(5001,"Poor password quality")
  
  set salt=$system.Encryption.GenCryptRand(8)
  set hash=$system.Encryption.PBKDF2(psw, ..#ITER, salt, ..#LENGTH)
  set i%passHash=$system.Encryption.Base64Encode(hash)
  set i%passSalt=$system.Encryption.Base64Encode(salt)
  quit $$$OK
}

Method pswQuality(psw) As %Boolean
{
  quit 1
}
}

There are two points,

the first (catchwords: server, interaction) was already answered by Dmitriy Maslennikov 

the second is your 10 second popup button.

In my over 40 years of IT-experience, there is one thing (along with others) I have learnd, is: every timeout is wrong, but messages with timeouts are evil! Whatever time you use, it's either too short or too long.

Imagine, the phone is ringing abd the user has a hot 20 minute discussion on the phone, in the meantime, your popups comes and goes! Unseen! Sometimes several times!

The only resonable solutions are,

- if the message is (just) informative and the message text never changes, then put it into a logfile and show nothing.  If the message text is a variable text ("Data is saved" vs. "Can't save: No disk space available") then do the popup with one button (or textinput), see below, but DO NOT use timeouts! 

- if the situation allows the user to choose between multiple answers, then let the popup window with those OK, YES, NO, CANCEL, etc. buttons stay there, as long as the user chooses one of them, or as an alternative (application dependent), offer an ordinary text input and at sometime the user types the answer and pushes the enter key.

Messages with (possible with short) timeouts requires a user all the time gazing on the display - which you can't expect.

justmy2cents

Instead of inserting debug_macros, try Intersystems TRACE utility.

write $$DIR^TRACE("c:\Temp") ; to set an output directory
write $$ON^TRACE(jobnr) ; the pid of the process you want to trace
; zn "appNamespace"
; do ^yourProgram
; zn "%SYS"
write $$OFF^TRACE(jobnr) ; to stopp the trace
do ^TRACE ; to display the trace result

TRACE displays the function-/method-calls with arguments.

After seeing several solutions I got the idea to make a comparison.
The bottom line is, it's advisable to check how an algorithm (or function or method etc.) performs over another.
So try the below program snippet... you will be surprised!

Test //
   s date="20201121090000"
   s new=""
   s t0=$zh

   f i=1:1:1E6 s new=$e(date,1,4)_"-"_$e(date,5,6)_"-"_$e(date,7,8)_" "_$e(date,9,10)_":"_$e(date,11,12)_":"_$e(date,13,14)
   s t1=$zh
   f i=1:1:1E6 s new=$tr("abcd-ef-gh ij:kl:mn","abcdefghijklmn",date)
   s t2=$zh
   f i=1:1:1E6 s new=$zd($zdh($e(date,1,8),8),3)_" "_$e(date,9,10)_":"_$e(date,11,12)_":"_$e(date,13,14)
   s t3=$zh
   f i=1:1:1E6 s new=$system.SQL.TOTIMESTAMP(date, "YYYYMMDDHHMISS") 
   s t4=$zh
   f i=1:1:1E6 &SQL(SELECT TO_TIMESTAMP(:date,'YYYYMMDDHHMISS') INTO :new)
   s t5=$zh

   w "$e() only",?12,t1-t0,!
   w "$tr()",?12,t2-t1,!
   w "$e()+$zd()",?12,t3-t2,!
   w "SQL/class",?12,t4-t3,!
   w "SQL/static",?12,t5-t4,!
   q

Of course, the results will depend on hardware,  on Cache/IRIS version and on utilisation of your system

Hello Kevin,

in most of the cases (but not always) the reason for this is the exhaustion of TCP ports, see

https://docs.microsoft.com/en-us/windows/client-management/troubleshoot-...

too. Grab a windows command prompt and start with

netsh int ipv4 show dynamicport tcp

this shows you how many ports you have.

netstat -ano | find "TCP"

shows you all the TCP ports in use (including the process numbers) and

netstat -an | find "TCP" | find "CLOSE"

shows you all the bad guys.

If this is your problem then the solution is:

- increase the number of ports (if it's possible)

- reduce the cases, where a new port is needed and immediate closing of unneeded ports

I'm not sure about what you mean with "unlimited amount of indexes"...

first, there is a limit of 255 for the parameters, this means you can have 

do ..setValue(key1,key2,...key254,value)

but you can't have 

 do ..setValue(key1,key2,...key254,key255,value)

second, if you meant to have many-many values (in sense of, we have to much data)

  do ..setValue(key1,value1)
  do ..setValue(key2,value2)
  // etc.

then, at some point you will reach the and of the available RAM (incl. swap/paging space.  Usually swap/paging space is much less then the space for the real data).

thirdly, if you meant variable number of indexes (in sense of variable number of parameters) then I can just refine Robert's solution to

ClassMethod setValue(params...) As %Status
{
   set base=$name(%session.Data)
   for i=1:1:params-1 { set base=$name(@base@(params(i)))
   set @base=params(params)
   quit 1
}

In case, you have both, variable number of indices and too much data then you could use a mix of the above with data placed in a temp file.

Somewhere, at the program start, let you point %sessionData to a temp file

   set %sessionData=$nam(^||SessionTemp)

and then consider this in the setValue method

ClassMethod setValue(params...) As %Status
{
   set bas=%sessionData
   ...
}

Of course, in your application, you have to change only those %session.Data(...) to  @%sessionData@(...)  which uses the "variable-big-data" part of %session.Data(). All other (the standard ones) stay unchanged.

I'm pretty sure, you intended to write

select COUNT (Arrival Time) FROM dbo.table where Arrival Time < DATEADD(ms, CONVERT(int,LEFT(1603173432000, 20)), '1970-01-01 00:00:00')

i.e. do not place propertynames under quote

By the way, I don't know, how many records you have in your table, but if you have thousands or millions of records, consider to compute constant things just one time!

DATEADD(ms, CONVERT(int,LEFT(1603173432000, 20)), '1970-01-01 00:00:00')

is a constant value, it's not neccessary to compute it for each record! Also, in this particular case, CONVERT() and LEFT() are also not needless, 1603173432000 is the value, and it is an integer.

As Robert said, XSLT... but as he indicated too, in this case XSLT would be an overkill.

But there is also a "forgotten" function, $locate() too for such cases! This function is almost never used in DC examples. Two or three lines of code, and you have the perfect solution... but for "simple" cases only. Beware of nested tags!

SetFixedValue(str,tag,value) Public
{
   set i=0, t="(?i)<"_tag_">[^<]+", s=$l(tag)+2
   while $locate(str,t,i,j,v) { set $e(str,j+s-$l(v),j-1)=value, i=j+s-$l(v)+$l(value) }
   quit str
}

It works like a charm...

set a="<Name>..."
write $$SetFixedValue^test(a,"rollno","***")  -->  <Name>ABC</Name><RollNo>***</RollNo><Name>XYZ</Name><RollNo>***</RollNo><Name>xyz</Name><RollNo>***</RollNo>