Changeset 142


Ignore:
Timestamp:
01/13/10 12:47:33 (15 years ago)
Author:
ogrimm
Message:
Various updates
Location:
Evidence
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • Evidence/Alarm/Alarm.cc

    r127 r142  
    55  - Checks periodically if all required servers are up
    66    (later it should try to start them if not)
    7   - Listens to the 'Status' service of each server. The server state
    8     is published as a DIM service.
     7  - Listens to the 'Status' service of each server.
     8  - A text describing the state of all servers is published as DIM service.
     9    The states are described in StateString[].
     10  - A master alarm (indicating most severe of individual alarms) is published.
    911   
    10   Oliver Grimm, September 2009
     12  Oliver Grimm, January 2010
    1113
    1214\********************************************************************/
     
    1416#define SERVER_NAME "Alarm"
    1517#include "../Evidence.h"
     18
     19#define SUMMARYSIZE 10000       // Bytes for alarm summary text
     20
     21const char* StateString[] = {"OK", "WARN", "ERROR", "FATAL", "UNAVAILABLE"};
    1622
    1723//
     
    2127   
    2228    DimStampedInfo **StatusService;
     29
    2330    void infoHandler();
    2431
     
    2633    AlarmHandler();
    2734    ~AlarmHandler();
    28    
     35
     36        DimService *Summary, *Master;
     37       
     38        char *AlarmSummary;
     39        int MasterAlarm;
     40        int *State;   
    2941    char **Server;
    3042    unsigned int NumServers;
    31     char *StateString;
    32     char *ServerList; 
     43    char *ServerList;
     44       
     45        void UpdateAlarmSummary();
    3346};
    3447
     
    3649AlarmHandler::AlarmHandler(): EvidenceServer(SERVER_NAME) {
    3750
     51  AlarmSummary = new char [SUMMARYSIZE];
     52  MasterAlarm = 0;
     53 
    3854  char *ServerNames = GetConfig(SERVER_NAME " servers");
     55
     56  // Create DIM services
     57  Summary = new DimService(SERVER_NAME"/Summary", AlarmSummary);
     58  Master = new DimService(SERVER_NAME"/MasterAlarm", MasterAlarm);
    3959
    4060  // Copy original list of servers to observe
     
    5373  // Subscribe with handler to 'Status' service of all servers
    5474  StatusService = new DimStampedInfo* [NumServers];
     75  State = new int [NumServers];
    5576 
    5677  for (int i=0; i<NumServers; i++) {
     
    5879    strcpy(Buffer, Server[i]);
    5980    strcat(Buffer, "/Status");
    60     StatusService[i] = new DimStampedInfo(Buffer, 0, this);
    61     printf("Subscribed to %s\n", Buffer);
    62     delete[] Buffer;
     81    StatusService[i] = new DimStampedInfo(Buffer, NO_LINK, this);
     82    delete[] Buffer;
     83       
     84        State[i] = 0;
    6385  }
    64  
    65   StateString = new char [NumServers+1];
    66   for (int i=0; i<NumServers; i++) StateString[i] = '1';
    67   StateString[NumServers] = '\0';
    6886}
    6987
     
    7391  for (int i=0; i<NumServers; i++) delete StatusService[i];
    7492  delete[] StatusService;
     93  delete Master;
     94  delete Summary;
     95  delete[] State;
    7596  delete[] Server;
    7697  delete[] ServerList;
     98  delete[] AlarmSummary;
    7799}
    78100
    79 // Print messages of status changes to screen
     101// Print messages of status changes to screen and update status string
    80102void AlarmHandler::infoHandler() {
    81103
    82   // Ignore empty messages
    83   if (strlen(getInfo()->getString()) == 0) return;
    84  
    85   // Print message
    86   time_t RawTime = getInfo()->getTimestamp();
    87   struct tm *TM = localtime(&RawTime);
    88   printf("%s (%02d:%02d:%02d): %s\n", getInfo()->getName(), TM->tm_hour,
    89         TM->tm_min, TM->tm_sec, getInfo()->getString());
     104  // Identify status service
     105  for (int i=0; i<NumServers; i++) if (getInfo() == StatusService[i]) {
    90106
    91   // Update status string
    92   for (int i=0; i<NumServers; i++) {
    93     if (strcmp(getInfo()->getName(),Server[i]) == 0) {
    94       StateString[i] = '2';
    95     }
     107        // Ignore DIS_DNS (has no status service)
     108        if (strcmp(getInfo()->getName(),"DIS_DNS/Status") == 0) return;
     109       
     110        // Update State if server is unavailable or with current severity of status 
     111        if (getInfo()->getSize()==strlen(NO_LINK)+1 &&
     112                strcmp(getInfo()->getString(), NO_LINK)==0) State[i] = 4;
     113        else {
     114          State[i] = *(getInfo()->getString()+strlen(getInfo()->getString())+2);
     115
     116          // Print message
     117          time_t RawTime = getInfo()->getTimestamp();
     118          struct tm *TM = localtime(&RawTime);
     119          printf("%s (%02d:%02d:%02d): %s\n", getInfo()->getName(), TM->tm_hour,
     120                TM->tm_min, TM->tm_sec, getInfo()->getString());         
     121        }
     122        UpdateAlarmSummary();
    96123  } 
    97124}
    98125
     126
     127// Update alarm status summary
     128void AlarmHandler::UpdateAlarmSummary() {
     129 
     130  int Offset = 0;
     131  MasterAlarm = 0;
     132   
     133  for (int i=0; i<NumServers; i++) {
     134    snprintf(AlarmSummary+Offset, SUMMARYSIZE-Offset, "%s: %s\n", Server[i], StateString[State[i]]);
     135        Offset += strlen(AlarmSummary+Offset);
     136        if (State[i] > MasterAlarm) MasterAlarm = State[i];
     137  }
     138  Summary->updateService();
     139  Master->updateService();
     140}
    99141
    100142//         
     
    113155  unsigned int Period = atoi(Alarm.GetConfig(SERVER_NAME " period"));
    114156
    115   // Create DIM services
    116   static DimService ServerState(SERVER_NAME"/State", Alarm.StateString);
    117   static DimService ServerList(SERVER_NAME"/Servers", Alarm.ServerList);
    118 
    119157  // Check periodically if servers are up
    120158  while(!EvidenceServer::ExitRequest) {
     
    125163        if (strcmp(ServerName, Alarm.Server[i]) == 0) Exists = true;
    126164      }
    127       if (!Exists) {
    128         Alarm.Msg(Alarm.WARN, "Server %s unavailable", Alarm.Server[i]);
    129         Alarm.StateString[i] = '0';
    130       }
    131       else if (Alarm.StateString[i] == '0') Alarm.StateString[i] = '1';
     165      if (!Exists) Alarm.State[i] = 4;
    132166    }
    133167   
    134     ServerState.updateService(Alarm.StateString);
     168    Alarm.UpdateAlarmSummary();
    135169    sleep(Period);
    136170  }
  • Evidence/Config/Config.cc

    r127 r142  
    55  - The configuration file is opened without buffering to catch changes
    66    without closing/opening.
    7   - If a configuration file change is detected through inotify, the service "Config/Modified"
    8     is updated (it contains no data) to inform applications.
    9   - The employed line buffer has conservatively at least the size of the configuration file. If
    10     needed, it will be enlarged.
     7  - The name of a configuration file can be given as command line argument
     8  - If a configuration file change is detected through inotify, the service
     9    "Config/ModifyTime" is updated with the current UNIX time to inform applications.
     10        The initial value of the server is the last file modification time.
     11  - The employed line buffer has conservatively at least the size of the
     12    configuration file. If needed, it will be enlarged.
    1113         
    1214  Oliver Grimm, November 2009
     
    1416\********************************************************************/
    1517
    16 #define CONFIG_FILE "configuration.txt"
     18#define DEFAULT_CONFIG "configuration.txt"
    1719#define SERVER_NAME "Config"
    1820
    1921#include "../Evidence.h"
     22#include <ctype.h>
    2023#include <sys/stat.h>
    2124#include <sys/inotify.h>
     
    3134    unsigned int BufferLength;
    3235    DimService *ConfigModified;
    33 
     36    int ModifyTime;
     37       
    3438    void rpcHandler();
    3539 
     
    4549        DimRpc("ConfigRequest", "C", "C"), EvidenceServer(SERVER_NAME) {
    4650
    47   // Create DIM service to indicate changes of configuration file
    48   ConfigModified = new DimService (SERVER_NAME"/Modified", (char *) "");
    49 
    5051  // Open configuration file
    5152  if ((File = fopen(Filename, "r")) == NULL) {
    52     Msg(FATAL, "Could not open configuration file '%s' (%s)\n", Filename, strerror(errno));
     53    State(FATAL, "Could not open configuration file '%s' (%s)\n", Filename, strerror(errno));
    5354  }
    5455
     56  // Create DIM service to indicate changes of configuration file
     57  struct stat Stat;
     58  if (stat(Filename, &Stat) == -1) {
     59    State(WARN, "Could not read last modification time of configuration file '%s' (%s)", Filename, strerror(errno));
     60        ModifyTime = 0;
     61  }
     62  else ModifyTime = Stat.st_mtime;   
     63  ConfigModified = new DimService (SERVER_NAME"/ModifyTime", ModifyTime);
     64
    5565  // Disable buffering, so file modifications are immediately seen
    56   if(setvbuf(File, NULL, _IONBF, 0) != 0) {
    57     Msg(WARN, "Error setting configuration file '%s' to unbuffered mode.\n", Filename);
     66  if (setvbuf(File, NULL, _IONBF, 0) != 0) {
     67    State(WARN, "Error setting configuration file '%s' to unbuffered mode", Filename);
    5868  }
    5969   
     
    7787  // Check if Buffer[] is large enough to hold full file, enlarge if necessary
    7888  if (fstat(fileno(File), &FileStatus) == -1) {
    79      Msg(ERROR, "Could not determine size of configuration file to allocate buffer (%s)\n", strerror(errno));
     89     State(FATAL, "Could not determine size of configuration file to allocate buffer (%s)", strerror(errno));
    8090  }
    8191  else if(BufferLength < FileStatus.st_size) {
     
    105115    if(Token1==NULL || Token2==NULL || Token3==NULL) continue;
    106116
    107     // Check for match and then send data
     117    // Check for match and then send data (removing trainlin whitespace)
    108118    if (strstr(Request, Token1)!=NULL && strstr(Request, Token2)!=NULL) {
     119          while (isspace(*Token3) != 0) Token3++;
    109120      setData(Token3);
    110121      break;
    111122    }
    112123  }
    113   Msg(INFO, "Client '%s' (ID %d) requested '%s'. Send '%s'.",
     124 
     125  // If configuration data not found, send empty string
     126  if (feof(File)!=0) setData((char *) "");
     127 
     128  State(INFO, "Client '%s' (ID %d) requested '%s'. Send '%s'.",
    114129                DimServer::getClientName(),
    115130                DimServer::getClientId(),
     
    120135void EvidenceConfig::ConfigChanged() {
    121136
     137  ModifyTime = time(NULL);
    122138  ConfigModified->updateService();
    123139}
     
    126142// Declaring class static ensures destructor is called when exit() is invoked
    127143//
    128 int main() {
     144int main(int argc, char *argv[]) {
    129145       
    130   static EvidenceConfig Config(CONFIG_FILE);
     146  static EvidenceConfig Config(argc<2 ? DEFAULT_CONFIG : argv[1]);
    131147
    132148  int Notify;
     
    134150 
    135151  if ((Notify = inotify_init()) == -1) {
    136     Config.Msg(EvidenceConfig::WARN, "inotify_init() failed, cannot monitor changes of configuration file (%s)\n", strerror(errno));
     152    Config.State(EvidenceConfig::WARN, "inotify_init() failed, cannot monitor changes of configuration file (%s)\n", strerror(errno));
    137153  }
    138   else if (inotify_add_watch(Notify, CONFIG_FILE, IN_MODIFY) == -1) {
    139       Config.Msg(EvidenceConfig::WARN, "Could not set inotify watch on configuration file (%s)\n", strerror(errno));
     154  else if (inotify_add_watch(Notify, argc<2 ? DEFAULT_CONFIG : argv[1], IN_MODIFY) == -1) {
     155      Config.State(EvidenceConfig::WARN, "Could not set inotify watch on configuration file (%s)\n", strerror(errno));
    140156          close(Notify);
    141157          Notify = -1;
  • Evidence/Config/Makefile

    r127 r142  
    22
    33PROG=Config
    4 CPPFLAGS += -I../DIM/ 
     4CPPFLAGS += -I../DIM/
    55LDLIBS += -lpthread
    66
  • Evidence/DColl/DColl.cc

    r127 r142  
    55  - DColl subscribes to all services given by the configuration
    66    server and writes these to the data file at every update.
    7   - For each service, it creates a new service with '.hist' appended that
    8     contains a history of events kept within a ring buffer. Each entry will
    9         be the result of a conversion to double of the text written to the data file.
     7  - One data file per day is generated, with roll-over at 13:00 local time.
     8  - For each service, it creates a new service with '.hist' appended
     9        that contains a history of events kept within a ring buffer. Each
     10        entry will be the result of a conversion to double of the text
     11        written to the data file. Only if the new value has changed by a
     12        minimum amout it will be added to the ring buffer.
    1013  - The command 'DColl/Log' writes the associated string to the log
    1114    file specified in the configuration.
     
    1720#define SERVER_NAME "DColl"
    1821
     22#define DATE_ROLLOVER 12 // localtime hour after which next date is used
     23
    1924#include "../Evidence.h"
    20 
    21 #define NO_LINK "__&DIM&NOLINK&__" // for checking if DIMserver is alive
     25#include <math.h>
     26#include <float.h>
     27#include <sys/stat.h>
     28#include <ctype.h>
     29#include <sys/types.h>
     30#include <regex.h>
    2231
    2332//
    2433// Class declaration
    2534//
    26 class DataHandler:      public DimClient, public DimCommand,
    27                                         public DimBrowser, public EvidenceServer {
    28   private:
    29     pthread_mutex_t DataMutex;
    30     pthread_mutex_t LogMutex;
    31 
     35class DataHandler:      public DimClient, public DimBrowser,
     36                                        public EvidenceServer {
     37
     38        struct Item {
     39          DimStampedInfo *DataItem;
     40      DimService *HistService;
     41          struct EvidenceHistoryItem *HistBuffer;
     42          int HistPointer;
     43          double LastValue;
     44          double MinAbsChange;
     45        } *List;
     46
     47        DimCommand *LogCommand;
     48               
    3249    unsigned int NumItems;
    3350    FILE *DataFile;
    3451    FILE *LogFile;
    35     char *Filename;
    36 
    37     DimStampedInfo **DataItem;
    38     DimService **HistService;       
     52        float DataSizekB, LogSizekB;
     53        int DataSizeLastUpdate, LogSizeLastUpdate;
     54        char *DataDir;
     55    DimService *LogSizeService, *DataSizeService;
    3956    int HistSize;
    40     struct EvidenceHistoryItem **HistBuffer;
    41         int *HistPointer;
    42        
     57        int SizeUpdateDelay;
     58        int TimeForNextFile;
     59       
     60        int RegExCount;
     61        regex_t *RegEx;
     62        double *MinChange;
     63       
    4364    void infoHandler();
    4465    void commandHandler();
    45     bool ReallocMem(void *&, int);
     66        void AddService(char *);
     67        float FileSize(FILE *);
    4668           
    4769  public:
     
    5375// Constructor
    5476//
    55 DataHandler::DataHandler(): DimCommand((char *) "DColl/Log", (char *) "C"), EvidenceServer(SERVER_NAME) {
     77DataHandler::DataHandler(): EvidenceServer(SERVER_NAME) {
    5678
    5779  // Initialization to prevent freeing unallocated memory
    5880  DataFile = NULL;
    59   Filename = NULL;
    60 
    61   DataItem = NULL;
    62   HistService = NULL;
    63   HistBuffer = NULL;
    64   HistPointer = NULL;
    65  
     81  LogFile = NULL;
     82  List = NULL;
     83  LogSizeService = NULL;
     84  DataSizeService = NULL;
     85 
     86  DataSizeLastUpdate = 0;
     87  LogSizeLastUpdate = 0;
     88  TimeForNextFile = 0;
     89
    6690  // Request configuration data
    67   char *Items = GetConfig(SERVER_NAME " items");
    68   char *DataDir = GetConfig(SERVER_NAME " datadir");
     91  char *Change = GetConfig(SERVER_NAME " minchange");
     92  DataDir = GetConfig(SERVER_NAME " datadir");
    6993  char *Logname = GetConfig(SERVER_NAME " logfile");
     94  SizeUpdateDelay = atoi(GetConfig(SERVER_NAME " sizeupdate"));
    7095  HistSize = atoi(GetConfig(SERVER_NAME " histsize"));
    7196  if (HistSize < 1) HistSize = 1; // Minimum one items
    7297   
    73   // Create mutex for thread synchronization
    74   if ((errno=pthread_mutex_init(&DataMutex, NULL)) != 0) {
    75     Msg(FATAL, "pthread_mutex_init() failed for data file (%s)", strerror(errno));
    76   }
    77   if ((errno=pthread_mutex_init(&LogMutex, NULL)) != 0) {
    78     Msg(FATAL, "pthread_mutex_init() failed for log file (%s)", strerror(errno));
    79   }
    80 
    81   // Interpret DIM services to observe and prepare history buffer
    82   char *Buf, *ServiceName, *Format;
    83   int NumServices;
    84   NumItems = 0;
    85   char *NextToken = strtok(Items, " \t");
    86   while (NextToken != NULL) {
    87     NumServices = getServices(NextToken);
    88     for (int j=0; j<NumServices; j++)  {
    89           if (getNextService(ServiceName, Format) == DimSERVICE) {
    90 
    91             // Check if already subsubed to this service
    92                 for (int i=0; i<NumItems; i++) {
    93                   if(strcmp(ServiceName, DataItem[i]->getName()) == 0) continue;
    94                 }
    95                
    96             // Increase capacity of arrays
    97             if (!ReallocMem((void *&) DataItem, NumItems+1)) continue;
    98             if (!ReallocMem((void *&) HistService, NumItems+1)) continue;
    99             if (!ReallocMem((void *&) HistBuffer, NumItems+1)) continue;
    100             if (!ReallocMem((void *&) HistPointer, NumItems+1)) continue;
    101 
    102                 // Subscribe to service
    103         DataItem[NumItems] = new DimStampedInfo(ServiceName, (char *) NO_LINK, this);
    104 
    105         // Create history service
    106         HistBuffer[NumItems] = new struct EvidenceHistoryItem [HistSize];
    107                 memset(HistBuffer[NumItems], 0, HistSize*sizeof(EvidenceHistoryItem));
    108                 HistPointer[NumItems] = 0;
    109 
    110         if (asprintf(&Buf, "%s.hist", ServiceName) == -1) {
    111           Msg(ERROR, "Could not create Hist service for %s because asprintf() failed", ServiceName);
    112         }
    113         else {
    114                   HistService[NumItems] = new DimService (Buf, (char *) "C",
    115                                                                 HistBuffer[NumItems], HistSize*sizeof(EvidenceHistoryItem));
    116           free(Buf);
    117                 }
    118                 NumItems++;
    119           }
    120         }
    121     NextToken = strtok(NULL, " \t");
    122   }
    123 
    124   // Open data file
    125   time_t rawtime = time(NULL);
    126   struct tm * timeinfo = gmtime(&rawtime);
    127   if(timeinfo->tm_hour >= 13) rawtime += 12*60*60;
    128   timeinfo = gmtime(&rawtime);
    129 
    130   if (asprintf(&Filename, "%s/%d%02d%02d.slow", DataDir, timeinfo->tm_year+1900, timeinfo->tm_mon+1, timeinfo->tm_mday) == -1) {
    131     Filename = NULL;
    132     Msg(FATAL, "Could not create filename with asprintf()(%s)", strerror(errno));
    133   }
    134   if ((DataFile = fopen(Filename, "a")) == NULL) {
    135     Msg(FATAL, "Could not open data file '%s' (%s)", Filename, strerror(errno));
    136   }
    137  
    13898  // Open log file
    13999  if ((LogFile = fopen(Logname, "a")) == NULL) {
    140     Msg(FATAL, "Could not open log file '%s' (%s)", Logname, strerror(errno));
    141   }
    142 
     100    State(FATAL, "Could not open log file '%s' (%s)", Logname, strerror(errno));
     101  }
     102
     103  // Provide logging command   
     104  LogCommand = new DimCommand("DColl/Log", (char *) "C", this);
     105             
     106  // Create services for file sizes
     107  DataSizekB = 0;
     108  DataSizeService = new DimService(SERVER_NAME "/DataSizekB", DataSizekB);
     109 
     110  LogSizekB = FileSize(LogFile);
     111  LogSizeService = new DimService(SERVER_NAME "/LogSizekB", LogSizekB);
     112
     113  // Count how many minimum change value regular expressions are present
     114  RegExCount = 0;
     115  char *Token = strtok(Change, "\t ");
     116  while (Token != NULL) {
     117        RegExCount++;
     118        Token = strtok(NULL, "\t ");
     119  }
     120 
     121  // Allocate memory for regular expressions and minimum change values
     122  RegEx = new regex_t[RegExCount];
     123  MinChange = new double [RegExCount];
     124
     125  // Compile regular expressions
     126  int Pos = 0;
     127  for (int i=0; i<RegExCount; i++) {
     128    int Len = strlen(Change+Pos) + 1;
     129    Token = strtok(Change + Pos, ": \t");
     130
     131    int Ret = regcomp(&RegEx[i], Token, REG_EXTENDED|REG_NOSUB);
     132        if (Ret != 0) {
     133          char ErrMsg[200];
     134          regerror(Ret, &RegEx[i], ErrMsg, sizeof(ErrMsg));
     135          State(WARN, "Error compiling regular expression '%s' (%s)", Token, ErrMsg);
     136        }
     137        else {
     138          if ((Token=strtok(NULL, "")) != NULL) MinChange[i] = atof(Token);
     139          else MinChange[i] = 0;
     140        }
     141        Pos += Len;
     142  }
     143
     144  // Subscribe to list of servers at DIS_DNS
     145  AddService((char *) "DIS_DNS/SERVER_LIST");
     146
     147  DimClient::sendCommand("DColl/Log", SERVER_NAME" *** Logging started ***");
    143148}
    144149
     
    148153DataHandler::~DataHandler() {
    149154
     155  // Delete DIM services and command first so handlers and not called anymore
     156  for (int i=0; i<NumItems; i++) {
     157        delete List[i].HistService;
     158    delete List[i].DataItem;
     159    delete[] List[i].HistBuffer;
     160  }
     161  free(List);
     162
     163  //delete LogSizeService; // These create segmentation faults?!
     164  //delete DataSizeService;
     165
     166  delete LogCommand;
     167
    150168  // Close files
    151   if (LogFile != NULL && fclose(LogFile) != 0) {
    152         Msg(ERROR, "Could not close log file (%s)", strerror(errno));
     169  if (LogFile != NULL) if (fclose(LogFile) != 0) {
     170        State(ERROR, "Could not close log file (%s)", strerror(errno));
    153171  }
    154172  if (DataFile != NULL && fclose(DataFile) != 0) {
    155         Msg(ERROR, "Error: Could not close data file (%s)", strerror(errno));
    156   }     
    157 
    158   // Free all memory
    159   for (int i=0; i<NumItems; i++) {
    160     delete[] HistBuffer[i];
    161     delete DataItem[i];
    162   }
    163 
    164   free(Filename);
    165   free(HistService);
    166   free(HistPointer);
    167   free(HistBuffer);
    168   free(DataItem);
    169  
    170   // Destroy mutex
    171   pthread_mutex_destroy (&LogMutex);
    172   pthread_mutex_destroy (&DataMutex);
     173        State(ERROR, "Error: Could not close data file (%s)", strerror(errno));
     174  }
     175
     176  // Free memory for regular expressions handling
     177  for (int i=0; i<RegExCount; i++) {
     178    regfree(&RegEx[i]);
     179  }
     180  delete[] MinChange;
     181  delete[] RegEx;
    173182}
    174183
     
    176185// Implementation of data handling
    177186//
    178 // More than one infoHandler() might run in parallel, therefore
    179 // the mutex mechanism is used to serialize writing to the file
     187// DIM ensures infoHandler() is called serialized, therefore
     188// no mutex is needed to serialize writing to the file
    180189void DataHandler::infoHandler() {
    181  
     190
    182191  DimInfo *Info = getInfo();
    183192
    184   // Check if service actually available, if it contains data and if data file is open
     193  // Check if service available
    185194  if (Info->getSize()==strlen(NO_LINK)+1 && strcmp(Info->getString(), NO_LINK)==0) return;
    186   if (Info->getSize() == 0 || DataFile == NULL) return;
    187 
     195
     196  // If service is DIS_DNS/SERVER_LIST, subscribe to all SERVICE_LIST services
     197  if (strcmp(Info->getName(), "DIS_DNS/SERVER_LIST") == 0) {   
     198        char *Token = strtok(Info->getString(), "+-!|@");       
     199        while (Token != NULL) {
     200          char *Buf;
     201          if (MakeString(&Buf, "%s/SERVICE_LIST", Token) != -1) {
     202            AddService(Buf);
     203                free(Buf);
     204          }
     205          else State(ERROR, "MakeString() failed for server %s", Token);
     206         
     207          Token = strtok(NULL, "|");
     208          Token = strtok(NULL, "+-!|@");       
     209        }       
     210        return;
     211  }
     212
     213  // If service is SERVICE_LIST of any server, scan all services.
     214  // Subscribe to all services (but not to commands and RPCs)
     215  if (strstr(Info->getName(), "/SERVICE_LIST") != NULL) {
     216        char *Name = strtok(Info->getString(), "+-!|");
     217        while (Name != NULL) {
     218          char *Type = strtok(NULL, "\n");
     219          if (Type == NULL) return; // for safety, should not happen
     220      if (strstr(Type, "|CMD")==NULL && strstr(Type, "|RPC")==NULL) {
     221                AddService(Name);
     222          }
     223          Name = strtok(NULL, "+-!|");
     224        }
     225        return;
     226  }
     227
     228  // If it is time to open new data file, close the current one
     229  if (time(NULL) >= TimeForNextFile) {
     230        if (DataFile != NULL && fclose(DataFile) != 0) {
     231          State(ERROR, "Error: Could not close data file (%s)", strerror(errno));
     232        }
     233        DataFile = NULL;
     234  }
     235 
     236  // Open new data file if necessary
     237  if (DataFile == NULL) {
     238        time_t Time = time(NULL);
     239        struct tm *T = localtime(&Time);
     240       
     241        if(T->tm_hour >= DATE_ROLLOVER) T->tm_mday++;
     242        if (mktime(T) == -1) State(ERROR, "mktime() failed, check filename");
     243
     244        char *Filename;
     245        if (MakeString(&Filename, "%s/%d%02d%02d.slow", DataDir, T->tm_year+1900, T->tm_mon+1, T->tm_mday) == -1) State(FATAL, "Could not create filename, MakeString() failed");
     246        if ((DataFile = fopen(Filename, "a")) == NULL) {
     247      State(FATAL, "Could not open data file '%s' (%s)", Filename, strerror(errno));
     248        }
     249        else State(INFO, "Opened data file '%s'", Filename);
     250        free(Filename);
     251       
     252        // Calculate time for next file opening
     253        T->tm_sec = 0;
     254        T->tm_min = 0;
     255        T->tm_hour = DATE_ROLLOVER;
     256        TimeForNextFile = mktime(T);
     257  }
     258   
    188259  // Identify index of service
    189260  int Service; 
    190   for (Service=0; Service<NumItems; Service++) if (Info == DataItem[Service]) break;
    191   if (Service == NumItems) return;  // Not found: should never happen
    192 
    193   pthread_mutex_lock(&DataMutex);
    194  
     261  for (Service=0; Service<NumItems; Service++) if (Info == List[Service].DataItem) break;
     262  if (Service == NumItems) return;  // Service not found
     263
     264  // If negative value for absolute change, ignore this entry
     265  if (List[Service].MinAbsChange < 0) return;
     266
    195267  // Write data header
    196268  time_t RawTime = Info->getTimestamp();
     
    199271  fprintf(DataFile, "%s %d %d %d %d %d %d %d %lu ", Info->getName(), TM->tm_year+1900, TM->tm_mon+1, TM->tm_mday, TM->tm_hour, TM->tm_min, TM->tm_sec, Info->getTimestampMillisecs(), Info->getTimestamp());
    200272
    201   // Translate info data into ASCII and write to file and to history buffer
     273  // Translate data into ASCII
    202274  char *Text = EvidenceServer::ToString(Info);
    203275  if (Text != NULL) {
     276        // Replace all control characters by white space
     277        for (int i=0; i<strlen(Text); i++) if (iscntrl(Text[i])) Text[i] = ' ';
     278       
     279        // Write to file
    204280    fprintf(DataFile, "%s\n", Text);
    205281       
    206         HistBuffer[Service][HistPointer[Service]].Seconds = Info->getTimestamp();
    207     HistBuffer[Service][HistPointer[Service]].Value = atof(Text);
    208     HistService[Service]->updateService();
    209         HistPointer[Service]++;
    210         if (HistPointer[Service] >= HistSize) HistPointer[Service] = 0;
    211 
     282        // Add to history buffer if change large enough
     283        if ((fabs(atof(Text)-List[Service].LastValue) > List[Service].MinAbsChange)) {
     284          List[Service].HistBuffer[List[Service].HistPointer].Seconds = Info->getTimestamp();
     285      List[Service].HistBuffer[List[Service].HistPointer].Value = atof(Text);
     286      List[Service].HistService->updateService();
     287          List[Service].HistPointer++;
     288          if (List[Service].HistPointer >= HistSize) List[Service].HistPointer = 0;
     289          List[Service].LastValue = atof(Text);
     290        }
    212291        free(Text);
    213292  }
     
    215294 
    216295  fflush(DataFile);
    217   if(ferror(DataFile)) Msg(ERROR, "Error writing to data file (%s)", strerror(errno));
    218 
    219   pthread_mutex_unlock(&DataMutex);
    220 }
    221 
    222 //
    223 // Implementation of log writing (the only command is 'DColl/Log')
     296
     297  // Terminate if error because otherwise infinite loop might result as
     298  // next call to this infoHandler() will try to (re-)open file
     299  if(ferror(DataFile)) {
     300    fclose(DataFile);
     301        DataFile = NULL;
     302        State(FATAL, "Error writing to data file, closed file (%s)", strerror(errno));
     303  }
     304
     305  // Update datafile size service
     306  if (time(NULL) - DataSizeLastUpdate > SizeUpdateDelay) {
     307        DataSizekB = FileSize(DataFile);
     308        DataSizeService->updateService();
     309        DataSizeLastUpdate = time(NULL);
     310  }
     311}
     312
     313//
     314// Implementation of log writing
    224315//
    225316void DataHandler::commandHandler() {
    226 
    227   if (LogFile == NULL) return;  // Handler might be called before file is opened
    228317 
    229   pthread_mutex_lock(&LogMutex);
    230 
     318  if (getCommand() != LogCommand || LogFile == NULL) return;
     319
     320  // Replace all carriage-return by line feed and non-printable characters
     321  char *Text = getCommand()->getString();
     322  for (unsigned int i=0; i<strlen(Text); i++) {
     323    if (Text[i] == '\r') Text[i] = '\n';
     324        if(isprint(Text[i])==0 && isspace(Text[i])==0) Text[i] = ' ';
     325  }
     326 
    231327  time_t RawTime = time(NULL);
    232328  struct tm *TM = localtime(&RawTime);
     
    234330  fprintf(LogFile, "%d/%d/%d %d:%d:%d: %s\n",
    235331                TM->tm_mday, TM->tm_mon+1, TM->tm_year+1900,
    236                 TM->tm_hour, TM->tm_min, TM->tm_sec, getString());
     332                TM->tm_hour, TM->tm_min, TM->tm_sec, Text);
    237333
    238334  fflush(LogFile);
    239   if(ferror(LogFile)) Msg(ERROR, "Error writing to log file (%s)", strerror(errno));
    240   pthread_mutex_unlock(&LogMutex);
    241 }
    242 
    243 
    244 //
    245 // Implementation of memory re-allocation for pointer arrays
    246 //
    247 bool DataHandler::ReallocMem(void *&Memory, int Size) {
    248 
    249   void *NewMem = realloc(Memory, Size*sizeof(void *));
    250 
    251   if (NewMem != NULL) {
    252     Memory = NewMem;
    253         return true;
    254   }
     335 
     336  // If error close file (otherwise infinite loop because State() also writes to log)
     337  if(ferror(LogFile)) {
     338    fclose(LogFile);
     339        LogFile = NULL;
     340    State(ERROR, "Error writing to log file, closing file (%s)", strerror(errno));
     341  }
     342   
     343  // Update logfile size service
     344  if (time(NULL) - LogSizeLastUpdate > SizeUpdateDelay) {
     345        LogSizekB = FileSize(LogFile);
     346        LogSizeService->updateService();
     347        LogSizeLastUpdate = time(NULL);
     348  }
     349}
     350
     351
     352//
     353// Add service to watch list
     354//
     355void DataHandler::AddService(char *Name) {
     356
     357  // Do not subscribe to history services (otherwise infinite loop)
     358  if (strstr(Name, ".hist") != NULL) return;
     359
     360  // Check if already subscribed to this service
     361  for (int i=0; i<NumItems; i++) {
     362        if(strcmp(Name, List[i].DataItem->getName()) == 0) return;
     363  }
     364
     365  // Increase capacity of item list                     
     366  struct Item *New = (struct Item *) realloc(List, (NumItems+1)*sizeof(struct Item));
     367  if (New != NULL) List = New;
    255368  else {
    256         Msg(ERROR, "Could not allocate memory ()", strerror(errno));
    257         return false;
    258   }
     369        State(ERROR, "Could not allocate memory for item list, service '' not added (%s)", Name, strerror(errno));
     370        return;
     371  }
     372 
     373  // Set minimum required change by comparing to regular expressions
     374  List[NumItems].MinAbsChange = 0;
     375  for (int i=0; i<RegExCount; i++) {
     376    if (regexec(&RegEx[i], Name, (size_t) 0, NULL, 0) == 0) {
     377          List[NumItems].MinAbsChange = MinChange[i];
     378        }
     379  }
     380 
     381  List[NumItems].LastValue = DBL_MAX;
     382               
     383  // Create history service
     384  List[NumItems].HistBuffer = new struct EvidenceHistoryItem [HistSize];
     385  memset(List[NumItems].HistBuffer, 0, HistSize*sizeof(EvidenceHistoryItem));
     386  List[NumItems].HistPointer = 0;
     387
     388  char *Buf;
     389  if (MakeString(&Buf, "%s.hist", Name) == -1) {
     390    State(ERROR, "Could not create history service for '%s', MakeString() failed", Name);
     391  }
     392  else {
     393        List[NumItems].HistService = new DimService (Buf, (char *) "C",
     394                                          List[NumItems].HistBuffer, HistSize*sizeof(EvidenceHistoryItem));
     395    free(Buf);
     396  }
     397
     398  // Subscribe to service
     399  List[NumItems].DataItem = new DimStampedInfo(Name, NO_LINK, this);
     400
     401  // Increase number only after all set-up
     402  NumItems++;
     403}
     404
     405//
     406// Determine size of file in kB
     407//
     408float DataHandler::FileSize(FILE *File) {
     409
     410  struct stat FileStatus;
     411
     412  if (fstat(fileno(File), &FileStatus) == -1) {
     413     State(WARN, "Could not determine size of file (%s)", strerror(errno));
     414         return -1;
     415  }
     416
     417  return (float) FileStatus.st_size/1024;
    259418}
    260419
     
    267426  static DataHandler Data;
    268427 
    269   pause();        // Sleep until signal caught
    270 }
     428  // Sleep until signal caught
     429  pause();
     430}
  • Evidence/Edd/Edd.cc

    r139 r142  
     1
    12/* ============================================================
    23
     
    1920        Qt::gray, Qt::darkGray, Qt::lightGray};
    2021
     22class GUI *Handler;
     23
    2124//////////////////////////////////////////
    2225// Text display for arbitary DIM service//
     
    2629Edd_Indicator::Edd_Indicator(QString DIMService, QWidget *P): QLineEdit(P) {
    2730
    28   ServiceName = qstrdup(DIMService.toAscii().data());
    29  
    3031  // Widget properties
    3132  setReadOnly(true);
    3233  setMaximumWidth(100);
    33   connect(this, SIGNAL(YEP(QString)), this, SLOT(setText(QString)));
     34  ShowAsTime = false;
     35 
     36  // Connect to DIM handler
     37  if (connect(Handler, SIGNAL(YEP(DimInfo*, int, QString, QByteArray, QString)), SLOT(Update(DimInfo*, int, QString, QByteArray, QString))) == false) {
     38    printf("Failed connection for %s\n", DIMService.toAscii().data());
     39  }
    3440
    3541  // Context menu
    3642  Menu = new QMenu(this);
     43  Menu->addAction("Open history", this, SLOT(MenuOpenHistory()));
    3744  Menu->addAction("Copy service", this, SLOT(MenuCopyService()));
    3845
    3946  // DIM client
    40   Data = new DimStampedInfo(ServiceName, INT_MAX, (char *) NO_LINK, this);
     47  Data = new DimStampedInfo(DIMService.toAscii().data(), INT_MAX, (char *) NO_LINK, Handler);
    4148}
    4249
     
    4451Edd_Indicator::~Edd_Indicator() {
    4552  delete Data;
    46   delete[] ServiceName;
    47 }
    48 
    49 // Handling of DIM service update
    50 void Edd_Indicator::infoHandler() {
    51 
     53}
     54
     55// Update widget
     56void Edd_Indicator::Update(DimInfo *Info, int Time, QString Format, QByteArray Data, QString Text) {
     57
     58  if (Info != this->Data) return;
     59   
    5260  QPalette Pal = palette(); 
    53   QString S;
    5461
    5562  // Check if service available
    56   if (getInfo()->getSize() == strlen(NO_LINK)+1 && strcmp(getInfo()->getString(), NO_LINK) == 0) {
    57     emit(YEP(QString("n/a")));
    58     setStatusTip(QString("%1:  unavailable").arg(ServiceName));
    59     Pal.setColor(backgroundRole(), Qt::red);
    60     setPalette(Pal);
    61     return;
    62   }
    63   Pal.setColor(backgroundRole(), Qt::white);
    64 
    65   // Translate data into ASCII
    66   char *Text = EvidenceServer::ToString(getInfo());
    67 
    68   // If this is a status indicator, adapt background colour
    69   if (getInfo()->getSize() == (int) strlen(Text)+3) {
    70     switch (*((char *) getInfo()->getData() + strlen(Text) + 2)) {
    71       case 0:  Pal.setColor(backgroundRole(), Qt::white); break;
    72       case 1:  Pal.setColor(backgroundRole(), Qt::cyan); break;
    73       case 2:  Pal.setColor(backgroundRole(), Qt::red); break;
    74       case 3:  Pal.setColor(backgroundRole(), Qt::red); break;
    75       default: break;
    76     }
    77   }
     63  if (Time == -1) {
     64    setText("n/a");
     65        setStatusTip(QString("%1:  unavailable").arg(Info->getName()));
     66    Pal.setColor(QPalette::Base, Qt::lightGray);
     67  }
     68  else {
     69    // If this is a status indicator, adapt background colour
     70    if (Data.size() == Text.size()+2) {
     71      switch (Data[Text.size() + 2]) {
     72        case 0:  Pal.setColor(QPalette::Base, Qt::white); break;
     73        case 1:  Pal.setColor(QPalette::Base, Qt::cyan); break;
     74        case 2:  Pal.setColor(QPalette::Base, Qt::red); break;
     75        case 3:  Pal.setColor(QPalette::Base, Qt::red); break;
     76        default: break;
     77      }
     78    }
     79    else Pal.setColor(QPalette::Base, Qt::white);
     80       
     81        if (!ShowAsTime) setText(Text);
     82        else setText(QDateTime::fromTime_t(Text.toInt()).toString());
     83       
     84    // Update status tip
     85    setStatusTip(QString("%1:  Last update %2  Format '%3'").arg(Info->getName(), QDateTime::fromTime_t(Time).toString()).arg(Format));
     86  }
     87 
    7888  setPalette(Pal);
    79  
    80   if (Text != NULL) {
    81     QTextStream(&S) << Text;
    82         free(Text);
    83   }
    84   else QTextStream(&S) << "Cannot interpret format identifier";
    85    
    86   if (strlen(getInfo()->getFormat()) > 1) {
    87     QTextStream(&S) << " (DIM format string longer)";
    88   }
    89  
    90   // Trigger display update
    91   emit(YEP(S));
    92  
    93   // Update status tip
    94   QDateTime Time = QDateTime::fromTime_t(getInfo()->getTimestamp());
    95   setStatusTip(QString("%1:  Last update %2  Format '%3'").arg(ServiceName, Time.toString()).arg(getInfo()->getFormat()));
    9689}
    9790
     
    111104
    112105  // If not, open new plot
    113   LastPlot = new Edd_Plot(ServiceName);
    114   LastPlot->show();
     106  Edd_Indicator::MenuOpenHistory();
    115107}
    116108
     
    129121  QDrag *Drag = new QDrag(this);
    130122  QMimeData *MimeData = new QMimeData;
    131   MimeData->setText(QString(ServiceName));
     123  MimeData->setText(QString(Data->getName()));
    132124  Drag->setMimeData(MimeData);
    133125  Drag->exec();
     
    142134}
    143135
     136// Open history plot
     137void Edd_Indicator::MenuOpenHistory() {
     138 
     139  LastPlot = new Edd_Plot(Data->getName());
     140  LastPlot->show();
     141}
     142
    144143// Copy service name
    145144void Edd_Indicator::MenuCopyService() {
    146145 
    147   QApplication::clipboard()->setText(QString(ServiceName));
     146  QApplication::clipboard()->setText(QString(Data->getName()));
    148147}
    149148
     
    161160 
    162161  // Graph properties
    163   QwtText XAxisTitle("Time (RJD-55000)");
     162  setAutoReplot(false);
     163  QwtText XAxisTitle("Time (RJD-55200)");
    164164  XAxisTitle.setFont(QFont("Helvetica", 10));
    165165  setAxisTitle(QwtPlot::xBottom, XAxisTitle);
    166   setAutoReplot(false);
    167166  setCanvasBackground(QColor(Qt::yellow));
    168  
     167
    169168  Zoomer = new QwtPlotZoomer(QwtPlot::xBottom,QwtPlot::yLeft,canvas());
    170169  connect(Zoomer, SIGNAL(zoomed(const QwtDoubleRect &)), this, SLOT(HandleZoom(const QwtDoubleRect &)));
     
    176175  Legend = new QwtLegend();
    177176  insertLegend(Legend, QwtPlot::TopLegend);
    178    
    179   // Threads may not call replot directly, but only through this signal
    180   connect(this, SIGNAL(YEP()), this, SLOT(UpdatePlot()));
     177
     178  // Connect to DIM handler
     179  if (connect(Handler, SIGNAL(YEP(DimInfo *, int, QString, QByteArray, QString)), SLOT(Update(DimInfo *, int, QString, QByteArray, QString))) == false) {
     180    printf("Failed connection for %s\n", DIMService.toAscii().data());
     181  }
    181182
    182183  // Context menu
     
    222223
    223224  QString HistName = Name+".hist";
    224  
     225
    225226  // Lock before accessing Items list
    226227  QMutexLocker Locker(&Mutex);
     
    233234    }
    234235  } 
    235 
     236 
    236237  // Generate new curve and subscribe to service
    237238  struct PlotItem New;
     
    239240  New.Signal->attach(this);
    240241  New.Signal->setTitle(HistName);
    241   New.Signal->setPen(QColor(LineColors[Items.size()%(sizeof(LineColors)/sizeof(Qt::GlobalColor))]));
     242  New.Signal->setPen(QColor(LineColors[Items.size() % (sizeof(LineColors)/sizeof(Qt::GlobalColor))]));
    242243  New.x = NULL;
    243244  New.y = NULL;
    244245  New.Count = 0;
    245   New.Data = new DimStampedInfo(HistName.toAscii(), NO_LINK, this);
    246   New.LiveData = new DimStampedInfo(Name.toAscii(), NO_LINK, this);
     246  New.Data = new DimStampedInfo(HistName.toAscii().data(), (char *) NO_LINK, Handler);
     247  New.LiveData = new DimStampedInfo(Name.toAscii().data(), (char *) NO_LINK, Handler);
    247248
    248249  Items.append(New);
    249250}
    250251
    251 //
    252 // Handle update of DIM service
    253 //
    254 void Edd_Plot::infoHandler() {
     252// Update widget (must happen in GUI thread)
     253void Edd_Plot::Update(DimInfo *Info, int Time, QString Format, QByteArray Data, QString Text) {
    255254
    256255  // Check if service available
    257   if (getInfo()->getSize() == strlen(NO_LINK)+1 && strcmp(getInfo()->getString(), NO_LINK) == 0) {
    258     setStatusTip(QString("%1:  unavailable").arg(getInfo()->getName()));
    259     return;
     256  if (Time == -1) {
     257        setStatusTip(QString("%1:  unavailable").arg(Info->getName()));
    260258  }
    261259
     
    265263  // Determine which plot item this call belongs to
    266264  int ItemNo;
    267   for (ItemNo=0; ItemNo<Items.size(); ItemNo++) if (Items[ItemNo].Data == getInfo()) {
     265  for (ItemNo=0; ItemNo<Items.size(); ItemNo++) if (Info == Items[ItemNo].Data) {
    268266    // This is a history service 
    269     EvidenceHistoryItem *Curr = (EvidenceHistoryItem *) getInfo()->getData();
    270     int Count=0, DataPoints = getInfo()->getSize()/sizeof(struct EvidenceHistoryItem);
     267    EvidenceHistoryItem *Curr = (EvidenceHistoryItem *) Data.data();
     268    int Count=0, DataPoints = Data.size()/sizeof(struct EvidenceHistoryItem);
    271269
    272270    delete[] Items[ItemNo].x;
     
    302300
    303301    // Update status tip
    304     QDateTime Time = QDateTime::fromTime_t(getInfo()->getTimestamp());
    305     setStatusTip(QString("%1:  Last update %2  Format '%3'").arg(getInfo()->getName(), Time.toString()).arg(getInfo()->getFormat()));
    306 
    307   } else if (Items[ItemNo].LiveData == getInfo()) {
     302    QDateTime Timex = QDateTime::fromTime_t(Time);
     303    StatusTip = QString("%1:  Last update %2  Format '%3'").arg(Info->getName(), Timex.toString()).arg(Format);
     304
     305  } else if (Info == Items[ItemNo].LiveData) {
    308306    // This is a live service
    309307
     
    313311    // Append data
    314312    struct EvidenceHistoryItem Data;
    315     Data.Seconds = getInfo()->getTimestamp();
    316     Data.Value = atof(EvidenceServer::ToString(getInfo()));           
     313    Data.Seconds = Time;
     314    Data.Value = atof(Text.toAscii().data());           
    317315    Items[ItemNo].Live.append(Data);
    318    
     316
    319317    // Update largest and smallest value   
    320318    if (Data.Value > Items[ItemNo].Largest) Items[ItemNo].Largest = Data.Value;
     
    324322  Locker.unlock();
    325323
    326   // Do not call replot() directly from this thread!
    327   emit(YEP());
    328 }
    329 
     324  UpdatePlot();
     325}
    330326
    331327//
     
    333329//
    334330void Edd_Plot::UpdatePlot() {
     331
     332  static QwtSymbol Symbol, Sym1;
     333  Symbol.setStyle(QwtSymbol::Ellipse);
     334  Symbol.setSize(4);
    335335
    336336  if (!YLogAction->isChecked()) {
     
    342342  QMutexLocker Locker(&Mutex);
    343343
     344  setStatusTip(StatusTip);
     345 
    344346  for (int ItemNo=0; ItemNo<Items.size(); ItemNo++) {
    345347
    346     if (Items[ItemNo].Count == 0) continue;
    347 
    348     if (StyleAction->isChecked()) Items[ItemNo].Signal->setStyle(QwtPlotCurve::Dots);
    349     else Items[ItemNo].Signal->setStyle(QwtPlotCurve::Lines);
    350 
    351     int DataPoints = Items[ItemNo].Count + Items[ItemNo].Live.size();     
     348    if (StyleAction->isChecked()) Items[ItemNo].Signal->setSymbol(Symbol);
     349    else Items[ItemNo].Signal->setSymbol(Sym1);
     350
     351    int DataPoints = Items[ItemNo].Count + Items[ItemNo].Live.size();
     352       
     353        if (DataPoints == 0) continue;
     354   
    352355    double *x = new double [DataPoints];
    353356    double *y = new double [DataPoints];
    354     
     357 
    355358    // Adapt time scale and normalize y scale if requested
    356359    for (int i=0; i<DataPoints; i++) {
    357360      if (i < Items[ItemNo].Count) {
    358         x[i] = Items[ItemNo].x[i] / 86400.0 + 40587.5 - 55000;
     361        x[i] = Items[ItemNo].x[i] / 86400.0 + 40587.5 - 55200;
    359362        y[i] = Items[ItemNo].y[i];
    360363      }
    361364      else {
    362         x[i]= Items[ItemNo].Live[i-Items[ItemNo].Count].Seconds / 86400.0 + 40587.5 - 55000;
     365        x[i]= Items[ItemNo].Live[i-Items[ItemNo].Count].Seconds / 86400.0 + 40587.5 - 55200;
    363366        y[i] = Items[ItemNo].Live[i-Items[ItemNo].Count].Value;
    364367      }
     
    498501
    499502
     503//////////////////
     504// Text display //
     505//////////////////
     506
     507// Constructor
     508Edd_Textout::Edd_Textout(QString DIMService, QWidget *P): QTextEdit(P) {
     509
     510  // Widget properties
     511  setReadOnly(true);
     512  setAutoFillBackground(true);
     513  document()->setMaximumBlockCount(1000);
     514  Accumulate = true;
     515 
     516  // Connect to DIM handler
     517  if (connect(Handler, SIGNAL(YEP(DimInfo*, int, QString, QByteArray, QString)), SLOT(Update(DimInfo*, int, QString, QByteArray, QString))) == false) {
     518    printf("Failed connection for %s\n", DIMService.toAscii().data());
     519  }
     520
     521  // DIM client
     522  Data = new DimStampedInfo(DIMService.toAscii().data(), INT_MAX, (char *) NO_LINK, Handler);
     523}
     524
     525// Destructor
     526Edd_Textout::~Edd_Textout() {
     527
     528  delete Data;
     529}
     530
     531// Handling of DIM service update
     532void Edd_Textout::Update(DimInfo *Info, int Time, QString Format, QByteArray, QString Text) {
     533
     534  if (Info != this->Data) return;
     535 
     536  QPalette Pal = palette(); 
     537
     538  // Check if service available
     539  if (Time == -1) {
     540        setStatusTip(QString("%1:  unavailable").arg(Info->getName()));
     541    Pal.setColor(QPalette::Base, Qt::lightGray);
     542  }
     543  else {
     544    Pal.setColor(QPalette::Base, Qt::white);
     545
     546        // Clear display in case text should not accumulate
     547        if (Accumulate == false) clear();
     548       
     549    // Add if service contains only a string
     550    if (Format == "C") insertPlainText(Text);
     551
     552    // Update status tip
     553    setStatusTip(QString("%1:  Last update %2  Format '%3'").arg(Info->getName(), QDateTime::fromTime_t(Time).toString()).arg(Format));
     554  }
     555  setPalette(Pal);
     556}
     557
     558
    500559//
    501560// Main GUI (all widgets have ultimately Central as parent)
    502561//
    503562GUI::GUI() {
    504 
    505   Edd_Indicator *Value;
    506   Edd_Plot *Graph;
    507   QString Text;
     563 
     564  Handler = this;
    508565 
    509566  // Set features of main window
     
    511568  setCentralWidget(Central);
    512569  setStatusBar(new QStatusBar(this));
    513  
     570  setGeometry(100, 100, 800, 650);
     571  setWindowTitle("Edd - Evidence Data Display");
     572
     573  Edd_Indicator *Value;
     574  Edd_Plot *Graph;
     575  Edd_Textout *Textout;
     576  QString Text;
     577
    514578   // TextBox for value
    515579  //Value = new Edd_Indicator((char *) "SQM/NSB", Central);
     
    532596  MainWidget = new QWidget();
    533597  MainLayout = new QGridLayout(MainWidget);
     598
     599  Value = new Edd_Indicator("DColl/DataSizekB");
     600  MainLayout->addWidget(Value, 3, 5, 1, 1);
     601
     602  Value = new Edd_Indicator("DColl/LogSizekB");
     603  MainLayout->addWidget(Value, 4, 5, 1, 1);
     604
     605  Value = new Edd_Indicator("Config/ModifyTime");
     606  Value->setMaximumWidth(200);
     607  Value->ShowAsTime = true;
     608  MainLayout->addWidget(Value, 5, 5, 1, 1);
     609
     610  Value = new Edd_Indicator("Alarm/MasterAlarm");
     611  MainLayout->addWidget(Value, 2, 0, 1, 1);
     612
     613  Textout = new Edd_Textout("Alarm/Summary");
     614  Textout->Accumulate = false;
     615  MainLayout->addWidget(Textout, 3, 0, 2, 1);
    534616
    535617  // Layout of all widgets
     
    557639    Graph->AddService(Text);
    558640  }
     641
    559642  BiasLayout->addWidget(Graph, 0, 4, 12, 3);
    560643  Value = new Edd_Indicator("BIAS/Status");
     
    562645  BiasLayout->addWidget(Value, 0, 0, 1, 3);     
    563646
     647  Textout = new Edd_Textout("BIAS/Textout");
     648  Textout->setFixedWidth(400);
     649  BiasLayout->addWidget(Textout, 10, 0, 4, 4);     
     650 
    564651  // Environment page
    565652  EnvironmentWidget = new QWidget();
     
    580667  Value = new Edd_Indicator("SQM/NSB");
    581668  EnvironmentLayout->addWidget(Value, 6, 0, 1, 1);     
    582    
     669
    583670  // Tab widget
    584671  TabWidget = new QTabWidget(Central);
     
    595682  QAction* QuitAction = Menu->addAction("Quit", qApp, SLOT(quit()));
    596683  QuitAction->setShortcut(Qt::CTRL + Qt::Key_Q);
     684
     685  // Show main window
     686  show();
    597687
    598688   
     
    601691}
    602692
     693
    603694void GUI::MenuAbout() {
    604695  QString Rev(SVN_REVISION);
    605   Rev.remove(0,1).chop(1);
     696  Rev.remove(0,1).chop(2);
    606697 
    607698  QMessageBox::about(this, "About Edd","Evidence Data Display\n\n"
    608699    "Written by Oliver Grimm, IPP, ETH Zurich\n"
    609700    "This version compiled "__DATE__" ("+Rev+")\n\n"
    610     "Graphical user interface implemented with Qt.\n"
     701    "Graphical user interface implemented with Qt and Qwt.\n"
    611702    "Evidence control system based on DIM (http://dim.web.cern.ch).\n\n"
    612703    "Comments to oliver.grimm@phys.ethz.ch.");
     
    639730}
    640731
     732// Handling of DIM service update
     733void GUI::infoHandler() {
     734
     735  // Check if service available
     736  if (getInfo()->getSize() == strlen(NO_LINK)+1 && strcmp(getInfo()->getString(), NO_LINK) == 0) {
     737    YEP(getInfo(), -1);
     738  }
     739  else {
     740    char *Txt = EvidenceServer::ToString(getInfo());
     741
     742    YEP(getInfo(), getInfo()->getTimestamp(), getInfo()->getFormat(), QByteArray((char *) getInfo()->getData(), getInfo()->getSize()), QString(Txt));
     743                free(Txt);
     744  }
     745}
     746
    641747//---------------------------------------------------------------------
    642748//************************ All functions ****************************
     
    654760
    655761int main(int argc, char *argv[]) {
    656   QApplication app(argc, argv);
    657  
     762
     763  QApplication app(argc, argv);
    658764  GUI MainWindow;
    659   MainWindow.setGeometry(100, 100, 800, 650);
    660   MainWindow.setWindowTitle("Edd - Evidence Data Display");
    661   MainWindow.show();
    662  
     765
    663766  return app.exec();
    664767}
  • Evidence/Edd/Edd.h

    r139 r142  
    1515#include <qwt_legend.h>
    1616#include <qwt_legend_item.h>
     17#include <qwt_symbol.h>
    1718
    1819#include <limits.h>
     
    2223#include "Evidence.h"
    2324
    24 #define NO_LINK "__&DIM&NOLINK&__" // for checking if DIMserver is alive
    2525#define SVN_REVISION "$Revision$"
    2626                 
    2727// General indicator for DIM service
    28 class Edd_Indicator: public QLineEdit, public DimClient, public DimBrowser {
     28class Edd_Indicator: public QLineEdit, public DimClient {
    2929    Q_OBJECT
    30 
    31     char *ServiceName;
    32     DimStampedInfo *Data;
    3330
    3431    QMenu *Menu;
    3532    QPoint dragStart;
    3633    QwtPlot *LastPlot;
    37        
    38     void infoHandler();
     34       
     35    DimStampedInfo *Data;
     36       
    3937    void mousePressEvent(QMouseEvent *);
    4038    void mouseReleaseEvent(QMouseEvent *);
     
    4543    ~Edd_Indicator();
    4644
     45        bool ShowAsTime;
     46       
    4747  private slots:
     48        void Update(DimInfo *, int, QString, QByteArray, QString);
    4849    void contextMenuEvent(QContextMenuEvent *);   
     50    void MenuOpenHistory();
    4951    void MenuCopyService();
    50 
    51   signals:
    52     void YEP(QString);
    5352};
    5453
     
    7271    QMutex Mutex;
    7372   
     73        QString StatusTip;
     74       
    7475    QMenu *Menu;
    7576    QAction *YLogAction;
     
    8283    QwtLegend *Legend;
    8384   
    84     void infoHandler();   
    8585    void dragEnterEvent(QDragEnterEvent *);
    8686    void dropEvent(QDropEvent *);
     
    9393  private slots:
    9494    void UpdatePlot();
     95        void Update(DimInfo* Info, int, QString, QByteArray, QString);
     96
    9597    void HandleZoom(const QwtDoubleRect &);
    9698    void contextMenuEvent(QContextMenuEvent *);   
     
    101103    void MenuPrint();
    102104    void MenuPasteService();
    103  
    104  signals:
    105      void YEP();
     105};
    106106
     107// Textout indicator for DIM service
     108class Edd_Textout: public QTextEdit, public DimClient {
     109    Q_OBJECT
     110
     111    DimStampedInfo *Data;
     112
     113  public:
     114    Edd_Textout(QString, QWidget * = NULL);
     115    ~Edd_Textout();
     116       
     117        bool Accumulate;
     118       
     119  private slots:
     120        void Update(DimInfo* Info, int, QString, QByteArray, QString);
    107121};
    108122
    109123// Main window class
    110 class GUI: public QMainWindow, public DimBrowser {
     124class GUI: public QMainWindow, public DimBrowser, public DimInfo {
    111125    Q_OBJECT
    112126
     
    118132    QTabWidget *TabWidget;
    119133           
    120     void closeEvent(QCloseEvent *);
    121 
     134    void closeEvent(QCloseEvent *);
     135        void infoHandler();
     136       
    122137  public:
    123138    GUI();
     
    127142    void MenuAbout();
    128143    void MenuNewHistory();
     144       
     145  signals:
     146    void YEP(DimInfo *, int, QString = QString(), QByteArray = QByteArray(), QString = QString());
    129147};
    130148
  • Evidence/Evidence.cc

    r127 r142  
    55  - The server is started with the given name.
    66  - DIM exit and error handlers are implemented.
    7   - The Status service is published.
    8     It can be updated with the Msg() method. The text will also be logged.
     7  - The Status service is published (special format, see below).
     8    It can be updated with the State() method. The text will also be logged.
     9  - If the severity of a State() call is FATAL, exit() will be called (with
     10    this severity, the call to State() is guranteed not to return).
    911  - Configuration data can be requested by GetConfig().
    1012  - Signal handlers to ignore common signals are installed.
     
    3032  // Initialize
    3133  Status = NULL;
    32   MsgBuffer = NULL;
    3334  ConfigList = NULL;
    3435  ConfigNum = 0;
     
    4142 
    4243  // Create server name
    43   if (asprintf(&ServerName, "%s/Status", Name) == -1) {
    44     ServerName = NULL;
    45     Msg(FATAL, "Could not generate service name, asprintf() failed");
     44  if (MakeString(&StatusName, "%s/Status", Name) == -1) {
     45    State(FATAL, "Could not generate service name, asprintf() failed");
    4646  }
    4747 
    4848  // Start server
    49   static char InitMsg[] = "OK" "\0" "0";
    50   Status = new DimService(ServerName, (char *) "C", InitMsg, sizeof(InitMsg));
    51   DimServer::start(Name);
    52   DimServer::addExitHandler(this);
    53  
    54   Msg(INFO, "Server started");
    55 }
    56 
    57 // Destructor: Frees all buffers
     49  Status = new DimService(StatusName, (char *) "Server started");
     50
     51  start(Name);
     52  addExitHandler(this);
     53}
     54
     55// Destructor: Free memory
    5856EvidenceServer::~EvidenceServer() {
    5957
    60   free(ServerName);
    61   free(MsgBuffer);
     58  free(StatusName);
    6259 
    6360  for (unsigned int i=0; i<ConfigNum; i++) {
    64     delete *((DimRpcInfo **) ConfigList + i);
     61        delete[] ConfigList[i].Name;
     62        delete[] ConfigList[i].Value;
    6563  }
    6664  free(ConfigList);
     
    6967// DIM exit handler
    7068void EvidenceServer::exitHandler(int Code) {
    71   Msg(INFO, "Server stopped (DIM exit code %d)", Code);
     69  State(INFO, "Server stopped (DIM exit code %d)", Code);
    7270  exit(EXIT_SUCCESS);
    7371}
     
    7573// DIM error handler
    7674void EvidenceServer::errorHandler(int Severity, int Code, char *Message) {   
    77   Msg(ERROR, "%s (DIM Error, code %d, severity %d)\n", Message, Code, Severity);
    78 }
    79 
    80 // Print message to screen, to log and to status
    81 //
    82 // The message format is special: after the string-terminating '\0'
    83 // the Severity is given, terminated by another '\0'
    84 // The buffer must have the same lifetime as the DIM service.
    85 // If Severity is FATAL, exit() will be invoked.
    86 void EvidenceServer::Msg(MsgType Severity, const char *Format, ...) {
    87 
    88   char *TmpBuffer;
    89   int Size;
    90  
    91   static char DefaultMsg[]="vasprintf() or asprintf() failed, cannot process message" "\0" "2";   // Severity 'Error'
    92    
     75  State(ERROR, "%s (DIM error code %d, severity %d)\n", Message, Code, Severity);
     76}
     77
     78// Set status of server
     79//
     80// The message format is special: after the string-terminating '\0' the Severity
     81// is given, terminated by another '\0'  The buffer for the DIM service must have
     82// the same lifetime as the DIM service. If Severity is FATAL, exit() will be invoked.
     83void EvidenceServer::State(StateType Severity, const char *Format, ...) {
     84
     85  static const char* StateString[] = {"Info", "Warn", "Error", "Fatal"};
     86  static char ErrorString[] = "vasprintf() failed in State()";
     87  static char SBuf[STATUS_SIZE];
     88  char TBuf[STATUS_SIZE];
     89  char *Tmp;
     90 
    9391  // Assemble message from application
    9492  va_list ArgumentPointer;
    9593  va_start(ArgumentPointer, Format);
    96   if (vasprintf(&TmpBuffer, Format, ArgumentPointer) == -1) TmpBuffer = NULL;
     94  if (vasprintf(&Tmp, Format, ArgumentPointer) == -1) Tmp = ErrorString;
    9795  va_end(ArgumentPointer);
    9896
    99   // Free previous message buffer if allocated dynamically
    100   if (MsgBuffer != DefaultMsg) free(MsgBuffer);
    101  
    102   // Assemble final message including headers and Severity encoding
    103   if (TmpBuffer == NULL) Size = -1;
    104   else Size = asprintf(&MsgBuffer, "%s: %s%s*%d", ServerName,
    105                 (Severity==FATAL) ? "(Fatal) ":"", TmpBuffer,
    106                 (unsigned char) Severity);
    107   free(TmpBuffer);
    108 
    109   // If memory could not be allocated, show default message,
    110   // otherwise replace '*' by '0' for severity encoding         
    111   if (Size == -1) {
    112     MsgBuffer = DefaultMsg;
    113         Size = sizeof(DefaultMsg) - 1;
    114   }
    115   else *(strrchr(MsgBuffer, '*')) = '\0';
    116 
    117   // Send message to console, log file and update DIM status service
    118   printf("%s\n", MsgBuffer);
    119   DimClient::sendCommand("DColl/Log", MsgBuffer);
    120   if (Status != NULL) Status->updateService(MsgBuffer, Size+1);
     97  snprintf(TBuf, sizeof(TBuf), "%s (%s): %s", StatusName, StateString[Severity], Tmp);
     98  snprintf(SBuf, sizeof(SBuf), "%s\0%d", Tmp);
     99
     100  if (Tmp != ErrorString) free(Tmp);
     101 
     102  // Send message to console and log file
     103  printf("%s\n", TBuf);
     104  if (Severity != INFO) DimClient::sendCommand("DColl/Log", TBuf);
     105
     106  // Update DIM status service (including severity encoding)
     107  if (Status != NULL) Status->updateService(SBuf, strlen(SBuf)+2);
    121108
    122109  // Terminate if message type is fatal
     
    129116// the destructor.
    130117char* EvidenceServer::GetConfig(const char *Item) {
    131 
    132   // Enlarge memory to hold new pointer
    133   DimRpcInfo **Config = (DimRpcInfo **) realloc(ConfigList, sizeof(void *) * (++ConfigNum));
    134   if (Config == NULL) {
    135     Msg(WARN, "Could not realloc() memory for DimRpcInfo list (%s)", strerror(errno));
    136     ConfigNum--; 
    137   }
    138   else ConfigList = (void *) Config;
    139  
    140   // Advance array pointer to position for new DimRpcInfo pointer
    141   Config += ConfigNum - 1;
     118 
     119  // Determine configuration file update time
     120  DimCurrentInfo ModifyTime("Config/ModifyTime", 0);
     121  int Time = ModifyTime.getInt(), ItemNo = -1;
     122 
     123  // Check if configuration request already in list
     124  for (int i=0; i<ConfigNum; i++) {
     125    if (strcmp(ConfigList[i].Name, Item) == 0) {
     126          // Return original value if still up to date
     127          if (ConfigList[i].Time >= Time) return ConfigList[i].Value;
     128
     129          // Otherwise, free memory of old value
     130          delete[] ConfigList[i].Name;
     131          delete[] ConfigList[i].Value;   
     132          ItemNo = i;
     133          break;
     134        }
     135  }
    142136 
    143137  // Make configuration request
    144   *Config = new DimRpcInfo((char *) "ConfigRequest", (char *) "");
    145   (*Config)->setData((char *) Item);
     138  DimRpcInfo Config((char *) "ConfigRequest", (char *) "");
     139  Config.setData((char *) Item);
    146140
    147141  // Terminate if not successful
    148   if (strlen((*Config)->getString()) == 0) {
    149     Msg(FATAL, "Missing configuration data '%s'", Item);
    150   }
    151 
    152   return (*Config)->getString();
     142  if (strlen(Config.getString()) == 0) {
     143    State(FATAL, "Missing configuration data '%s'", Item);
     144  }
     145
     146  // Enlarge memory to hold new pointer if necessary
     147  if (ItemNo == -1) {
     148        void *N = realloc(ConfigList, sizeof(struct ConfigItem)*(++ConfigNum));
     149        if (N == NULL) {
     150          State(WARN, "Could not realloc() memory for configuration, will lose memory (%s)", strerror(errno));
     151          ConfigNum--; 
     152        }
     153        else ConfigList = (struct ConfigItem *) N;
     154       
     155        ItemNo = ConfigNum-1;
     156  }
     157 
     158  // Allocate memory for strings, and copy data to this memory
     159  ConfigList[ItemNo].Value = new char [strlen(Config.getString())+1];
     160  ConfigList[ItemNo].Name = new char [strlen(Item)+1];
     161  strcpy(ConfigList[ItemNo].Name, Item);
     162  strcpy(ConfigList[ItemNo].Value, Config.getString());
     163
     164  ConfigList[ItemNo].Time = Time;
     165 
     166  // Return address to configuration value 
     167  return ConfigList[ItemNo].Value;
    153168}
    154169
     
    156171// ====== Static methods ======
    157172
    158 // Signal handler (causes pause() to return)
     173// Signal handler (causes pause() and other syscalls to return)
    159174void EvidenceServer::SignalHandler(int) {
     175
    160176  EvidenceServer::ExitRequest = true;
    161177}
    162178
    163179// Translates DIMInfo to string (memory has to be freed by caller)
     180// No DIM structures are supported (only a single number or string is converted)
    164181char *EvidenceServer::ToString(DimInfo *Item) {
    165182
    166183  char *Text;
    167   int R;
    168  
    169   // Cannot handle complex data formats
     184 
    170185  if (strlen(Item->getFormat()) != 1) return NULL;
    171186 
    172   // Interpret format
    173187  switch (*(Item->getFormat())) {
    174     case 'I':  R = asprintf(&Text, "%d", Item->getInt());   break;
    175     case 'C':  R = asprintf(&Text, "%s", Item->getString());   break;
    176     case 'S':  R = asprintf(&Text, "%hd", Item->getShort());   break;
    177     case 'F':  R = asprintf(&Text, "%.3f", Item->getFloat());   break;
    178     case 'D':  R = asprintf(&Text, "%.3f", Item->getDouble());   break;
    179     case 'X':  R = asprintf(&Text, "%lld", Item->getLonglong());   break;
     188    case 'I':  MakeString(&Text, "%d", Item->getInt());   break;
     189    case 'C':  MakeString(&Text, "%s", Item->getString());   break;
     190    case 'S':  MakeString(&Text, "%hd", Item->getShort());   break;
     191    case 'F':  MakeString(&Text, "%.5f", Item->getFloat());   break;
     192    case 'D':  MakeString(&Text, "%.5f", Item->getDouble());   break;
     193    case 'X':  MakeString(&Text, "%lld", Item->getLonglong());   break;
    180194    default: return NULL;
    181195  }
    182196
    183   // Check if aprintf() worked OK
    184   if (R != -1) return Text;
    185   else return NULL;
    186 }
     197  return Text;
     198}
     199
     200//
     201// Generate string with vasprintf()
     202//
     203// The pointer will be set to NULL in case of error, so can always safely passed to free().
     204// In case vasprintf() is not available on a particular system, the functionality can
     205// be manually implemented in this routine.
     206//
     207int EvidenceServer::MakeString(char **Pointer, const char *Format, ...) {
     208
     209  int Ret;
     210  va_list ArgumentPointer; 
     211
     212  va_start(ArgumentPointer, Format); 
     213  if ((Ret = vasprintf(Pointer, Format, ArgumentPointer)) == -1) Pointer = NULL;
     214  va_end(ArgumentPointer);
     215 
     216  return Ret;
     217}
  • Evidence/Evidence.h

    r127 r142  
    99#include "dic.hxx"
    1010
     11#define NO_LINK (char *) "__&DIM&NOLINK&__" // Data if no link available
     12#define STATUS_SIZE 1000                                        // Bytes for status service string
    1113
    1214// Declaration of item for history buffer (see DColl.cc)
     
    1719
    1820// Class declation of Evidence server
    19 class EvidenceServer: public DimExitHandler, public DimErrorHandler {
     21class EvidenceServer: public DimServer {
    2022
    2123  private:
    22     char *ServerName;
    23         char *MsgBuffer;
    24         void *ConfigList;
     24        struct ConfigItem {
     25          char *Name;
     26          char *Value;
     27          int Time;
     28        } *ConfigList;
    2529        unsigned int ConfigNum;
     30
     31    char *StatusName;
    2632    DimService *Status;
    2733
     
    3440        ~EvidenceServer();
    3541       
    36         enum MsgType {INFO=0, WARN=1, ERROR=2, FATAL=3};
     42        enum StateType {INFO=0, WARN=1, ERROR=2, FATAL=3};
    3743
    38         void Msg(MsgType, const char *, ...);
     44        void State(StateType, const char *, ...);
     45
    3946        char* GetConfig(const char *);
    4047        static char *ToString(DimInfo *);
     48        static int MakeString(char **Pointer, const char *Format, ...);
    4149       
    4250    static bool ExitRequest;
Note: See TracChangeset for help on using the changeset viewer.