source: fact/BIASctrl/User.cc@ 11915

Last change on this file since 11915 was 11915, checked in by ogrimm, 13 years ago
Ramping also for decreasing voltage
File size: 18.1 KB
Line 
1//
2// Class processing user input
3//
4
5#include "User.h"
6#include <readline/readline.h>
7
8using namespace std;
9
10// Branch table for command evaluation
11static const struct CL_Struct { const char *Name;
12 void (User::*CommandPointer)();
13 unsigned int MinNumParameter;
14 bool NeedCrate;
15 const char *Parameters;
16 const char *Help;
17 } CommandList[] =
18 {{"pixel", &User::cmd_hv, 2, true, "<range> <voltage|default|info>", "Change bias of pixels"},
19 {"channel", &User::cmd_hv, 2, true, "<range> <voltage|default|info>", "Change bias of channels of active crate"},
20 {"gs", &User::cmd_gs, 1, true, "[crate] <volt>", "Global voltage set active crate"},
21 {"reset", &User::cmd_reset, 0, true, "", "Reset active crate"},
22 {"synch", &User::cmd_synch, 0, true, "", "Synchronize active crate"},
23 {"status", &User::cmd_status, 0, false, "[dac|current]", "Show status information (DAC values if requested)"},
24 {"mode", &User::cmd_mode, 1, true, "<static|dynamic>", "Set voltage stabilization mode (experimental)"},
25 {"load", &User::cmd_load, 1, true, "<file>", "Load and set bias settings from file"},
26 {"save", &User::cmd_save, 1, true, "<file>", "Save current bias settings to file"},
27 {"help", &User::cmd_help, 0, false, "", "Print help"},
28 {"exit", &User::cmd_exit, 0, false, "", "Exit program"},
29 {".", &User::cmd_shell, 1, false, "<command>", "Execute shell command"}};
30
31//
32// Constructor
33//
34User::User(string Board): EvidenceServer(SERVER_NAME) {
35
36 MainThread = pthread_self();
37
38 // DIM console service used in PrintMessage()
39 ConsoleText = NULL;
40 ConsoleOut = new DimService(SERVER_NAME"/ConsoleOut", (char *) "");
41
42 // Get/initialize configuration data
43 if (Board.empty()) Board = GetConfig("Boards", "dummy");
44 GetConfig("TimeOut");
45 GetConfig("VoltageLimit");
46 GetConfig("MinResetPeriod");
47 GetConfig("RampSpeed");
48 GetConfig("UpdatePeriod");
49 vector<string> Text = Tokenize(GetConfig("DefaultVoltage", ""), " \t");
50
51 for (unsigned int i=0; i<Text.size(); i++) {
52 DefaultVoltage.push_back(atof(Text[i].c_str()));
53 }
54
55 // Open device
56 Dev = new class Crate(Board, 0, this);
57
58 if (Dev->InitOK) PrintMessage("Synchronized and reset crate %s\n", Board.c_str());
59 else {
60 Message(WARN, "Failed to synchronize crate %s", Board.c_str());
61 delete Dev;
62 Dev = NULL;
63 }
64
65 // Create PixelMap instance (map from config server)
66 DimRpcInfo RPC((char *) "ConfigRequest", (char *) "");
67 RPC.setData((char *) "Misc PixelMap");
68 PixMap = new PixelMap(std::string(RPC.getString(), RPC.getSize()));
69
70 // Install DIM command (after all initialized)
71 DIMCommand = new DimCommand((char *) SERVER_NAME"/Command", (char *) "C", this);
72
73 // Create monitor thread and make accessible for sending signal
74 if ((pthread_create(&Thread, NULL, (void * (*)(void *)) LaunchMonitor,(void *) this)) != 0) {
75 Message(FATAL, "pthread_create() failed with Monitor thread");
76 }
77}
78
79//
80// Destructor
81//
82User::~User() {
83
84 int Ret;
85
86 // Wait for thread to quit (ignore error if thread did already exit)
87 if ((Ret = pthread_cancel(Thread)) != 0) {
88 if (Ret != ESRCH) Message(ERROR, "pthread_cancel() failed (%s)", strerror(Ret));
89 }
90 if ((Ret = pthread_join(Thread, NULL)) != 0) Message(ERROR, "pthread_join() failed (%s)", strerror(Ret));
91
92 // Delete crate
93 delete Dev;
94
95 delete DIMCommand;
96 delete PixMap;
97 delete ConsoleOut;
98 free(ConsoleText);
99}
100
101//
102// Process user input
103//
104void User::commandHandler() {
105
106 // Build string safely
107 string Command = string(getCommand()->getString(), getCommand()->getSize());
108
109 // Check if command is legal and ignore empty commands
110 if (getCommand() != DIMCommand || Command.size() < 2) return;
111
112 // Parse command into tokens
113 Parameter = Tokenize(Command, " ");
114
115 // Special handling of shell execution
116 if (Command[0] == '.') {
117 Parameter.clear();
118 Parameter.push_back(".");
119 Parameter.push_back(Command.substr(1));
120 }
121
122 // Search for command in command list
123 for(unsigned int CmdNumber=0; CmdNumber<sizeof(CommandList)/sizeof(CL_Struct); CmdNumber++) {
124 if (Match(Parameter[0], CommandList[CmdNumber].Name)) {
125 // Requested command help?
126 if (Parameter.size() == 2 && Match(Parameter[1], "?")) {
127 PrintMessage("Usage: %s %s\n%s\n", CommandList[CmdNumber].Name, CommandList[CmdNumber].Parameters, CommandList[CmdNumber].Help);
128 return;
129 }
130
131 // Incorrect number of parameters?
132 if (Parameter.size()-1 < CommandList[CmdNumber].MinNumParameter) {
133 PrintMessage("Usage: %s %s\n", CommandList[CmdNumber].Name, CommandList[CmdNumber].Parameters);
134 return;
135 }
136
137 // Check if crate needed
138 if (CommandList[CmdNumber].NeedCrate && Dev==NULL) {
139 PrintMessage("No crate available\n");
140 return;
141 }
142
143 // Jump to command function
144 (this->*CommandList[CmdNumber].CommandPointer)();
145 return;
146 }
147 }
148 PrintMessage("Unknown command '%s'\n", Parameter[0].c_str());
149}
150
151
152// Print help
153void User::cmd_help() {
154
155 for(unsigned int i=0; i<sizeof(CommandList)/sizeof(CL_Struct); i++) {
156 PrintMessage("%-10s%s\n", CommandList[i].Name, CommandList[i].Help);
157 }
158
159 PrintMessage("\nUse '?' as argument to get more extensive help.\n"
160 "Commands 'pixel' and 'channel' allow and arbitary number of argument pairs.\n"
161 "Items in <> are mandatory, in [] optional, | indicates mutual exclusive or.\n"
162 "Ranges can be 'all', a single number or in the form 'a-b'.\n");
163}
164
165//
166// Synchronize crate
167//
168void User::cmd_synch() {
169
170 if (Dev->Synch()) PrintMessage("Synchronized crate\n");
171 else PrintMessage("Failed to synchronize crate\n");
172}
173
174//
175// Set new bias voltage
176//
177void User::cmd_hv() {
178
179 unsigned int Channel, Errors = 0;
180 double Double;
181 struct Range Chan, Pixel;
182 map<unsigned int, double> Voltages;
183 vector<unsigned int> Channels;
184
185 // Loop over all parameters
186 for (unsigned int n=1; n < Parameter.size()-1; n+=2) {
187
188 // Convert voltage value and check format
189 if (!ConvertToDouble(Parameter[n+1], &Double) && !Match(Parameter[n+1], "default") && !Match(Parameter[n+1], "info")) {
190 PrintMessage("Error: Wrong number format for voltage setting\n");
191 continue;
192 }
193
194 // Extract affected channels for this argument pair
195 Channels.clear();
196
197 // Pixel identification?
198 if (Match(Parameter[0], "pixel")) {
199 Pixel.Min = 0;
200 Pixel.Max = 1439;
201
202 if (!ConvertToRange(Parameter[n], Pixel)) {
203 PrintMessage("Pixel ID out-of-range for parameter %d, skipping channel\n", n);
204 continue;
205 }
206
207 for (int i=Pixel.Min; i<=Pixel.Max; i++) {
208 Channel = PixMap->Pixel_to_HVboard(i)*NUM_CHANNELS + PixMap->Pixel_to_HVchannel(i);
209
210 // Skip if pixel ID or corresponding channels not existing
211 if (PixMap->Pixel_to_HVcrate(i) == 0 && Channel<MAX_NUM_BOARDS*NUM_CHANNELS) {
212 Channels.push_back(Channel);
213 }
214 }
215 }
216 // Channel identification
217 else {
218 Chan.Min = 0;
219 Chan.Max = MAX_NUM_BOARDS*NUM_CHANNELS-1;
220
221 if (!ConvertToRange(Parameter[n], Chan)) {
222 PrintMessage("Numeric conversion or out-of-range error for parameter %d, skipping channel\n", n);
223 continue;
224 }
225
226 for (int i=Chan.Min; i<=Chan.Max; i++) Channels.push_back(i);
227 }
228
229 // Loop over all given channels
230 for (unsigned int i=0; i<Channels.size(); i++) {
231 Channel = Channels[i];
232
233 // Should only information be printed?
234 if (Match(Parameter[n+1], "info")) {
235 PrintMessage("Channel %d ", Channel);
236 if (!Dev->Present[Channel]) PrintMessage(" is not present in crate\n");
237 else PrintMessage("is on board %d, board channel %d\n", Channel/32, Channel%32);
238
239 // Print pixel information
240 vector<unsigned int> List = PixMap->HV_to_Pixel(0, Channel/NUM_CHANNELS, Channel%NUM_CHANNELS);
241 PrintMessage("\n Default voltage: %.2f Pixel IDs: ", Channel < DefaultVoltage.size() ? DefaultVoltage[Channel] : 0);
242 for (unsigned int j=0; j<List.size(); j++) PrintMessage("%u ", List[j]);
243 if (List.empty()) PrintMessage("none");
244
245 // Print voltage and current
246 PrintMessage("\n Voltage setpoint: %.2f V (DAC %u) Current: %.2f uA ", Dev->GetVoltage(Channel), Dev->GetDAC(Channel), Dev->GetCurrent(Channel));
247
248 if (Dev->OC[Channel]) PrintMessage("(overcurrent)\n");
249 else PrintMessage("\n");
250
251 continue;
252 }
253
254 // Get current voltage on first change of a channel
255 if (Voltages.count(Channel) == 0) Voltages[Channel] = Dev->GetVoltage(Channel);
256
257 // Voltage change (number starts with + oder -) ignored if voltage is zero
258 if (Parameter[n+1][0]=='+' || Parameter[n+1][0]=='-') if (Voltages[Channel] == 0) continue;
259
260 // Should the default value be set?
261 if (Match(Parameter[n+1], "default")) {
262 if (Channel < DefaultVoltage.size()) Double = DefaultVoltage[Channel];
263 else Double = 0;
264 }
265
266 // Relative or absolute change?
267 if (Parameter[n+1][0]=='+' || Parameter[n+1][0]=='-') Voltages[Channel] += Double;
268 else Voltages[Channel] = Double;
269 } // Channels
270 } // Loop over command argument
271
272 // Ramp voltages and update DIM services
273 Errors += RampVoltages(Voltages);
274 Dev->UpdateDIM();
275
276 // Error message only if not yet too many errors
277 if (Errors > 0) {
278 if (Dev->ErrorCount > MAX_ERR_COUNT) return;
279 Message(ERROR, "%d errors occurred from SetChannels()", Errors);
280 }
281}
282
283//
284// Load bias settings from file
285//
286void User::cmd_load() {
287
288 char Buffer[MAX_COM_SIZE];
289 int Errors = 0;
290 unsigned int Channel;
291 double Value;
292 FILE *File;
293 map<unsigned int, double> Voltages;
294
295 // Open file
296 if ((File=fopen(Parameter[1].c_str(), "r")) == NULL) {
297 PrintMessage("Error: Could not open file '%s' (%s)\n", Parameter[1].c_str(), strerror(errno));
298 return;
299 }
300
301 // Scan through file line by line
302 while (fgets(Buffer, sizeof(Buffer), File) != NULL) if (Match(Dev->Name, Buffer)) {
303 PrintMessage("Found bias settings for crate %s\n\r", Dev->Name);
304
305 Voltages.clear();
306 Channel = 0;
307 while (fscanf(File, "%lf", &Value)==1 && Channel<MAX_NUM_BOARDS*NUM_CHANNELS) {
308 Voltages[Channel++] = Value;
309 }
310
311 // Ramp channels
312 Errors += RampVoltages(Voltages);
313
314 // Update DIM service
315 Dev->UpdateDIM();
316
317 if (ferror(File) != 0) {
318 PrintMessage("Error reading DAC value from file, terminating. (%s)\n",strerror(errno));
319 return;
320 }
321 else PrintMessage("\nFinished updating board\n");
322 } // if()
323
324 if (Errors != 0) PrintMessage("Warning: %d error(s) occurred\n", Errors);
325 if (fclose(File) != 0) PrintMessage("Error: Could not close file '%s'\n", Parameter[1].c_str());
326}
327
328//
329// Reset crate
330//
331void User::cmd_reset() {
332
333 if (Dev->SystemReset()) PrintMessage("System reset of crate\n");
334 else PrintMessage("Error: Could not reset crate\n");
335}
336
337//
338// Read channel
339//
340void User::cmd_gs() {
341
342 double Voltage;
343
344 if (!ConvertToDouble(Parameter[1], &Voltage)) {
345 PrintMessage("Error: Wrong number format\n");
346 return;
347 }
348
349 if (!Dev->GlobalSet(Voltage)) {
350 PrintMessage("Error: Could not global set crate\n");
351 }
352}
353
354//
355// Save bias settings of all boards
356//
357void User::cmd_save() {
358
359 FILE *File;
360 time_t Time = time(NULL);
361
362 if ((File = fopen(Parameter[1].c_str(), "w")) == NULL) {
363 PrintMessage("Error: Could not open file '%s' (%s)\n", Parameter[1].c_str(), strerror(errno));
364 return;
365 }
366
367 fprintf(File,"********** Bias settings of %s **********\n\n", ctime(&Time));
368 fprintf(File, "%s\n\n", Dev->Name);
369
370 for (unsigned int j=0; j<MAX_NUM_BOARDS*NUM_CHANNELS; j++) fprintf(File,"%.3f ", Dev->GetVoltage(j));
371 fprintf(File, "\n");
372
373 if (fclose(File) != 0) {
374 PrintMessage("Error: Could not close file '%s' (%s)\n", Parameter[1].c_str(), strerror(errno));
375 }
376}
377
378//
379// Set operation mode
380//
381void User::cmd_mode() {
382
383 if (Match(Parameter[1], "static")) Mode = mode_static;
384 else {
385 Mode = mode_dynamic;
386 Dev->SetRefCurrent();
387 }
388}
389
390//
391// Print status
392//
393void User::cmd_status() {
394
395 // Overview information
396 PrintMessage("Update delay: %2d sec Time out: %.2f sec Minium reset delay: %u sec\n",
397 min(atoi(GetConfig("UpdatePeriod").c_str()), 1), atof(GetConfig("TimeOut").c_str()),
398 atoi(GetConfig("MinResetPeriod").c_str()));
399 PrintMessage("Voltage limit: %.2f V Ramp speed: %.2f V/10 ms\n",
400 atof(GetConfig("VoltageLimit").c_str()), atof(GetConfig("RampSpeed").c_str()));
401
402 if (Dev == NULL) return;
403
404 // Details
405 PrintMessage("\nCRATE %s Wrap counter: %s (%d) Reset: %s Error count: %d %s\n ",
406 Dev->Name, Dev->WrapOK ? "ok":"error", Dev->WrapCount,
407 Dev->ResetHit ? "yes" : "no", Dev->ErrorCount, Dev->Disabled ? "(DISABLED)":"");
408
409 // Read all channels
410 if (!Dev->ReadAll()) {
411 PrintMessage("Could not update status, ReadAll() failed\n");
412 return;
413 }
414
415 // Voltage or current list
416 if (Parameter.size() == 1) PrintMessage("Channel voltages (in V)");
417 else if (Match(Parameter[1], "dac")) PrintMessage("Channel voltages (in DAC values)");
418 else PrintMessage("Channel currents (in uA)");
419
420 for (unsigned int j=0; j<MAX_NUM_BOARDS*NUM_CHANNELS; j++) {
421 if (j%12 == 0) PrintMessage("\n%3.1d: ", j);
422 if (!Dev->Present[j]) PrintMessage(" - ");
423 else if (Parameter.size() == 1) PrintMessage("%#5.2f", Dev->GetVoltage(j));
424 else if (Match(Parameter[1], "dac")) PrintMessage("%5d", Dev->GetDAC(j));
425 else PrintMessage("%#5.2f", Dev->GetCurrent(j));
426 // Print overcurrent marker
427 PrintMessage("%s ", Dev->OC[j] ? "*":" ");
428 }
429 PrintMessage("\n");
430}
431
432//
433// Exit program (Signal makes readline return and sets ExitRequest)
434//
435void User::cmd_exit() {
436
437 pthread_kill(MainThread, SIGTERM);
438}
439
440//
441// Execute shell command
442//
443void User::cmd_shell() {
444
445 if (system(Parameter[1].c_str()) == -1) PrintMessage("Error with system() call\n");
446}
447
448
449//
450// Print message to screen and to DIM text service
451//
452void User::PrintMessage(const char *Format, ...) {
453
454 static char Error[] = "vasprintf() failed in PrintMessage()";
455 char *Text;
456
457 // Evaluate arguments
458 va_list ArgumentPointer;
459 va_start(ArgumentPointer, Format);
460 if (vasprintf(&Text, Format, ArgumentPointer) == -1) Text = Error;
461 va_end(ArgumentPointer);
462
463 if (strlen(Text) == 0) return;
464
465 // Print to console
466 printf("%s", Text);
467 fflush(stdout);
468 if (Text[strlen(Text)-1] == '\n') rl_on_new_line(); // New prompt
469
470 // Send to DIM text service
471 ConsoleOut->updateService(Text);
472
473 // Free old text
474 if (ConsoleText != Error) free(ConsoleText);
475 ConsoleText = Text;
476}
477
478
479// Ramp to new voltage with given maximum step
480unsigned int User::RampVoltages(map<unsigned int, double> Voltages) {
481
482 map<unsigned int, double> Target;
483 int Errors = 0;
484 double V, Lim, MaxDiff = atof(GetConfig("RampSpeed").c_str());
485
486 while (!Voltages.empty() && Errors < MAX_ERR_COUNT) {
487 // Voltage limit evaluate here in case of asynchronous update in config file
488 Lim = atof(GetConfig("VoltageLimit").c_str());
489
490 // Remove channels already at target or at voltage limit
491 for (map<unsigned int, double>::iterator it = Voltages.begin(); it != Voltages.end(); ++it) {
492 // Channel at target?
493 if (fabs(Dev->GetVoltage(it->first)-it->second) < 0.001) Voltages.erase(it);
494 // Channel at voltage limit?
495 if (it->second > Lim && Dev->GetVoltage(it->first) == Lim) Voltages.erase(it);
496 if (it->second < 0 && Dev->GetVoltage(it->first) == 0) Voltages.erase(it);
497 }
498
499 // Limit voltage changes to MaxDiff
500 Target = Voltages;
501 for (map<unsigned int, double>::iterator it = Target.begin(); it != Target.end(); ++it) {
502 V = Dev->GetVoltage(it->first);
503 // Ramp up
504 if ((V < it->second) && (V + MaxDiff < it->second)) it->second = V + MaxDiff;
505 // Ramp down
506 if ((V > it->second) && (V - MaxDiff > it->second)) it->second = V - MaxDiff;
507 }
508
509 // Set channels to next target and wait 10 ms
510 if (!Dev->SetChannels(Target)) Errors++;
511 usleep(10000);
512 }
513
514 return Errors;
515}
516
517
518//
519// Check status
520//
521void User::Monitor() {
522
523 int Ret;
524
525 // End if no crate available
526 if (Dev == NULL) return;
527
528 while (!ExitRequest) {
529 // Wait (this is the only allowed cancelation point)
530 if ((Ret=pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL)) != 0) {
531 Message(FATAL, "pthread_setcancelstate() failed (%s)", strerror(Ret));
532 }
533 sleep(min(atoi(GetConfig("UpdatePeriod").c_str()), 1));
534 if ((Ret=pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL)) != 0) {
535 Message(FATAL, "pthread_setcancelstate() failed (%s)", strerror(Ret));
536 }
537
538 // Check crate
539 if (Dev->Disabled) continue;
540
541 // Read all channels
542 if (!Dev->ReadAll()) {
543 Message(ERROR, "Monitor thread could not read status, ReadAll() failed\n");
544 continue;
545 }
546
547 // Check if crate push button was hit
548 if (Dev->ResetHit) {
549 Message(INFO, "Manual reset of crate, setting voltages to zero and issuing system reset");
550 if (!Dev->GlobalSet(0)) Message(ERROR, "Global set to zero voltage of crate failed");
551 if (!Dev->SystemReset()) Message(ERROR, "System reset of crate failed");
552 }
553
554 // Check for overcurrent and set voltage to zero if occurred
555 map<unsigned int, double> Voltages;
556
557 for (unsigned int j=0; j<MAX_NUM_BOARDS*NUM_CHANNELS; j++) if (Dev->OC[j]) Voltages[j] = 0;
558 if (!Voltages.empty()) {
559 if (!Dev->SetChannels(Voltages)) Message(ERROR, "Error setting voltage to zero for channels with overcurrent");
560 Dev->UpdateDIM();
561 }
562
563 if (Mode == mode_dynamic) Dev->AdaptVoltages();
564 } // while
565}
566
567// Call monitor loop inside class
568void User::LaunchMonitor(User *m) {
569
570 m->Monitor();
571}
572
573
574//
575// Check if two strings match (min 1 character must match)
576//
577bool User::Match(string str, const char *cmd) {
578
579 return strncasecmp(str.c_str(),cmd,strlen(str.c_str())==0 ? 1:strlen(str.c_str())) ? false:true;
580}
581
582//
583// Conversion function from string to double or int
584//
585// Return false if conversion did not stop on whitespace or EOL character
586bool User::ConvertToDouble(string String, double *Result) {
587
588 char *EndPointer;
589
590 *Result = strtod(String.c_str(), &EndPointer);
591 if(!isspace(*EndPointer) && *EndPointer!='\0') return false;
592 return true;
593}
594
595bool User::ConvertToInt(string String, int *Result) {
596
597 char *EndPointer;
598
599 *Result = (int) strtol(String.c_str(), &EndPointer, 0);
600 if(!isspace(*EndPointer) && *EndPointer!='\0') return false;
601 return true;
602}
603
604//
605// Interprets a range
606//
607bool User::ConvertToRange(string String, struct User::Range &R) {
608
609 int N=0, M=0; // Init to avoid compiler warning
610
611 // Full range
612 if (Match(String, "all")) return true;
613
614 // Single number
615 if (ConvertToInt(String, &N)) {
616 if (N>= R.Min && N<=R.Max) {
617 R.Max = R.Min = N;
618 return true;
619 }
620 return false;
621 }
622
623 // Range a-b
624 vector<string> V = EvidenceServer::Tokenize(String, "-");
625 if (V.size()==2 && ConvertToInt(V[0], &N) && ConvertToInt(V[1], &M) && N>=R.Min && M<=R.Max) {
626 R.Min = N;
627 R.Max = M;
628 return true;
629 }
630
631 return false;
632}
Note: See TracBrowser for help on using the repository browser.