Magic Release Notes (4.8?) that detail how to connect to a remote computer

12 views
Skip to first unread message

Steve Mogg

unread,
Feb 3, 2012, 1:18:41 PM2/3/12
to NPR - Report Writing
Hello Everyone,

Does anyone have documentation for using the OPEN command, or O, to
open a connection to another server?
O[“”](!,"i"_/IP_"p"_/PORT_IF{F "l"_F}
_"e"_255.255.255.255.255.255_"I8")^XXX,

I had to modify a report to fool the syntax checker using the above
syntax. I got the report working but I would like to understand the
code that opens to a remote computer (We have a custom report that
sends info to Ontario's Health Card Validator). I asked the original
developer Ram Trumpickas and he thought he got the specs from the
Magic 4.8 OS release notes. Sadly these are no longer available
online.

I didn't include all the code to protect the Ontario Health Card
Authority.

Does any one have any documentation on how to use the open command O()
in the way that I have described?

Thanks,
Steve

Tom Tarbotton

unread,
Feb 3, 2012, 1:46:41 PM2/3/12
to NPR - Report Writing
If you really want an explanation of the code I can work thru it for
you. I did that a few years ago when I needed to write my own tcp
interface from OE. But there's an easier way with no syntax checker
issues - a Z program that does it for you - Z.tcp.open. You just need
to pass in 3 arguments - prefix, IP, and port. The 4th argument -
local port - is optional. For prefix I use 9 which crresponds to !. So
an example call would be %Z.tcp.open(9,/IP,/PORT)

;A= ^prefix
;B= IP address or computer name to open to
;C= Port to go to
;D= local port (not required)

Steve Mogg

unread,
Feb 3, 2012, 2:36:59 PM2/3/12
to npr---repo...@googlegroups.com
Thanks for your quick reply Tom!
 
My aim really isn't to do low level magic programming but to change the report to be as maintainable as possible without requiring any special skill or complex documentation in the code. I want to help the next person that has to edit and maintain this. Also we may want to leverage this technique for something else in the future.
 
Do you have documenation as to what !s|1 and !s|2 are? Can I refer to them in the same manner when using Z.tcp.open? It isn't that apparent to me what @tcp.connection returns in Z.tcp.open. My code below also refers to a buffer ![/LEN].
 
I assuming that you need a Z.tcp.close call when you are done?
 
I included the whole code with my documentation where I have removed IP and our facility information below my email signature. If you don't have time to read it, I understand. Maybe seeing your code would help me figure things out. I have bolded the open command to easily find the code that references the open connection.
 
At least I know what to look for now I can play around a bit to find my answers. I am used to learning things the hard way (although hopefully this group changes that!).
 
Thanks,
Steve
 
Steve Mogg
Applications Analyst
North Bay Regional Health Centre
North Bay, ON
 
;<steve>MOH Specification can be obtained on MOH website.
;It is newer than the spec used to create the code below:
;http://www.health.gov.on.ca/english/providers/pub/ohip/ohipvalid_manual/
;ohipvalid_manual/ohipvalid_manual.pdf</steve>
;************ Configuration *******************
;<steve>IP address and port info of MOH</steve>
"See documentation"^/IP,
"See documentation"^/PORT,
;<steve>Appears to be MOH user id for our facility</steve>
"Use your MOH user ID"^/USRID,
"Use your MOH Facility ID"^/FACID,
""^/PROID,
"30"^/TIMEOUT,
;**********************************************
;<steve>Assign HCN inputted by user at run-time to variable TTT</steve>
@health.care.number[c.urn]^TTT,
;<steve>Parse user input into HCN (Health Care Number) and VER (Version).
;#"0-" tokenizes the string using the - char as the delimiter. Recall that tokens
;are zero based. e.g. 1234567897-AA "1234567897-AA"#"0-" returns the string "1234567897".
;Similarly, "1234567897-AA"#"1-" returns "AA".</steve>
TTT#"0-"^HCN,
TTT#"1-"^VER,
;<steve>$10 takes first 10 chars of string.
;:10L formats the field to 10 chars left-justified.</steve>
;$2 takes first 2 chars of string.
;:2L formats the field ot 2 chars left-justified.</steve>
VER$2:2L^/VER,
;<steve>/.LOC=@.logical.device e.g. IS12LT.2</steve>
/.LOC$8:8L^/DEV,
;<steve>@.user=/.USR e.g. MOGGST (Steve Mogg's username).</steve>
/.USR$8:8L^/USR,
;<steve>Take first 8 chars of string, $8, and left justify to 8 chars, :8L.
;This is the facility userid defined above.</steve>
/USRID$8:8L^/USRID,
;<steve>Take first 7 chars of string, $7, and left justify to 8 chars, :7L.</steve>
/FACID$7:7L^/FACID,
;<steve>Take first 10 chars of stirng, $10, and left justify to 10 chars, :10L
;Note the value of this field is unitialized.</steve>
/PROID$10:10L^/PROID,
@.today^/DATE,
;<steve>Formatted Time e.g. 12:22:39</steve>
$TIME(@.sd)^/TIME,
;<steve>Format data and time to first 20 chars, $20, and left justify to 20 chars, :20L
;e.g. 20110526 12:37:30</steve>
(/DATE_" "_/TIME)$20:20L^/TEXT,
;<steve>D(0) is the Ascii NUL (null) char. D(28) is the FS (File Separtor) char.
;D(4) is the Ascii EOT (end of transmission) char.
;D(205) is an extended Ascii code.</steve>
;<steve>Appears to be first string sent after connection opened to MOH.</steve>
D(0)_D(28)_D(0)_D(0)_"*TRNREQ*RPVR0500"_/USRID^/MOHTRM,
;<steve>Third string sent to MOH once connection is opened.</steve>
D(0)_D(4)_D(0)_D(0)^/MOHEOM,
D(0)_D(205)_D(0)_D(0)_"RPVR0500 "_/HCN_/VER_/USRID_/FACID_/PROID_/USR_/DEV_/TEXT^/DT1,
;<steve>Repeat space char 79 times.</steve>
" ":79^/TRK1,
;<steve>Repeat space char 40 times.</steve>
" ":40^/TRK2,
;<steve>Second string send to MOH after connection is opened.</steve>
/DT1_/TRK1_/TRK2^/DT2,
;<steve>Close and stack current ! prefix (output selected by user) to use for connection
;to MOH.</steve>
C(!S),
;<steve>Initialize variables.</steve>
""^XXX^/CONN^/DATAIN^/LEN^/EOM,
;<steve>Open connection to MOH using format for i /IP p /PORT.
;The variable F is not defined by the programmer but its existence is checked for.
;e appears to denote the subnet mask.
;I8 not sure what this represents but it is a litteral string.
;Note use of O[""] instead of ZZ%O syntax to fool checker.
;The ZZ%O syntax did not return a value to the variable XXX, so a check for a value
;in that field failed. Probably okay because a check for !s|2 returns a value is
;connection is successful (I tested commenting out the line and entering a bogus port #).
;Change is O[""] syntax stops fooling syntax checker with version later than Magic 5.62.
;</steve>

O[""](!,"i"_/IP_"p"_/PORT_IF{F "l"_F}_"e"_255.255.255.255.255.255_"I8")^XXX,
;<steve>
;If XXX is nil then
;    Connection is not open, assign nil to /CONN.
;Else assign !s|2 to variable /CONN.
;    Note !s|2 appears to be a connection number. Its value changes per connection.
;End If
;<steve>
IF{'XXX "";!s|2}^/CONN,
;<steve>
;If /CONN has a value, (connection number).
;    Send string built above to MOH
;    Get value !s|1 and assign it to stat. This appears to be constant in testing
;      as a value of 3.
;    If /STAT is 3 then
;        IF /TIMEOUT assign value of variable /TIMEOUT e.g. 30 to !S
;        Else assign hardcoded value of 3 to !S.
;        End If
;        If ![1] contains nil i.e when value concatenated with a period is
;          equal to a period then
;            Assign 0 to !S (Close connection? Prompt for data?)
;            Assign "Timeout" to variable /DATA (Connection timeout?)
;        Else
;            Get first item returned by MOH. >!^/LEN
;            Note: +![/LEN]^/LEN is equivalent since there is only one subscript.
;            Assign last subscripted value of ! to variable /DATAIN
;            Assign 0 to !S (Close connection? Prompt for data?)
;            Assign "Timeout" to variable /DATA (Connection timeout?)
;        Else
;            Get first item returned by MOH. >!^/LEN
;            Note: +![/LEN]^/LEN is equivalent since there is only one subscript.
;            Assign last subscripted value of ! to variable /DATAIN
;            Assign 0 to !S (Close connection? Prompt for data?)
;        End If
;    End If
;    If /DATAIN does not equal "Timeout" and length of /DATAIN > 150 Then
;        IF /TIMEOUT assign value of variable /TIMEOUT e.g. 30 to !S
;        Else assign hardcoded value of 3 to !S.
;        End If
;        If ![1] contains nil i.e when value concatenated with a period is
;          equal to a period then
;            Assign 0 to !S (Close connection? Prompt for data?)
;            Assign "Timeout" to variable /EOM (end of message?).
;        Else
;            Get first item returned by MOH. >!^/LEN
;            Note: +![/LEN]^/LEN is equivalent since there is only one subscript.
;            Assign last subscripted value of ! to variable /EOM (end of message?)
;            Assign 0 to !S (Close connection? Prompt for data?)
;        End If
;    End If
;End If
IF{/CONN /MOHTRM^!,/DT2^!,/MOHEOM^!,
         !s|1^/STAT,
         IF{/STAT=3 IF{/TIMEOUT;3}^!S,
                    IF{![1]^YYY_.=. 0^!S,"Timeout"^/DATAIN;
                       >!^/LEN,
                       ![/LEN]^/DATAIN,0^!S}},
         IF{(/DATAIN'="Timeout")&(L(/DATAIN)>150) IF{/TIMEOUT;3}^!S,
                                                  IF{![1]^YYY_.=. 0^!S,"Timeout"^/EOM;
                                                     >!^/LEN,
                                                     ![/LEN]^/EOM,0^!S}}},
;<steve>Original code used OPEN(!U). I first decided to Fool syntax checker to open file
;using ZZ%O syntax. ZZ%O(!U),
;The close command accomplishes the same task without the need for syntax fooling.
;Code restores old value of !. e.g. Print device selected by user to run report.</steve>
C(!U),
;<steve>Check that returned value /EOM contains string "*CSMOKY*"
;If yes, then the connection appears to be okay.</steve>
IF{L(/EOM,"*CSMOKY*")'=L(/EOM) "OK";""}^/R.FRAG.VAL["MOH","STATUS"],
;<steve>Parse returned value in string /DATAIN. Reference in Variables on Report.</steve>
 
,,, rest of code omitted...
Source Code for Z.tcp.open:
 
Z.tcp.open
;A= ^prefix
;B= IP address or computer name to open to
;C= Port to go to
;D= local port (not required)
;
@Translate.as.haltable(""),
IF{@IP.ADDRESS B;
%Z.tcp.name(B)}^B,
IF{'A!'B!'C "";
"i"_B_"p"_C_IF{D "l"_D}_"e"_@ETH_"I8"^X,
V("O("_("#$%&*?\/:!"#A^p)_",X)")^X,
'X V("C("_p_")"),"";
V(p_"s|2")}^@tcp.connection;
ETH
255.255.255.255.255.255
IP.ADDRESS
B^X,0^x,DO{IF{X#0.?0N^y!0&255=y x+1^x,X'$1.^X}},
x=4&'X

--
You received this message because you are subscribed to the Google Groups "NPR - Report Writing" group.
To post to this group, send email to npr---repo...@googlegroups.com.
To unsubscribe from this group, send email to npr---report-wri...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/npr---report-writing-?hl=en.


Tom Tarbotton

unread,
Feb 4, 2012, 8:41:21 AM2/4/12
to NPR - Report Writing
Other than setting up the message that is being sent my whole code is
just a few lines. As far as maintaining it, other than changing the IP
or port as needed there shouldn't be anything that would ever change.
You might need to increase the ack wait time for some interfaces, but
other than that the code should just work.

I'm sure that using %Z.tcp.close(9) would than the CL(!U) that I'm
using. It just calls $DEV.C(A) so you could also use $DEV.C(9). That
should properly shut down the connection as opposed to CL(!U) which
just cuts it off. However I've been using this code as it is since
2004 and have not had a single problem with it as long as the
receiving system is actually listening.

It looks like your code is checking timeouts and then for a specific
text string in ![/LEN] to know if the message was sent. All you really
need to do is check for an ack at the end. If you get one then all is
well. If you don't then it doesn't matter if it timed out or dropped
or whatever, the message did not get thru. That's all you're returning
in /R.FRAG.VAL anyway - OK or nil.

I think s is the saf containing the data received back from the
listening system which should be the ack. I have no reference for that
other than seeing how it's used in various programs. @tcp.connection
translates as /Z.TCP.CONN. It is nil if the connection failed and the
connection number if the connection was successful. It should contain
the same data as !s|2. You can still reference !s|1 and ![/LEN] as you
have been doing, but I don't see the point. The fewer things for
someone else to get confused by the better.

CL(!S),
%Z.tcp.open(9,IP,PORT),
D(11)^!,
@SEND.MESSAGE,
D(28,13)^!,
@GET.ACKNOWLEDGEMENT,
CL(!U)

GET.ACKNOWLEDGEMENT
0^/ACK,""^ZKRH,
DO{(/ACK<3)&('ZKRH)&(@RUN) H(5),>!^ZKRH,/ACK+1^/ACK},
IF{>! ![>!]^ZP;"ERROR"^FAIL}

RUN
(!s|1)'=0'=4'=6'=9

SEND.MESSAGE
/MSH_D(13)^!,
/PID_D(13)^!,
etc
> steven.m...@nbrhc.on.ca
> *O[""](!,"i"_/IP_"p"_/PORT_IF{F
> "l"_F}_"e"_255.255.255.255.255.255_"I8")^XXX,*
> I...@IP.ADDRESS B;
> %Z.tcp.name(B)}^B,
> IF{'A!'B!'C "";
> "i"_B_"p"_C_IF{D "l"_D}_"e"_@ETH_"I8"^X,
> V("O("_("#$%&*?\/:!"#A^p)_",X)")^X,
> 'X V("C("_p_")"),"";
> V(p_"s|2")}...@tcp.connection;
> ETH
> 255.255.255.255.255.255
> IP.ADDRESS
> B^X,0^x,DO{IF{X#0.?0N^y!0&255=y x+1^x,X'$1.^X}},
> x=4&'X
> On Fri, Feb 3, 2012 at 1:46 PM, Tom Tarbotton <blackcatsma...@gmail.com>wrote:
>
>
>
>
>
>
>
> > If you really want an explanation of the code I can work thru it for
> > you. I did that a few years ago when I needed to write my own tcp
> > interface from OE. But there's an easier way with no syntax checker
> > issues - a Z program that does it for you - Z.tcp.open. You just need
> > to pass in 3 arguments - prefix, IP, and port. The 4th argument -
> > local port - is optional. For prefix I use 9 which crresponds to !. So
> > an example call would be %Z.tcp.open(9,/IP,/PORT)
>
> > ;A= ^prefix
> > ;B= IP address or computer name to open to
> > ;C= Port to go to
> > ;D= local port (not required)
>
> > On Feb 3, 1:18 pm, Steve Mogg <sjm...@gmail.com> wrote:
> > > Hello Everyone,
>
> > > Does anyone have documentation for using the OPEN command, or O, to
> > > open a connection to another server?
> > > O[“”](!,"i"_/IP_"p"_/PORT_IF{F "l"_F}
> > > _"e"_255.255.255.255.255.255_"I8")^XXX,
>
> > > I had to modify a report to fool the syntax checker using the above
> > > syntax. I got the report working but I would like to understand the
> > > code that opens to a remote computer (We have a custom report that
>
> ...
>
> read more »
Reply all
Reply to author
Forward
0 new messages