kevingill1966 / vavista-rpc

RPC access to VA VistA (using FMQL implementation)
1 stars 4 forks source link

vavista.rpc (Python access to VistA RPCs)

The vavista.rpc module provides a very simple mechanism for calling VistA RPCs. Simply connect, and call the PRC you are interested in.

The vavista.rpc module does not depend on the M interfaces modules, i.e. it is not bound to a GT.M interpreter. It communicates client server over a TCP connection.

Credit

The brokerRPC.py code belongs to Caregraph.org's FMQL product. I put it here for ease of reuse.

Setup Information

Application Context

In order to access RPCs in Vista, you need to have a valid Application Context Id.

The application context provides the security management for RPCs. The context must exist in the table "OPTION" (19), with the Type = "Broker (client/server)" (B).

I created an option called "RPC DEMO". I did not assign it any RPCs, so only public RPCs should be available. ::

Select OPTION NAME: RPC DEMO
Not a known package or a local namespace.
  Are you adding 'RPC DEMO' as a new OPTION (the 10852ND)? No// y  (Yes)
     OPTION MENU TEXT: RPC DEMO
MENU TEXT: RPC DEMO// 
TYPE: B  Broker (Client/Server)

RPC Port

VistA provides a RPC (remote procedure call) broker. This sits on an agreed port.

Clients connect to the broker and create a session. In the GT.M context, a server is forked to process the request. The server waits, receives a request, processes it and returns a response. The mechanism is single-threaded and synchronous.

9210 - where is this configured ?? TODO RPC BROKER SITE PARAMETERS seems do describe other sites configurations, not this sites.

How individual Requests are Configured in Vista

The available RPCs are listed in Fileman file "REMOTE PROCEDURE" (8994).

TODO: rpc availability what is public??

The RPC may be restricted to one application. TODO: how???

How to set up package: TODO

The Option allows you to assign restricted RPCs to the application context.

All results from RPCs are returned as a single string. RPCs have the following return types::

   1        SINGLE VALUE (string)
   2        ARRAY (string split by \r\n)
   3        WORD PROCESSING (string split by \r\n)
   4        GLOBAL ARRAY (string split by \r\n)
   5        GLOBAL INSTANCE (string)

Parameters can be::

   1        LITERAL - PLiteral
   2        LIST - PList
   2        Word Processing - PLiteral
   4        REFERENCE - PReference

connect

Create a connection::

from vavista import rpc, PLiteral, PList, PReference
c = rpc.connect(hostname, port, access-code, verify-code, context, debug=False)

    - TCP communication information (hostname, port)
    - VistA's security (access, verify)
    - application context - See note above
    - the debug flag is useful for interactive use.

This function creates a connection to the VISTA RPC server.

Methods

invoke calls the rpc identified by rpcid, passing through all parameters. l_invoke converts the response to a list (spliting in '\r\n'). ::

c.invoke(rpcid, [param1, [param2, ... [param n]], return_formatter=None)
c.l_invoke(rpcid, [param1, [param2, ... [param n]])

return_formatter is a function, which take one parameter, the return value. If a return_formatter is passed in, it processes the return value, before the invoke method returns. An example is the list formatter, which is passed in by the l_invoke method.

Parameter Types

The rpc module provides a number of helper classes to convert from your internal type to the encoded string for the wire.::

PLiteral   - value is a literal
PList      - value is a list
PReference - value is a reference
PEncoded   - the application encode the value, pass it straight through

If a parameter is not one of the above, if it is a dict, it is passed as a PList, otherwise it is passed as a PLiteral.

Example Code

From prompt::

$ python
>>> from vavista.rpc import connect, PLiteral, PList, PReference, PEncoded
>>> c = connect('localhost', 9210, "VISTAIS#1", "#1ISVISTA", "RPC DEMO", debug=True)

>>> print c.invoke("XWB EGCHO STRING", PLiteral("THIS IS A STRING"))
THIS IS A STRING

# types other than dicts default to type PLiteral
>>> print c.invoke("XWB EGCHO STRING", "THIS IS A STRING")
THIS IS A STRING

# If the return type is a List, under normal conventions, the return
# values are separated via '\r\n' characters.
>>> print c.invoke("XWB EGCHO LIST")[:50]
List Item #1
List Item #2
List Item #3
List Ite

# However, you can make the call extract the return type
>>> print c.l_invoke("XWB EGCHO LIST")[:4]
['List Item #1', 'List Item #2', 'List Item #3', 'List Item #4']

# This is how to pass an Array - M does not have a real concept of arrays.
# It just has a dict style data type. This can be generated by either an list of 
# tuples or a dict.
>>> print c.invoke("XWB EGCHO SORT LIST", "LO", PList([('1', ''), ('10', ''), ('190', ''), ('89', '')]))
['1', '10', '89', '190']

# A dict is assumed to be a list
>>> print c.invoke("XWB EGCHO SORT LIST", "HI", {'1': '', '10': '', '190': '', '89': ''})
['190', '89', '10', '1']

# You can encode the parameter yourself if you know what you are doing
>>> print c.invoke("XWB EGCHO STRING", PEncoded("0014I ENCODED THISf"))
I ENCODED THIS

# Reference parameters are passed using the PReference type.
>>> print c.invoke("XWB GET VARIABLE VALUE", PReference("DUZ"))
10000000020
>>> print c.invoke("XWB GET VARIABLE VALUE", PReference("DUZ(0)"))
@

Simple script::

import getopt, sys

from vavista.rpc import connect, PLiteral, PList, PReference

context = "RPC DEMO"   # see not above about creating this option.

opts, args = getopt.getopt(sys.argv[1:], "")
if len(args) < 4:
    print args
    sys.stderr.write("Enter <host> <port> <access> <verify>\n")
    sys.exit(1)

host, port, access, verify = args[0], int(args[1]), args[2], args[3]

c = connect(host, port, access, verify, context)

# Prints out "THIS IS A STRING"
print c.invoke("XWB EGCHO STRING", "THIS IS A STRING")

# This "list" RPC returns a list of items delimited by the DOS line ending
print c.invoke("XWB EGCHO LIST")[:100]
print c.l_invoke("XWB EGCHO LIST")[:10]

# This "list" RPC returns a list of items delimited by the DOS line ending
l = c.l_invoke("XWB EGCHO BIG LIST")
print l[:5], " ... ", l[-5:]

# This is how 'arrays' are passed
print c.l_invoke("XWB EGCHO SORT LIST", "HI", {'1': '', '10': '', '190': 'x', '89': ''})

print c.l_invoke("XWB EGCHO SORT LIST", "LO", PList([('1', ''), ('10', ''), ('190', 'x'), ('89', '')]))

References