The tag utility

Here's an example of a command-line session using the tag utility with the simulator device, sim000:

[wintermute@ttyp0] tag -m1000 s_pc10b9=1 s_pc8=259 \
s_pc9b0 s_pc9b1 s_pc9b2
Data Set 217, acquired 2003 10 09 14:32:36.16915388 (delta 0 ns)
Tag "s_pc9b0" 0
Tag "s_pc9b1" 1
Tag "s_pc9b2" 0

Data Set 227, acquired 2003 10 09 14:32:37.17816996 (delta 0 ns)
Tag "s_pc9b0" 0
Tag "s_pc9b1" 0
Tag "s_pc9b2" 1

Data Set 237, acquired 2003 10 09 14:32:38.18866655 (delta 0 ns)
Tag "s_pc9b0" 0
Tag "s_pc9b1" 1
Tag "s_pc9b2" 1

Data Set 247, acquired 2003 10 09 14:32:39.19768263 (delta 0 ns)
Tag "s_pc9b0" 0
Tag "s_pc9b1" 0
Tag "s_pc9b2" 0

Data Set 257, acquired 2003 10 09 14:32:40.20668071 (delta 0 ns)
Tag "s_pc9b0" 0
Tag "s_pc9b1" 1
Tag "s_pc9b2" 0

In this example, I've instructed tag to set the tag s_pc10b9 to the value 1, and the tag s_pc8 to the value 259 (decimal). Then, via the -m option, I told tag to dump out the values for the three tags s_pc9b0, s_pc9b1, and s_pc9b2 every 1000 milliseconds. I killed the tag program via CtrlC after five samples were printed.

Part of the functionality present in the tag utility is identical to that from showsamp, namely the setup of shared memory, mapping the shared memory to a pointer, and setting up the various utility pointers to point into items of interest in the shared memory.

Basically, tag can do three things:

  1. Set a tag to a particular raw or converted value.
  2. Display the raw and converted value of a tag once.
  3. Display the raw and converted value of a tag repeatedly.

Obviously, the last two are mutually exclusive. Setting a tag is performed within the option processor optproc(), while all display activities are deferred until after the shared memory has been set up. This is because in order to set a tag, we don't need the shared memory—we can talk directly to the device driver and get it to perform the work. However, in order to read a tag, we need to have access to the shared memory, because that's where we will be reading the tag from.

To set a tag, the library function adios_set_tag_raw() or adios_set_tag_span() is called. What I mean by “raw” is that the value that you are writing is not interpreted by the configuration file's optional span keyword—on a card with a 12-bit D/A converter, the raw value would be between 0 and 4095. Contrast that with the span version, where we look at the configuration file and determine, for example, that the analog output point has a range of -5 to +5, so only values in that range would be valid. (They're converted from floating-point to raw values by the library anyway—this is a nicety for the applications programmer.)

To display a tag, the option processor adds the tag into a list of tags. After option processing has finished, the list of tags is finalized (by looking into shared memory and finding the actual offsets into the data set), and displayed. The function fixup_display_list() that does this is long and boring. Basically, what it does is match the channel, port, and, in the case of digital I/O points, the bit number, against the data contained in the CIS, and then it determines the offset.

You can see the result of this by looking at the function that actually displays the data, display_list():

static int bits [16] = {
  0x0001, 0x0002, 0x0004, 0x0008, 
  0x0010, 0x0020, 0x0040, 0x0080,
  0x0100, 0x0200, 0x0400, 0x0800,
  0x1000, 0x2000, 0x4000, 0x8000};

static void
display_list (void)
  adios_data_header_t  *dhead;
  int                  ds;
  int                  i;
  char                 buf [BUFSIZ];
  uint16_t             *u16;
  uint8_t              *u8;
  struct tm            *tm;
  double               v;

  ds = daq -> head;
  dhead = (adios_data_header_t *) (database
        + ds * daq -> element_size);

  i = dhead -> t0ns / 1000000000LL;
  tm = localtime (&i);
  strftime (buf, sizeof (buf), "%Y %m %d %H:%M:%S", tm);
  u16 = (uint16_t *) (dhead + 1);   // get ptr to sample set
  u8 = (uint8_t *) u16;             // get 8-bit version too

  printf ("Data Set %d, acquired %s.%09lld (delta %lld ns)\n", 
          ds, buf, dhead -> t1ns % 1000000000LL, 
          dhead -> t1ns - dhead -> t0ns);

  for (i = 0; i < ntags; i++) {
    if (tags [i].type == 'a') {
      printf ("Tag \"%s\" raw value 0x%04X", tags [i].name, 
              u16 [tags [i].offset / 2]);
      if (tags [i].span_low != tags [i].span_high) {
        v = u16 [tags [i].offset / 2];
        v = v / 4095. * (tags [i].span_high - tags [i].span_low)
          + tags [i].span_low;
        printf (" span-compensated value %s%g",
                v > 0.0 ? "+" : "", v);
      printf ("\n");
    if (tags [i].type == 'd') {
      printf ("Tag \"%s\" %d\n", tags [i].name,
        !!(u8 [tags [i].offset] & bits [tags [i].bitnum]));
  printf ("\n");

It has similar date and time printing and calculation logic as the showsamp utility. The for loop runs through all of the tags, and determines if they are analog or digital. If the tag is an analog tag, we display the raw value. If the tag has a span range that's not zero, we calculate and display the converted value as well. In the case of a digital value, we simply fetch the appropriate byte, and mask it off with the appropriate bit number.

The fine distinction between displaying the values once and displaying the values repeatedly is handled by main():

do {
  display_list ();
  if (optm != -1) {
    delay (optm);
} while (optm != -1);

The do-while ensures that the code executes at least once. The delay factor, optm (from the command line -m), is used as both a flag (when the value is -1, meaning “display once”) and the actual delay count in milliseconds (if not -1).