source: hvcontrol/hvcontrol.cpp@ 91

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