1
2 /* Instrument command line application support functions */
3
4 /*
5 * Argyll Color Correction System
6 *
7 * Author: Graeme W. Gill
8 * Date: 10/3/2001
9 *
10 * Copyright 2001 - 2013 Graeme W. Gill
11 * All rights reserved.
12 *
13 * This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
14 * see the License2.txt file for licencing details.
15 */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <stdarg.h>
20 #include <ctype.h>
21 #include <string.h>
22 #include <time.h>
23 #ifndef SALONEINSTLIB
24 #include "copyright.h"
25 #include "aconfig.h"
26 #else
27 #include "sa_config.h"
28 #endif /* !SALONEINSTLIB */
29 #include "numsup.h"
30 #include "xspect.h"
31 #include "conv.h"
32 #include "insttypes.h"
33
34 #include "icoms.h"
35 #include "inst.h"
36 #include "rspec.h"
37 #include "insttypeinst.h"
38 #include "instappsup.h"
39
40 /* ================================================================= */
41 /* a default user interaction handler */
42
43 typedef struct _uicontext {
44 int emit_ret; /* Emit \n on inst_triggered */
45 int cut; /* The character that caused the termination */
46 int uih[256]; /* User interrupt handling key table. Value can be: */
47 /* DUIH_OK, DUIH_ABORT, DUIH_TERM, DUIH_TRIG, DUIH_CMND */
48 } uicontext;
49
50 static uicontext def_uicntx = { 1, 0, { 0 } };
51
def_uicallback(void * cntx,inst_ui_purp purp)52 static inst_code def_uicallback(void *cntx, inst_ui_purp purp) {
53 uicontext *p = (uicontext *)cntx;
54
55 if (purp == inst_triggered) {
56 if (p->emit_ret)
57 printf("\n");
58 return inst_ok;
59
60 } else if (purp == inst_negcoms
61 || purp == inst_armed
62 || purp == inst_measuring) {
63 int c;
64
65 c = poll_con_char();
66 if (c != 0) {
67 p->cut = c;
68 c = p->uih[c];
69 if (c & (DUIH_ABORT | DUIH_TERM | DUIH_CMND))
70 return inst_user_abort;
71 if (c & DUIH_TRIG)
72 return inst_user_trig;
73 }
74
75 } else if (purp == inst_measuring) {
76 return inst_ok;
77 }
78 return inst_ok;
79 }
80
81 /* Return the default uicallback function */
inst_get_uicallback()82 inst_code (*inst_get_uicallback())(void *, inst_ui_purp) {
83 return &def_uicallback;
84 }
85
86 /* Return the default uicallback context */
inst_get_uicontext()87 void *inst_get_uicontext() {
88 return (void *)&def_uicntx;
89 }
90
91 /* Install the default uicallback function in the given inst */
inst_set_uicallback(inst * p)92 void inst_set_uicallback(inst *p) {
93 p->set_uicallback(p, def_uicallback, (void *)&def_uicntx);
94 }
95
96 /* Set the return on trigger to true or false */
inst_set_uicb_trigret(int set)97 void inst_set_uicb_trigret(int set) {
98 uicontext *p = &def_uicntx;
99 p->emit_ret = set;
100 }
101
102 /* Reset user interaction handling to default (Esc, ^C, q or 'Q' = Abort) */
inst_reset_uih()103 void inst_reset_uih() {
104 uicontext *p = &def_uicntx;
105 int i;
106
107 for (i = 0; i < 255; i++)
108 p->uih[i] = DUIH_NONE;
109
110 p->uih[0x1b] = DUIH_ABORT; /* Escape */
111 p->uih['q'] = DUIH_ABORT; /* q */
112 p->uih['Q'] = DUIH_ABORT; /* Q */
113 p->uih[0x03] = DUIH_ABORT; /* ^C */
114 }
115
116 /* Set a key range to the given handling type */
117 /* min & max are between 0 and 255, status is one of */
118 /* DUIH_OK, DUIH_USER, DUIH_TERM, DUIH_TRIG, DUIH_CMND */
inst_set_uih(int min,int max,int status)119 void inst_set_uih(
120 int min, /* Start key code */
121 int max, /* End key code (inclusive) */
122 int status /* ICOM_OK, ICOM_USER, ICOM_TERM, ICOM_TRIG, ICOM_CMND */
123 ) {
124 uicontext *p = &def_uicntx;
125 int i;
126
127 if (min < 0)
128 min = 0;
129 else if (min > 255)
130 min = 255;
131 if (max < 0)
132 max = 0;
133 else if (max > 255)
134 max = 255;
135
136 if (status != DUIH_NONE
137 && status != DUIH_ABORT
138 && status != DUIH_TERM
139 && status != DUIH_CMND
140 && status != DUIH_TRIG)
141 status = DUIH_NONE;
142
143 for (i = min; i <= max; i++) {
144 p->uih[i] = status;
145 }
146 }
147
148 /* Get the character that caused the user interrupt */
149 /* + its key type in the upper 8 bits. */
150 /* Clear it to 0x00 after reading it. */
inst_get_uih_char()151 int inst_get_uih_char() {
152 uicontext *p = &def_uicntx;
153 int c = p->cut;
154 c |= p->uih[c];
155 p->cut = 0;
156 return c;
157 }
158
159 /* ================================================================= */
160
161 /* A default calibration user interaction handler using the console. */
162 /* This handles both normal and display based calibration interaction */
163 /* with the instrument, if a disp_setup function and pointer to disp_win_info */
164 /* is provided. */
inst_handle_calibrate(inst * p,inst_cal_type calt,inst_cal_cond calc,inst_code (* disp_setup)(inst * p,inst_cal_cond calc,disp_win_info * dwi),disp_win_info * dwi,int doimmediately)165 inst_code inst_handle_calibrate(
166 inst *p,
167 inst_cal_type calt, /* Calibration type to do */
168 inst_cal_cond calc, /* Current current condition */
169 inst_code (*disp_setup) (inst *p,inst_cal_cond calc, disp_win_info *dwi),
170 /* Callback for handling a display calibration - May be NULL */
171 disp_win_info *dwi, /* Information to be able to open a display test patch - May be NULL */
172 int doimmediately /* If nz, don't wait for user, calibrate immediatley */
173 ) {
174 inst_code rv = inst_ok, ev;
175 int usermes = 0; /* User was given a message */
176 inst_calc_id_type idtype; /* Condition identifier type */
177 char id[200]; /* Condition identifier */
178 int ch;
179
180 a1logd(p->log,1,"inst_handle_calibrate called\n");
181
182 /* Untill we're done with the calibration */
183 for (;;) {
184
185 a1logd(p->log,1,"About to call calibrate at top of loop\n");
186 ev = p->calibrate(p, &calt, &calc, &idtype, id);
187 a1logd(p->log,1,"Calibrate returned calt 0x%x, calc 0x%x, ev 0x%x\n",calt,calc,ev);
188
189 /* We're done */
190 if ((ev & inst_mask) == inst_ok) {
191 if ((calc & inst_calc_cond_mask) == inst_calc_message) {
192 /* (Or could create our own message text based on value of idtype) */
193 printf("%s\n",id);
194 }
195 if (usermes)
196 printf("Calibration complete\n");
197 fflush(stdout);
198 a1logd(p->log,1,"inst_handle_calibrate done 0x%x\n",ev);
199 return ev;
200 }
201
202 /* User aborted */
203 if ((ev & inst_mask) == inst_user_abort) {
204 a1logd(p->log,1,"inst_handle_calibrate user aborted 0x%x\n",ev);
205 return ev;
206 }
207
208 /* Retry on an error */
209 if ((ev & inst_mask) != inst_cal_setup) {
210 if ((ev & inst_mask) == inst_unsupported) {
211 a1logd(p->log,1,"inst_handle_calibrate err 0x%x, calibration type 0x%x not supported\n",ev, calt);
212 return inst_unsupported;
213 }
214
215 printf("Calibration failed with '%s' (%s)\n",
216 p->inst_interp_error(p, ev), p->interp_error(p, ev));
217
218 if (doimmediately)
219 return inst_user_abort;
220
221 printf("Hit any key to retry, or Esc or Q to abort:\n");
222
223 empty_con_chars();
224 ch = next_con_char();
225 printf("\n");
226 if (ch == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
227 a1logd(p->log,1,"inst_handle_calibrate user aborted 0x%x\n",inst_user_abort);
228 fflush(stdout);
229 return inst_user_abort;
230 }
231
232 } else {
233
234 printf("\n");
235
236 /* Get user to do/setup calibration */
237 switch (calc & inst_calc_cond_mask) {
238 case inst_calc_uop_ref_white:
239 printf("Do a reflective white calibration,\n");
240 printf(" and then hit any key to continue,\n");
241 break;
242
243 case inst_calc_uop_trans_white:
244 printf("Do a transmissive white calibration,\n");
245 printf(" and then hit any key to continue,\n");
246 break;
247
248 case inst_calc_uop_trans_dark:
249 printf("Do a transmissive dark calibration,\n");
250 printf(" and then hit any key to continue,\n");
251 break;
252
253 case inst_calc_man_ref_white:
254 printf("Place the instrument on its reflective white reference S/N %s,\n",id);
255 printf(" and then hit any key to continue,\n");
256 break;
257
258 case inst_calc_man_ref_whitek:
259 printf("Click the instrument on its reflective white reference %s,\n",id);
260 break;
261
262 case inst_calc_man_ref_dark:
263 printf("Place the instrument on light trap, or in the dark,\n");
264 printf("and distant from any surface,\n");
265 printf(" and then hit any key to continue,\n");
266 break;
267
268 case inst_calc_man_dark_gloss:
269 printf("Place the instrument on black gloss reference\n");
270 printf(" and then hit any key to continue,\n");
271 break;
272
273 case inst_calc_man_em_dark:
274 printf("Place cap on the instrument, or place on a dark surface,\n");
275 printf("or place on the calibration reference,\n");
276 printf(" and then hit any key to continue,\n");
277 break;
278
279 case inst_calc_man_am_dark:
280 printf("Place ambient adapter and cap on the instrument,\n");
281 printf("or place on the calibration reference,\n");
282 printf(" and then hit any key to continue,\n");
283 break;
284
285 case inst_calc_man_cal_smode:
286 printf("Set instrument sensor to calibration position,\n");
287 printf(" and then hit any key to continue,\n");
288 break;
289
290 case inst_calc_man_trans_white:
291 printf("Place the instrument on its transmissive white source,\n");
292 printf(" and then hit any key to continue,\n");
293 break;
294
295 case inst_calc_man_trans_dark:
296 printf("Use the appropriate tramissive blocking to block the transmission path,\n");
297 printf(" and then hit any key to continue,\n");
298 break;
299
300 case inst_calc_change_filter:
301 printf("Change filter on instrument to %s,\n",id);
302 printf(" and then hit any key to continue,\n");
303 break;
304
305 case inst_calc_message:
306 printf("%s\n",id);
307 printf(" Hit any key to continue,\n");
308 break;
309
310 case inst_calc_emis_white:
311 if (disp_setup == NULL || dwi == NULL) { /* No way of creating a test window */
312 printf("Place the instrument on a 100%% white test patch,\n");
313 printf(" and then hit any key to continue,\n");
314 } else {
315 /* We need to display a 100% white patch to proceed with this */
316 /* type of calibration */
317 if ((rv = disp_setup(p, calc, dwi)) != inst_ok)
318 return rv;
319 }
320 break;
321
322 case inst_calc_emis_80pc:
323 if (disp_setup == NULL || dwi == NULL) { /* No way of creating a test window */
324 printf("Place the instrument on a 80%% white test patch,\n");
325 printf(" and then hit any key to continue,\n");
326 } else {
327 /* We need to display a 80% white patch to proceed with this */
328 /* type of calibration */
329 if ((rv = disp_setup(p, calc, dwi)) != inst_ok)
330 return rv;
331 }
332 break;
333
334 case inst_calc_emis_grey:
335 case inst_calc_emis_grey_darker:
336 case inst_calc_emis_grey_ligher:
337 if (dwi == NULL) { /* No way of creating a test window */
338 if ((calc & inst_calc_cond_mask) == inst_calc_emis_grey) {
339 p->cal_gy_level = 0.6;
340 p->cal_gy_count = 0;
341 } else if ((calc & inst_calc_cond_mask) == inst_calc_emis_grey_darker) {
342 p->cal_gy_level *= 0.7;
343 p->cal_gy_count++;
344 } else if ((calc & inst_calc_cond_mask) == inst_calc_emis_grey_ligher) {
345 p->cal_gy_level *= 1.4;
346 if (p->cal_gy_level > 1.0)
347 p->cal_gy_level = 1.0;
348 p->cal_gy_count++;
349 }
350 if (p->cal_gy_count > 4) {
351 printf("Cell ratio calibration failed - too many tries at setting grey level.\n");
352 a1logd(p->log,1,"inst_handle_calibrate too many tries at setting grey level 0x%x\n",inst_internal_error);
353 return inst_internal_error;
354 } else {
355 printf("Place the instrument on a %d%% white test patch,\n", (int)(p->cal_gy_level * 100.0 + 0.5));
356 printf(" and then hit any key to continue,\n");
357 }
358 } else {
359
360 /* We need to display a test patch to proceed with this
361 * type of calibration. Typically this will be:
362 *
363 * inst_calc_xxxx_grey:
364 * set p->cal_gy_level = 0.6
365 * set p->cal_gy_count = 0;
366 *
367 * inst_calc_xxxx_grey_darker:
368 * set p->cal_gy_level *= 0.7
369 * set p->cal_gy_count++
370 *
371 * inst_calc_xxxx_grey_ligher:
372 * set p->cal_gy_level *= 1.4
373 * set p->cal_gy_count++
374 *
375 * and return failure if p->cal_gy_count > 4
376 */
377
378 if ((rv = disp_setup(p, calc, dwi)) != inst_ok)
379 return rv;
380 }
381 break;
382
383 default:
384 /* Something isn't being handled */
385 a1logd(p->log,1,"inst_handle_calibrate unhandled calc case 0x%x, err 0x%x\n",calc,inst_internal_error);
386 return inst_internal_error;
387 }
388 if (calc & inst_calc_optional_flag)
389 printf(" or hit Esc or Q to abort, or S to skip: ");
390 else
391 printf(" or hit Esc or Q to abort: ");
392 fflush(stdout);
393
394 usermes = 1;
395
396 /* If we should wait for user to say we're in the right condition, */
397 /* and this isn't a calibration that requires clicking the instrument */
398 /* on the calibration tile, then wait for the an OK or abort or skip. */
399 if (!doimmediately
400 && (calc & inst_calc_cond_mask) != inst_calc_man_ref_whitek) {
401 empty_con_chars();
402 ch = next_con_char();
403 printf("\n");
404 /* If optional calib. and user wants to skip it */
405 /* Loop back to calibrate() with inst_calc_optional_flag still set */
406 if ((calc & inst_calc_optional_flag) != 0 && (ch == 's' || ch == 'S')) {
407 printf("Skipped\n");
408 goto oloop;
409 }
410 if (ch == 0x1b || ch == 0x3 || ch == 'q' || ch == 'Q') {
411 a1logd(p->log,1,"inst_handle_calibrate user aborted 0x%x\n",inst_user_abort);
412 return inst_user_abort;
413 }
414 }
415 /* Remove any skip flag and continue with the calibration */
416 calc &= inst_calc_cond_mask;
417 }
418 oloop:;
419 }
420 }
421
422 /* ============================================================================= */
423
424 /* A helper function to display -y flag usage for each instrument type available */
425 /* Return accumulated capabilities2 of all the instruments */
426 /* Return all possible capabilities if there are no instruments */
427 /* If docbib is nz, then only display the base calibration display types */
inst_show_disptype_options(FILE * fp,char * oline,icompaths * icmps,int docbib)428 inst2_capability inst_show_disptype_options(FILE *fp, char *oline, icompaths *icmps, int docbib) {
429 int i, j;
430 char buf[200], *bp;
431 char extra[40];
432 int olen, pstart;
433 int notall = 0; /* Not all instruments are USB */
434 int gotone = 0; /* Found at least one USB instrument */
435 inst2_capability acap = 0; /* Accumulate capabilities */
436
437 if (icmps == NULL)
438 return 0;
439
440 /* Locate the end of the option */
441 for (bp = oline; *bp != '\000' && *bp == ' '; bp++)
442 ;
443 for (; *bp != '\000' && *bp != ' '; bp++)
444 ;
445 pstart = bp - oline;
446 if (pstart > 10)
447 pstart = 10;
448 strncpy(buf, oline, pstart);
449 buf[pstart++] = ' ';
450
451 olen = strlen(oline); /* lenth of option part of line */
452
453 for (i = 0; icmps != NULL && i < icmps->ndpaths[dtix_inst]; i++) {
454 inst *it;
455 inst2_capability cap;
456 int k;
457
458 if ((it = new_inst(icmps->dpaths[dtix_inst][i], 1, g_log, NULL, NULL)) == NULL) {
459 notall = 1;
460 continue;
461 }
462 gotone = 1;
463
464 it->capabilities(it, NULL, &cap, NULL);
465 acap |= cap;
466
467 if (cap & inst2_disptype) {
468 int nsel;
469 inst_disptypesel *sels;
470
471 if (it->get_disptypesel(it, &nsel, &sels, 1, 0) != inst_ok) {
472 it->del(it);
473 continue;
474 }
475 for (j = 0; j < nsel; j++) {
476 int m;
477
478 if (docbib && sels[j].cbid == 0)
479 continue; /* Skip non cbid type */
480
481 m = pstart;
482 for (k = 0; k < (INST_DTYPE_SEL_LEN-1); k++) {
483 if (sels[j].sel[k] == '\000')
484 break;
485 if (m > pstart)
486 buf[m++] = '|';
487 buf[m++] = sels[j].sel[k];
488 }
489 while (m < (olen+1)) /* Indent it by 1 */
490 buf[m++] = ' ';
491 buf[m++] = '\000';
492
493 extra[0] = '\000';
494 if ((sels[j].flags & inst_dtflags_default) || sels[j].cbid != 0) {
495 strcat(extra, " [");
496 if (sels[j].flags & inst_dtflags_default) {
497 strcat(extra, "Default");
498 if (sels[j].cbid != 0)
499 strcat(extra, ",");
500 }
501 if (sels[j].cbid != 0) {
502 sprintf(extra + strlen(extra), "CB%d",sels[j].cbid);
503 }
504 strcat(extra, "]");
505 }
506
507 fprintf(fp, "%s%s: %s%s\n",buf, inst_sname(it->itype), sels[j].desc, extra);
508
509 if (j == 0) {
510 for (m = 0; m < pstart; m++)
511 buf[m] = ' ';
512 }
513 }
514 }
515 it->del(it);
516 }
517 /* Output a default desciption if not all instruments are USB */
518 if (notall) {
519 int m = pstart;
520 buf[m++] = 'l';
521 buf[m++] = '|';
522 buf[m++] = 'c';
523 while (m < olen)
524 buf[m++] = ' ';
525 buf[m++] = '\000';
526 fprintf(fp, "%s%s\n",buf, " Other: l = LCD, c = CRT");
527 }
528 if (!gotone)
529 acap = ~0;
530
531 return acap;
532 }
533
534 /* A helper function to turn a -y flag into a selection index */
535 /* If docbib is nz, then only allow base calibration display types */
536 /* Return -1 on error */
inst_get_disptype_index(inst * it,int c,int docbib)537 int inst_get_disptype_index(inst *it, int c, int docbib) {
538 inst2_capability cap;
539 int j, k;
540
541 it->capabilities(it, NULL, &cap, NULL);
542
543 if (cap & inst2_disptype) {
544 int nsel;
545 inst_disptypesel *sels;
546
547 if (it->get_disptypesel(it, &nsel, &sels, 1, 0) != inst_ok) {
548 return -1;
549 }
550 for (j = 0; j < nsel; j++) {
551 if (docbib && sels[j].cbid == 0)
552 continue; /* Skip non cbid type */
553
554 for (k = 0; k < (INST_DTYPE_SEL_LEN-1); k++) {
555 if (sels[j].sel[k] == '\000')
556 break;
557 if (sels[j].sel[k] == c) {
558 return j;
559 }
560 }
561 }
562 }
563 return -1;
564 }
565
566 /* ================================================================= */
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587