zingo 1.0.0
A command line tool for interacting with a Tango control system
Requires Python: >=3.9

# Zingo!

Zingo is a command line interface to query a Tango control system.

It can do many of the basic tasks that are often done using Jive, but
is entirely terminal driven, and tries to do things in shell-friendly
ways. That means that it should work well with other shell utilities,
e.g. via pipes, for further filtering or processing, or writing to
files.

Note: Zingo is basically "read only" and can't be used to make changes
to the Tango DB or devices. However it has limited ability to run
device commands.

It can output most things in "human readable", TSV (tab separated) or
JSON formats.  If "rich" is installed, most commands automatically use
it to show nice colored tables.

Most commands query the Tango DB, and you can use regular expressions
to filter most inputs (devices, servers, properties, ...). Zingo
is intended to be efficient, and tries to use minimal database queries.


Features
========

- Listing devices based on filters
- Showing information about devices
- Listing device attributes along with their details
- Reading device attributes, instantaneously or continuously
- Reading and searching device and attribute properties using regexps
- Running device commands (experimental!)
- Collecting and presenting "black box" information about devices
- Receiving Tango device log messages (experimental!)
- Easy to use with other shell command like grep, cut, sort, ...


Requirements
============
- python >= 3.6
- pytango
- (optional) rich


Installation
========

Currently there's no real package, install by checking out the repo, cd:ing into it and running:

    pip install -e .

Then you can update by just doing git pull in the repo dir.

Or, if you have `uv` you can just do:

    uv tool install git+https://gitlab.maxiv.lu.se/johfor/zingo.git

Add `--with rich` if you want fancier output with tables and colors.


Usage
========

To get a summary of all commands and global options, use `zingo --help`.
Help on individual commands is available as `zingo <cmd> --help`.

If `rich` is installed, you should get some nicely formatted tables of
output by default, unless piping to another command. However this can
be annoying if the output is very wide. You can turn the tables off by
adding the `--raw` argument.

Here follow some examples.

Devices
----

Show information about all devices of a certain class (regex allowed)

    $ zingo dev --class TangoTest
    name           state    class      instance        host
    sys/tg_test/1  RUNNING  TangoTest  TangoTest/test  w-johfor-pc-0


Show more details about a device (several devices can be given, or you
can use regex to match many)

    $ zingo dev sys/tg_test/1 --details
    name           class      instance        host           started                      stopped                      error
    sys/tg_test/1  TangoTest  TangoTest/test  w-johfor-pc-0  29th April 2021 at 08:38:56  29th April 2021 at 08:38:55


List the names of all motors in a given sardana pool

    $ zingo dev --class Motor --server Pool/1 -l
    motor/motctrl02/1
    motor/motctrl02/2
    motor/motctrl02/3
    motor/motctrl02/4

(The "-l" flag means that zingo will only output the names, which is
often what you want for further shell scripting.)


Attributes
----

List all attributes of some devices:

    $ zingo attr sys/tg_test/1 sys/tg_test/2
    sys/tg_test/1  ampli
    sys/tg_test/1  boolean_scalar
    sys/tg_test/1  double_scalar
    sys/tg_test/1  double_scalar_rww
    ...
    sys/tg_test/2  ampli
    sys/tg_test/2  boolean_scalar
    sys/tg_test/2  double_scalar
    sys/tg_test/2  double_scalar_rww
    ...


Show all attributes of a device, with some details:

    $ zingo attr sys/tg_test/1 -d
    sys/tg_test/1  State                DevState    SCALAR       READ
    sys/tg_test/1  Status               DevString   SCALAR       READ
    sys/tg_test/1  ampli                DevDouble   SCALAR       WRITE
    ...

(There are more flags for further information, see `zingo attr --help`)

Read some attributes of some devices:

    $ zingo read x1/alarm/bfkyduw x1/alarm/vbztd -a state -a Status
    x1/alarm/bfkyduw	state	ON
    x1/alarm/bfkyduw	Status	The device is in ON state.
    x1/alarm/vbztd	state	ON
    x1/alarm/vbztd	Status	The device is in ON state.

The `-c` (`--continuous`) flag means that Zingo will keep reading and
printing values until interrupted. It will try to subscribe to
attribute change events.  If not available, it falls back to
polling. If you want polling in any case, use the `-f`
(`--force-polling`) flag. You can control the polling period with `-p`
(`--period`).

Properties
----

Read some properties of a device:

    $ zingo prop sys/tg_test/2 -p polled_attr -p OtherProp
    Device         Property     Line  Value
    sys/tg_test/2  polled_attr  0     boolean_scalar
    sys/tg_test/2  polled_attr  1     3000
    sys/tg_test/2  OtherProp    0     I am another property!

Search for the value "8762" in device properties of devices matching a pattern:

    $ zingo prop "sys/tg_test/.*" --search 8762

By instead using the `attrprop` command, we can do similar things with
attribute properties.

For *writing* properties from the shell, I recommend the official
`tango_admin` utility, as this is currently outside of the scope of
zingo. Or, for more controlled usage, use `dsconfig` (can be installed
with `pip`).


Shell tricks
----

Combine Zingo with other shell tools to do much more sophisticated things:

Count how many of a set of devices are in some given state (`-r` is a
shortcut for reading `State` of each matching device):

    $ zingo dev ".*alarm.*" -r | grep -w ALARM | wc -l
    7

(Here, "-w" to grep ensures that we only match on the stand-alone word
"ALARM", not the letters "ALARM" e.g. as part of a device name. Also,
note that surrounding the device pattern with quotes ensures that the
shell doesn't try to match it against the filesytem.)

Piping the output of zingo back into zingo can be useful! Read the
Status attribute on all power supplies that are in FAULT:

    $ zingo dev --class PowerSupply -r | grep -w FAULT | cut -f1 | xargs zingo read -a Status
    ps/0/agcsfzptuo	Status	Could not connect to hardware.
    ps/3/byqrmze	Status	Hardware reports error 871.
    ps/4/deciqvgf	Status	Hardware reports error 98.
    ...

(Here, "cut" is used to get only the first field of the output - the
device names - and "xargs" is used to insert the results into the
commandline of the second call to zingo.)


Black box
-----

The "black box" is a low level Tango feature that shows the latest
client calls to a given device. It is sometimes very useful for
diagnosing issues but can be cumbersome to use. Zingo wraps this
functionality to make it a bit more accessible. The `bbox` command can
be pointed it to one or more devices. It has three modes of operation:

* If no flags are given, it will retrieve the "black box" info once
  from each device and print it out in a slightly more readable
  fashion.
* With the `--tail` flag, "black box" will be called on the device(s)
  periodically, and new events are printed out as they come in, until
  interrupted by Ctrl+C. Somewhat like tailing a logfile.
* With the `--statistics` flag, the command will collect "black box"
  information periodically until interrupted. Then it prints out a
  summary of the calls, most common first.

Note that a limitation of the black box is that it stores (by default)
only the 50 latest events each time it's called. That means that if
the frequency of client calls is very high, you may miss events. It's
not possible to avoid this in general, but zingo will warn if the risk
exists. The `--period` flag can be used to increase the rate of calls
if this is an issue. On the other hand, increasing the frequency too
much can result in the actual "black box" calls dominating the
statistics :)

To filter out the blackbox calls themselves (which are usually not interesting):

    $ zingo bbox -f sys/tg_test/1 | grep -v blackbox


Tango Logging (experimental)
----

Zingo has experimental support for the "Tango logging system". The
idea is to attach to a running, remote device (or several), set a log
level and print any log messages on the terminal as they arrive. Use
Ctrl-C to exit.

    $ zingo log sys/tg_test/1 -l 5
    2021-06-29T17:19:20.444000	DEBUG	sys/tg_test/2	DataGenerator::generating data
    2021-06-29T17:19:22.029000	DEBUG	sys/tg_test/2	TangoTest::always_executed_hook()  sys/tg_test/2
    2021-06-29T17:19:22.029000	DEBUG	sys/tg_test/2	TangoTest::read_attr_hardware(vector<long> &attr_list) entering...
    ...

The log level given corresponds to the "trace level" that can be set
when starting a Tango server, using `-v` (see `log --help` for a
detailed list)

Note: the previous logging settings *should* be restored when the
program exits (on break), but this is a hacky solution and it's not
well tested. The logging settings are not changed permamently and will
be restored anyway at the next device restart.

Note: this feature can only pick up log messages that go through the
Tango logging system, not e.g. if a normal python logger is used in
the device code.

Under the hood, zingo creates a temporary "nodb" device, conforming to
the Tango logger standard, running in-process, and configures the
remote device to talk directly to this logger device. This way no new
device is created in the Tango database, however there is some trace
left in the remote device's `__SubDevice` property. This should have
no impact on the control system but may look slightly strange. It is
always called `zingo/<your local username>/logger`.

It's possible that Tango gets confused about hostnames. For this
command to work, communication must be established between the remote
Tango device and Zingo. Zingo tries to guess which hostname to use,
but if you know what your own hostname is "supposed" to be, you can
specify it using `--host`. You can also specify which port to use for
communication using `--port`. Otherwise a random free port will be
selected, which is usually fine, but network setups may vary.