source: hvcontrol/hvcontrol.cpp@ 138

Last change on this file since 138 was 126, checked in by ogrimm, 15 years ago
Implemented DIM servers
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 DummyHandler(int);
33void CrashHandler(int);
34void ExitFunction();
35
36// ================
37// Main program
38// ================
39//
40// Several unlikely system call failures are handled via throwing an exception.
41
42int main(int argc, char *argv[]) {
43
44 char str[MAX_COM_SIZE];
45 pthread_t thread_ConsoleCommand,thread_HVMonitor,thread_CCCommand;
46 int LockDescriptor;
47
48 // Interpret command line (do before lockfile creation in case of exit())
49 if((argc==3 && strcmp(argv[1],"-c")!=0) || argc==2) {
50 printf("Usage: %s [-c <ConfigFile>] Default file is \"%s\"\n", argv[0], DEFAULT_CONFIG);
51 exit(EXIT_SUCCESS);
52 }
53
54 // Assure only one instance of the HV control program runs
55 // The flag O_EXCL together with O_CREAT assure that the lock
56 // file cannot be opened by another instance, i.e. there are no parallel write accesses
57 if((LockDescriptor = open(LOCKFILE,O_WRONLY|O_CREAT|O_EXCL,S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)) == -1) {
58 if(errno==EEXIST) {
59 printf("Error: Lock file already existing\n");
60 sprintf(str,"paste %s -s -d ' '",LOCKFILE);
61 system(str);
62 }
63 else printf("Could not create lock file %s (%s)\n", LOCKFILE, strerror(errno));
64 exit(EXIT_FAILURE);
65 }
66 close(LockDescriptor);
67 sprintf(str,"echo Created >%s; date >>%s; echo by $USER@$HOSTNAME>>%s",LOCKFILE,LOCKFILE,LOCKFILE);
68 system(str);
69
70 system("clear");
71 printf("\n*** HV Control built %s, %s (S.Commichau, S.Stark, O.Grimm) ***\n\n",__DATE__,__TIME__);
72
73 // Install signal handler and set signal SIGUSR1 to interrupt blocking system calls
74 signal(SIGUSR1, &DummyHandler);
75 siginterrupt (SIGUSR1, true);
76
77 // Assure lock file is deleted in case of a program crash or call to exit()
78 signal(SIGILL, &CrashHandler);
79 signal(SIGABRT, &CrashHandler);
80 signal(SIGFPE, &CrashHandler);
81 signal(SIGSEGV, &CrashHandler);
82 signal(SIGBUS, &CrashHandler);
83 atexit(&ExitFunction);
84
85 // Construct main instance and create mutex for thread synchronization
86 ProcessIO pio(argc==3 ? argv[2] : DEFAULT_CONFIG);
87 if (pthread_mutex_init(&pio.control_mutex, NULL) != 0) {
88 perror("pthread_mutex_init failed");
89 throw;
90 }
91
92 // These signals were set during construction of EvidenceServer
93 signal(SIGQUIT, &CrashHandler); // CTRL-Backspace
94 signal(SIGINT, &CrashHandler); // CTRL-C
95 signal(SIGHUP, &CrashHandler); // CTRL-Backspace
96 signal(SIGTERM, &CrashHandler);
97
98 // Create threads
99 if ((pthread_create(&thread_ConsoleCommand, NULL, (void * (*)(void *)) ConsoleCommand,(void *) &pio)) != 0) {
100 perror("pthread_create failed with console thread");
101 throw;
102 }
103 if ((pthread_create(&thread_HVMonitor, NULL, (void * (*)(void *)) HVMonitor,(void *) &pio)) != 0) {
104 perror("pthread_create failed with HVMonitor thread");
105 throw;
106 }
107 if ((pthread_create(&thread_CCCommand, NULL, (void * (*)(void *)) CCCommand,(void *) &pio)) != 0) {
108 perror("pthread_create failed with socket thread");
109 throw;
110 }
111
112 // Threads should be accessible for sending signals
113 pio.HVMonitor = thread_HVMonitor;
114 pio.SocketThread = thread_CCCommand;
115
116 // Wait for threads to quit
117 pthread_join(thread_CCCommand, NULL);
118 pthread_join(thread_ConsoleCommand, NULL);
119 pthread_join(thread_HVMonitor, NULL);
120
121 // Destruct mutex and main instance
122 pthread_mutex_destroy (&pio.control_mutex);
123 pio.~ProcessIO();
124
125 // Remove lockfile
126 if (remove(LOCKFILE)==-1) {
127 sprintf(str, "Could not remove lock file %s", LOCKFILE);
128 perror(str);
129 exit(EXIT_FAILURE);
130 }
131
132 exit(EXIT_SUCCESS);
133}
134
135
136/********************************************************************\
137
138 ConsoleCommand thread
139
140 Handle console input using readline library functions to allow
141 line editing and history capability
142
143\********************************************************************/
144
145void ConsoleCommand(ProcessIO *m) {
146
147 char *Command;
148
149 while (!m->Exit) {
150
151 // Assemble prompt
152 snprintf(m->Prompt, sizeof(m->Prompt),"\rHV");
153 if (m->NumHVBoards == 0) sprintf(m->Prompt+strlen(m->Prompt),"> ");
154 else {
155 if (m->FirstChain == m->LastChain) sprintf(m->Prompt+strlen(m->Prompt),"|C%d",m->FirstChain);
156 else sprintf(m->Prompt+strlen(m->Prompt),"|C%d-%d",m->FirstChain,m->LastChain);
157
158 if (m->NumHVBoards == 0) sprintf(m->Prompt+strlen(m->Prompt),"> ");
159 else if (m->FirstBoard == m->LastBoard) sprintf(m->Prompt+strlen(m->Prompt),"|B%d> ",m->FirstBoard);
160 else snprintf(m->Prompt,sizeof(m->Prompt),"\rDAQ|B%d-%d> ",m->FirstBoard,m->LastBoard);
161 }
162
163 // Read Command
164 Command = readline(m->Prompt);
165 if (Command==NULL) {
166 m->PrintMessage("Error reading command line input\n");
167 continue;
168 }
169 if(strlen(Command)>0) add_history(Command);
170
171 // Log command
172 m->PrintMessage(MsgToLog, "CONSOLE> %s\n", Command);
173
174 // Process command
175 pthread_mutex_lock(&m->control_mutex);
176 m->CommandControl(Command);
177 pthread_mutex_unlock(&m->control_mutex);
178
179 free(Command);
180 }
181}
182
183
184/********************************************************************\
185
186 CCCommand thread
187
188 Listen to commands from socket (Central Control)
189
190 This thread will block execution in the accept() and read() socket function
191 while waiting for a connection or data. If the exit function is invoked through
192 keyboard command, these blocking functions are interrupted by raising the signal
193 SIGUSR1. Testing on errno=EINTR indicates this termination. The dummy signal
194 handler below is needed to prevent the standard thread termination occurring
195 when this signal is received.
196
197\********************************************************************/
198
199
200void CCCommand(ProcessIO *m) {
201
202 int ServerSocket,ConnectionSocket,ReadResult;
203 struct sockaddr_in SocketAddress, ClientAddress;
204 struct hostent *ClientName;
205 socklen_t SizeClientAddress=sizeof(ClientAddress);
206 char Command[MAX_COM_SIZE];
207
208 // Set up server socket
209 if ((ServerSocket = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
210 m->PrintMessage("Could not open server socket, no remote connection possible (%s).\n", strerror(errno));
211 return;
212 }
213
214 // Allows immediate reuse of socket after closing (circumvents TIME_WAIT)
215 int Value=1;
216 if (setsockopt(ServerSocket, SOL_SOCKET, SO_REUSEADDR, (char *) &Value, sizeof (Value)) == -1) {
217 m->PrintMessage("Warning: Could not set server socket option SO_REUSEADDR (%s)\n", strerror(errno));
218 }
219
220 SocketAddress.sin_family = PF_INET;
221 SocketAddress.sin_port = htons((unsigned short) m->config->fCCPort);
222 SocketAddress.sin_addr.s_addr = INADDR_ANY;
223
224 if (bind(ServerSocket, (struct sockaddr *) &SocketAddress, sizeof(SocketAddress)) == -1) {
225 m->PrintMessage("Could not bind port to socket (%s)\n", strerror(errno));
226 close(ServerSocket);
227 return;
228 }
229 if (listen(ServerSocket, 0) == -1) {
230 m->PrintMessage("Could not set socket to listening (%s)\n", strerror(errno));
231 close(ServerSocket);
232 return;
233 }
234
235 while (!m->Exit) { // Looping to wait for incoming connection
236
237 if ((ConnectionSocket = accept(ServerSocket, (struct sockaddr *) &ClientAddress, &SizeClientAddress)) == -1) {
238 if (errno!=EINTR) m->PrintMessage("Failed to accept incoming connection (%s)\n", strerror(errno));
239 close(ServerSocket);
240 return;
241 }
242
243 ClientName = gethostbyaddr((char *) &ClientAddress.sin_addr ,sizeof(struct sockaddr_in),AF_INET);
244 m->PrintMessage("Connected to client at %s (%s).\n", inet_ntoa(ClientAddress.sin_addr), ClientName!=NULL ? ClientName->h_name:"name unknown");
245 m->Socket = ConnectionSocket;
246
247 // Looping as long as client exists and program not terminated
248 while (!m->Exit) {
249
250 // Try to read command from socket
251 memset(Command, 0, sizeof(Command));
252 ReadResult = read(ConnectionSocket, Command, MAX_COM_SIZE);
253 if (ReadResult==0) break; // Client does not exist anymore
254 if (ReadResult==-1) {
255 if (errno!=EINTR) m->PrintMessage("Error read from socket (%s)\n", strerror(errno));
256 break;
257 }
258 if (Command[strlen(Command)-1]=='\n') Command[strlen(Command)-1]='\0'; // Remove trailing newline
259
260 // Log command
261 m->PrintMessage(MsgToConsole|MsgToLog, "SOCKET> %s\n", 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 DummyHandler(int Signal) {
317 return;
318}
319
320// This function will be implicitly called by exit()
321void ExitFunction() {
322 remove(LOCKFILE);
323 return;
324}
Note: See TracBrowser for help on using the repository browser.