source: fact/BIASctrl/ProcessIO.cc@ 9965

Last change on this file since 9965 was 9917, checked in by ogrimm, 14 years ago
Initial commit of BIASctrl
File size: 14.9 KB
Line 
1//
2// Class processing user input
3//
4
5#include "ProcessIO.h"
6
7using namespace std;
8
9// Branch table for command evaluation
10static const struct CL_Struct { const char *Name;
11 void (ProcessIO::*CommandPointer)();
12 unsigned int MinNumParameter;
13 const char *Parameters;
14 const char *Help;
15 } CommandList[] =
16 {{"hv", &ProcessIO::cmd_hv, 2, "<id>|<ch>|<all> <v>", "Change bias of pixel or (all) chan. of active boards"},
17 {"status", &ProcessIO::cmd_status, 0, "[dac]", "Show status information (DAC values if requested)"},
18 {"load", &ProcessIO::cmd_load, 1, "<file>", "Load and set bias settings from file"},
19 {"save", &ProcessIO::cmd_save, 1, "<file>", "Save current bias settings to file"},
20 {"exit", &ProcessIO::cmd_exit, 0, "", "Exit program"},
21 {"rate", &ProcessIO::cmd_rate, 1, "<rate>", "Set refresh rate in Hz"},
22 {"timeout", &ProcessIO::cmd_timeout, 1, "<time>", "Set timeout to return from read in seconds"},
23 {"reset", &ProcessIO::cmd_reset, 1, "<crates>", "Reset crates"},
24 {"help", &ProcessIO::cmd_help, 0, "", "Print help"}};
25
26
27//
28// Constructor
29//
30ProcessIO::ProcessIO(): EvidenceServer(SERVER_NAME) {
31
32 // DIM console service used in PrintMessage()
33 ConsoleText = NULL;
34 ConsoleOut = new DimService(SERVER_NAME"/ConsoleOut", (char *) "");
35
36 // Get configuration data
37 vector<string> Boards = Tokenize(GetConfig("Boards"), " \t");
38 fTimeOut = atof(GetConfig("TimeOut").c_str());
39 fStatusRefreshRate = atof(GetConfig("StatusRefreshRate").c_str());
40 fMaxDiff = atoi(GetConfig("HVMaxDiff").c_str());
41
42 if (fStatusRefreshRate < MIN_RATE || fStatusRefreshRate > MAX_RATE) fStatusRefreshRate = 1;
43
44 // Open devices
45 for (unsigned int i=0; i<Boards.size(); i++) {
46
47 class Crate *New = new class Crate(Boards[i], Crates.size(), this);
48
49 if (New->InitOK && New->Synch()) {
50 PrintMessage("Synchronized and reset board %s (#%d)\n", Boards[i].c_str(), Crates.size());
51 Crates.push_back(New);
52 }
53 else {
54 Message(WARN, "Failed to synchronize board %s", Boards[i].c_str());
55 delete New;
56 }
57 }
58
59 // Create instances
60 pm = new PixelMap(GetConfig("PixMapTable"));
61
62 // Install DIM command (after all initialized)
63 DIMCommand = new DimCommand((char *) SERVER_NAME"/Command", (char *) "C", this);
64
65 // Create monitor thread and make accessible for sending signal
66 if ((pthread_create(&Thread, NULL, (void * (*)(void *)) LaunchMonitor,(void *) this)) != 0) {
67 Message(FATAL, "pthread_create() failed with Monitor thread");
68 }
69}
70
71
72//
73// Destructor
74//
75ProcessIO::~ProcessIO() {
76
77 // Wait for thread to quit
78 if (pthread_join(Thread, NULL) != 0) {
79 PrintMessage("pthread_join() failed");
80 }
81
82 // Delete all crates
83 for (unsigned int i=0; i<Crates.size(); i++) delete Crates[i];
84
85 delete DIMCommand;
86 delete pm;
87 delete ConsoleOut;
88 free(ConsoleText);
89}
90
91//
92// Process user input
93//
94void ProcessIO::commandHandler() {
95
96 // Build string safely
97 string Command = string(getCommand()->getString(), getCommand()->getSize());
98
99 // Check if command is legal and ignore empty commands
100 if (getCommand() != DIMCommand || Command.size() < 2) return;
101
102 // Shell command
103 if(Command[0]=='.') {
104 system(Command.c_str()+1);
105 return;
106 }
107
108 // Parse command into tokens
109 Parameter = Tokenize(Command, " ");
110
111 // Search for command in command list
112 for(unsigned int CmdNumber=0; CmdNumber<sizeof(CommandList)/sizeof(CL_Struct); CmdNumber++) {
113 if (Match(Parameter[0], CommandList[CmdNumber].Name)) {
114 if(Parameter.size()-1 < CommandList[CmdNumber].MinNumParameter) {
115 PrintMessage("Usage: %s %s\n", CommandList[CmdNumber].Name, CommandList[CmdNumber].Parameters);
116 return;
117 }
118
119 // Jump to command function
120 (this->*CommandList[CmdNumber].CommandPointer)();
121 return;
122 }
123 }
124 PrintMessage("Unknown command '%s'\n", Parameter[0].c_str());
125}
126
127
128// Print help
129void ProcessIO::cmd_help() {
130
131 char Buffer[MAX_COM_SIZE];
132 for(unsigned int i=0; i<sizeof(CommandList)/sizeof(CL_Struct); i++) {
133 snprintf(Buffer, sizeof(Buffer), "%s %s", CommandList[i].Name, CommandList[i].Parameters);
134 PrintMessage("%-28s%s\n", Buffer, CommandList[i].Help);
135 }
136
137 PrintMessage(".<command> Execute shell command\n\n"
138 "Items in <> are mandatory, in [] optional, | indicates mutual exclusive or.\n");
139}
140
141//
142// Set new bias voltage
143//
144void ProcessIO::cmd_hv() {
145
146 unsigned int DACValue, Errors=0, B, C;
147 double Double;
148 struct Range Crt, Chan;
149
150 // Loop over all parameters
151 for (unsigned int n=1; n < Parameter.size()-1; n++) {
152
153 // Extract channel identification
154 if (pm->Pixel_to_HVboard(Parameter[n]) != 999999999) {
155 Crt.Min = Crt.Max = pm->Pixel_to_HVboard(Parameter[n]);
156 Chan.Min = Chan.Max = pm->Pixel_to_HVchain(Parameter[n])*NUM_CHANNELS + pm->Pixel_to_HVchannel(Parameter[n]);
157 }
158 else {
159 vector<string> T = Tokenize(Parameter[n], "/");
160 if (T.size() == 2) {
161 Crt = ConvertToRange(T[0]);
162 Chan = ConvertToRange(T[1]);
163 }
164 else {
165 Crt.Min = Crt.Max = 0;
166 Chan = ConvertToRange(T[0]);
167 }
168 }
169
170 // Check validity of ranges
171 if (Crt.Min < 0 || Chan.Min < 0 || Crt.Max >= (int) Crates.size() || Chan.Max >= MAX_NUM_BOARDS*NUM_CHANNELS) {
172 PrintMessage("Numeric conversion or out-of-range error for parameter %d, skipping channel\n", n);
173 continue;
174 }
175
176 // Convert voltage value and check format
177 if (!ConvertToDouble(Parameter[n+1], &Double)) {
178 PrintMessage("Error: Wrong number format for voltage setting\n");
179 continue;
180 }
181
182 // Loop over given crates and channels
183 for (int i=Crt.Min; i<=Crt.Max; i++) for (int j=Chan.Min; j<=Chan.Max; j++) {
184 // Board and channel number
185 B = j / NUM_CHANNELS;
186 C = j % NUM_CHANNELS;
187
188 // Voltage change (number starts with + oder -) ignored if current DAC value is zero
189 if (isdigit(Parameter[n+1][0])==0 && Crates[i]->DAC[B][C] == 0) continue;
190
191 // Relative or absolute change?
192 if (isdigit(Parameter[n+1][0]) == 0) DACValue = Crates[i]->DAC[B][C] + Double/90*0x0fff;
193 else DACValue = Double/90*0x0fff;
194
195 // Set new voltage
196 if (!RampVoltage(DACValue, i, B, C)) Errors++;
197
198 } // Channels
199 } // Loop over command argument
200
201 // Update DIM service
202 for (unsigned int i=0; i<=Crates.size(); i++) Crates[i]->BiasVolt->updateService();
203
204 if (Errors > 0) PrintMessage("Errors on %d channel(s) occurred\n", Errors);
205}
206
207//
208// Load bias settings from file
209//
210void ProcessIO::cmd_load() {
211
212 char Buffer[MAX_COM_SIZE];
213 int Errors = 0, Board, Channel;
214 unsigned int DACValue, NBoards = 0;
215 FILE *File;
216
217 // Open file
218 if ((File=fopen(Parameter[1].c_str(), "r")) == NULL) {
219 PrintMessage("Error: Could not open file '%s' (%s)\n", Parameter[1].c_str(), strerror(errno));
220 return;
221 }
222
223 // Scan through file line by line
224 while (fgets(Buffer, sizeof(Buffer), File) != NULL) {
225 for (unsigned int Crate=0; Crate<Crates.size(); Crate++) if (Match(Crates[Crate]->Name, Buffer)) {
226
227 PrintMessage("Found bias settings for board %s (#%d)\n\r", Crates[Crate]->Name, Crate);
228
229 Board = 0; Channel = 0;
230 while (fscanf(File, "%u", &DACValue)==1 && Board<MAX_NUM_BOARDS) {
231 // Ramp channel to new voltage
232 if (!RampVoltage(DACValue, Crate, Board, Channel)) {
233 Errors++;
234 PrintMessage("Error: Could not ramp board %d, channel %d\n", Board, Channel);
235 }
236 else {
237 PrintMessage("Ramped board %d, channel %d to %u (%.2f V) \r",
238 Board, Channel, DACValue, (double) DACValue/0x0fff*90);
239 }
240
241 if(++Channel == NUM_CHANNELS) {
242 Board++;
243 Channel = 0;
244 }
245 }
246
247 // Update DIM service
248 Crates[Crate]->BiasVolt->updateService();
249
250 if (ferror(File) != 0) {
251 PrintMessage("Error reading DAC value from file, terminating. (%s)\n",strerror(errno));
252 return;
253 }
254 else PrintMessage("\nFinished updating board\n");
255 NBoards++;
256 } // Loop over boards
257 } // while()
258
259 if (NBoards != Crates.size()) PrintMessage("Warning: Could not load bias settings for all connected crates\n");
260 else if (Errors == 0) PrintMessage("Success: Read bias settings for all connected crates\n");
261
262 if (Errors != 0) PrintMessage("Warning: Errors on %d channel(s) occurred\n", Errors);
263
264 if (fclose(File) != 0) PrintMessage("Error: Could not close file '%s'\n", Parameter[1].c_str());
265}
266
267//
268// Set refresh rate
269//
270void ProcessIO::cmd_rate() {
271
272 double Rate;
273
274 if (!ConvertToDouble(Parameter[1], &Rate)) {
275 PrintMessage("Error: Wrong number format\n");
276 return;
277 }
278
279 // Check limits
280 if (Rate<MIN_RATE || Rate>MAX_RATE) {
281 PrintMessage("Refresh rate out of range (min: %.2f Hz, max: %.2f Hz)\n", MIN_RATE, MAX_RATE);
282 return;
283 }
284
285 fStatusRefreshRate = Rate;
286 PrintMessage("Refresh rate set to %.2f Hz\n", fStatusRefreshRate);
287}
288
289//
290// Reset crates
291//
292void ProcessIO::cmd_reset() {
293
294 struct Range R = ConvertToRange(Parameter[1]);
295
296 for (int i=0; i<(int) Crates.size(); i++) if (i>= R.Min && i<=R.Max) {
297 if (Crates[i]->SystemReset() == 1) PrintMessage("System reset of crate %s (#%d)\n", Crates[i]->Name, i);
298 else PrintMessage("Error: Could not reset board %s (#%d)\n", Crates[i]->Name, i);
299 }
300}
301
302//
303// Save bias settings of all boards
304//
305void ProcessIO::cmd_save() {
306
307 FILE *File;
308 time_t Time = time(NULL);
309
310 if ((File = fopen(Parameter[1].c_str(), "w")) == NULL) {
311 PrintMessage("Error: Could not open file '%s' (%s)\n", Parameter[1].c_str(), strerror(errno));
312 return;
313 }
314
315 fprintf(File,"********** Bias settings of %s **********\n\n", ctime(&Time));
316
317 for (unsigned int i=0; i<Crates.size(); i++) {
318 fprintf(File, "%s\n\n", Crates[i]->Name);
319
320 for (int j=0; j<MAX_NUM_BOARDS; j++) {
321 for (int k=0; k<NUM_CHANNELS; k++) fprintf(File,"%5d ",Crates[i]->DAC[j][k]);
322 }
323 fprintf(File, "\n");
324 }
325
326 if (fclose(File) != 0) {
327 PrintMessage("Error: Could not close file '%s' (%s)\n", Parameter[1].c_str(), strerror(errno));
328 }
329}
330
331//
332// Print status
333//
334void ProcessIO::cmd_status() {
335
336 PrintMessage(" Refresh rate: %.2f Hz\n", fStatusRefreshRate);
337 PrintMessage(" Number of crates: %d\n", Crates.size());
338 PrintMessage(" Time out: %.2f s\n\n", fTimeOut);
339 PrintMessage(" MaxDiff : %u\n", fMaxDiff);
340
341 for (unsigned int i=0; i<Crates.size(); i++) {
342 PrintMessage(" CRATE %d (%s) Wrap counter: %s (%d) Manual reset: %s\n Error count: %d\n\n",
343 i, Crates[i]->Name, Crates[i]->WrapOK ? "ok":"error", Crates[i]->WrapCount,
344 Crates[i]->ResetHit ? "yes" : "no", Crates[i]->ErrorCount);
345
346 for (int j=0; j<MAX_NUM_BOARDS*NUM_CHANNELS; j++) {
347 if (j%8 == 0) PrintMessage("\n%3.1d: ", j);
348 if (Parameter.size() == 2) PrintMessage("%5d ", Crates[i]->DAC[j/NUM_CHANNELS][j%NUM_CHANNELS]);
349 else PrintMessage("%#5.2f ",Crates[i]->Volt[j/NUM_CHANNELS][j%NUM_CHANNELS]);
350 PrintMessage(" (%#5.2f %s) ", Crates[i]->Current[j/NUM_CHANNELS][j%NUM_CHANNELS], Crates[i]->OC[j/NUM_CHANNELS][j%NUM_CHANNELS] ? "OC":"");
351 PrintMessage("\n");
352 }
353 }
354}
355
356//
357// Set timeout to return from read
358//
359void ProcessIO::cmd_timeout() {
360
361 double Timeout;
362
363 if (!ConvertToDouble(Parameter[1], &Timeout)) {
364 PrintMessage("Error: Wrong number format\n");
365 return;
366 }
367
368 fTimeOut = Timeout;
369}
370
371//
372// Exit program
373//
374void ProcessIO::cmd_exit() {
375
376 ExitRequest = true;
377 pthread_kill(Thread, SIGUSR1);
378}
379
380
381//
382// Print message to screen and to DIM text service
383//
384void ProcessIO::PrintMessage(const char *Format, ...) {
385
386 static char Error[] = "vasprintf() failed in PrintMessage()";
387 char *Text;
388
389 // Evaluate arguments
390 va_list ArgumentPointer;
391 va_start(ArgumentPointer, Format);
392 if (vasprintf(&Text, Format, ArgumentPointer) == -1) Text = Error;
393 va_end(ArgumentPointer);
394
395 // Print to console
396 if(strlen(Text)>0 && Text[strlen(Text)-1]=='\n') printf("\r%s%s", Text, "\rBias> "); // New prompt
397 else printf("%s", Text);
398 fflush(stdout);
399
400 // Send to DIM text service
401 ConsoleOut->updateService(Text);
402
403 // Free old text
404 if (ConsoleText != Error) free(ConsoleText);
405 ConsoleText = Text;
406}
407
408
409// Ramp to new voltage with maximum step size given in fMaxDiff
410// No ramping when decreasing voltage
411bool ProcessIO::RampVoltage(unsigned int Target, int Crate, int Board, int Channel) {
412
413 while (Crates[Crate]->DAC[Board][Channel] != (int) Target) {
414 int Diff = Target - Crates[Crate]->DAC[Board][Channel];
415 if (Diff > (int) fMaxDiff) Diff = fMaxDiff;
416
417 if (Crates[Crate]->ChannelSet(Board, Channel, Crates[Crate]->DAC[Board][Channel]+Diff) != 1) {
418 Message(ERROR, "Could not set bias of crate %d, board %d, channel %d. Skipping channel\n", Crate, Board, Channel);
419 return false;
420 }
421 }
422
423 return true;
424}
425
426
427//
428// Check status
429//
430void ProcessIO::Monitor() {
431
432 static bool Warned = false;
433
434 while (!ExitRequest) {
435 for (unsigned int i=0; i<Crates.size(); i++) {
436 if (Crates[i]->ErrorCount > 10) {
437 if (!Warned) {
438 Warned = true;
439 Message(WARN, "Warning: Crate %d has many read/write errors, further error reporting disabled", i);
440 }
441 continue;
442 }
443
444 if (Crates[i]->ResetHit) {
445 Message(INFO, "Manual reset of board %d", i);
446 Crates[i]->SystemReset();
447 }
448
449 if (!Crates[i]->WrapOK) {
450 Message(ERROR, "Wrap counter mismatch of board %d", i);
451 }
452
453 for (int j=0; j<MAX_NUM_BOARDS*NUM_CHANNELS; j++) {
454 if (Crates[i]->ReadChannel(j/NUM_CHANNELS, j%NUM_CHANNELS) != 1) {
455 Message(ERROR, "Monitor could not read status of crate %d, board %d, channel %d", i, j/NUM_CHANNELS, j%NUM_CHANNELS);
456 continue;
457 }
458 if (Crates[i]->OC[j/NUM_CHANNELS][j%NUM_CHANNELS]) {
459 Message(WARN, "Overcurrent on crate %d, board %d, channel %d, resetting board", i, j/NUM_CHANNELS, j%NUM_CHANNELS);
460 Crates[i]->SystemReset();
461 }
462 }
463 } // for
464
465 // Wait
466 usleep((unsigned long) floor(1000000./fStatusRefreshRate));
467 } // while
468}
469
470// Call monitor loop inside class
471void ProcessIO::LaunchMonitor(ProcessIO *m) {
472
473 m->Monitor();
474}
475
476
477//
478// Check if two strings match (min 1 character must match)
479//
480bool Match(string str, const char *cmd) {
481
482 return strncasecmp(str.c_str(),cmd,strlen(str.c_str())==0 ? 1:strlen(str.c_str())) ? false:true;
483}
484
485//
486// Conversion function from string to double or int
487//
488// Return false if conversion did not stop on whitespace or EOL character
489bool ConvertToDouble(string String, double *Result) {
490
491 char *EndPointer;
492
493 *Result = strtod(String.c_str(), &EndPointer);
494 if(!isspace(*EndPointer) && *EndPointer!='\0') return false;
495 return true;
496}
497
498bool ConvertToInt(string String, int *Result) {
499
500 char *EndPointer;
501
502 *Result = (int) strtol(String.c_str(), &EndPointer, 0);
503 if(!isspace(*EndPointer) && *EndPointer!='\0') return false;
504 return true;
505}
506
507//
508// Interprets a range
509//
510struct Range ConvertToRange(string String) {
511
512 struct Range R;
513
514 // Full range
515 if (Match(String, "all")) {
516 R.Min = 0;
517 R.Max = std::numeric_limits<int>::max();
518 return R;
519 }
520
521 // Single number
522 if (ConvertToInt(String, &R.Min)) {
523 R.Max = R.Min;
524 return R;
525 }
526
527 // Range a-b
528 vector<string> V = EvidenceServer::Tokenize(String, "-");
529 if (V.size() == 2 && ConvertToInt(V[0], &R.Min) && ConvertToInt(V[1], &R.Max)) return R;
530
531 R.Min = R.Max = -1;
532 return R;
533}
Note: See TracBrowser for help on using the repository browser.