Changeset 142
- Timestamp:
- 01/13/10 12:47:33 (15 years ago)
- Location:
- Evidence
- Files:
-
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
Evidence/Alarm/Alarm.cc
r127 r142 5 5 - Checks periodically if all required servers are up 6 6 (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. 9 11 10 Oliver Grimm, September 200912 Oliver Grimm, January 2010 11 13 12 14 \********************************************************************/ … … 14 16 #define SERVER_NAME "Alarm" 15 17 #include "../Evidence.h" 18 19 #define SUMMARYSIZE 10000 // Bytes for alarm summary text 20 21 const char* StateString[] = {"OK", "WARN", "ERROR", "FATAL", "UNAVAILABLE"}; 16 22 17 23 // … … 21 27 22 28 DimStampedInfo **StatusService; 29 23 30 void infoHandler(); 24 31 … … 26 33 AlarmHandler(); 27 34 ~AlarmHandler(); 28 35 36 DimService *Summary, *Master; 37 38 char *AlarmSummary; 39 int MasterAlarm; 40 int *State; 29 41 char **Server; 30 42 unsigned int NumServers; 31 char *StateString; 32 char *ServerList; 43 char *ServerList; 44 45 void UpdateAlarmSummary(); 33 46 }; 34 47 … … 36 49 AlarmHandler::AlarmHandler(): EvidenceServer(SERVER_NAME) { 37 50 51 AlarmSummary = new char [SUMMARYSIZE]; 52 MasterAlarm = 0; 53 38 54 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); 39 59 40 60 // Copy original list of servers to observe … … 53 73 // Subscribe with handler to 'Status' service of all servers 54 74 StatusService = new DimStampedInfo* [NumServers]; 75 State = new int [NumServers]; 55 76 56 77 for (int i=0; i<NumServers; i++) { … … 58 79 strcpy(Buffer, Server[i]); 59 80 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; 63 85 } 64 65 StateString = new char [NumServers+1];66 for (int i=0; i<NumServers; i++) StateString[i] = '1';67 StateString[NumServers] = '\0';68 86 } 69 87 … … 73 91 for (int i=0; i<NumServers; i++) delete StatusService[i]; 74 92 delete[] StatusService; 93 delete Master; 94 delete Summary; 95 delete[] State; 75 96 delete[] Server; 76 97 delete[] ServerList; 98 delete[] AlarmSummary; 77 99 } 78 100 79 // Print messages of status changes to screen 101 // Print messages of status changes to screen and update status string 80 102 void AlarmHandler::infoHandler() { 81 103 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]) { 90 106 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(); 96 123 } 97 124 } 98 125 126 127 // Update alarm status summary 128 void 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 } 99 141 100 142 // … … 113 155 unsigned int Period = atoi(Alarm.GetConfig(SERVER_NAME " period")); 114 156 115 // Create DIM services116 static DimService ServerState(SERVER_NAME"/State", Alarm.StateString);117 static DimService ServerList(SERVER_NAME"/Servers", Alarm.ServerList);118 119 157 // Check periodically if servers are up 120 158 while(!EvidenceServer::ExitRequest) { … … 125 163 if (strcmp(ServerName, Alarm.Server[i]) == 0) Exists = true; 126 164 } 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; 132 166 } 133 167 134 ServerState.updateService(Alarm.StateString);168 Alarm.UpdateAlarmSummary(); 135 169 sleep(Period); 136 170 } -
Evidence/Config/Config.cc
r127 r142 5 5 - The configuration file is opened without buffering to catch changes 6 6 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. 11 13 12 14 Oliver Grimm, November 2009 … … 14 16 \********************************************************************/ 15 17 16 #define CONFIG_FILE"configuration.txt"18 #define DEFAULT_CONFIG "configuration.txt" 17 19 #define SERVER_NAME "Config" 18 20 19 21 #include "../Evidence.h" 22 #include <ctype.h> 20 23 #include <sys/stat.h> 21 24 #include <sys/inotify.h> … … 31 34 unsigned int BufferLength; 32 35 DimService *ConfigModified; 33 36 int ModifyTime; 37 34 38 void rpcHandler(); 35 39 … … 45 49 DimRpc("ConfigRequest", "C", "C"), EvidenceServer(SERVER_NAME) { 46 50 47 // Create DIM service to indicate changes of configuration file48 ConfigModified = new DimService (SERVER_NAME"/Modified", (char *) "");49 50 51 // Open configuration file 51 52 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)); 53 54 } 54 55 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 55 65 // 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); 58 68 } 59 69 … … 77 87 // Check if Buffer[] is large enough to hold full file, enlarge if necessary 78 88 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)); 80 90 } 81 91 else if(BufferLength < FileStatus.st_size) { … … 105 115 if(Token1==NULL || Token2==NULL || Token3==NULL) continue; 106 116 107 // Check for match and then send data 117 // Check for match and then send data (removing trainlin whitespace) 108 118 if (strstr(Request, Token1)!=NULL && strstr(Request, Token2)!=NULL) { 119 while (isspace(*Token3) != 0) Token3++; 109 120 setData(Token3); 110 121 break; 111 122 } 112 123 } 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'.", 114 129 DimServer::getClientName(), 115 130 DimServer::getClientId(), … … 120 135 void EvidenceConfig::ConfigChanged() { 121 136 137 ModifyTime = time(NULL); 122 138 ConfigModified->updateService(); 123 139 } … … 126 142 // Declaring class static ensures destructor is called when exit() is invoked 127 143 // 128 int main( ) {144 int main(int argc, char *argv[]) { 129 145 130 static EvidenceConfig Config( CONFIG_FILE);146 static EvidenceConfig Config(argc<2 ? DEFAULT_CONFIG : argv[1]); 131 147 132 148 int Notify; … … 134 150 135 151 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)); 137 153 } 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)); 140 156 close(Notify); 141 157 Notify = -1; -
Evidence/Config/Makefile
r127 r142 2 2 3 3 PROG=Config 4 CPPFLAGS += -I../DIM/ 4 CPPFLAGS += -I../DIM/ 5 5 LDLIBS += -lpthread 6 6 -
Evidence/DColl/DColl.cc
r127 r142 5 5 - DColl subscribes to all services given by the configuration 6 6 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. 10 13 - The command 'DColl/Log' writes the associated string to the log 11 14 file specified in the configuration. … … 17 20 #define SERVER_NAME "DColl" 18 21 22 #define DATE_ROLLOVER 12 // localtime hour after which next date is used 23 19 24 #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> 22 31 23 32 // 24 33 // Class declaration 25 34 // 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 35 class 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 32 49 unsigned int NumItems; 33 50 FILE *DataFile; 34 51 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; 39 56 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 43 64 void infoHandler(); 44 65 void commandHandler(); 45 bool ReallocMem(void *&, int); 66 void AddService(char *); 67 float FileSize(FILE *); 46 68 47 69 public: … … 53 75 // Constructor 54 76 // 55 DataHandler::DataHandler(): DimCommand((char *) "DColl/Log", (char *) "C"),EvidenceServer(SERVER_NAME) {77 DataHandler::DataHandler(): EvidenceServer(SERVER_NAME) { 56 78 57 79 // Initialization to prevent freeing unallocated memory 58 80 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 66 90 // 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"); 69 93 char *Logname = GetConfig(SERVER_NAME " logfile"); 94 SizeUpdateDelay = atoi(GetConfig(SERVER_NAME " sizeupdate")); 70 95 HistSize = atoi(GetConfig(SERVER_NAME " histsize")); 71 96 if (HistSize < 1) HistSize = 1; // Minimum one items 72 97 73 // Create mutex for thread synchronization74 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 buffer82 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 service92 for (int i=0; i<NumItems; i++) {93 if(strcmp(ServiceName, DataItem[i]->getName()) == 0) continue;94 }95 96 // Increase capacity of arrays97 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 service103 DataItem[NumItems] = new DimStampedInfo(ServiceName, (char *) NO_LINK, this);104 105 // Create history service106 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 file125 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 138 98 // Open log file 139 99 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 ***"); 143 148 } 144 149 … … 148 153 DataHandler::~DataHandler() { 149 154 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 150 168 // 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)); 153 171 } 154 172 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; 173 182 } 174 183 … … 176 185 // Implementation of data handling 177 186 // 178 // More than one infoHandler() might run in parallel, therefore179 // the mutex mechanism is used to serialize writing to the file187 // DIM ensures infoHandler() is called serialized, therefore 188 // no mutex is needed to serialize writing to the file 180 189 void DataHandler::infoHandler() { 181 190 182 191 DimInfo *Info = getInfo(); 183 192 184 // Check if service a ctually available, if it contains data and if data file is open193 // Check if service available 185 194 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 188 259 // Identify index of service 189 260 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 195 267 // Write data header 196 268 time_t RawTime = Info->getTimestamp(); … … 199 271 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()); 200 272 201 // Translate info data into ASCII and write to file and to history buffer273 // Translate data into ASCII 202 274 char *Text = EvidenceServer::ToString(Info); 203 275 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 204 280 fprintf(DataFile, "%s\n", Text); 205 281 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 } 212 291 free(Text); 213 292 } … … 215 294 216 295 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 224 315 // 225 316 void DataHandler::commandHandler() { 226 227 if (LogFile == NULL) return; // Handler might be called before file is opened228 317 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 231 327 time_t RawTime = time(NULL); 232 328 struct tm *TM = localtime(&RawTime); … … 234 330 fprintf(LogFile, "%d/%d/%d %d:%d:%d: %s\n", 235 331 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); 237 333 238 334 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 // 355 void 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; 255 368 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 // 408 float 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; 259 418 } 260 419 … … 267 426 static DataHandler Data; 268 427 269 pause(); // Sleep until signal caught 270 } 428 // Sleep until signal caught 429 pause(); 430 } -
Evidence/Edd/Edd.cc
r139 r142 1 1 2 /* ============================================================ 2 3 … … 19 20 Qt::gray, Qt::darkGray, Qt::lightGray}; 20 21 22 class GUI *Handler; 23 21 24 ////////////////////////////////////////// 22 25 // Text display for arbitary DIM service// … … 26 29 Edd_Indicator::Edd_Indicator(QString DIMService, QWidget *P): QLineEdit(P) { 27 30 28 ServiceName = qstrdup(DIMService.toAscii().data());29 30 31 // Widget properties 31 32 setReadOnly(true); 32 33 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 } 34 40 35 41 // Context menu 36 42 Menu = new QMenu(this); 43 Menu->addAction("Open history", this, SLOT(MenuOpenHistory())); 37 44 Menu->addAction("Copy service", this, SLOT(MenuCopyService())); 38 45 39 46 // 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); 41 48 } 42 49 … … 44 51 Edd_Indicator::~Edd_Indicator() { 45 52 delete Data; 46 delete[] ServiceName; 47 } 48 49 // Handling of DIM service update 50 void Edd_Indicator::infoHandler() { 51 53 } 54 55 // Update widget 56 void Edd_Indicator::Update(DimInfo *Info, int Time, QString Format, QByteArray Data, QString Text) { 57 58 if (Info != this->Data) return; 59 52 60 QPalette Pal = palette(); 53 QString S;54 61 55 62 // 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 78 88 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 update91 emit(YEP(S));92 93 // Update status tip94 QDateTime Time = QDateTime::fromTime_t(getInfo()->getTimestamp());95 setStatusTip(QString("%1: Last update %2 Format '%3'").arg(ServiceName, Time.toString()).arg(getInfo()->getFormat()));96 89 } 97 90 … … 111 104 112 105 // If not, open new plot 113 LastPlot = new Edd_Plot(ServiceName); 114 LastPlot->show(); 106 Edd_Indicator::MenuOpenHistory(); 115 107 } 116 108 … … 129 121 QDrag *Drag = new QDrag(this); 130 122 QMimeData *MimeData = new QMimeData; 131 MimeData->setText(QString( ServiceName));123 MimeData->setText(QString(Data->getName())); 132 124 Drag->setMimeData(MimeData); 133 125 Drag->exec(); … … 142 134 } 143 135 136 // Open history plot 137 void Edd_Indicator::MenuOpenHistory() { 138 139 LastPlot = new Edd_Plot(Data->getName()); 140 LastPlot->show(); 141 } 142 144 143 // Copy service name 145 144 void Edd_Indicator::MenuCopyService() { 146 145 147 QApplication::clipboard()->setText(QString( ServiceName));146 QApplication::clipboard()->setText(QString(Data->getName())); 148 147 } 149 148 … … 161 160 162 161 // Graph properties 163 QwtText XAxisTitle("Time (RJD-55000)"); 162 setAutoReplot(false); 163 QwtText XAxisTitle("Time (RJD-55200)"); 164 164 XAxisTitle.setFont(QFont("Helvetica", 10)); 165 165 setAxisTitle(QwtPlot::xBottom, XAxisTitle); 166 setAutoReplot(false);167 166 setCanvasBackground(QColor(Qt::yellow)); 168 167 169 168 Zoomer = new QwtPlotZoomer(QwtPlot::xBottom,QwtPlot::yLeft,canvas()); 170 169 connect(Zoomer, SIGNAL(zoomed(const QwtDoubleRect &)), this, SLOT(HandleZoom(const QwtDoubleRect &))); … … 176 175 Legend = new QwtLegend(); 177 176 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 } 181 182 182 183 // Context menu … … 222 223 223 224 QString HistName = Name+".hist"; 224 225 225 226 // Lock before accessing Items list 226 227 QMutexLocker Locker(&Mutex); … … 233 234 } 234 235 } 235 236 236 237 // Generate new curve and subscribe to service 237 238 struct PlotItem New; … … 239 240 New.Signal->attach(this); 240 241 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))])); 242 243 New.x = NULL; 243 244 New.y = NULL; 244 245 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); 247 248 248 249 Items.append(New); 249 250 } 250 251 251 // 252 // Handle update of DIM service 253 // 254 void Edd_Plot::infoHandler() { 252 // Update widget (must happen in GUI thread) 253 void Edd_Plot::Update(DimInfo *Info, int Time, QString Format, QByteArray Data, QString Text) { 255 254 256 255 // 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())); 260 258 } 261 259 … … 265 263 // Determine which plot item this call belongs to 266 264 int ItemNo; 267 for (ItemNo=0; ItemNo<Items.size(); ItemNo++) if (I tems[ItemNo].Data == getInfo()) {265 for (ItemNo=0; ItemNo<Items.size(); ItemNo++) if (Info == Items[ItemNo].Data) { 268 266 // 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); 271 269 272 270 delete[] Items[ItemNo].x; … … 302 300 303 301 // 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 (I tems[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) { 308 306 // This is a live service 309 307 … … 313 311 // Append data 314 312 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()); 317 315 Items[ItemNo].Live.append(Data); 318 316 319 317 // Update largest and smallest value 320 318 if (Data.Value > Items[ItemNo].Largest) Items[ItemNo].Largest = Data.Value; … … 324 322 Locker.unlock(); 325 323 326 // Do not call replot() directly from this thread! 327 emit(YEP()); 328 } 329 324 UpdatePlot(); 325 } 330 326 331 327 // … … 333 329 // 334 330 void Edd_Plot::UpdatePlot() { 331 332 static QwtSymbol Symbol, Sym1; 333 Symbol.setStyle(QwtSymbol::Ellipse); 334 Symbol.setSize(4); 335 335 336 336 if (!YLogAction->isChecked()) { … … 342 342 QMutexLocker Locker(&Mutex); 343 343 344 setStatusTip(StatusTip); 345 344 346 for (int ItemNo=0; ItemNo<Items.size(); ItemNo++) { 345 347 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 352 355 double *x = new double [DataPoints]; 353 356 double *y = new double [DataPoints]; 354 357 355 358 // Adapt time scale and normalize y scale if requested 356 359 for (int i=0; i<DataPoints; i++) { 357 360 if (i < Items[ItemNo].Count) { 358 x[i] = Items[ItemNo].x[i] / 86400.0 + 40587.5 - 55 000;361 x[i] = Items[ItemNo].x[i] / 86400.0 + 40587.5 - 55200; 359 362 y[i] = Items[ItemNo].y[i]; 360 363 } 361 364 else { 362 x[i]= Items[ItemNo].Live[i-Items[ItemNo].Count].Seconds / 86400.0 + 40587.5 - 55 000;365 x[i]= Items[ItemNo].Live[i-Items[ItemNo].Count].Seconds / 86400.0 + 40587.5 - 55200; 363 366 y[i] = Items[ItemNo].Live[i-Items[ItemNo].Count].Value; 364 367 } … … 498 501 499 502 503 ////////////////// 504 // Text display // 505 ////////////////// 506 507 // Constructor 508 Edd_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 526 Edd_Textout::~Edd_Textout() { 527 528 delete Data; 529 } 530 531 // Handling of DIM service update 532 void 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 500 559 // 501 560 // Main GUI (all widgets have ultimately Central as parent) 502 561 // 503 562 GUI::GUI() { 504 505 Edd_Indicator *Value; 506 Edd_Plot *Graph; 507 QString Text; 563 564 Handler = this; 508 565 509 566 // Set features of main window … … 511 568 setCentralWidget(Central); 512 569 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 514 578 // TextBox for value 515 579 //Value = new Edd_Indicator((char *) "SQM/NSB", Central); … … 532 596 MainWidget = new QWidget(); 533 597 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); 534 616 535 617 // Layout of all widgets … … 557 639 Graph->AddService(Text); 558 640 } 641 559 642 BiasLayout->addWidget(Graph, 0, 4, 12, 3); 560 643 Value = new Edd_Indicator("BIAS/Status"); … … 562 645 BiasLayout->addWidget(Value, 0, 0, 1, 3); 563 646 647 Textout = new Edd_Textout("BIAS/Textout"); 648 Textout->setFixedWidth(400); 649 BiasLayout->addWidget(Textout, 10, 0, 4, 4); 650 564 651 // Environment page 565 652 EnvironmentWidget = new QWidget(); … … 580 667 Value = new Edd_Indicator("SQM/NSB"); 581 668 EnvironmentLayout->addWidget(Value, 6, 0, 1, 1); 582 669 583 670 // Tab widget 584 671 TabWidget = new QTabWidget(Central); … … 595 682 QAction* QuitAction = Menu->addAction("Quit", qApp, SLOT(quit())); 596 683 QuitAction->setShortcut(Qt::CTRL + Qt::Key_Q); 684 685 // Show main window 686 show(); 597 687 } 598 688 … … 601 691 } 602 692 693 603 694 void GUI::MenuAbout() { 604 695 QString Rev(SVN_REVISION); 605 Rev.remove(0,1).chop( 1);696 Rev.remove(0,1).chop(2); 606 697 607 698 QMessageBox::about(this, "About Edd","Evidence Data Display\n\n" 608 699 "Written by Oliver Grimm, IPP, ETH Zurich\n" 609 700 "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" 611 702 "Evidence control system based on DIM (http://dim.web.cern.ch).\n\n" 612 703 "Comments to oliver.grimm@phys.ethz.ch."); … … 639 730 } 640 731 732 // Handling of DIM service update 733 void 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 641 747 //--------------------------------------------------------------------- 642 748 //************************ All functions **************************** … … 654 760 655 761 int main(int argc, char *argv[]) { 656 QApplication app(argc, argv); 657 762 763 QApplication app(argc, argv); 658 764 GUI MainWindow; 659 MainWindow.setGeometry(100, 100, 800, 650); 660 MainWindow.setWindowTitle("Edd - Evidence Data Display"); 661 MainWindow.show(); 662 765 663 766 return app.exec(); 664 767 } -
Evidence/Edd/Edd.h
r139 r142 15 15 #include <qwt_legend.h> 16 16 #include <qwt_legend_item.h> 17 #include <qwt_symbol.h> 17 18 18 19 #include <limits.h> … … 22 23 #include "Evidence.h" 23 24 24 #define NO_LINK "__&DIM&NOLINK&__" // for checking if DIMserver is alive25 25 #define SVN_REVISION "$Revision$" 26 26 27 27 // General indicator for DIM service 28 class Edd_Indicator: public QLineEdit, public DimClient , public DimBrowser{28 class Edd_Indicator: public QLineEdit, public DimClient { 29 29 Q_OBJECT 30 31 char *ServiceName;32 DimStampedInfo *Data;33 30 34 31 QMenu *Menu; 35 32 QPoint dragStart; 36 33 QwtPlot *LastPlot; 37 38 void infoHandler(); 34 35 DimStampedInfo *Data; 36 39 37 void mousePressEvent(QMouseEvent *); 40 38 void mouseReleaseEvent(QMouseEvent *); … … 45 43 ~Edd_Indicator(); 46 44 45 bool ShowAsTime; 46 47 47 private slots: 48 void Update(DimInfo *, int, QString, QByteArray, QString); 48 49 void contextMenuEvent(QContextMenuEvent *); 50 void MenuOpenHistory(); 49 51 void MenuCopyService(); 50 51 signals:52 void YEP(QString);53 52 }; 54 53 … … 72 71 QMutex Mutex; 73 72 73 QString StatusTip; 74 74 75 QMenu *Menu; 75 76 QAction *YLogAction; … … 82 83 QwtLegend *Legend; 83 84 84 void infoHandler();85 85 void dragEnterEvent(QDragEnterEvent *); 86 86 void dropEvent(QDropEvent *); … … 93 93 private slots: 94 94 void UpdatePlot(); 95 void Update(DimInfo* Info, int, QString, QByteArray, QString); 96 95 97 void HandleZoom(const QwtDoubleRect &); 96 98 void contextMenuEvent(QContextMenuEvent *); … … 101 103 void MenuPrint(); 102 104 void MenuPasteService(); 103 104 signals: 105 void YEP(); 105 }; 106 106 107 // Textout indicator for DIM service 108 class 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); 107 121 }; 108 122 109 123 // Main window class 110 class GUI: public QMainWindow, public DimBrowser {124 class GUI: public QMainWindow, public DimBrowser, public DimInfo { 111 125 Q_OBJECT 112 126 … … 118 132 QTabWidget *TabWidget; 119 133 120 void closeEvent(QCloseEvent *); 121 134 void closeEvent(QCloseEvent *); 135 void infoHandler(); 136 122 137 public: 123 138 GUI(); … … 127 142 void MenuAbout(); 128 143 void MenuNewHistory(); 144 145 signals: 146 void YEP(DimInfo *, int, QString = QString(), QByteArray = QByteArray(), QString = QString()); 129 147 }; 130 148 -
Evidence/Evidence.cc
r127 r142 5 5 - The server is started with the given name. 6 6 - 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). 9 11 - Configuration data can be requested by GetConfig(). 10 12 - Signal handlers to ignore common signals are installed. … … 30 32 // Initialize 31 33 Status = NULL; 32 MsgBuffer = NULL;33 34 ConfigList = NULL; 34 35 ConfigNum = 0; … … 41 42 42 43 // 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"); 46 46 } 47 47 48 48 // 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 58 56 EvidenceServer::~EvidenceServer() { 59 57 60 free(ServerName); 61 free(MsgBuffer); 58 free(StatusName); 62 59 63 60 for (unsigned int i=0; i<ConfigNum; i++) { 64 delete *((DimRpcInfo **) ConfigList + i); 61 delete[] ConfigList[i].Name; 62 delete[] ConfigList[i].Value; 65 63 } 66 64 free(ConfigList); … … 69 67 // DIM exit handler 70 68 void EvidenceServer::exitHandler(int Code) { 71 Msg(INFO, "Server stopped (DIM exit code %d)", Code);69 State(INFO, "Server stopped (DIM exit code %d)", Code); 72 70 exit(EXIT_SUCCESS); 73 71 } … … 75 73 // DIM error handler 76 74 void 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 status81 // 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. 83 void 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 93 91 // Assemble message from application 94 92 va_list ArgumentPointer; 95 93 va_start(ArgumentPointer, Format); 96 if (vasprintf(&Tmp Buffer, Format, ArgumentPointer) == -1) TmpBuffer = NULL;94 if (vasprintf(&Tmp, Format, ArgumentPointer) == -1) Tmp = ErrorString; 97 95 va_end(ArgumentPointer); 98 96 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); 121 108 122 109 // Terminate if message type is fatal … … 129 116 // the destructor. 130 117 char* 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 } 142 136 143 137 // 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); 146 140 147 141 // 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; 153 168 } 154 169 … … 156 171 // ====== Static methods ====== 157 172 158 // Signal handler (causes pause() to return)173 // Signal handler (causes pause() and other syscalls to return) 159 174 void EvidenceServer::SignalHandler(int) { 175 160 176 EvidenceServer::ExitRequest = true; 161 177 } 162 178 163 179 // 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) 164 181 char *EvidenceServer::ToString(DimInfo *Item) { 165 182 166 183 char *Text; 167 int R; 168 169 // Cannot handle complex data formats 184 170 185 if (strlen(Item->getFormat()) != 1) return NULL; 171 186 172 // Interpret format173 187 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; 180 194 default: return NULL; 181 195 } 182 196 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 // 207 int 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 9 9 #include "dic.hxx" 10 10 11 #define NO_LINK (char *) "__&DIM&NOLINK&__" // Data if no link available 12 #define STATUS_SIZE 1000 // Bytes for status service string 11 13 12 14 // Declaration of item for history buffer (see DColl.cc) … … 17 19 18 20 // Class declation of Evidence server 19 class EvidenceServer: public Dim ExitHandler, public DimErrorHandler {21 class EvidenceServer: public DimServer { 20 22 21 23 private: 22 char *ServerName; 23 char *MsgBuffer; 24 void *ConfigList; 24 struct ConfigItem { 25 char *Name; 26 char *Value; 27 int Time; 28 } *ConfigList; 25 29 unsigned int ConfigNum; 30 31 char *StatusName; 26 32 DimService *Status; 27 33 … … 34 40 ~EvidenceServer(); 35 41 36 enum MsgType {INFO=0, WARN=1, ERROR=2, FATAL=3};42 enum StateType {INFO=0, WARN=1, ERROR=2, FATAL=3}; 37 43 38 void Msg(MsgType, const char *, ...); 44 void State(StateType, const char *, ...); 45 39 46 char* GetConfig(const char *); 40 47 static char *ToString(DimInfo *); 48 static int MakeString(char **Pointer, const char *Format, ...); 41 49 42 50 static bool ExitRequest;
Note:
See TracChangeset
for help on using the changeset viewer.