.. _tcs-python-interface: Tcs Python Interface ******************** .. py:module:: pytcs The **Tcs Python** Interface modules provide low-level access to the Tcs Control Systems. These are not a pure python modules since there are C++ modules from the Tcs system that are linked into the Python code. Note that the modules in the **Tcs Python** Interface will be migrated to the **pytcs** package some time soon. Tcs Python Modules ================== .. _tcssubsystem-label: tcssubsystem.py --------------- .. py:currentmodule:: tcssubsystem .. py:class:: TCSSubSystem(name, system_route) The workhorse class ``TCSSubSystem`` is the only class defined in this file. ``name`` should be a unique string that will not conflict with any other named connection on the computer. ``system_route`` is the :ref:`Route_def-label` URL for the :ref:`Control_System_def-label` you want to connect to. The URL's for a specific :ref:`Control_System_def-label` can be found through the TCSNamed class defined in :ref:`TCSNamed-label`. :: import tcssubsystem import os, random basename = os.path.basename(__file__) + str(random.random()) # a unique name tracker = tcssubsystem.TCSSubSystem( basename, 'tcp://192.168.66.31:30400') .. py:classmethod:: generic_handler_call() The client object returned by will contact the :ref:`Control_System_def-label` and generate class methods for all the :ref:`Handler_def-label` available in the :ref:`Control_System_def-label`. For example, the tracker :ref:`Control_System_def-label` has a :ref:`Handler_def-label` named ``moveRTF`` which is called with a set of RTF coordinates. Accessing this :ref:`Handler_def-label` through ``tcssubsystem.py`` is as simple as :: tracker.moveRTF(x=0.0, y=0.0, z=0.0, theta=0.0, phi=0.0, rho=0.0, type='abs', vel='TRK_FAST') The :ref:`Handler_def-label` description for a :ref:`Control_System_def-label` can be found in the documentation for that specific :ref:`Control_System_def-label`. [DEFINE LOCATION] .. py:classmethod:: help(method_name) This client object has a help function which will list the documentation for a single method if the optional parameter ``handler_name`` is given or for all commands if no command is given in the function call. :: tracker.help(command='moveRTF') tracker::moveRTF (8182) Sends a move command in RTF coordinates. Sends with tmcs_mova_rtf or tmcs_movr_rtf command depending on the type parameter. Required parameters are: phi - rho - theta - type - vel - x - y - z - or tracker.help() . . . .. py:classmethod:: wait(msgid) The wait() function will wait on a pending command given by ``msgid`` and blah, blah, blah. [Find out what this does!] :: tracker.wait(msgid) (this does something) .. _TCSNamed-label: TCSNamed.py ----------- .. py:currentmodule:: TCSNamed .. py:class:: TCSNamed(name, named_route) The Tcs interface uses a number of network protocols, addresses, and ports numbers. Rather than making the users remember the URL's for :ref:`Route_def-label`, :ref:`Event_Route_def-label`, and other configurable variables, the Tcs interface provides a standalone process knowns as ``tcsnamed`` that will respond to queries and provide the answers. See the ``tcsnamed`` documentation [DEFINE LOCATION] for further details The ``TCSNamed.py`` interface provides a connection to the tcsnamed server and that will do query look ups. Of course, you must know the route to ``tcsnamed`` prior to calling it. The commonly used named_route is defined in the HET Interface package ``tcsutils.py``, see :ref:`tcsutils-label` :: import TCSNamed named_route = 'tcp://192.168.66.99:30000' n = TCSNamed.TCSNamed(named_route) .. py:classmethod:: lookup(key) To use the named service the method ``lookup`` is provided. The key names that can be queried are defined in [DEFINE LOCATION] n.lookup(key='tcs-route') 'tcp://192.168.66.31:30300' .. _TCSLog-label: TCSLog.py --------- .. py:currentmodule:: TCSLog .. py:class:: TCSLog(name, logRelayRoute, namedRoute) The **Tcs Python** interface does not provide the full capability for python scripts to issue :ref:`Events_def-label` to other system. However, it does provide a mechanism to issue log :ref:`Events_def-label` of the type ``pytcs..log_[debug|info|warn|error|fatal|alarm]`` (where is your unique name for the connection to the :ref:`logrelay-label`:. These :ref:`Events_def-label` can be monitored by other programs and will be placed in the nightly database log. See the documentation for :ref:`Control_System_def-label` and related :ref:`Events_def-label` [DEFINE LOCATION] for further details. The class TCSLog requires a unique name and can take either the ``logRelayRoute`` or the ``namedRoute``. It is not necessary to pass in both the ``logRelayRoute`` and the ``namedRoute``. If the ``logRelayRoute`` is not give but the ``namedRoute`` is, then TCSLog will do a look up of ``logRelayRoute`` through :ref:`TCSNamed-label`:. If ``logRelayRoute`` is given, then TCSLog uses it directly and does not use the ``namedRoute`` at all. .. py:classmethod:: log_(fmt, *args) The available methods are: .. py:method:: log_debug(fmt, *args) .. py:method:: log_info(fmt, *args) .. py:method:: log_warn(fmt, *args) .. py:method:: log_error(fmt, *args) .. py:method:: log_fatal(fmt, *args) .. py:method:: log_alarm(fmt, *args) Where ``fmt`` and ``args`` are treated as a python format string and a list of arguments. As an illustration of the use of this module this code sequence will issue a log_debug message :: import TCSLog named_route = 'tcp://192.168.66.99:30000' l = TCSLog.TCSLog( 'my_random_name', namedRoute=named_route ) l.log_debug('This is test number {} of {}', 1, 2) The result as shown by watching the log relay server output with the the :ref:`monitor-label` program is :: > monitor -v -r { "time": "2017-08-08T16:42:41.108", "pytcs.my_random_name.log_debug": { "file": "", "line": 1 "function": "", "message": "This is test number 1 of 2", "__system": "pytcs", "__source": "my_random_name", "__key": "log_debug", "__data_time": "1502210561.108609033", "__wire_time": "1502210561.108689667", "__data": "false" } } .. _tcsdb-label: tcsdb.py -------- .. py:currentmodule:: tcsdb .. py:class:: tcsdb(dbname) :ref:`Events_def-label` that are generated by the :ref:`Control_System_def-label` and by scripts are recorded in an sqlite3 database. The tcsdb module provides an interface to the daily sqlite3 database or databases generated by the ``monitor`` script. :: import tcsdb d = tcsdb.tcsdb('/opt/het/hetdex/logs/Mon/2017/08/20170802T180005.db') The start/stop times of the database in Unix seconds can be obtained with .. py:classmethod:: start() .. py:classmethod:: stop() for example :: d.start() 1501624142.5000026 d.stop() 1501804799.9950001 .. py:classmethod:: events() The events method returns a list of all event names seen in the database. Events have the naming convention ``system.source.key``. Note that they are not listed in alphabetical order. However, this is a list so you may invoke the sort() method if you want alphabetical ordering. :: from pprint import pprint pprint( d.events()) ['tcs.tracker.position', 'tracker.tmcs.status', 'tcs.root.ra_dec', 'pfip.VirusMonitor2.status', 'apc.VEncl2Misc.VEncl2MiscPDU_status', 'apc.ControlRoom.ControlRoomPDU_status', 'log-relay.receiver.heartbeat', 'legacy.receiver.heartbeat', 'apc.PFIP.PFIPPDU_status', 'tcs.root.ra_dec', ... ] .. py:classmethod:: keys('event_name') This method will provide a list of the keywords for a specific event given in the argument. :: pprint(d.keys('tcs.root.ra_dec')) ['__data', '__data_time', '__key', '__pid', '__source', '__system', '__wire_time', 'az', 'correction.emp.x', 'correction.emp.y', 'correction.sky.x', 'correction.sky.y', 'dec', 'dec_2000', 'dec_offset', 'el', 'hour_angle', 'itf.phi', 'itf.rho', 'itf.t', 'itf.theta', 'itf.x', 'itf.y', 'itf.z', 'lst', 'offset.focus.w', 'offset.rho', 'offset.sky.x', 'offset.sky.y', 'offset.tiptilt.phi', 'offset.tiptilt.theta', 'parallactic_angle', 'ra', 'ra_2000', 'ra_offset', 'setup', 'zenith_distance'] .. py:classmethod:: system.source.key.keword() The actual values of a particular keyword can be found by using the constructed method name based on the event/keyword name. These functions are of the form ``system.source.key.keword()`` and they return two lists; the first is a list of the time stamps of the events and the second being the values of the event/keyword for the time stamp. The following example illustrates this. Note that the daily database is quite large and it may take a few minutes for the database to be read. :: # Note that this can take a few minutes on a slow machine ra_t, ra = d.tcs.root.ra_dec.ra() len(ra_t) 418498 len(ra) 418498 pprint(ra_t) [1501696806.765 1501696806.968 1501696807.170 1501696807.373 1501696807.575 ... ] pprint(ra) [5.709843 5.709899 5.709956 5.710012 5.710069 ... ] It is also possible to return a full event and do the parsing yourself. :: radec_t, radec = d.tcs.root.ra_dec() len(radec) 426640 # Note that there are more ra_dec events then there are ra keys # shown above. Events may have missing keys and if you do your own # parsing you need to be aware of this pprint(radec) ['__data': 'false', '__data_time': 1501696806.765, '__key': 'ra_dec', '__pid': 57480, '__source': 'root', '__system': 'tcs', '__wire_time': 1501696806.782876, 'az': 285.551159, 'correction': {'emp': {'x': 0, 'y': 0}, 'sky': {'x': 0, 'y': 0}}, 'dec': 34.051544, 'dec_2000': 34.042592, 'dec_offset': 0, 'el': 55.079064, 'hour_angle': 2.125675, 'itf': {'phi': -0.001449, 'rho': -0.040128, 't': 1501696806.765, 'theta': 0.220831, 'x': 11.001893, 'y': -1836.970907, 'z': -4.965364}, 'lst': 7.835518, 'offset': {'focus': {'w': -1.95}, 'rho': 0, 'sky': {'x': 0, 'y': 0}, 'tiptilt': {'phi': 0, 'theta': 0}}, 'parallactic_angle': 262.910534, 'ra': 5.709843, 'ra_2000': 5.690575, 'ra_offset': 0, 'setup': 'false', 'zenith_distance': 34.920936}, {'__data': 'false', '__data_time': 1501696806.968, '__key': 'ra_dec', '__pid': 57480, '__source': 'root', '__system': 'tcs', '__wire_time': 1501696806.9851422, 'az': 285.551159, 'correction': {'emp': {'x': 0, 'y': 0}, 'sky': {'x': 0, 'y': 0}}, 'dec': 34.051544, 'dec_2000': 34.042593, 'dec_offset': 0, 'el': 55.079064, 'hour_angle': 2.125675, 'itf': {'phi': -0.001449, 'rho': -0.040128, 't': 1501696806.968, 'theta': 0.220831, 'x': 11.001792, 'y': -1836.970806, 'z': -4.965364}, 'lst': 7.835574, 'offset': {'focus': {'w': -1.95}, 'rho': 0, 'sky': {'x': 0, 'y': 0}, 'tiptilt': {'phi': 0, 'theta': 0}}, 'parallactic_angle': 262.910534, 'ra': 5.709899, 'ra_2000': 5.690631, 'ra_offset': 0, 'setup': 'false', 'zenith_distance': 34.920936}, ...] ra = [] for r in radec: try: r.append(r['ra']) except: pass Keys and events are more fully described in [DEFINE LOCATION]