Package de.desy.tine.histUtils

Source Code of de.desy.tine.histUtils.THistory

package de.desy.tine.histUtils;

import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;
import java.util.Vector;
import de.desy.tine.addrUtils.TSrvEntry;
import de.desy.tine.client.TLink;
import de.desy.tine.dataUtils.*;
import de.desy.tine.definitions.*;
import de.desy.tine.exceptions.UnresolvedAddressException;
import de.desy.tine.queryUtils.TPropertyQuery;
import de.desy.tine.queryUtils.TQuery;
import de.desy.tine.server.histories.THistoryRecordStruct;
import de.desy.tine.server.histories.THistorySpecification;
import de.desy.tine.server.logger.DbgLog;
import de.desy.tine.server.logger.MsgLog;
import de.desy.tine.types.*;
import de.desy.tine.stringUtils.*;

/**
* THistory is a utility class with numerous static methods for access
* archive data from either the central archive system of the local
* history subsystems of targeted servers. 
*
* @author duval
*/
public final class THistory
{
  private static final int HEARTBEAT = 60 * 15;
  private static final int EXTENSION_LIMIT = 15*60;
  private static final int MAX_HSTCFG_SIZE = 500;
  private static HstCfg[] cfgs = new HstCfg[MAX_HSTCFG_SIZE];
  private static McaCfg[] mcacfgs = new McaCfg[MAX_HSTCFG_SIZE];
  private static PrfCfg[] prfcfgs = new PrfCfg[MAX_HSTCFG_SIZE];
  private static HstView[] views = new HstView[MAX_HSTCFG_SIZE];
  private static boolean isInitialized = false;
  private static void initCfgs()
  {
    if (isInitialized) return;
    for (int i=0; i<MAX_HSTCFG_SIZE; i++)
    {
      cfgs[i] = new HstCfg();
      mcacfgs[i] = new McaCfg();
      prfcfgs[i] = new PrfCfg();
      views[i] = new HstView();
    }
    isInitialized = true;
  }
  private static boolean isCentralArchiveServer(String server)
  {
    return server.endsWith("HISTORY");
  }
  private static boolean isCentralArchiver(String server)
  {
    return server.endsWith("ARCHIVER");
  }
  private static String getQueryPropertyRoot(String property)
  {
    int slen = property.length();
    if (property.endsWith(".HIST") || property.endsWith(".ARCH"))
      return property.substring(0, slen-5);
    if (property.endsWith(".HST") || property.endsWith(".ARC"))
      return property.substring(0, slen-4);
    if (property.endsWith(".AR"))
      return property.substring(0, slen-3);
    return property;
  }
  private static boolean isScheduledProperty(String property)
  {
    int idx = property.lastIndexOf('.');
    if (idx < 0) return false;
    String dec = property.substring(idx+1);
    if (dec == null) return false;
    return dec.toUpperCase().startsWith("SCH");
  }
  private static boolean isArchiveQueryProperty(String property)
  {
    if (property.endsWith(".HIST")) return true;
    if (property.endsWith(".HST")) return true;
    if (property.endsWith(".ARCH")) return true;
    if (property.endsWith(".ARC")) return true;
    if (property.endsWith(".AR")) return true;
    return false;
  }
  private static synchronized int getArchivedNumPointsInInterval(String context,String server, String property, String device, double start, double stop,int timeout)
  {
    int cc = 0;
    int[] startstop = new int[2];
    int[] npts = new int[1];
    TDataType dout, din;
   
    if (server == null && context == null) return TErrorList.argument_list_error;
    if (context == null) context = "DEFAULT";
    if (server == null) server = "HISTORY"; // assume the standard central archive server for the given context
    startstop[0] = (int)start;
    startstop[1] = (int)stop;
    din = new TDataType(startstop);
    dout = new TDataType(npts);
   
    if (isCentralArchiver(server))
    { // trap a potential redirection problem here:
      if (device.compareToIgnoreCase("keyword") == 0) device = "#0";
    }
    String dname = new String("/" + context + "/" + server + "/" + device);
    String pname = new String(property);
    if (!isCentralArchiveServer(server))
    { // not a call to a central archive server
      if (!isArchiveQueryProperty(property))
        pname = pname + ".HIST"; // meta extension not given, try this ...
    }
    try
    {
      TLink tl = new TLink(dname,pname,dout,din,TAccess.CA_READ);
      cc = tl.execute(timeout, true);
      tl.close();
    }
    catch (UnresolvedAddressException e)
    {
      cc = TErrorList.address_unresolved;
    }
    return cc > 0 ? -cc : npts[0];
  }
  public static String[] getArchiveSource(String context,String server, String property, String device,int timeout)
  {
    String[] src = null;
   
    int cc = 0;
    TDataType dout;
   
    if (server == null && context == null) return src;
    if (context == null) context = "DEFAULT";
    if (server == null) server = "HISTORY"; // assume the standard central archive server for the given context
    if (srcTbl != null)
    {
      String value = device+"["+property+"]";
      if (srcTbl.containsValue(value))
      {
        Set<String> kset = srcTbl.keySet()
        Iterator<String> i = kset.iterator();
        String key;
        while (i.hasNext())
        {
          key = i.next();
          if (srcTbl.get(key).compareToIgnoreCase(value) == 0)
            return new String[]{key};
        }
      }
    }
    String dname = new String("/" + context + "/" + server + "/" + property);
    try
    {
      if (isCentralArchiveServer(server) || isCentralArchiver(server))
      { // call to a central archive server
        String[] keysrcs = new String[100];
        dout = new TDataType(keysrcs);
        TLink tl = new TLink(dname,"KEYWORD.SOURCE",dout,null,TAccess.CA_READ);
        cc = tl.executeAndClose(timeout);
        if (cc == 0)
        {
          int len =dout.getCompletionLength();
          if (len < 100) keysrcs = Arrays.copyOf(keysrcs, len);
          src = keysrcs;
          if (srcTbl != null && selectedContext != null &&
              context.compareToIgnoreCase(selectedContext) == 0)
          { // if there's a hash table going, add to it
            String dev;
            String key = "keyword["+property+"]";
            String[] devs = null;
            if (keysrcs.length > 1)
            {
              devs = TQuery.getDeviceNames(context, server, property);
            }
            for (int k=0; k<keysrcs.length; k++)
            { // key is the src string (per device)
              if (keysrcs[k] == null) break;
              if (keysrcs.length > 1)
              {
                dev = (devs == null || k >= devs.length) ? "#"+k : devs[k];
                key = dev+"["+property+"]";
              }
              srcTbl.put(keysrcs[k],key);
            }
          }
        }
      }
      else
      {
        src = new String[]{"/"+context+"/"+server+"/"+device+"["+property+"]"};
      }
    }
    catch (UnresolvedAddressException e)
    {
      cc = TErrorList.address_unresolved;
    }
    if (cc != 0) MsgLog.log("getArchiveSource", TErrorList.toString(cc),cc,null,1);
   
    return src;
  }
  public static synchronized FLTFLT getToleranceOverInterval(String context,String server, String property, double start, double stop,int timeout)
  {
    int cc = 0;
    int[] startstop = new int[2];
    TDataType dout, din;
    FLTFLT tols = new FLTFLT(0,0);
   
    if (server == null && context == null) return tols;
    if (context == null) context = "DEFAULT";
    if (server == null) server = "HISTORY"; // assume the standard central archive server for the given context
    startstop[0] = (int)start;
    startstop[1] = (int)stop;
   
    String dname = new String("/" + context + "/" + server + "/" + property);
    try
    {
      if (isCentralArchiveServer(server))
      { // call to a central archive server
      FLTFLT[] dtols = new FLTFLT[1];
        dtols[0] = tols;
        din = new TDataType(startstop);
        dout = new TDataType(dtols);
        TLink tl = new TLink(dname,"KEYWORD.TOLERANCE",dout,din,TAccess.CA_READ);
        cc = tl.executeAndClose(timeout);
      }
      else
      {
        THistoryRecordStruct[] hrs = new THistoryRecordStruct[1];
        hrs[0] = new THistoryRecordStruct();
        din = new TDataType(property);
        dout = new TDataType(hrs);
        TLink tl = new TLink(dname,"HISTORIES",dout,din,TAccess.CA_READ);
        cc = tl.executeAndClose(timeout);
        if (cc == 0)
        {
          tols.f1val = hrs[0].getAbsoluteTolerance();
          tols.f2val = hrs[0].getPercentTolerance();
        }
      }
    }
    catch (UnresolvedAddressException e)
    {
      cc = TErrorList.address_unresolved;
    }
    if (cc != 0) MsgLog.log("getToleranceOverInterval", TErrorList.toString(cc),cc,null,1);
    return tols;
  }
  public static double[] getToleratedUpperValues(double[] data,float tolAbsolute,float tolRelative)
  {
    if (data == null) return null;
    double[] d = new double[data.length];
    for (int i=0; i<data.length; i++)
    {
      d[i] = data[i]*(1.0 + tolRelative) + tolAbsolute;
    }
    return d;
  }
  public static double[] getToleratedLowerValues(double[] data,float tolAbsolute,float tolRelative)
  {
    if (data == null) return null;
    double[] d = new double[data.length];
    for (int i=0; i<data.length; i++)
    {
      d[i] = data[i]*(1.0 - tolRelative) - tolAbsolute;
    }
    return d;
  }
  public static int getArchivedStatus(String context,String server, String property, String device, double start, double stop,NAME32DBLDBL[] data)
  {
    return getStatusArray(context,server,property,device,start,stop,data,1000);
 
  public static int getArchivedStatus(String context,String server, String property, String device, double start, double stop,NAME32DBLDBL[] data,int timeout)
  {
    return getStatusArray(context,server,property,device,start,stop,data,timeout);
 
  private static synchronized int getStatusArray(String context,String server, String property, String device, double start, double stop, TCompoundDataObject[] data, int timeout)
  {
    if (context == null || property == null || data == null) return TErrorList.invalid_parameter;
    if (data.length < 1) return -TErrorList.dimension_error;
    if (timeout < 100) timeout = 100;
    int idx;
    String s1 = server;
    String p1 = property;
    if (s1 == null) s1 = "HISTORY";
    if ((idx=property.indexOf(".HIST")) >= 0 ||
        (idx=property.indexOf(".HST")) >= 0 ||
        (idx=property.indexOf(".AR")) >= 0)
    { // passed with .HIST appended, remove it!
      property = property.substring(0, idx);
    }
    if (!isCentralArchiveServer(s1))
    { // a specific server was passed
      p1 = property + ".ARCH"; // redirect to same channel at the central archiver
    }
    if (!(data instanceof NAME32DBLDBL[]))
    { // CF_NAME32DBLDBL and ONLY CF_NAME32DBLDBL
      return -TErrorList.illegal_format;
    }
    int nret = getArchivedDataArray(context,s1,p1,device,0,start,stop,data,timeout);
    if (nret == -TErrorList.illegal_format) nret = 0; // old archive server
    return nret;
  }
  public static synchronized int getAnnotationTimes(String context, String property, double start, double stop, double[] data, int timeout)
  {
    return getAnnotationTimes(context,property,start,stop,data,null,timeout);
  }
  public static synchronized int getAnnotationTimes(String context, String property, double start, double stop, double[] data, boolean[] isActive, int timeout)
  {
    if (context == null || property == null || data == null) return -TErrorList.invalid_parameter;
    if (data.length < 1) return -TErrorList.dimension_error;
    if (timeout < 100) timeout = 100;
    INTINT[] iidata = null;
    int[] startstop = new int[2];
    startstop[0] = (int)start;
    startstop[1] = (int)stop;
    TDataType din, dout;
    if (startstop[0] == 0 && startstop[1] == 0)
    {
      din = new TDataType();
    }
    else
    {
      din = new TDataType(startstop);
    }
    String dname = new String("/"+context+"/HISTORY/"+property);
    if (isActive != null && isActive.length >= data.length)
    {
      iidata = new INTINT[data.length];
      dout = new TDataType(iidata);
    }
    else
    {
      isActive = null;
      dout = new TDataType(data);
    }
    TLink tl = new TLink(dname,"RECORD.CMTS",dout,din,TAccess.CA_READ);
    int cc = tl.executeAndClose(timeout);
    if (cc != 0) return -cc;
    int n = dout.getCompletionLength();
    if (isActive != null)
    {
      for (int i=0; i<n; i++)
      {
        data[i] = iidata[i].i1val;
        isActive[i] = iidata[i].i2val != 0;
      }
    }
    else
    {
      if (n > 0) Arrays.sort(data,0,n);
    }
    return n;
  }
  public static synchronized int getAnnotation(String context, String property, double target, String[] data, int timeout)
  {
    if (context == null || property == null || data == null) return TErrorList.invalid_parameter;
    if (data.length < 1) return TErrorList.dimension_error;
    if (timeout < 100) timeout = 100;
    TDataType dout;
    char[] cmt = new char[1024];
    dout = new TDataType(cmt);

    String dname = new String("/"+context+"/HISTORY/"+property+"@"+(int)target);

    TLink tl = new TLink(dname,"RECORD.CMT",dout,null,TAccess.CA_READ);
    int cc = tl.executeAndClose(timeout);
    if (cc != 0) return cc;
    int len = dout.getCompletionLength();
    data[0] = new String(cmt,0,len);
    return 0;
  }
  /**
   * Annotates the designated context in the central archive at the current
   * time.
   *
   * @param context is the context of the Central Archive Server
   * @param text is the desired annotation text
   *
   * @return 0 upon success or a TINE error code
   */
  public static synchronized int setAnnotation(String context, String text)
  {
    return setAnnotation(context,"State",0,text,false,1000);
  }
  /**
   * Annotates the designated record property in the central archive at the current
   * time.
   *
   * @param context is the context of the Central Archive Server
   * @param property is the targeted record keyword name
   * @param text is the desired annotation text
   *
   * @return 0 upon success or a TINE error code
   */
  public static synchronized int setAnnotation(String context, String property, String text)
  {
    return setAnnotation(context,property,0,text,false,1000);
  }
  /**
   * Annotates the designated record property in the central archive
   *
   * @param context is the context of the Central Archive Server
   * @param property is the targeted record keyword name
   * @param targetTime is the desired annotation time (if <= 0 then the
   * current time will be used)
   * @param text is the desired annotation text
   * @param timeout is the allowed duration of the annotation call before issuing
   * a timeout message.
   *
   * @return 0 upon success or a TINE error code
   */
  public static synchronized int setAnnotation(String context, String property, double targetTime, String text, int timeout)
  {
    return setAnnotation(context,property,targetTime,text,false,timeout);
  }
  /**
   * Annotates the designated record property in the central archive
   *
   * @param context is the context of the Central Archive Server
   * @param property is the targeted record keyword name
   * @param targetTime is the desired annotation time (if <= 0 then the
   * current time will be used)
   * @param text is the desired annotation text
   * @param keepAlive if 'true' this will flag the annotation as 'active'
   * (i.e. valid until cleared by the next annotation)
   * @param timeout is the allowed duration of the annotation call before issuing
   * a timeout message.
   *
   * @return 0 upon success or a TINE error code
   */
  public static synchronized int setAnnotation(String context, String property, double targetTime, String text, boolean keepAlive, int timeout)
  {
    if (context == null || property == null || text == null) return TErrorList.invalid_parameter;
    if (timeout < 100) timeout = 100;
    if (targetTime <= 0) targetTime = TDataTime.getDataTimeStamp();
    TDataType din = null;
    if (text != null && text.length() > 0)
    {
      din = new TDataType(text.toCharArray());
    }
    String keepString = keepAlive ? "[KEEPALIVE]" : "";
    String dname = new String("/"+context+"/HISTORY/"+property+"@"+(int)targetTime+keepString);

    TLink tl = new TLink(dname,"RECORD.CMT",null,din,TAccess.CA_WRITE);
    return tl.executeAndClose(timeout);
  }
  private static String lastDataKey = null;
  private static NAME32DBLDBL[] ndd = null;
  private static double[] ats = null;
  private static boolean[] atsActive = null;
  private static synchronized int getDataArray(String context,String server, String property, String device, int index, double start, double stop, TCompoundDataObject[] data, THistorySource src, int timeout)
  {
    int idx, cc = 0;
    HstSrc[] srvs = new HstSrc[2];
    String s1 = server;
    String p1 = property;
    if (s1 == null) s1 = "HISTORY";
    if ((idx=property.indexOf(".HIST")) >= 0 ||
        (idx=property.indexOf(".HST")) >= 0 ||
        (idx=property.indexOf(".AR")) >= 0)
    { // passed with .HIST appended, remove it!
      property = property.substring(0, idx);
    }
    String s2 = s1; // server 2nd source
    if (!isCentralArchiveServer(s1))
    { // a specific server was passed
      p1 = property + ".ARCH"; // redirect to same channel at the central archiver
    }
    else
    { // 2nd source must redirect back to find the target server's local histories
      s2 = "ARCHIVER";
    }
    double npstart, npstop;
    boolean extendedRange = (src != null && src.hasExtendedTimeRange());
    npstart = extendedRange ? start + HEARTBEAT*2 : start;
    npstop = extendedRange ? stop - HEARTBEAT*2 : stop;
    int npts1 = getArchivedNumPointsInInterval(context, s1, p1, device, npstart, npstop, 500);
    if (npts1 < 0)
    { // a problem with this entry
      npts1 = 0;
      lastDataKey = null;
      ndd = null;
      ats = null;
      atsActive = null;
    }
    else
    { // central archive is alive and knows about the record
      String thisDataKey = "/"+context+"["+property+"]"+start+"->"+stop;
      if (lastDataKey == null || thisDataKey.compareToIgnoreCase(lastDataKey) != 0)
      {       
        int ndays = (int)((stop - start)) / 86400;
        if (ndays < 1) ndays = 1;
        ndd = new NAME32DBLDBL[ndays*100];
        int nr = getArchivedStatus(context, "HISTORY", property, device, start, stop, ndd, timeout);
        ndd = nr <= 0 ? null : Arrays.copyOf(ndd, nr);
        ats = new double[ndays*10];
        atsActive = new boolean[ndays*10];
        nr = getAnnotationTimes(context, property, start, stop, ats, atsActive, timeout);
        ats = nr <= 0 ? null : Arrays.copyOf(ats, nr);
        atsActive = nr <= 0 ? null : Arrays.copyOf(atsActive, nr);
        lastDataKey = thisDataKey;
      }
    }
    srvs[0] = new HstSrc("Central Archive",s1,npts1);
    String p2 = property + ".HIST";
    int npts2 = getArchivedNumPointsInInterval(context, s2, p2, device, npstart, npstop, 500);
    if (npts2 < 0)
    { // a problem with this entry
      if (npts2 == -TErrorList.un_allocated)
      {
        String[] hists = getArchivedProperties(context,s2);
        if (hists != null)
        { // there are histories available ....
          if (isScheduledProperty(property))
          { // look for unscheduled alternative
            for (String h : hists)
            {
              if (property.startsWith(h))
              {
                p2 = h+".HIST";
                npts2 = getArchivedNumPointsInInterval(context, s2, p2, device, npstart, npstop, 500);
                break;
              }
            }
          }
          else
          { // look for scheduled alternative
            for (String h : hists)
            {
              if (isScheduledProperty(h))
              {
                if (h.startsWith(property))
                {
                  p2 = h+".HIST";
                  npts2 = getArchivedNumPointsInInterval(context, s2, p2, device, npstart, npstop, 500);
                  break;            
                }
              }
            }       
          }
        }
      }
      if (npts2 < 0) npts2 = 0;
    }
    srvs[1] = new HstSrc("Local History",s2,npts2);
   
    boolean done = false;
    int nret = 0;
    if (npts1 > 500 || npts1 > npts2)
    { // central archive has 'enough' points or at least more than the local history
      nret = getArchivedDataArray(context,s1,p1,device,index,start,stop,data,timeout);     
      if (nret > 0)
      { // everything's okay
        srvs[0].setAsDefault(true);
        srvs[0].setNumberReturned(nret);
        done = true;
      }
      else
      {
        cc = -nret;
      }
    }
    if (!done && npts2 > 0)
    { // too few (or no) points upstairs, so try the local history ...
      nret = getArchivedDataArray(context,s2,p2,device,index,start,stop,data,timeout);
      srvs[1].setAsDefault(true);
      if (nret > 0)
      {
        srvs[1].setNumberReturned(nret);
        done = true;
      }
      else
      {
        cc = -nret;
      }
      if (cc != 0)
      {
        if (npts1 > 0)
        { // would have been a nice place for a 'goto' ....
          nret = getArchivedDataArray(context,s1,p1,device,index,start,stop,data,timeout);     
          if (nret > 0)
          { // everything's okay
            srvs[1].setAsDefault(false);
            srvs[0].setAsDefault(true);
            srvs[0].setNumberReturned(nret);
            done = true;
          }
        }
      }
    }
    if (!done && npts2 == 0 &&
        !(data instanceof INTFLTINT[]) &&
        !isCentralArchiveServer(server) &&
        !isCentralArchiver(server))
    { // last desperate attempt (possible legacy doocs server ?)
      int ifi_len = data.length;
      if (ifi_len < 2000) ifi_len = 2000;
      INTFLTINT[] ifi = new INTFLTINT[ifi_len];
      int rst = 1, dn = 0, n = 0;
      double interim = 0;
      while (start < stop)
      {
        nret = getArchivedDataArray(context,s2,p2,device,index,start,stop,ifi,timeout);
        if (nret > 1)
        {
          if (dn == 0)
          {
            dn = (ifi[nret-1].i1val-(int)start);
            if (dn <= 0) dn = 1;
            rst = (int)(stop-start) / dn;
            if (rst < 1) rst = 1;
          }
          for (int i=0; n<data.length && i<nret; i += rst)
          {
            if (ifi[i] == null)
            {
              nret = i;
              break;
            }
            if (data instanceof FLTINT[])
              ((FLTINT[])data)[n] = new FLTINT(ifi[i].fval,ifi[i].i1val);
            else if (data instanceof DBLDBL[])
              ((DBLDBL[])data)[n] = new DBLDBL((double)ifi[i].fval,(double)ifi[i].i1val);
            n++;
          }
          interim = (double)(ifi[nret-1].i1val + 1);
          if (interim <= start) break;
          start = interim;
          done = true;
          if (rst == 1) break;
        }
        else
        {
          break;
        }
      }
      srvs[1].setAsDefault(true);
      srvs[1].setNumberPoints(n);
      srvs[1].setNumberReturned(n);
    }
    if (src != null)
    { // was passed, fill it in ...
      src.CentralArchive = srvs[0];
      src.LocalHistory = srvs[1];
      src.setNumberOfPointsReturned(nret);
      src.setStatusList(ndd);
      src.setAnnotationTimes(ats);
      src.setAnnotationActive(atsActive);
    }
    return cc;
  }
  private static synchronized int getArchivedDataArray(String context,String server, String property, String device, int index, double start, double stop, TCompoundDataObject[] data, int timeout)
  {
    return getArchivedDataArray(context,server,property,device,index,0,start,stop,data,timeout);
  }
  private static synchronized int getArchivedDataArray(String context,String server, String property, String device, int index, int sample, double start, double stop, TCompoundDataObject[] data, int timeout)
  {
    int cc = 0;
    int[] startstop = new int[4];
    TDataType dout, din;

    if (server == null && context == null) return -TErrorList.argument_list_error;
    if (data == null) return -TErrorList.argument_list_error;
    if (context == null) context = "DEFAULT";
    if (server == null) server = "HISTORY"; // assume the standard central archive server for the given context
    if (timeout < 100) timeout = 100;
    startstop[0] = (int)start;
    startstop[1] = (int)stop;
    startstop[2] = index;
    startstop[3] = sample;
    if (startstop[0] == 0 && startstop[1] == 0)
    {
      din = new TDataType();
    }
    else if (data instanceof INTFLTINT[])
    {
      WINDOW[] twnd = new WINDOW[1];
      twnd[0] = new WINDOW(startstop[0],startstop[1],0,0);
      din = new TDataType(twnd);
    }
    else
    {
      din = new TDataType(startstop);
    }
    dout = new TDataType(data);
   
    if (isCentralArchiver(server))
    { // trap a potential redirection problem here:
      if (device.compareToIgnoreCase("keyword") == 0) device = "#0";
    }
    String dname = new String("/" + context + "/" + server + "/" + device);
    String pname = new String(property);
    String rname = getQueryPropertyRoot(property);
    boolean tryAlternateProperty = false;
    if (!isCentralArchiveServer(server))
    { // not a call to a central archive server
      tryAlternateProperty = true;
      if (!isArchiveQueryProperty(property))
        pname = pname + ".HIST"; // meta extension not given, try this ...
    }
    TLink tl = new TLink(dname,pname,dout,din,TAccess.CA_READ);
    cc = tl.executeAndClose(timeout);
    if (cc == TErrorList.un_allocated && tryAlternateProperty)
    {
      String[] hists = getArchivedProperties(context,server);
      if (hists != null)
      { // there are histories available ....
        if (isScheduledProperty(rname))
        { // look for unscheduled alternative
          for (String h : hists)
          {
            if (rname.startsWith(h))
            {
              pname = h+".HIST";
              tl = new TLink(dname,pname,dout,din,TAccess.CA_READ);
              cc = tl.executeAndClose(timeout);
              break;
            }
          }
        }
        else
        { // look for scheduled alternative
          for (String h : hists)
          {
            if (isScheduledProperty(h))
            {
              if (h.startsWith(rname))
              {
                pname = h+".HIST";
                tl = new TLink(dname,pname,dout,din,TAccess.CA_READ);
                cc = tl.executeAndClose(timeout);
                break;            
              }
            }
          }       
        }
      }
    }
    if (cc != 0 && isCentralArchiver(server))
    { // special 2nd chance !
      if (isArchiveQueryProperty(property))
      {
        pname = rname + ".ARCH";
      }
      else
      {
        pname = new String(property) + ".ARCH";
      }
      tl = new TLink(dname,pname,dout,din,TAccess.CA_READ);
      cc = tl.executeAndClose(timeout);
    }
    return cc == 0 ? dout.getCompletionLength() : -cc;
 
  private static int getArchivedDataArray(String context,String server, String property, String device, int index, double start, double stop,TCompoundDataObject[] data)
  {
    return getArchivedDataArray(context,server,property,device,index,start,stop,data,2000);
  }
  private static synchronized int getArchivedDataSnapshot(String context,String server, String property, String device, double[] targetTime,float[] data)
  {
    int cc = 0;
    double[] dstartstop = new double[2];
    TDataType dout, din;

    if (server == null && context == null) return TErrorList.argument_list_error;
    if (targetTime == null || data == null) return TErrorList.argument_list_error;
    if (context == null) context = "DEFAULT";
    if (server == null) server = "HISTORY"; // assume the standard central archive server for the given context
    dstartstop[0] = targetTime[0];
    dstartstop[1] = targetTime[0];
    din = new TDataType(dstartstop);
    dout = new TDataType(data);
   
    String dname = new String("/" + context + "/" + server + "/" + device);
    String[] pname = new String[2];
    pname[0] = new String(property);
    int ntries = 1;
    if (!server.endsWith("HISTORY"))
    { // not a call to a central archive server
      if (!isArchiveQueryProperty(property))
      { // meta extension not given, try this ...
        pname[0] = property + ".HIST";
        pname[1] = property + ".ARCH";
        ntries = 2;
      }
    }
    for (int i=0; i<ntries; i++)
    {
      TLink tl = new TLink(dname,pname[i],dout,din,TAccess.CA_READ);
      cc = tl.execute(2000, true);
      targetTime[0] = tl.getLastDataTimeStamp();
      tl.close();
      if (cc == 0 && dout.getCompletionLength() > 0) break;
    }
    return cc;
 
 
  private static synchronized int getArchivedDataSnapshot(String context,String server, String property, String device, double[] targetTime,String[] data)
  {
    int cc = 0;
    double[] dstartstop = new double[2];
    TDataType dout, din;

    if (server == null && context == null) return TErrorList.argument_list_error;
    if (targetTime == null || data == null) return TErrorList.argument_list_error;
    if (context == null) context = "DEFAULT";
    if (server == null) server = "HISTORY"; // assume the standard central archive server for the given context
    dstartstop[0] = targetTime[0];
    dstartstop[1] = targetTime[0];
    din = new TDataType(dstartstop);
    dout = new TDataType(data);
   
    String dname = new String("/" + context + "/" + server + "/" + device);
    String[] pname = new String[2];
    pname[0] = new String(property);
    int ntries = 1;
    if (!server.endsWith("HISTORY"))
    { // not a call to a central archive server
      if (!isArchiveQueryProperty(property))
      { // meta extension not given, try this ...
        pname[0] = property + ".HIST";
        pname[1] = property + ".ARCH";
        ntries = 2;
      }
    }
    for (int i=0; i<ntries; i++)
    {
      TLink tl = new TLink(dname,pname[i],dout,din,TAccess.CA_READ);
      cc = tl.execute(2000, true);
      targetTime[0] = tl.getLastDataTimeStamp();
      tl.close();
      if (cc == 0 && dout.getCompletionLength() > 0) break;
    }
    return cc;
 
  /**
   * Retrieves an archive data set.
   *
   * Retrieve an archive data set (value - timestamp pairs) for the target property and device
   * given and over the time range specified. If server is null, an empty string, or "HISTORY",
   * the central archive server is queried for the data. Otherwise the local history
   * subsystem of the device server specified is queried. 
   *
   * @param context is the desired context 
   * @param server is the device server which manages the archived parameter. If null
   * then the central archive will be queried for the property and device
   * @param property is the desired archived property
   * @param device is the desired device name
   * @param start is the start time in seconds (UTC double, a TINE timestamp)
   * @param stop is the stop time in seconds (UTC double, a TINE timestamp)
   * @param data is a reference to a (doublet) data array object to contain the returned data.
   * This can be one of FLTINT[], DBLDBL[], NAME64I[], and so on.  The array should be
   * dimensioned to the maximum number of points desired (effectively determining a raster
   * over which the archived data will be distributed. 
   * The reference object can also be an array of HISTORY[] object instances, which have been
   * constructed with the desired data type (usually a compound data type) to be returned.   
   * The number of points returned in the call will often be less than array dimension
   * so one should pay attention to the return value of the call.
   *
   * @return if positive, the number of points returned in the archive call; if negative,
   * the negative of a TINE error code.
   */
  public static int getArchivedData(String context,String server, String property, String device, double start, double stop,TCompoundDataObject[] data)
  {
    return getArchivedDataArray(context,server,property,device,0,start,stop,data);
  }
  public static int getArchivedData(String context,String server, String property, String device, double start, double stop,DBLDBL[] data)
  {
    return getArchivedDataArray(context,server,property,device,0,start,stop,data);
  }
  /**
   * Retrieves an archive data set.
   *
   * Retrieve an archive data set (value - timestamp pairs) for the target property and device
   * given and over the time range specified.  Both the central archive and local archive are
   * queried for the number of stored points over the interval. Data are usually
   * retrieved from the central archive system if there are at least 500 points over the
   * time interval in question.  Otherwise the local archive system is used.  By examining
   * the THistorySource object one can see which source was chosen and how many points both
   * sources have over the interval.  For dedicated retrieval from one source or the
   * other, please use an overloaded method which does not take the THistorySource parameter!
   *
   * @param context is the desired context 
   * @param server is the device server which manages the archived parameter. If null
   * then the central archive will be queried for the property and device
   * @param property is the desired archived property
   * @param device is the desired device name
   * @param start is the start time in seconds (UTC double, a TINE timestamp)
   * @param stop is the stop time in seconds (UTC double, a TINE timestamp)
   * @param data is a reference to a (doublet) data array object to contain the returned data.
   * This can be one of FLTINT[], DBLDBL[], NAME64I[], and so on.  The array should be
   * dimensioned to the maximum number of points desired (effectively determining a raster
   * over which the archived data will be distributed.
   * The reference object can also be an array of HISTORY[] object instances, which have been
   * constructed with the desired data type (usually a compound data type) to be returned.   
   * The src parameter will give the
   * exact number of points actually stored over the interval specified.
   * The number of points returned in the call will often be less than array dimension and
   * less than the exact number of points stored over the interval.
   * @param src is a reference to a THistorySource object to receive the history source information.
   * @param timeout gives the number of milliseconds to wait for the call to complete
   * (usually 1000 is sufficient).
   * @return 0 upon success or a TINE error code.
   */
  public static int getArchivedData(String context,String server, String property, String device, double start, double stop,TCompoundDataObject[] data,THistorySource src,int timeout)
  {
    return getDataArray(context,server,property,device,0,start,stop,data,src,timeout);
  }
  public static int getArchivedData(String context,String server, String property, String device, double start, double stop,DBLDBL[] data,THistorySource src,int timeout)
  {
    return getDataArray(context,server,property,device,0,start,stop,data,src,timeout);
  }
  public static int getArchivedData(String context,String server, String property, String device, double start, double stop,TCompoundDataObject[] data,THistorySource src,int timeout,boolean extendedRange)
  {
    return getArchivedData(context,server,property,device,0,start,stop,data,src,timeout,extendedRange);
  }
  public static int getArchivedData(String context,String server, String property, String device, double start, double stop,DBLDBL[] data,THistorySource src,int timeout,boolean extendedRange)
  {
    return getArchivedData(context,server,property,device,0,start,stop,data,src,timeout,extendedRange);
  }
  public static int getArchivedData(String context,String server, String property, String device, int index, double start, double stop,TCompoundDataObject[] data,THistorySource src,int timeout,boolean extendedRange)
  {
    if (extendedRange)
    {
      start -= HEARTBEAT*2;
      stop  += HEARTBEAT*2;
      if (src != null) src.setExtendedTimeRange(true);
    }
    return getDataArray(context,server,property,device,index,start,stop,(TCompoundDataObject[])data,src,timeout);         
  }
  /**
   * Retrieves an archive data set.
   *
   * Retrieve an archive data set (value - timestamp pairs) for the target property and device
   * given and over the time range specified. If server is null, an empty string, or "HISTORY",
   * the central archive server is queried for the data. Otherwise the local history
   * subsystem of the device server specified is queried. 
   *
   * @param context is the desired context 
   * @param server is the device server which manages the archived parameter. If null
   * then the central archive will be queried for the property and device
   * @param property is the desired archived property
   * @param device is the desired device name
   * @param start is the start time in seconds (UTC double, a TINE timestamp)
   * @param stop is the stop time in seconds (UTC double, a TINE timestamp)
   * @param data is a reference to a (doublet) data array object to contain the returned data.
   * This can be one of FLTINT[], DBLDBL[], NAME64I[], and so on.  The array should be
   * dimensioned to the maximum number of points desired (effectively determining a raster
   * over which the archived data will be distributed. 
   * The reference object can also be an array of HISTORY[] object instances, which have been
   * constructed with the desired data type (usually a compound data type) to be returned.   
   * The number of points returned in the call will often be less than array dimension
   * so one should pay attention to the return value of the call.
   *
   * @return if positive, the number of points returned in the archive call; if negative,
   * the negative of a TINE error code.
   */
  public static int getArchivedData(String context,String server, String property, String device, int index,double start, double stop,TCompoundDataObject[] data)
  {
    return getArchivedDataArray(context,server,property,device,index,start,stop,data);
  }
  /**
   * Retrieves an archive data set.
   *
   * Retrieve an archive data set (value - timestamp pairs) for the target property and device
   * given and over the time range specified. If server is null, an empty string, or "HISTORY",
   * the central archive server is queried for the data. Otherwise the local history
   * subsystem of the device server specified is queried. 
   *
   * @param context is the desired context 
   * @param server is the device server which manages the archived parameter. If null
   * then the central archive will be queried for the property and device
   * @param property is the desired archived property
   * @param device is the desired device name
   * @param start is the start time in seconds (UTC double, a TINE timestamp)
   * @param stop is the stop time in seconds (UTC double, a TINE timestamp)
   * @param data is a reference to a (doublet) data array object to contain the returned data.
   * This can be one of FLTINT[], DBLDBL[], NAME64I[], and so on.  The array should be
   * dimensioned to the maximum number of points desired (effectively determining a raster
   * over which the archived data will be distributed. 
   * The reference object can also be an array of HISTORY[] object instances, which have been
   * constructed with the desired data type (usually a compound data type) to be returned.   
   * The number of points returned in the call will often be less than array dimension
   * so one should pay attention to the return value of the call.
   * @param timeout gives the number of milliseconds to wait for the call to complete
   * (usually 1000 is sufficient).
   *
   * @return if positive, the number of points returned in the archive call; if negative,
   * the negative of a TINE error code.
   */
    public static int getArchivedData(String context,String server, String property, String device, int index,double start, double stop,TCompoundDataObject[] data,int timeout)
    {
      return getArchivedDataArray(context,server,property,device,index,start,stop,data,timeout);
    }
  /**
   * Retrieves an archive data set.
   *
   * Retrieve an archive data set (value - timestamp pairs) for the target property and device
   * given and over the time range specified. If server is null, an empty string, or "HISTORY",
   * the central archive server is queried for the data. Otherwise the local history
   * subsystem of the device server specified is queried. 
   *
   * @param context is the desired context 
   * @param server is the device server which manages the archived parameter. If null
   * then the central archive will be queried for the property and device
   * @param property is the desired archived property
   * @param device is the desired device name
   * @param start is the start time in seconds (UTC double, a TINE timestamp)
   * @param stop is the stop time in seconds (UTC double, a TINE timestamp)
   * @param sample is the desired raster sample.  Passing a '0' will instruct
   * the targeted server to select a raster which will best fill in the
   * desired time range.  If (e.g.) a '1' is passed then no raster will be
   * used.  This means that the desired 'stop' time may not have been reached.
   * Thus the returned data need to be examined in order to determine whether the
   * call should be repeated (after adjusting the 'start' time).
   * @param data is a reference to a HISTORY[] data array object to contain the returned data.
   * This object passed should be an array of HISTORY instances, which have been
   * constructed with the desired data type (usually a compound data type) to be
   * returned. The array should be
   * dimensioned to the maximum number of points desired (effectively determining a raster
   * over which the archived data will be distributed. 
   * The number of points returned in the call will often be less than array dimension
   * so one should pay attention to the return value of the call.
   * @param timeout gives the number of milliseconds to wait for the call to complete
   * (usually 1000 is sufficient).
   *
   * @return if positive, the number of points returned in the archive call; if negative,
   * the negative of a TINE error code.
   */
  public static int getArchivedData(String context,String server, String property, String device, int index,int sample,double start, double stop,TCompoundDataObject[] data,int timeout)
  {
    return getArchivedDataArray(context,server,property,device,index,sample,start,stop,data,timeout);
  }
  public static int writeArchivedDataToFile(String filename,String context,String server, String property, String device,double start, double stop)
  {
    try
    {
      TPropertyQuery[] tpq = TQuery.getPropertyInformation(context, server, device, property);
      if (tpq == null) return TErrorList.non_existent_property;
      // THistory.getNumPointsInInterval(context, server, property, device, start, stop, 2000);
      // n.b. determine num points in interval with another call and warm user acquisition time if too many
      FileWriter fw = new FileWriter(filename);
      fw.write("#/"+context+"/"+server+"/"+device+"["+property+"], ("+tpq[0].prpUnits+"), start "+TDataTime.toString(start)+", stop "+TDataTime.toString(stop)+"\r\n");
      fw.write("time string, time stamp, system stamp, value\r\n");
      double ts=0, srtt = start;
      int ntot = 0, npts = 1000;
      HISTORY[] hsts = new HISTORY[1000];
      for (int i=0; i<1000; i++)
      {
        hsts[i] = new HISTORY(new TDataType(1,tpq[0].prpFormat));
      }
      TDataType tdt;
      String lnout;
      long t = System.currentTimeMillis();
      while (npts == 1000)
      {
        npts = THistory.getArchivedData(context, server, property,device, 0, 1,srtt, stop, hsts, 1000);
        for (int k=0; k<npts; k++)
        {
          tdt = hsts[k].getDataObject();
          ts = (double)tdt.getDataTimeStamp()/1000.0;
          lnout = TDataTime.toString(ts)+", "+String.format("%10.3f", ts)+", "+tdt.getSystemDataStamp()+", "+tdt.toString()+"\n";
          fw.write(lnout);
        }
        ntot += npts;
        if (ts <= srtt) break;
        srtt = ts;
      }
      t = System.currentTimeMillis() - t;
      fw.write("#access time:, "+t+" (msec), number points in interval:, "+ntot+"\n");
      fw.close();
    }
    catch (Exception x)
    {
      System.out.println("cannot write to "+filename+" : "+x.getMessage());
      return TErrorList.code_failure;
    }
    return 0;
  }
  /**
   * Retrieves the total number of stored data points in the interval specified.
   *
   * Retrieve the number of points stored for the given parameters
   * and over the time range specified. If server is null, an empty string, or "HISTORY",
   * the central archive server is queried for the data. Otherwise the local history
   * subsystem of the device server specified is queried. 
   *
   * @param context is the desired context 
   * @param server is the device server which manages the archived parameter. If null
   * then the central archive will be queried for the property and device
   * @param property is the desired archived property
   * @param device is the desired device name
   * @param start is the start time in seconds (UTC double, a TINE timestamp)
   * @param stop is the stop time in seconds (UTC double, a TINE timestamp)
   *
   * @return if positive, the number of points returned in the archive call; if negative,
   * the negative of a TINE error code.
   */
  public static int getNumPointsInInterval(String context,String server, String property, String device, double start, double stop)
  {
    return getArchivedNumPointsInInterval(context, server, property, device, start, stop, 1000);
  }
  /**
   * Retrieves the total number of stored data points in the interval specified.
   *
   * Retrieve the number of points stored for the given parameters
   * and over the time range specified. If server is null, an empty string, or "HISTORY",
   * the central archive server is queried for the data. Otherwise the local history
   * subsystem of the device server specified is queried. 
   *
   * @param context is the desired context 
   * @param server is the device server which manages the archived parameter. If null
   * then the central archive will be queried for the property and device
   * @param property is the desired archived property
   * @param device is the desired device name
   * @param start is the start time in seconds (UTC double, a TINE timestamp)
   * @param stop is the stop time in seconds (UTC double, a TINE timestamp)
   * @param timeout gives the number of milliseconds to wait for the call to complete
   * (usually 1000 is sufficient).
   *
   * @return if positive, the number of points returned in the archive call; if negative,
   * the negative of a TINE error code.
   */
  public static int getNumPointsInInterval(String context,String server, String property, String device, double start, double stop,int timeout)
  {
    return getArchivedNumPointsInInterval(context, server, property, device, start, stop, timeout);
  }
 
  /**
   * Retrieves an archived data array 'snapshot' at the time specified.
   *
   * Retrieve an archive data set for the target property and device
   * given at the time specified. If server is null, an empty string, or "HISTORY",
   * the central archive server is queried for the data. Otherwise the local history
   * subsystem of the device server specified is queried. The returned data set
   * represents the entire data array, be it a multi-channel array, trace array, or
   * scalar at a stored time nearest (but not greater than) the given time. 
   *
   * @param context is the desired context 
   * @param server is the device server which manages the archived parameter. If null
   * then the central archive will be queried for the property and device
   * @param property is the desired archived property
   * @param device is the desired device name
   * @param targetTime is the targetted time in seconds (UTC double, a TINE timestamp)
   * This is passed as a single-valued array, so that the targetted time can be
   * altered by the call to reflect the time found.
   * @param data is a reference to a String data array object to contain the returned data.
   * The array should be dimensioned to the number of points in the data record.
   *
   * @return 0 if success or a TINE error code.
   */
  public static int getArchivedSnapshot(String context,String server, String property, String device, double[] targetTime,String[] data)
  {
    return getArchivedDataSnapshot(context,server,property,device,targetTime,data);
  }
  /**
   * Retrieves an archived data array 'snapshot' at the time specified.
   *
   * Retrieve an archive data set for the target property and device
   * given at the time specified. If server is null, an empty string, or "HISTORY",
   * the central archive server is queried for the data. Otherwise the local history
   * subsystem of the device server specified is queried. The returned data set
   * represents the entire data array, be it a multi-channel array, trace array, or
   * scalar at a stored time nearest (but not greater than) the given time. 
   *
   * @param context is the desired context 
   * @param server is the device server which manages the archived parameter. If null
   * then the central archive will be queried for the property and device
   * @param property is the desired archived property
   * @param device is the desired device name
   * @param targetTime is the targetted time in seconds (UTC double, a TINE timestamp)
   * This is passed as a single-valued array, so that the targetted time can be
   * altered by the call to reflect the time found.
   * @param data is a reference to a float data array object to contain the returned data.
   * The array should be dimensioned to the number of points in the data record.
   *
   * @return 0 if success or a TINE error code.
   */
  public static int getArchivedSnapshot(String context,String server, String property, String device, double[] targetTime,float[] data)
  {
    return getArchivedDataSnapshot(context,server,property,device,targetTime,data);
  }
  public static int addToLocalHistory(
      String context,String server, String property, String device,
      int datasiz,int datafmt,int pollInterval,int archInterval,
      int depthShort,int depthLong,int heartBeat,float pTolerance,float aTolerance)
  {
    THistoryRecordStruct[] hrs = new THistoryRecordStruct[1];
    THistorySpecification hsp = new THistorySpecification(pollInterval, archInterval, depthShort, depthLong, heartBeat, pTolerance, aTolerance, null);
    hrs[0] = new THistoryRecordStruct(device, property, datasiz, datafmt, hsp);
    String dev = "/"+context+"/"+server;
    TLink lnk = new TLink(dev,"ADDHISTORY",null,new TDataType(hrs),TAccess.CA_WRITE);
    return lnk.executeAndClose();
  }
  public static String[] getLocalHistoryProperties(String context,String server,String device)
  {
    int[] n = new int[1];
    String dev = "/"+context+"/"+server;
    TLink lnk = new TLink(dev,"NHISTORIES",new TDataType(n),null,TAccess.CA_READ);
    if (lnk.executeAndClose() != 0) return null;
    if (n[0] == 0)
    { // could be a doocs server
      String eqm = TSrvEntry.getSrvEntryEqmName(context, server);
      if (eqm == null || eqm.compareTo(TSrvEntry.DOOCSEQM) != 0) return null;
      String[] prps = TQuery.getDeviceProperties(context, server, device);
      if (prps == null) return null;
      ArrayList<String>al = new ArrayList<String>();
      for (String p : prps)
      {
        if (p.endsWith(".HIST")) al.add(p.substring(0, p.lastIndexOf('.')))
      }
      if (al.isEmpty()) return null;
      return al.toArray(new String[0]);
    }
    NAME64[] nlst = new NAME64[n[0]];
    lnk = new TLink(dev,"HISTORIES",new TDataType(nlst),null,TAccess.CA_READ);
    if (lnk.executeAndClose() != 0) return null;
    return NameToString.nameArrayToString(nlst);
  }
  public static THistoryRecordStruct getLocalHistoryRecord(
      String context,String server, String property, String device)
  {
    THistoryRecordStruct[] hrs = new THistoryRecordStruct[1];
    hrs[0] = new THistoryRecordStruct();
    String dev = "/"+context+"/"+server;
    TDataType dout = new TDataType(hrs);
    TDataType din = new TDataType(property.toCharArray());
    TLink lnk = new TLink(dev,"HISTORIES",dout,din,TAccess.CA_READ);
    int cc = lnk.executeAndClose();
    if (cc != 0) return null;
    if (dout.getCompletionLength() < 1) return null;
    return hrs[0];
  }
  /**
   * Retrieve a list of properties archived by the central archive server for the context given.
   *
   * @param context is the targeted context
   *
   * @return a list of available archived properties
   */
  public static String[] getArchivedProperties(String context)
  {
    return getArchivedProperties(context,null,null);
  }
  /**
   * Retrieve a list of properties archived by the given server.
   *
   * @param context is the targeted server context
   * @param server is the targeted server name
   *
   * @return a list of available archived properties
   */
  public static String[] getArchivedProperties(String context,String server)
  {
    return getArchivedProperties(context,server,null);
  }
  /**
   * Retrieve a list of properties archived by the given server.
   *
   * @param context is the targeted server context
   * @param server is the targeted server name
   * @param subsystem is the targeted subsystem
   * (applicable when the server = "HISTORY", i.e. the central archive server)
   *
   * @return a list of available archived properties
   */
  public static String[] getArchivedProperties(String context,String server,String subsystem)
  {
    if (server == null && context == null) return null;
    if (context == null) context = "DEFAULT";
    if (server == null) server = "HISTORY";
    boolean isCentral = isCentralArchiveServer(server);
    TDataType dtsubs;
    if (subsystem != null && subsystem.length() > 0 &&
        !subsystem.startsWith("ALL") && !subsystem.startsWith("All"))
    {
      dtsubs = new TDataType(subsystem);
    }
    else
    {
      dtsubs = new TDataType();
    }
    NAME32[] nlist = null; //TODO:  why not NAME64 ?
    int[] nprps = new int[1];
    String ntagsString, tagsString;
    TDataType dtnprps = new TDataType(nprps);
    TDataType dtprps;
    tagsString = isCentral ? "TAGS" : "HISTORIES";
    ntagsString = "N" + tagsString;
   
    String dname = "/" + context + "/" + server;
    TLink tl = new TLink(dname,ntagsString,dtnprps,dtsubs,TAccess.CA_READ);
    int cc = tl.execute(800, true);
    tl.close();
    if (cc != 0)
    { // TODO: throw an exception here
      return null;
    }
    if (nprps[0] > 0)
    {
      nlist = new NAME32[nprps[0]];
      dtprps = new TDataType(nlist);
      tl = new TLink(dname,tagsString,dtprps,dtsubs,TAccess.CA_READ);
      cc = tl.execute(800, true);
      tl.close();
      if (cc != 0)
      { // TODO: throw an exception here
        return null;
      }
    }
    if (nlist == null) return null;
    String nam;
    Vector<String> v = new Vector<String>(nlist.length);
    int n = 0;
    for (int i=0; i<nlist.length; i++)
    {
      nam = nlist[i].getName();
      if (nam.endsWith(".NAM")) continue;
      if (nam.endsWith(".ENUM")) continue;
      if (nam.endsWith(".SRCADDR")) continue;
      if (nam.endsWith(".DESC")) continue;
      v.add(nam);
      n++;
    }
    return v.toArray(new String[n]);
  }
  public static String[] getAnnotatedProperties(String context,double start, double stop, int timeout)
  {
    if (context == null) return null;
    NAME64[] nlist = null;   
    double[] ststp = new double[]{start,stop};
    String dname = "/"+context+"/HISTORY";
    nlist = new NAME64[100];
    TLink tl = new TLink(dname,"RECORDS.WITH.CMTS",new TDataType(nlist),new TDataType(ststp),TAccess.CA_READ);
    int cc = tl.executeAndClose(800);
    if (cc != 0)
    { // TODO: throw an exception here
      return null;
    }
    return NameToString.nameArrayToString(nlist,tl.getOutputDataObject().getCompletionLength());
  }
  private static void fillinHstPrpVectors(NAME64[] plist,int len,Vector<String> nv,Vector<String> ev,Vector<String> sav, Vector<String> dv)
  {
    String p, ucnam;
    for (int i=0; i<len; i++)
    {
      ucnam = plist[i].getName().toUpperCase();
      if (ucnam.endsWith(".NAM"))
      {
        p = ucnam.substring(0, ucnam.lastIndexOf(".NAM"));
        if (!nv.contains(p)) nv.add(p); continue;
      }
      if (ucnam.endsWith(".ENUM"))
      {
        p = ucnam.substring(0, ucnam.lastIndexOf(".ENUM"));
        if (!ev.contains(p)) ev.add(p); continue;
      }
      if (ucnam.endsWith(".SRCADDR"))
      {
        p = ucnam.substring(0, ucnam.lastIndexOf(".SRCADDR"));
        if (!sav.contains(p)) sav.add(p); continue;
      }
      if (ucnam.endsWith(".DESC"))
      {
        int idx = ucnam.lastIndexOf(".NAM.DESC");
        if (idx < 0) idx = ucnam.lastIndexOf(".DESC");
        p = ucnam.substring(0, idx);
        if (!dv.contains(p)) dv.add(p); continue;
      }         
    }
  }
  /**
   * Retrieve a list of properties archived by the given server as a list of
   * HstPrp instances, where additional information is returned along with the
   * property name.
   *
   * @param context is the targeted server context
   * @param server is the targeted server name
   * @param subsystem is the targeted subsystem
   * (applicable when the server = "HISTORY", i.e. the central archive server)
   *
   * @return a list of available archived properties
   */
  public static HstPrp[] getArchivedPropertyList(String context,String server,String subsystem)
  {
    if (server == null && context == null) return null;
    if (context == null) context = "DEFAULT";
    if (server == null) server = "HISTORY";
    boolean isCentral = isCentralArchiveServer(server);
    TDataType dtsubs;
    if (subsystem != null && subsystem.length() > 0 &&
        !subsystem.startsWith("ALL") && !subsystem.startsWith("All"))
    {
      dtsubs = new TDataType(subsystem);
    }
    else
    {
      dtsubs = new TDataType();
    }
    NAME64[] nlist = null;
    int[] nprps = new int[1];
    String ntagsString, tagsString;
    tagsString = isCentral ? "TAGS" : "HISTORIES";
    ntagsString = "N" + tagsString;
   
    String dname = "/" + context + "/" + server;
    TLink tl = new TLink(dname,ntagsString,new TDataType(nprps),dtsubs,TAccess.CA_READ);
    int cc = tl.executeAndClose(800);
    if (cc != 0)
    { // TODO: throw an exception here
      return null;
    }
    if (nprps[0] > 0)
    { // get OFFICIAL list of archived elements
      nlist = new NAME64[nprps[0]];
      tl = new TLink(dname,tagsString, new TDataType(nlist),dtsubs,TAccess.CA_READ);
      cc = tl.executeAndClose(800);
      if (cc != 0)
      { // TODO: throw an exception here
        return null;
      }
    }
    if (nlist == null) return null;
    String ucnam, nam;
    Vector<String> nv = new Vector<String>(nlist.length);
    Vector<String> ev = new Vector<String>(25);
    Vector<String> sav = new Vector<String>(nlist.length);
    Vector<String> dv = new Vector<String>(25);
    Hashtable<String,HstPrp> pl = new Hashtable<String,HstPrp>();
    for (int i=0; i<nlist.length; i++)
    {
      nam = nlist[i].getName();
      if (nam.trim().length() == 0) continue;
      ucnam = nam.toUpperCase();
      if (ucnam.endsWith(".NAM"))
      {
        nv.add(ucnam.substring(0, ucnam.indexOf(".NAM"))); continue;
      }
      if (ucnam.endsWith(".ENUM"))
      {
        ev.add(ucnam.substring(0, ucnam.indexOf(".ENUM"))); continue;
      }
      if (ucnam.endsWith(".SRCADDR"))
      {
        sav.add(ucnam.substring(0, ucnam.indexOf(".SRCADDR"))); continue;
      }
      if (ucnam.endsWith(".DESC"))
      {
        int idx = ucnam.indexOf(".NAM.DESC");
        if (idx < 0) idx = ucnam.indexOf(".DESC");
        dv.add(ucnam.substring(0, idx)); continue;
      }
      pl.put(ucnam, new HstPrp(nam));
    }
    if (pl.isEmpty()) return new HstPrp[0];
    if (isCentral)
    { // take care of other contingencies at the Central Archive Server
      NAME64[] plist = new NAME64[nlist.length*2];
      TDataType tdt = new TDataType(plist);
      tl = new TLink(dname,"PROPERTIES",tdt,null,TAccess.CA_READ);
      if (tl.executeAndClose(800) == 0)
      {
        fillinHstPrpVectors(plist,tdt.getCompletionLength(),nv,ev,sav,dv);
      }
      tl = new TLink(dname,"SRVALIASLIST",tdt,null,TAccess.CA_READ);
      if (tl.executeAndClose(800) == 0)
      {
        fillinHstPrpVectors(plist,tdt.getCompletionLength(),nv,ev,sav,dv);
      }     
    }   
    HstPrp hp;
    for (String s : nv) if ((hp=pl.get(s)) != null) hp.hasNameList = true;
    for (String s : ev) if ((hp=pl.get(s)) != null) hp.hasEnumList = true;
    for (String s : sav) if ((hp=pl.get(s)) != null) hp.hasSrcList = true;
    for (String s : dv) if ((hp=pl.get(s)) != null) hp.hasDescList = true;
    return pl.values().toArray(new HstPrp[pl.size()]);
  }
  private static String selectedContext = null;
  private static Hashtable<String, String> srcTbl = null;
  public static String getHistoryServerDropString(String context,String sourceDropString)
  {
    if (sourceDropString == null) return sourceDropString;
    if (sourceDropString.startsWith("TINE/")) return sourceDropString;
    if (selectedContext == null || selectedContext.compareToIgnoreCase(context) != 0)
    {
      selectedContext = context;
      srcTbl = getArchivedSources(context,"HISTORY",1000);
      if (srcTbl == null)
        return sourceDropString.startsWith("/") ? "TINE"+sourceDropString : "TINE/"+sourceDropString;
    }
    String key;
    int idx = sourceDropString.indexOf('[');
    if (idx > 0)
    {
      key = sourceDropString;
    }
    else
    {
      idx = sourceDropString.lastIndexOf('/');
      if (idx < 0) // punt
        return sourceDropString.startsWith("/") ? "TINE"+sourceDropString : "TINE/"+sourceDropString;
      String prp = sourceDropString.substring(idx+1);
      key = sourceDropString.substring(0,idx)+"["+prp+"]";
    }
    if (srcTbl == null) // punt
      return sourceDropString.startsWith("/") ? "TINE"+sourceDropString : "TINE/"+sourceDropString;
    if (!key.startsWith("/")) key = "/"+key;
    if (srcTbl.containsKey(key))
    {
      String v = srcTbl.get(key);
      key = "/"+context+"/ARCHIVER/"+v;
    }
    // re-form to the viewers expectation
    idx = key.indexOf('[');
    if (idx < 0) return "TINE"+key; // punt
    int lidx = key.indexOf(']');
    String prp = key.substring(idx+1,lidx);
    key = key.substring(0,idx)+"/"+prp;     
    return "TINE"+key;
  }
  public static Hashtable<String, String> getArchivedSources(String context,String server,int timeout)
  {
    Hashtable<String, String> srcTbl = new Hashtable<String, String>();
    if (server == null && context == null) return null;
    if (context == null) context = "DEFAULT";
    if (server == null) server = "HISTORY";
    NAME64[] nlist = new NAME64[1000];
    TDataType dtprps = new TDataType(nlist);
   
    String dname = "/" + context + "/" + server;
    TLink tl = new TLink(dname,"*.SRCADDR",dtprps,null,TAccess.CA_READ);
    int cc = tl.executeAndClose(800);
    if (cc != 0)
    { // TODO: throw an exception here
      return null;
    }
    int listsize = dtprps.getCompletionLength();
    if (listsize > 0)
    {
      String prp;
      String[] srcs;
      for (int i=0; i<listsize; i++)
      {
        prp = nlist[i].getName();
        prp = prp.substring(0, prp.length()-8);
        srcs = getArchiveSource(context, server, prp, "#0", timeout);
        if (srcs != null)
        {
          String key = "keyword["+prp+"]";
          String dev;
          String[] devs = null;
          if (srcs.length > 1)
          {
            devs = TQuery.getDeviceNames(context, server, prp);
          }
          for (int k=0; k<srcs.length; k++)
          { // key is the src string (per device)
            if (srcs[k] == null) break;
            if (srcs.length > 1)
            {
              dev = (devs == null || k >= devs.length) ? "#"+k : devs[k];
              key = dev+"["+prp+"]";
            }
            srcTbl.put(srcs[k],key);
          }
        }
      }
      return srcTbl;
    }
    return null;
  }
  /**
   * Retrieve a list of archived devices for the given server and property.
   *
   * @param context is the targeted server context
   * @param server is the targeted server name
   * @param property is the targeted property
   *
   * @return a list of available archived device names
   */
  public static String[] getArchivedDevices(String context,String server, String property)
  {
    return TQuery.getDeviceNames(context, server, property);
  }
  /**
   * Retrieve a list of available subsystems at the central archive server for the given context
   *
   * @param context is the targeted server context
   *
   * @return a list of available subsystems
   */
  public static String[] getArchivedSubsystems(String context)
  {
    return getArchivedSubsystems(context,null);
  }
  public static String[] getArchivedSubsystems(String context,String server)
  {
    if (server == null && context == null) return null;
    if (context == null) context = "DEFAULT";
    if (server == null) server = "HISTORY"; // assume the standard central archive server for the given context
    if (!isCentralArchiveServer(server)) return null;
    int cc;
    NAME32[] nlist = null;
    int[] nsubs = new int[1];
    String ntagsString, tagsString;
    TDataType dtnsubs = new TDataType(nsubs);
    TDataType dtsubs;
    tagsString = "SUBSYSTEMS";
    ntagsString = "N" + tagsString;
   
    String dname = "/" + context + "/" + server;
    TLink tl = new TLink(dname,ntagsString,dtnsubs,null,TAccess.CA_READ);
    cc = tl.execute(800, true);
    tl.close();
    if (cc != 0)
    { // TODO: throw an exception here
      return null;
    }
    if (nsubs[0] > 0)
    {
      nlist = new NAME32[nsubs[0]];
      dtsubs = new TDataType(nlist);
      tl = new TLink(dname,tagsString,dtsubs,null,TAccess.CA_READ);
      cc = tl.execute(800, true);
      tl.close();
      if (cc != 0)
      { // TODO: throw an exception here
        return null;
      }
    }
    String nam;
    Vector<String> v = new Vector<String>(nlist.length);
    int n = 0;
    for (int i=0; i<nlist.length; i++)
    {
      nam = nlist[i].getName();
      if (nam.length() == 0) continue;
      v.add(nam);
      n++;
    }
    return v.toArray(new String[n]);
  }
  public static String[] getOperationHistoryContexts()
  {
    return TQuery.getContexts("STATE");
  }
  public static String[] getOperationHistoryContexts(String importance)
  {
    return TQuery.getContexts("STATE",importance);
  }
  /**
   * Retrieve a list of available contexts where a central archive server exists
   *
   * @return a list of available contexts
   */
  public static String[] getArchiveServerContexts()
  {
    return TQuery.getContexts("HISTORY");
  }
  /**
   * Retrieve a list of available contexts where a central archive server exists
   * and has the importance specified
   *
   * @param importance is the targeted importance level
   * (one of "ALL", "IMPORTANT", "CRITICAL", "ESSENTIAL")
   * @return a list of available contexts
   */
  public static String[] getArchiveServerContexts(String importance)
  {
    return TQuery.getContexts("HISTORY",importance);
  }
  public static String[] getOperationHistoryConfigurationNames(String context)
  {
    if (context == null) return null;
    return TQuery.getDeviceNames(context, "STATE", "CONFIGURATION");
  }
  public static String[] getViewerMcaConfigurationNames(String context)
  {
    if (context == null) return null;
    return TQuery.getDeviceNames(context, "HISTORY", "CONFIGURATION.MCA");
  }
  public static String[] getViewerConfigurationNames(String context)
  {
    if (context == null) return null;
    return TQuery.getDeviceNames(context, "HISTORY", "CONFIGURATION");
  }
  public static String[] getViewerTraceConfigurationNames(String context)
  {
    if (context == null) return null;
    String[] nms = TQuery.getDeviceNames(context, "HISTORY", "CONFIGURATION.TRACE");
    if (nms == null || nms.length < 2) return nms;
    String[] nmsr = new String[nms.length-2];
    boolean hasProperties = false, hasTraces = false;
    for (int i=0, k=0; i<nms.length; i++)
    {
      if (nms[i].compareToIgnoreCase("properties") == 0)
      {
        hasProperties = true;
        continue;
      }
      if (nms[i].compareToIgnoreCase("traces") == 0)
      {
        hasTraces = true;
        continue;
      }
      if (k<nms.length-2) nmsr[k++] = nms[i];
    }
    return hasProperties && hasTraces ? nmsr : nms;
  }
  public static String[] getViewerConfigurationNames(String context,String group)
  {
    if (context == null) return null;
    String prp = "CONFIGURATION";
    if (group != null) prp += "." + group;
    String[] nms = TQuery.getDeviceNames(context, "HISTORY", prp);
    if (nms == null || nms.length < 2) return nms;
    String[] nmsr = new String[nms.length-1];
    boolean hasProperties = false;
    for (int i=0, k=0; i<nms.length; i++)
    {
      if (nms[i].compareToIgnoreCase("properties") == 0)
      {
        hasProperties = true;
        continue;
      }
      if (k<nms.length-1) nmsr[k++] = nms[i];
    }
    return hasProperties ? nmsr : nms;
  }
  public static HstView[] getViewerConfigurationList(String context,String group)
  {
    if (context == null) return null;
    if (!isInitialized) initCfgs();
    String prp = "CONFIGURATION";
    if (group != null) prp += "." + group;
    prp += ".NAM";
    TDataType dout = new TDataType(views);
    String dname = "/" + context + "/HISTORY";
    TLink tl = new TLink(dname,prp,dout,null,TAccess.CA_READ);
    if (tl.executeAndClose(1000) != 0) return null;
    int len = dout.getCompletionLength();
    if (len == 0) return null;
    HstView[] list = new HstView[len];
    for (int i=0; i<len; i++) list[i] = new HstView(views[i]);
    return list;
  }
  public static int setViewerConfigurationList(String context,String group,HstView[] list)
  {
    if (context == null || list == null) return TErrorList.argument_list_error;
    if (!isInitialized) initCfgs();
    String prp = "CONFIGURATION";
    if (group != null) prp += "." + group;
    prp += ".NAM";
    TDataType din = new TDataType(list);
    String dname = "/" + context + "/HISTORY";
    TLink tl = new TLink(dname,prp,null,din,TAccess.CA_WRITE);
    return tl.executeAndClose(800);
  }
  public static String[] getViewerConfigurationGroups(String context)
  {
    if (context == null) return null;
    NAME64[] prps = new NAME64[100];
    TDataType dtprps = new TDataType(prps);
    String dname = "/" + context + "/HISTORY";
    String dprop = "CONFIGURATION.*";
    TLink tl = new TLink(dname,dprop,dtprps,null,TAccess.CA_READ);
    int cc = tl.execute(800, true);
    tl.close();
    if (cc != 0)
    { // TODO: throw an exception here
      return null;
    }
    int len = dtprps.getCompletionLength();
    String nam;
    Vector<String> v = new Vector<String>(len);
    int n = 0;
    for (int i=0; i<len; i++)
    {
      nam = prps[i].getName();
      if (nam.compareToIgnoreCase("CONFIGURATION") == 0) continue;
      if (nam.length() == 0) continue;
      if (nam.endsWith(".NAM")) continue;
      if (nam.endsWith(".MCA")) continue;
      if (nam.endsWith(".TRACE")) continue;
      v.add(nam.substring(14));
      n++;
    }
    return n > 0 ? v.toArray(new String[n]) : null;   
  }
  public static HstCfg[] getSelectableProptiesConfiguration(String context,String group)
  {
    if (context == null || group == null) return null;
    return getViewerConfiguration(context,"properties",group);
  }
  public static HstCfg[] getSelectableTracesConfiguration(String context)
  {
    if (context == null) return null;
    return getViewerConfiguration(context,"traces","TRACE");
  }
  public static HstCfg[] getViewerConfiguration(String context,String name)
  {
    return getViewerConfiguration(context,name,null);
  }
  public static synchronized HstCfg[] getViewerConfiguration(String context,String name,String group)
  {
    if (context == null || name == null) return null;
    if (!isInitialized) initCfgs();
    TDataType dtcfg = new TDataType(cfgs);
    String dname = "/" + context + "/HISTORY/" + name;
    String dprop = "CONFIGURATION";
    if (group != null && group.length() > 0) dprop += "." + group;
    TLink tl = new TLink(dname,dprop,dtcfg,null,TAccess.CA_READ);
    int cc = tl.execute(800, true);
    tl.close();
    if (cc != 0)
    { // TODO: throw an exception here
      DbgLog.log("getViewerConfiguration",TErrorList.getErrorString(cc));
      return null;
    }
    int num = dtcfg.getCompletionLength();
    if (num == 0) return null;
    boolean hasPropertiesList = false;
    for (int i=0; i<num; i++)
    { // is 'properties' in the list ?
      if (cfgs[i].getDescription().compareToIgnoreCase("properties") == 0)
      {
        hasPropertiesList = true;
        break;
      }
    }
    int ncfg = hasPropertiesList ? num - 1 : num;
    HstCfg[] cfg = new HstCfg[ncfg];
    for (int i=0, n=0; i<num; i++)
    {
      if (cfgs[i].getDescription().compareToIgnoreCase("properties") != 0)
      {
        cfg[n++] = new HstCfg(cfgs[i]);       
      }
    }
    return cfg;
  }
  public static synchronized int setViewerConfiguration(String context,String name,String group,HstCfg[] hstCfgs)
  {
    if (context == null || name == null) return TErrorList.argument_list_error;
    if (!isInitialized) initCfgs();
    TDataType dtcfg = new TDataType(hstCfgs);
    String dname = "/" + context + "/HISTORY/" + name;
    String dprop = "CONFIGURATION";
    if (group != null && group.length() > 0) dprop += "." + group;
    TLink tl = new TLink(dname,dprop,null,dtcfg,TAccess.CA_WRITE);
    return tl.executeAndClose(800);
  }
  public static synchronized int setTraceViewerConfiguration(String context,String name,HstCfg[] hstCfgs)
  {
    if (context == null || name == null) return TErrorList.argument_list_error;
    if (!isInitialized) initCfgs();
    TDataType dtcfg = new TDataType(hstCfgs);
    String dname = "/" + context + "/HISTORY/" + name;
    String dprop = "CONFIGURATION.TRACE";
    TLink tl = new TLink(dname,dprop,null,dtcfg,TAccess.CA_WRITE);
    return tl.executeAndClose(800);
  }
  public static synchronized McaCfg[] getMcaViewerConfiguration(String context,String name)
  {
    if (context == null || name == null) return null;
    if (!isInitialized) initCfgs();
    TDataType dtcfg = new TDataType(mcacfgs);
    String dname = "/" + context + "/HISTORY/" + name;
    String dprop = "CONFIGURATION.MCA";
    TLink tl = new TLink(dname,dprop,dtcfg,null,TAccess.CA_READ);
    int cc = tl.execute(800, true);
    tl.close();
    if (cc != 0)
    { // TODO: throw an exception here
      DbgLog.log("getMcaViewerConfiguration",TErrorList.getErrorString(cc));
      return null;
    }
    int num = dtcfg.getCompletionLength();
    if (num == 0) return null;
    McaCfg[] cfg = new McaCfg[num];
    for (int i=0; i<num; i++)
    {
      cfg[i] = new McaCfg(mcacfgs[i]);
    }
    return cfg;
  }
  public static synchronized int setMcaViewerConfiguration(String context,String name,McaCfg[] mcaCfgs)
  {
    if (context == null || name == null) return TErrorList.argument_list_error;
    if (!isInitialized) initCfgs();
    TDataType dtcfg = new TDataType(mcaCfgs);
    String dname = "/" + context + "/HISTORY/" + name;
    String dprop = "CONFIGURATION.MCA";
    TLink tl = new TLink(dname,dprop,null,dtcfg,TAccess.CA_WRITE);
    return tl.executeAndClose(800);
  }
  public static synchronized McaCfg[] getOperationHistoryConfiguration(String context,String name)
  {
    if (context == null || name == null) return null;
    if (!isInitialized) initCfgs();
    TDataType dtcfg = new TDataType(mcacfgs);
    String dname = "/" + context + "/STATE/" + name;
    String dprop = "CONFIGURATION";
    TLink tl = new TLink(dname,dprop,dtcfg,null,TAccess.CA_READ);
    int cc = tl.execute(800, true);
    tl.close();
    if (cc != 0)
    { // TODO: throw an exception here
      DbgLog.log("getOperationHistoryConfiguration",TErrorList.getErrorString(cc));
      return null;
    }
    int num = dtcfg.getCompletionLength();
    if (num == 0) return null;
    McaCfg[] cfg = new McaCfg[num];
    for (int i=0; i<num; i++)
    {
      cfg[i] = new McaCfg(mcacfgs[i]);
    }
    return cfg;
  }
  public static synchronized PrfCfg[] getOperationHistoryProfile(String context,String name)
  {
    if (context == null || name == null) return null;
    if (!isInitialized) initCfgs();
    TDataType dtcfg = new TDataType(prfcfgs);
    String dname = "/" + context + "/STATE/" + name;
    String dprop = "PROFILE";
    TLink tl = new TLink(dname,dprop,dtcfg,null,TAccess.CA_READ);
    int cc = tl.execute(800, true);
    tl.close();
    if (cc != 0)
    { // TODO: throw an exception here
      DbgLog.log("getOperationHistoryProfile",TErrorList.getErrorString(cc));
      return null;
    }
    int num = dtcfg.getCompletionLength();
    if (num == 0) return null;
    PrfCfg[] cfg = new PrfCfg[num];
    for (int i=0; i<num; i++)
    {
      cfg[i] = new PrfCfg(prfcfgs[i]);
    }
    return cfg;
  }
}
TOP

Related Classes of de.desy.tine.histUtils.THistory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.