source: hvcontrol/hvcontrol.cpp@ 117

Last change on this file since 117 was 111, checked in by ogrimm, 15 years ago
Small bug fix to printing routine
File size: 10.0 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 char *Command;
144
145 while (!m->Exit) {
146
147 // Assemble prompt
148 snprintf(m->Prompt, sizeof(m->Prompt),"\rHV");
149 if (m->NumHVBoards == 0) sprintf(m->Prompt+strlen(m->Prompt),"> ");
150 else {
151 if (m->FirstChain == m->LastChain) sprintf(m->Prompt+strlen(m->Prompt),"|C%d",m->FirstChain);
152 else sprintf(m->Prompt+strlen(m->Prompt),"|C%d-%d",m->FirstChain,m->LastChain);
153
154 if (m->NumHVBoards == 0) sprintf(m->Prompt+strlen(m->Prompt),"> ");
155 else if (m->FirstBoard == m->LastBoard) sprintf(m->Prompt+strlen(m->Prompt),"|B%d> ",m->FirstBoard);
156 else snprintf(m->Prompt,sizeof(m->Prompt),"\rDAQ|B%d-%d> ",m->FirstBoard,m->LastBoard);
157 }
158
159 // Read Command
160 Command = readline(m->Prompt);
161 if (Command==NULL) {
162 m->PrintMessage("Error reading command line input\n");
163 continue;
164 }
165 if(strlen(Command)>0) add_history(Command);
166
167 // Log command
168 m->PrintMessage(MsgToLog, "CONSOLE> %s\n", Command);
169
170 // Process command
171 pthread_mutex_lock(&m->control_mutex);
172 m->CommandControl(Command);
173 pthread_mutex_unlock(&m->control_mutex);
174
175 free(Command);
176 }
177}
178
179
180/********************************************************************\
181
182 CCCommand thread
183
184 Listen to commands from socket (Central Control)
185
186 This thread will block execution in the accept() and read() socket function
187 while waiting for a connection or data. If the exit function is invoked through
188 keyboard command, these blocking functions are interrupted by raising the signal
189 SIGUSR1. Testing on errno=EINTR indicates this termination. The dummy signal
190 handler below is needed to prevent the standard thread termination occurring
191 when this signal is received.
192
193\********************************************************************/
194
195
196void CCCommand(ProcessIO *m) {
197
198 int ServerSocket,ConnectionSocket,ReadResult;
199 struct sockaddr_in SocketAddress, ClientAddress;
200 struct hostent *ClientName;
201 socklen_t SizeClientAddress=sizeof(ClientAddress);
202 char Command[MAX_COM_SIZE];
203
204 // Set up server socket
205 if ((ServerSocket = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
206 m->PrintMessage("Could not open server socket, no remote connection possible (%s).\n", strerror(errno));
207 return;
208 }
209
210 // Allows immediate reuse of socket after closing (circumvents TIME_WAIT)
211 int Value=1;
212 if (setsockopt(ServerSocket, SOL_SOCKET, SO_REUSEADDR, (char *) &Value, sizeof (Value)) == -1) {
213 m->PrintMessage("Warning: Could not set server socket option SO_REUSEADDR (%s)\n", strerror(errno));
214 }
215
216 SocketAddress.sin_family = PF_INET;
217 SocketAddress.sin_port = htons((unsigned short) m->config->fCCPort);
218 SocketAddress.sin_addr.s_addr = INADDR_ANY;
219
220 if (bind(ServerSocket, (struct sockaddr *) &SocketAddress, sizeof(SocketAddress)) == -1) {
221 m->PrintMessage("Could not bind port to socket (%s)\n", strerror(errno));
222 close(ServerSocket);
223 return;
224 }
225 if (listen(ServerSocket, 0) == -1) {
226 m->PrintMessage("Could not set socket to listening (%s)\n", strerror(errno));
227 close(ServerSocket);
228 return;
229 }
230
231 while (!m->Exit) { // Looping to wait for incoming connection
232
233 if ((ConnectionSocket = accept(ServerSocket, (struct sockaddr *) &ClientAddress, &SizeClientAddress)) == -1) {
234 if (errno!=EINTR) m->PrintMessage("Failed to accept incoming connection (%s)\n", strerror(errno));
235 close(ServerSocket);
236 return;
237 }
238
239 ClientName = gethostbyaddr((char *) &ClientAddress.sin_addr ,sizeof(struct sockaddr_in),AF_INET);
240 m->PrintMessage("Connected to client at %s (%s).\n", inet_ntoa(ClientAddress.sin_addr), ClientName!=NULL ? ClientName->h_name:"name unknown");
241 m->Socket = ConnectionSocket;
242
243 // Looping as long as client exists and program not terminated
244 while (!m->Exit) {
245
246 // Try to read command from socket
247 memset(Command, 0, sizeof(Command));
248 ReadResult = read(ConnectionSocket, Command, MAX_COM_SIZE);
249 if (ReadResult==0) break; // Client does not exist anymore
250 if (ReadResult==-1) {
251 if (errno!=EINTR) m->PrintMessage("Error read from socket (%s)\n", strerror(errno));
252 break;
253 }
254 if (Command[strlen(Command)-1]=='\n') Command[strlen(Command)-1]='\0'; // Remove trailing newline
255
256 // Log command
257 m->PrintMessage(MsgToConsole|MsgToLog, "SOCKET> %s\n", Command);
258
259 // Process command
260 pthread_mutex_lock(&m->control_mutex);
261 m->CmdFromSocket = true;
262 m->CommandControl(Command);
263 m->CmdFromSocket = false;
264 pthread_mutex_unlock(&m->control_mutex);
265 }
266
267 m->Socket = -1;
268 m->PrintMessage("Disconnected from client.\n");
269 close(ConnectionSocket);
270 }
271 close(ServerSocket);
272}
273
274
275/********************************************************************\
276
277 HVMonitor
278
279 Monitor HV board status
280 Sebastian Commichau, November 2008
281
282\********************************************************************/
283
284void HVMonitor(ProcessIO *m) {
285
286 while (!m->Exit) {
287 if (m->state == active) {
288 pthread_mutex_lock(&m->control_mutex);
289 m->Monitor();
290 pthread_mutex_unlock(&m->control_mutex);
291 }
292 usleep((unsigned long)floor(1000000./(m->NumHVBoards*m->fStatusRefreshRate)));
293 }
294}
295
296
297/********************************************************************\
298
299 Signal handlers
300
301\********************************************************************/
302
303// Remove lock file before running default signal code
304void CrashHandler(int Signal) {
305 remove(LOCKFILE);
306 printf("Caught signal number %d. Removing lockfile and performing standard signal action. Good luck.\n",Signal);
307 signal(Signal, SIG_DFL);
308 raise(Signal);
309}
310
311// Dummy signal handler to return from blocking syscalls
312void SignalHandler(int Signal) {
313 return;
314}
Note: See TracBrowser for help on using the repository browser.