source: hvcontrol/hvcontrol.cpp@ 107

Last change on this file since 107 was 93, checked in by ogrimm, 15 years ago
Added writing of slow data
File size: 10.3 KB
Line 
1
2/**************************************************************\
3
4 Main program for the CTX G-APD HV supply, sends commands,
5 reads and monitors status of the G-APD HV supply
6
7 Sebastian Commichau, Sabrina Stark, Oliver Grimm
8
9\**************************************************************/
10
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <pthread.h>
15#include <signal.h>
16#include <sys/socket.h>
17#include <netdb.h>
18#include <arpa/inet.h>
19
20#include "ProcessIO.h"
21
22#include <readline/readline.h>
23#include <readline/history.h>
24
25#define DEFAULT_CONFIG "../config/HV.conf" // Default configuration file
26#define LOCKFILE "/tmp/CTX_HV_LOCK"
27
28// Function prototypes
29void ConsoleCommand(ProcessIO *);
30void CCCommand(ProcessIO *);
31void HVMonitor(ProcessIO *);
32void SignalHandler(int);
33void CrashHandler(int);
34
35// ================
36// Main program
37// ================
38//
39// Several unlikely system call failures are handled via throwing an exception.
40
41int main(int argc, char *argv[]) {
42
43 char str[MAX_COM_SIZE];
44 pthread_t thread_ConsoleCommand,thread_HVMonitor,thread_CCCommand;
45 int LockDescriptor;
46
47 // Interpret command line (do before lockfile creation in case of exit())
48 if((argc==3 && strcmp(argv[1],"-c")!=0) || argc==2) {
49 printf("Usage: %s [-c <ConfigFile>] Default file is \"%s\"\n", argv[0], DEFAULT_CONFIG);
50 exit(EXIT_SUCCESS);
51 }
52
53 // Assure only one instance of the HV control program runs
54 // The flag O_EXCL together with O_CREAT assure that the lock
55 // file cannot be opened by another instance, i.e. there are no parallel write accesses
56 if((LockDescriptor = open(LOCKFILE,O_WRONLY|O_CREAT|O_EXCL,S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)) == -1) {
57 if(errno==EEXIST) {
58 printf("Error: Lock file already existing\n");
59 sprintf(str,"paste %s -s -d ' '",LOCKFILE);
60 system(str);
61 }
62 else printf("Could not create lock file %s (%s)\n", LOCKFILE, strerror(errno));
63 exit(EXIT_FAILURE);
64 }
65 close(LockDescriptor);
66 sprintf(str,"echo Created >%s; date >>%s; echo by $USER@$HOSTNAME>>%s",LOCKFILE,LOCKFILE,LOCKFILE);
67 system(str);
68
69 system("clear");
70 printf("\n*** HV Control built %s, %s (S.Commichau, S.Stark, O.Grimm) ***\n\n",__DATE__,__TIME__);
71
72 // Install signal handler and set signal SIGUSR1 to interrupt blocking system calls
73 signal(SIGUSR1, &SignalHandler);
74 siginterrupt (SIGUSR1, true);
75
76 // Install signals to assure that the lock file is deleted in case of a program crash
77 signal(SIGQUIT, &CrashHandler);
78 signal(SIGILL, &CrashHandler);
79 signal(SIGABRT, &CrashHandler);
80 signal(SIGFPE, &CrashHandler);
81 signal(SIGSEGV, &CrashHandler);
82 signal(SIGBUS, &CrashHandler);
83 signal(SIGTERM, &CrashHandler);
84 signal(SIGINT, &CrashHandler);
85 signal(SIGHUP, &CrashHandler);
86
87 // Construct main instance and create mutex for thread synchronization
88 ProcessIO pio(argc==3 ? argv[2] : DEFAULT_CONFIG);
89 if (pthread_mutex_init(&pio.control_mutex, NULL) != 0) {
90 perror("pthread_mutex_init failed");
91 throw;
92 }
93
94 // Create threads
95 if ((pthread_create(&thread_ConsoleCommand, NULL, (void * (*)(void *)) ConsoleCommand,(void *) &pio)) != 0) {
96 perror("pthread_create failed with console thread");
97 throw;
98 }
99 if ((pthread_create(&thread_HVMonitor, NULL, (void * (*)(void *)) HVMonitor,(void *) &pio)) != 0) {
100 perror("pthread_create failed with HVMonitor thread");
101 throw;
102 }
103 if ((pthread_create(&thread_CCCommand, NULL, (void * (*)(void *)) CCCommand,(void *) &pio)) != 0) {
104 perror("pthread_create failed with socket thread");
105 throw;
106 }
107
108 // Threads should be accessible for sending signals
109 pio.HVMonitor = thread_HVMonitor;
110 pio.SocketThread = thread_CCCommand;
111
112 // Wait for threads to quit
113 pthread_join(thread_CCCommand, NULL);
114 pthread_join(thread_ConsoleCommand, NULL);
115 pthread_join(thread_HVMonitor, NULL);
116
117 // Destruct mutex and main instance
118 pthread_mutex_destroy (&pio.control_mutex);
119 pio.~ProcessIO();
120
121 // Remove lockfile
122 if (remove(LOCKFILE)==-1) {
123 sprintf(str, "Could not remove lock file %s", LOCKFILE);
124 perror(str);
125 exit(EXIT_FAILURE);
126 }
127
128 exit(EXIT_SUCCESS);
129}
130
131
132/********************************************************************\
133
134 ConsoleCommand thread
135
136 Handle console input using readline library functions to allow
137 line editing and history capability
138
139\********************************************************************/
140
141void ConsoleCommand(ProcessIO *m) {
142
143 time_t Time;
144 char Buf[MAX_COM_SIZE], *Command;
145
146 while (!m->Exit) {
147
148 // Assemble prompt
149 snprintf(m->Prompt, sizeof(m->Prompt),"\rHV");
150 if (m->NumHVBoards == 0) sprintf(m->Prompt+strlen(m->Prompt),"> ");
151 else {
152 if (m->FirstChain == m->LastChain) sprintf(m->Prompt+strlen(m->Prompt),"|C%d",m->FirstChain);
153 else sprintf(m->Prompt+strlen(m->Prompt),"|C%d-%d",m->FirstChain,m->LastChain);
154
155 if (m->NumHVBoards == 0) sprintf(m->Prompt+strlen(m->Prompt),"> ");
156 else if (m->FirstBoard == m->LastBoard) sprintf(m->Prompt+strlen(m->Prompt),"|B%d> ",m->FirstBoard);
157 else snprintf(m->Prompt,sizeof(m->Prompt),"\rDAQ|B%d-%d> ",m->FirstBoard,m->LastBoard);
158 }
159
160 // Read Command
161 Command = readline(m->Prompt);
162 if (Command==NULL) {
163 m->PrintMessage("Error reading command line input\n");
164 continue;
165 }
166 if(strlen(Command)>0) add_history(Command);
167
168 // Log command
169 strftime(Buf,MAX_COM_SIZE, "%d/%m/%y %X", localtime(&(Time=time(NULL))));
170 m->PrintMessage(MsgToLog, "CONSOLE(%s)> %s\n", Buf, Command);
171
172 // Process command
173 pthread_mutex_lock(&m->control_mutex);
174 m->CommandControl(Command);
175 pthread_mutex_unlock(&m->control_mutex);
176
177 free(Command);
178 }
179}
180
181
182/********************************************************************\
183
184 CCCommand thread
185
186 Listen to commands from socket (Central Control)
187
188 This thread will block execution in the accept() and read() socket function
189 while waiting for a connection or data. If the exit function is invoked through
190 keyboard command, these blocking functions are interrupted by raising the signal
191 SIGUSR1. Testing on errno=EINTR indicates this termination. The dummy signal
192 handler below is needed to prevent the standard thread termination occurring
193 when this signal is received.
194
195\********************************************************************/
196
197
198void CCCommand(ProcessIO *m) {
199
200 int ServerSocket,ConnectionSocket,ReadResult;
201 struct sockaddr_in SocketAddress, ClientAddress;
202 struct hostent *ClientName;
203 socklen_t SizeClientAddress=sizeof(ClientAddress);
204 char Command[MAX_COM_SIZE], Buf[MAX_COM_SIZE];
205 time_t Time;
206
207 // Set up server socket
208 if ((ServerSocket = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
209 m->PrintMessage("Could not open server socket, no remote connection possible (%s).\n", strerror(errno));
210 return;
211 }
212
213 // Allows immediate reuse of socket after closing (circumvents TIME_WAIT)
214 int Value=1;
215 if (setsockopt(ServerSocket, SOL_SOCKET, SO_REUSEADDR, (char *) &Value, sizeof (Value)) == -1) {
216 m->PrintMessage("Warning: Could not set server socket option SO_REUSEADDR (%s)\n", strerror(errno));
217 }
218
219 SocketAddress.sin_family = PF_INET;
220 SocketAddress.sin_port = htons((unsigned short) m->config->fCCPort);
221 SocketAddress.sin_addr.s_addr = INADDR_ANY;
222
223 if (bind(ServerSocket, (struct sockaddr *) &SocketAddress, sizeof(SocketAddress)) == -1) {
224 m->PrintMessage("Could not bind port to socket (%s)\n", strerror(errno));
225 close(ServerSocket);
226 return;
227 }
228 if (listen(ServerSocket, 0) == -1) {
229 m->PrintMessage("Could not set socket to listening (%s)\n", strerror(errno));
230 close(ServerSocket);
231 return;
232 }
233
234 while (!m->Exit) { // Looping to wait for incoming connection
235
236 if ((ConnectionSocket = accept(ServerSocket, (struct sockaddr *) &ClientAddress, &SizeClientAddress)) == -1) {
237 if (errno!=EINTR) m->PrintMessage("Failed to accept incoming connection (%s)\n", strerror(errno));
238 close(ServerSocket);
239 return;
240 }
241
242 ClientName = gethostbyaddr((char *) &ClientAddress.sin_addr ,sizeof(struct sockaddr_in),AF_INET);
243 m->PrintMessage("Connected to client at %s (%s).\n", inet_ntoa(ClientAddress.sin_addr), ClientName!=NULL ? ClientName->h_name:"name unknown");
244 m->Socket = ConnectionSocket;
245
246 // Looping as long as client exists and program not terminated
247 while (!m->Exit) {
248
249 // Try to read command from socket
250 memset(Command, 0, sizeof(Command));
251 ReadResult = read(ConnectionSocket, Command, MAX_COM_SIZE);
252 if (ReadResult==0) break; // Client does not exist anymore
253 if (ReadResult==-1) {
254 if (errno!=EINTR) m->PrintMessage("Error read from socket (%s)\n", strerror(errno));
255 break;
256 }
257 if (Command[strlen(Command)-1]=='\n') Command[strlen(Command)-1]='\0'; // Remove trailing newline
258
259 // Log command
260 strftime(Buf, MAX_COM_SIZE, "%d/%m/%y %X", localtime(&(Time=time(NULL))));
261 m->PrintMessage(MsgToConsole|MsgToLog, "SOCKET(%s)> %s\n", Buf, Command);
262
263 // Process command
264 pthread_mutex_lock(&m->control_mutex);
265 m->CmdFromSocket = true;
266 m->CommandControl(Command);
267 m->CmdFromSocket = false;
268 pthread_mutex_unlock(&m->control_mutex);
269 }
270
271 m->Socket = -1;
272 m->PrintMessage("Disconnected from client.\n");
273 close(ConnectionSocket);
274 }
275 close(ServerSocket);
276}
277
278
279/********************************************************************\
280
281 HVMonitor
282
283 Monitor HV board status
284 Sebastian Commichau, November 2008
285
286\********************************************************************/
287
288void HVMonitor(ProcessIO *m) {
289
290 while (!m->Exit) {
291 if (m->state == active) {
292 pthread_mutex_lock(&m->control_mutex);
293 m->Monitor();
294 pthread_mutex_unlock(&m->control_mutex);
295 }
296 usleep((unsigned long)floor(1000000./(m->NumHVBoards*m->fStatusRefreshRate)));
297 }
298}
299
300
301/********************************************************************\
302
303 Signal handlers
304
305\********************************************************************/
306
307// Remove lock file before running default signal code
308void CrashHandler(int Signal) {
309 remove(LOCKFILE);
310 printf("Caught signal number %d. Removing lockfile and performing standard signal action. Good luck.\n",Signal);
311 signal(Signal, SIG_DFL);
312 raise(Signal);
313}
314
315// Dummy signal handler to return from blocking syscalls
316void SignalHandler(int Signal) {
317 return;
318}
Note: See TracBrowser for help on using the repository browser.