3. Tcs Python Interface

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.

3.1. Tcs Python Modules

3.1.1. tcssubsystem.py

class tcssubsystem.TCSSubSystem(name, system_route)[source]

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 Route URL for the Control System you want to connect to. The URL’s for a specific Control System can be found through the TCSNamed class defined in TCSNamed.py.

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')
classmethod tcssubsystem.generic_handler_call()

The client object returned by will contact the Control System and generate class methods for all the Handler available in the Control System. For example, the tracker Control System has a Handler named moveRTF which is called with a set of RTF coordinates. Accessing this Handler 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 Handler description for a Control System can be found in the documentation for that specific Control System. [DEFINE LOCATION]

classmethod tcssubsystem.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<double> -
    rho<double> -
    theta<double> -
    type<string> -
    vel<string> -
    x<double> -
    y<double> -
    z<double> -

  or tracker.help()
  .
  .
  .
classmethod tcssubsystem.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)

3.1.2. TCSNamed.py

class TCSNamed.TCSNamed(name, named_route)[source]

The Tcs interface uses a number of network protocols, addresses, and ports numbers. Rather than making the users remember the URL’s for Route, Event-route, 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 tcsutils.py

import TCSNamed

named_route = 'tcp://192.168.66.99:30000'

n = TCSNamed.TCSNamed(named_route)
classmethod TCSNamed.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

3.1.3. TCSLog.py

class TCSLog.TCSLog(name, logRelayRoute, namedRoute)[source]

The Tcs Python interface does not provide the full capability for python scripts to issue Events to other system. However, it does provide a mechanism to issue log Events of the type pytcs.<sysname>.log_[debug|info|warn|error|fatal|alarm] (where <sysname> is your unique name for the connection to the logrelay-label:. These Events can be monitored by other programs and will be placed in the nightly database log. See the documentation for Control System and related Events [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 TCSNamed.py:. If logRelayRoute is given, then TCSLog uses it directly and does not use the namedRoute at all.

classmethod TCSLog.log_(fmt, *args)

The available methods are:

TCSLog.log_debug(fmt, *args)
TCSLog.log_info(fmt, *args)
TCSLog.log_warn(fmt, *args)
TCSLog.log_error(fmt, *args)
TCSLog.log_fatal(fmt, *args)
TCSLog.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 monitor program is

> monitor -v -r

{ "time": "2017-08-08T16:42:41.108",
  "pytcs.my_random_name.log_debug": {
      "file": "<stdin>",
      "line": 1
      "function":     "<module>",
      "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"
} }

3.1.4. tcsdb.py

class tcsdb.tcsdb(dbname)[source]

Events that are generated by the Control System 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

classmethod tcsdb.start()
classmethod tcsdb.stop()

for example

d.start()
1501624142.5000026
d.stop()
1501804799.9950001
classmethod tcsdb.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',
 ...
]
classmethod tcsdb.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']
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]

Table Of Contents

Previous topic

2. Installation

Next topic

4. Hetlib Python Interface

This Page