source: tags/arb-6.0/WINDOW/AW_status.cxx

Last change on this file was 12303, checked in by westram, 10 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.8 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : AW_status.cxx                                     //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include <aw_root.hxx>
12#include <aw_question.hxx>
13#include <aw_awars.hxx>
14#include <aw_window.hxx>
15#include "aw_msg.hxx"
16#include "aw_status.hxx"
17
18#include <arbdbt.h>
19#include <arb_strbuf.h>
20#include <SigHandler.h>
21
22#include <cerrno>
23#include <cstdarg>
24#include <ctime>
25#include <unistd.h>
26
27using namespace std;
28
29#define FD_SET_TYPE
30
31// Globals
32#define AW_GAUGE_SIZE        40 // length of gauge display (in characters)
33#define AW_GAUGE_GRANULARITY 1000 // how fine the gauge is transported to status (no of steps) [old value = 255]
34
35#define AW_GAUGE_PERCENT(pc) ((pc)*AW_GAUGE_GRANULARITY/100)
36
37#define AW_STATUS_KILL_DELAY       4000             // in ms
38#define AW_STATUS_LISTEN_DELAY     300              // in ms
39#define AW_STATUS_HIDE_DELAY       60               // in sec
40#define AW_STATUS_PIPE_CHECK_DELAY 1000*2           // in ms (a pipe check every 2 seconds)
41
42#define AWAR_STATUS         "tmp/status/"
43#define AWAR_STATUS_TITLE   AWAR_STATUS "title"
44#define AWAR_STATUS_TEXT    AWAR_STATUS "text"
45#define AWAR_STATUS_GAUGE   AWAR_STATUS "gauge"
46#define AWAR_STATUS_ELAPSED AWAR_STATUS "elapsed"
47
48#define AW_MESSAGE_LINES        500
49
50#if defined(DEBUG)
51
52// ARB_LOGGING should always be undefined in SVN version!
53// #define ARB_LOGGING
54// #define TRACE_STATUS // // // enable debug output for status window (which runs forked!)
55// #define TRACE_STATUS_MORE // // enable more debug output
56// #define PIPE_DEBUGGING // // enable debug output for pipes (for write commands)
57
58#endif // DEBUG
59
60enum StatusCommand {
61    // messages send from status-process to main-process :
62    AW_STATUS_OK    = 0,
63    AW_STATUS_ABORT = 1,
64    // messages send from main-process to status-process :
65    AW_STATUS_CMD_INIT,
66    AW_STATUS_CMD_OPEN,
67    AW_STATUS_CMD_CLOSE,
68    AW_STATUS_CMD_NEW_TITLE,
69    AW_STATUS_CMD_TEXT,
70    AW_STATUS_CMD_GAUGE,
71    AW_STATUS_CMD_MESSAGE
72};
73
74#define AW_EST_BUFFER 5
75
76struct aw_stg_struct {
77    int        fd_to[2];
78    int        fd_from[2];
79    bool       mode;
80    int        hide;
81    int        hide_delay;                          // in seconds
82    pid_t      pid;
83    bool       is_child;                            // true in status window process
84    int        pipe_broken;
85    int        err_no;
86    AW_window *aws;
87    AW_window *awm;
88    bool       status_initialized;
89    char      *lines[AW_MESSAGE_LINES];
90    bool       need_refresh;                        // if true -> message list needs to refresh
91    time_t     last_refresh_time;
92    time_t     last_message_time;
93    int        local_message;
94    time_t     last_start;                          // time of last status start
95    long       last_est_count;
96    long       last_estimation[AW_EST_BUFFER];
97    long       last_used_est;
98};
99
100static aw_stg_struct aw_stg = {
101    { 0, 0 },                                       // fd_to
102    { 0, 0 },                                       // fd_from
103    AW_STATUS_OK,                                   // mode
104    0,                                              // hide
105    0,                                              // hide_delay
106    0,                                              // pid
107    false,                                          // is_child
108    0,                                              // pipe_broken
109    0,                                              // errno
110    0,                                              // aws
111    0,                                              // awm
112    false,                                          // status_initialized
113    { 0, 0, 0 },                                    // lines
114    false,                                          // need_refresh
115    0,                                              // last_refresh_time
116    0,                                              // last_message_time
117    0,                                              // local_message
118    0,                                              // last_start
119    0,                                              // last_est_count
120    { 0 },                                          // last_estimation
121    -1,                                             // last_used_est
122};
123
124// timeouts :
125
126#define POLL_TIMEOUT 0         // idle wait POLL_TIMEOUT microseconds before returning EOF when polling
127
128#if defined(DEBUG)
129#define WRITE_TIMEOUT 1000      // 1 second for debug version (short because it always reaches timeout inside debugger)
130#else
131#define WRITE_TIMEOUT 10000     // 10 seconds for release
132#endif // DEBUG
133
134static void mark_pipe_broken(int err_no) {
135#if defined(PIPE_DEBUGGING)
136    if (aw_stg.pipe_broken != 0) {
137        fprintf(stderr,
138                "Pipe already broken in mark_pipe_broken(); pipe_broken=%i aw_stg.errno=%i errno=%i\n",
139                aw_stg.pipe_broken, aw_stg.err_no, err_no);
140    }
141
142    fprintf(stderr, "Marking pipe as broken (errno=%i)\n", err_no);
143#endif // PIPE_DEBUGGING
144
145    aw_stg.err_no       = err_no;
146    aw_stg.pipe_broken = 1;
147
148    static bool error_shown = false;
149    if (!error_shown) {
150        fprintf(stderr,
151                "******************************************************************\n"
152                "The connection to the status window was blocked unexpectedly!\n"
153                "This happens if you run the program from inside the debugger\n"
154                "or when the process is blocked longer than %5.2f seconds.\n"
155                "Further communication with the status window is suppressed.\n"
156                "******************************************************************\n"
157                , WRITE_TIMEOUT/1000.0);
158    }
159}
160
161static ssize_t safe_write(int fd, const char *buf, int count) {
162    if (aw_stg.pipe_broken != 0) {
163#if defined(PIPE_DEBUGGING)
164        fprintf(stderr, "pipe is broken -- avoiding write of %i bytes\n", count);
165#endif // PIPE_DEBUGGING
166        return -1;
167    }
168
169    aw_assert(count>0); // write nothing - bad idea
170
171    ssize_t result = -1;
172    {
173        fd_set         set;
174        struct timeval timeout;
175        timeout.tv_sec  = WRITE_TIMEOUT/1000;
176        timeout.tv_usec = WRITE_TIMEOUT%1000;
177
178        FD_ZERO(&set);
179        FD_SET(fd, &set);
180
181        int sel_res = select(fd+1, 0, &set, 0, &timeout);
182
183        if (sel_res == -1) {
184            fprintf(stderr, "select (before write) returned error (errno=%i)\n", errno);
185            exit(EXIT_FAILURE);
186        }
187
188        bool pipe_would_block = !FD_ISSET(fd, &set);
189
190#if defined(PIPE_DEBUGGING)
191        fprintf(stderr, "select returned %i, pipe_would_block=%i (errno=%i)\n",
192                sel_res, int(pipe_would_block), errno);
193
194        if (pipe_would_block) {
195            fprintf(stderr, "  Avoiding to write to pipe (because it would block!)\n");
196        }
197        else {
198            fprintf(stderr, "  Write %i bytes to pipe.\n", count);
199        }
200#endif // PIPE_DEBUGGING
201
202        if (!pipe_would_block) {
203            result = write(fd, buf, count);
204        }
205    }
206
207    if (result<0) {
208        mark_pipe_broken(errno);
209    }
210    else if (result != count) {
211#if defined(PIPE_DEBUGGING)
212        fprintf(stderr, "write wrote %i bytes instead of %i as requested.\n", result, count);
213#endif // PIPE_DEBUGGING
214        mark_pipe_broken(0);
215    }
216
217    return result;
218}
219
220static void aw_status_write(int fd, int cmd) {
221    char buf = cmd;
222    safe_write(fd, &buf, 1);
223}
224
225static int aw_status_read_byte(int fd, int poll_flag)
226    /* read one byte from the pipe,
227     * if poll ==1 then don't wait for any data, but return EOF */
228{
229    int erg;
230    unsigned char buffer[2];
231
232    if (poll_flag) {
233        fd_set         set;
234        struct timeval timeout;
235        timeout.tv_sec  = POLL_TIMEOUT/1000;
236        timeout.tv_usec = POLL_TIMEOUT%1000;
237
238        FD_ZERO (&set);
239        FD_SET (fd, &set);
240
241        erg = select(FD_SETSIZE, FD_SET_TYPE &set, NULL, NULL, &timeout);
242        if (erg == 0) return EOF;
243    }
244    erg = read(fd, (char *)&(buffer[0]), 1);
245    if (erg<=0) {
246        //      process died
247        fprintf(stderr, "father died, now i kill myself\n");
248        exit(EXIT_FAILURE);
249    }
250    return buffer[0];
251}
252
253static int aw_status_read_int(int fd, int poll_flag) {
254    /* read one integer from the pipe,
255     * if poll ==1 then don't wait for any data, but return EOF */
256
257    int erg;
258
259    if (poll_flag) {
260        fd_set set;
261        struct timeval timeout;
262        timeout.tv_sec  = POLL_TIMEOUT/1000;
263        timeout.tv_usec = POLL_TIMEOUT%1000;
264
265        FD_ZERO (&set);
266        FD_SET (fd, &set);
267
268        erg = select(FD_SETSIZE, FD_SET_TYPE &set, NULL, NULL, &timeout);
269        if (erg == 0) return EOF;
270    }
271    union {
272        unsigned char buffer[sizeof(int)+1];
273        int as_int;
274    } input;
275
276    erg = read(fd, input.buffer, sizeof(int));
277    if (erg<=0) { // process died
278        fprintf(stderr, "father died, now i kill myself\n");
279        exit(EXIT_FAILURE);
280    }
281    return input.as_int;
282}
283
284static int aw_status_read_command(int fd, int poll_flag, char*& str, int *gaugePtr = 0)
285{
286    char buffer[1024];
287    int  cmd = aw_status_read_byte(fd, poll_flag);
288
289    if (cmd == AW_STATUS_CMD_TEXT ||
290            cmd == AW_STATUS_CMD_OPEN ||
291            cmd == AW_STATUS_CMD_NEW_TITLE ||
292            cmd == AW_STATUS_CMD_MESSAGE) {
293        char *p = buffer;
294        int c;
295        for (
296             c = aw_status_read_byte(fd, 0);
297             c;
298             c = aw_status_read_byte(fd, 0)) {
299            *(p++) = c;
300        }
301        *(p++) = c;
302
303        str = strdup(buffer);
304    }
305    else if (cmd == AW_STATUS_CMD_GAUGE) {
306        int gauge = aw_status_read_int(fd, 0);
307        if (gaugePtr) *gaugePtr = gauge;
308
309        char *p = buffer;
310        int   i = 0;
311
312        int rough_gauge = (gauge*AW_GAUGE_SIZE)/AW_GAUGE_GRANULARITY;
313        for (; i<rough_gauge && i<AW_GAUGE_SIZE; ++i) *p++ = '*';
314        for (; i<AW_GAUGE_SIZE; ++i) *p++ = '-';
315
316        if (rough_gauge<AW_GAUGE_SIZE) {
317            int fine_gauge = (gauge*AW_GAUGE_SIZE*4)/AW_GAUGE_GRANULARITY;
318            buffer[rough_gauge]  = "-\\|/"[fine_gauge%4];
319        }
320
321        *= 0;
322        str = strdup(buffer);
323    }
324    else {
325        str = 0;
326    }
327    return cmd;
328}
329
330static void aw_status_check_pipe()
331{
332    if (getppid() <= 1) {
333#if defined(TRACE_STATUS)
334        fprintf(stderr, "Terminating status process.\n");
335#endif // TRACE_STATUS
336        exit(EXIT_FAILURE);
337    }
338}
339
340static void aw_status_wait_for_open(int fd)
341{
342    char *str = 0;
343    int cmd;
344    int erg;
345
346    for (cmd = 0; cmd != AW_STATUS_CMD_INIT;) {
347        for (erg = 0; !erg;) {
348            struct timeval timeout;
349            timeout.tv_sec  = AW_STATUS_PIPE_CHECK_DELAY / 1000;
350            timeout.tv_usec = AW_STATUS_PIPE_CHECK_DELAY % 1000;
351
352            fd_set set;
353
354            FD_ZERO (&set);
355            FD_SET (fd, &set);
356
357#if defined(TRACE_STATUS)
358            fprintf(stderr, "Waiting for status open command..\n"); fflush(stderr);
359#endif // TRACE_STATUS
360            erg = select(FD_SETSIZE, FD_SET_TYPE &set, NULL, NULL, &timeout);
361            if (!erg) aw_status_check_pipe();   // time out
362        }
363        free(str);
364        cmd = aw_status_read_command(fd, 0, str);
365    }
366    aw_stg.mode = AW_STATUS_OK;
367    free(str);
368
369#if defined(TRACE_STATUS)
370    fprintf(stderr, "OK got status open command!\n"); fflush(stderr);
371#endif // TRACE_STATUS
372}
373
374
375static unsigned aw_status_timer_hide_event(AW_root *) {
376    if (aw_stg.hide) {
377        aw_stg.aws->show();
378        aw_stg.hide = 0;
379    }
380    return 0; // do not recall
381}
382
383static void aw_status_hide(AW_window *aws)
384{
385    aw_stg.hide = 1;
386    aws->hide();
387
388    // install timer event
389    aws->get_root()->add_timed_callback(aw_stg.hide_delay*1000, makeTimedCallback(aw_status_timer_hide_event));
390
391    // increase hide delay for next press of hide button
392    if (aw_stg.hide_delay < (60*60)) { // max hide delay is 1 hour
393        aw_stg.hide_delay *= 3; // initial: 60sec -> 3min -> 9min -> 27min -> 60min (max)
394    }
395    else {
396        aw_stg.hide_delay = 60*60;
397    }
398}
399
400
401static unsigned aw_status_timer_event(AW_root *awr)
402{
403#if defined(TRACE_STATUS_MORE)
404    fprintf(stderr, "in aw_status_timer_event\n"); fflush(stdout);
405#endif // TRACE_STATUS_MORE
406    if (aw_stg.mode == AW_STATUS_ABORT) {
407        int action = aw_question(NULL,
408                                 "Couldn't quit properly in time.\n"
409                                 "Now you can either\n"
410                                 "- wait again (recommended),\n"
411                                 "- kill the whole application(!) or\n"
412                                 "- continue.",
413                                 "Wait again,Kill application!,Continue");
414
415        switch (action) {
416            case 0:
417                break;
418            case 1: {
419                char buf[255];
420                sprintf(buf, "kill -9 %i", aw_stg.pid);
421                aw_message_if(GBK_system(buf));
422                exit(EXIT_SUCCESS);
423            }
424            case 2: {
425                char *title    = awr->awar(AWAR_STATUS_TITLE)->read_string();
426                char *subtitle = awr->awar(AWAR_STATUS_TEXT)->read_string();
427
428                aw_message(GBS_global_string("If you think the process should be made abortable,\n"
429                                             "please send the following information to devel@arb-home.de:\n"
430                                             "\n"
431                                             "Calculation not abortable from status window.\n"
432                                             "Title:    %s\n"
433                                             "Subtitle: %s\n",
434                                             title, subtitle));
435                aw_stg.mode = AW_STATUS_OK;
436
437                free(subtitle);
438                free(title);
439                break;
440            }
441        }
442    }
443    return 0; // do not recall
444}
445
446static void aw_status_kill(AW_window *aws)
447{
448    if (aw_stg.mode == AW_STATUS_ABORT) {
449        aw_status_timer_event(aws->get_root());
450        if (aw_stg.mode == AW_STATUS_OK) { // continue
451            return;
452        }
453    }
454    else {
455        if (!aw_ask_sure("aw_status_kill", "Are you sure to abort running calculation?")) {
456            return; // don't abort
457        }
458        aw_stg.mode = AW_STATUS_ABORT;
459    }
460    aw_status_write(aw_stg.fd_from[1], aw_stg.mode);
461
462    if (aw_stg.mode == AW_STATUS_ABORT) {
463#if defined(TRACE_STATUS_MORE)
464        fprintf(stderr, "add aw_status_timer_event with delay = %i\n", AW_STATUS_KILL_DELAY); fflush(stdout);
465#endif // TRACE_STATUS_MORE
466        aws->get_root()->add_timed_callback(AW_STATUS_KILL_DELAY, makeTimedCallback(aw_status_timer_event)); // install timer event
467    }
468}
469
470static void aw_refresh_tmp_message_display(AW_root *awr) {
471    GBS_strstruct *stru = GBS_stropen(AW_MESSAGE_LINES*60); // guessed size
472
473    for (int i = AW_MESSAGE_LINES-1; i>=0; i--) {
474        if (aw_stg.lines[i]) {
475            GBS_strcat(stru, aw_stg.lines[i]);
476            GBS_chrcat(stru, '\n');
477        }
478    };
479
480    char *str = GBS_strclose(stru);
481    awr->awar(AWAR_ERROR_MESSAGES)->write_string(str);
482    free(str);
483
484    aw_stg.need_refresh      = false;
485    aw_stg.last_refresh_time = aw_stg.last_message_time;
486}
487
488static void aw_insert_message_in_tmp_message_delayed(const char *message) {
489    free(aw_stg.lines[0]);
490    for (int i = 1; i< AW_MESSAGE_LINES; i++) {
491        aw_stg.lines[i-1] = aw_stg.lines[i];
492    };
493
494    time_t      t    = time(0);
495    struct tm  *lt   = localtime(&t);
496    const char *lf   = strchr(message, '\n');
497    char       *copy = 0;
498
499    if (lf) { // contains linefeeds
500        const int indentation = 10;
501        int       count       = 1;
502
503        while (lf) { lf = strchr(lf+1, '\n'); ++count; }
504
505        int newsize = strlen(message)+count*indentation+1;
506        copy        = (char*)malloc(newsize);
507
508        lf       = strchr(message, '\n');
509        char *cp = copy;
510        while (lf) {
511            int len  = lf-message;
512            memcpy(cp, message, len+1);
513            cp      += len+1;
514            memset(cp, ' ', indentation);
515            cp      += indentation;
516
517            message = lf+1;
518            lf      = strchr(message, '\n');
519        }
520
521        strcpy(cp, message);
522
523        message = copy;
524    }
525
526    aw_stg.lines[AW_MESSAGE_LINES-1] = GBS_global_string_copy("%02i:%02i.%02i  %s",
527                                                              lt->tm_hour, lt->tm_min, lt->tm_sec,
528                                                              message);
529    aw_stg.last_message_time         = t;
530    free(copy);
531
532    aw_stg.need_refresh = true;
533}
534
535static void aw_insert_message_in_tmp_message(AW_root *awr, const char *message) {
536    aw_insert_message_in_tmp_message_delayed(message);
537    aw_refresh_tmp_message_display(awr);
538}
539
540inline const char *sec2disp(long seconds) {
541    static char buffer[50];
542
543    if (seconds<0) seconds = 0;
544    if (seconds<100) {
545        sprintf(buffer, "%li sec", seconds);
546    }
547    else {
548        long minutes = (seconds+30)/60;
549
550        if (minutes<60) {
551            sprintf(buffer, "%li min", minutes);
552        }
553        else {
554            long hours = minutes/60;
555            minutes    = minutes%60;
556
557            sprintf(buffer, "%lih:%02li min", hours, minutes);
558        }
559    }
560    return buffer;
561}
562
563#ifdef ARB_LOGGING
564static void aw_status_append_to_log(const char* str)
565{
566    static const char *logname = 0;
567    if (!logname) {
568        logname = GBS_global_string_copy("%s/arb.log", GB_getenvHOME());
569    }
570
571    int fd           = open(logname, O_WRONLY|O_APPEND);
572    if (fd == -1) fd = open(logname, O_CREAT|O_WRONLY|O_APPEND, S_IWUSR | S_IRUSR);
573    if (fd == -1) return;
574
575    write(fd, str, strlen(str));
576    write(fd, "\n", 1);
577    close(fd);
578}
579#endif
580
581
582static unsigned aw_status_timer_listen_event(AW_root *awr)
583{
584    static int  delay      = AW_STATUS_LISTEN_DELAY;
585    int         cmd;
586    char       *str        = 0;
587    int         gaugeValue = 0;
588
589#if defined(TRACE_STATUS_MORE)
590    fprintf(stderr, "in aw_status_timer_listen_event (aw_stg.is_child=%i)\n", (int)aw_stg.is_child); fflush(stdout);
591#endif // TRACE_STATUS_MORE
592
593    if (aw_stg.need_refresh && aw_stg.last_refresh_time != aw_stg.last_message_time) {
594        aw_refresh_tmp_message_display(awr); // force refresh each second
595    }
596
597    cmd = aw_status_read_command(aw_stg.fd_to[0], 1, str, &gaugeValue);
598    if (cmd == EOF) {
599        aw_status_check_pipe();
600        delay = delay*3/2+1;      // wait a longer time
601        if (aw_stg.need_refresh) aw_refresh_tmp_message_display(awr); // and do the refresh here
602    }
603    else {
604        delay = delay*2/3+1;       // shorten time  (was *2/3+1)
605    }
606    char *gauge = 0;
607    while (cmd != EOF) {
608        switch (cmd) {
609            case AW_STATUS_CMD_OPEN:
610#if defined(TRACE_STATUS)
611                fprintf(stderr, "received AW_STATUS_CMD_OPEN\n"); fflush(stdout);
612#endif // TRACE_STATUS
613                aw_stg.mode           = AW_STATUS_OK;
614                aw_stg.last_start     = time(0);
615                aw_stg.last_est_count = 0;
616                aw_stg.last_used_est  = -1;
617                aw_stg.aws->show();
618                aw_stg.hide           = 0;
619                aw_stg.hide_delay     = AW_STATUS_HIDE_DELAY;
620
621#if defined(ARB_LOGGING)
622                aw_status_append_to_log("----------------------------");
623#endif // ARB_LOGGING
624                awr->awar(AWAR_STATUS_TITLE)->write_string(str);
625                awr->awar(AWAR_STATUS_GAUGE)->write_string("----------------------------");
626                awr->awar(AWAR_STATUS_TEXT)->write_string("");
627                awr->awar(AWAR_STATUS_ELAPSED)->write_string("");
628                cmd = EOF;
629                free(str);
630                continue;       // break while loop
631
632            case AW_STATUS_CMD_CLOSE:
633#if defined(TRACE_STATUS)
634                fprintf(stderr, "received AW_STATUS_CMD_CLOSE\n"); fflush(stdout);
635#endif // TRACE_STATUS
636                aw_stg.mode = AW_STATUS_OK;
637                aw_stg.aws->hide();
638                break;
639
640            case AW_STATUS_CMD_TEXT:
641#if defined(TRACE_STATUS)
642                fprintf(stderr, "received AW_STATUS_CMD_TEXT\n"); fflush(stdout);
643#endif // TRACE_STATUS
644#if defined(ARB_LOGGING)
645                aw_status_append_to_log(str);
646#endif // ARB_LOGGING
647                awr->awar(AWAR_STATUS_TEXT)->write_string(str);
648                break;
649
650            case AW_STATUS_CMD_NEW_TITLE:
651#if defined(TRACE_STATUS)
652                fprintf(stderr, "received AW_STATUS_CMD_NEW_TITLE\n"); fflush(stdout);
653#endif // TRACE_STATUS
654#if defined(ARB_LOGGING)
655                aw_status_append_to_log(str);
656#endif // ARB_LOGGING
657                awr->awar(AWAR_STATUS_TITLE)->write_string(str);
658                break;
659
660            case AW_STATUS_CMD_GAUGE: {
661#if defined(TRACE_STATUS)
662                fprintf(stderr, "received AW_STATUS_CMD_GAUGE\n"); fflush(stdout);
663#endif // TRACE_STATUS
664
665                reassign(gauge, str);
666
667                if (gaugeValue>0) {
668                    long sec_elapsed   = time(0)-aw_stg.last_start;
669                    long sec_estimated = (sec_elapsed*AW_GAUGE_GRANULARITY)/gaugeValue; // guess overall time
670
671#define PRINT_MIN_GAUGE AW_GAUGE_PERCENT(2)
672
673                    char buffer[200];
674                    int  off  = 0;
675                    off      += sprintf(buffer+off, "%i%%  ", gaugeValue*100/AW_GAUGE_GRANULARITY);
676                    off      += sprintf(buffer+off, "Elapsed: %s  ", sec2disp(sec_elapsed));
677
678                    // rotate estimations
679                    memmove(aw_stg.last_estimation, aw_stg.last_estimation+1, sizeof(aw_stg.last_estimation[0])*(AW_EST_BUFFER-1));
680                    aw_stg.last_estimation[AW_EST_BUFFER-1] = sec_estimated;
681
682                    if (aw_stg.last_est_count == AW_EST_BUFFER) { // now we can estimate!
683                        long used_estimation = 0;
684                        int  i;
685
686                        for (i = 0; i<AW_EST_BUFFER; ++i) {
687                            used_estimation += aw_stg.last_estimation[i];
688                        }
689                        used_estimation /= AW_EST_BUFFER;
690
691                        if (aw_stg.last_used_est != -1) {
692                            long diff = labs(aw_stg.last_used_est-used_estimation);
693                            if (diff <= 1) { used_estimation = aw_stg.last_used_est; } // fake away low frequency changes
694                        }
695
696                        long sec_rest         = used_estimation-sec_elapsed;
697                        off                  += sprintf(buffer+off, "Rest: %s", sec2disp(sec_rest));
698                        aw_stg.last_used_est  = used_estimation;
699                    }
700                    else {
701                        aw_stg.last_est_count++;
702                        off += sprintf(buffer+off, "Rest: ???");
703                    }
704
705                    awr->awar(AWAR_STATUS_ELAPSED)->write_string(buffer);
706
707#if defined(TRACE_STATUS)
708                    fprintf(stderr, "gauge=%i\n", gaugeValue); fflush(stdout);
709#endif // TRACE_STATUS
710                }
711                else if (gaugeValue == 0) { // restart timer
712                    aw_stg.last_start     = time(0);
713                    aw_stg.last_est_count = 0;
714                    aw_stg.last_used_est  = 0;
715#if defined(TRACE_STATUS)
716                    fprintf(stderr, "reset status timer (gauge=0)\n"); fflush(stdout);
717#endif // TRACE_STATUS
718                }
719                break;
720            }
721            case AW_STATUS_CMD_MESSAGE:
722#if defined(TRACE_STATUS)
723                fprintf(stderr, "received AW_STATUS_CMD_MESSAGE\n"); fflush(stdout);
724#endif // TRACE_STATUS
725#if defined(ARB_LOGGING)
726                aw_status_append_to_log(str);
727#endif // ARB_LOGGING
728                aw_stg.awm->activate();
729                aw_insert_message_in_tmp_message_delayed(str);
730                break;
731
732            default:
733                break;
734        }
735        free(str);
736        cmd = aw_status_read_command(aw_stg.fd_to[0], 1, str, &gaugeValue);
737    }
738
739#if defined(TRACE_STATUS_MORE)
740    fprintf(stderr, "exited while loop\n"); fflush(stdout);
741#endif // TRACE_STATUS_MORE
742
743    if (gauge) {
744        awr->awar(AWAR_STATUS_GAUGE)->write_string(gauge);
745        free(gauge);
746    }
747    if (delay>AW_STATUS_LISTEN_DELAY) delay = AW_STATUS_LISTEN_DELAY;
748    else if (delay<0) delay                 = 0;
749#if defined(TRACE_STATUS_MORE)
750    fprintf(stderr, "add aw_status_timer_listen_event with delay = %i\n", delay); fflush(stdout);
751#endif // TRACE_STATUS_MORE
752    aw_assert(delay>0);
753    return delay;
754}
755
756void aw_clear_message_cb(AW_window *aww) {
757    AW_root *awr = aww->get_root();
758    for (int i = 0; i< AW_MESSAGE_LINES; i++) freenull(aw_stg.lines[i]);
759    awr->awar(AWAR_ERROR_MESSAGES)->write_string("");
760}
761
762static void aw_clear_and_hide_message_cb(AW_window *aww) {
763    aw_clear_message_cb(aww);
764    AW_POPDOWN(aww);
765}
766
767static void create_status_awars(AW_root *aw_root) {
768    aw_root->awar_string(AWAR_STATUS_TITLE,   "------------------------------------");
769    aw_root->awar_string(AWAR_STATUS_TEXT,    "");
770    aw_root->awar_string(AWAR_STATUS_GAUGE,   "------------------------------------");
771    aw_root->awar_string(AWAR_STATUS_ELAPSED, "");
772    aw_root->awar_string(AWAR_ERROR_MESSAGES, "");
773}
774
775void aw_initstatus() {
776    // fork status window.
777    // Note: call this function once as early as possible
778   
779    aw_assert(aw_stg.pid == 0);                     // do not init status twice!
780    aw_assert(!AW_root::SINGLETON);                 // aw_initstatus has to be called before constructing AW_root
781    aw_assert(GB_open_DBs() == 0);                  // aw_initstatus has to be called before opening the first ARB-DB
782
783    int error = pipe(aw_stg.fd_to);
784    if (error) GBK_terminate("Cannot create socketpair [1]");
785    error = pipe(aw_stg.fd_from);
786    if (error) GBK_terminate("Cannot create socketpair [2]");
787
788    aw_stg.pid = getpid();
789    GB_install_pid(1);
790    pid_t clientid = fork();
791
792    if (clientid) { // i am the father
793#if defined(TRACE_STATUS)
794        fprintf(stderr, "Forked status! (i am the father)\n"); fflush(stderr);
795#endif // TRACE_STATUS
796        return;
797    }
798    else {
799        GB_install_pid(1);
800
801#if defined(TRACE_STATUS)
802        fprintf(stderr, "Forked status! (i am the child)\n"); fflush(stderr);
803#endif // TRACE_STATUS
804
805        aw_stg.is_child = true; // mark as child
806
807        GB_shell shell;
808        AW_root *aw_root = new AW_root("status.arb", "ARB_STATUS", true, new NullTracker, 0, NULL); // uses_pix_res("icons/ARB_STATUS.xpm"); see ../SOURCE_TOOLS/check_ressources.pl@uses_pix_res
809        create_status_awars(aw_root);
810
811        AW_window_simple *aws = new AW_window_simple;
812        aws->init(aw_root, "STATUS_BOX", "STATUS BOX");
813        aws->load_xfig("status.fig");
814
815        aws->button_length(AW_GAUGE_SIZE+4);
816        aws->at("Titel");
817        aws->create_button(0, AWAR_STATUS_TITLE);
818
819        aws->at("Text");
820        aws->create_button(0, AWAR_STATUS_TEXT);
821
822        aws->at("Gauge");
823        aws->create_button(0, AWAR_STATUS_GAUGE);
824
825        aws->at("elapsed");
826        aws->create_button(0, AWAR_STATUS_ELAPSED);
827
828        aws->at("Hide");
829        aws->callback(aw_status_hide);
830        aws->create_button("HIDE", "Hide", "h");
831
832        aws->at("Kill");
833        aws->callback(aw_status_kill);
834        aws->create_button("ABORT", "Abort", "k");
835
836        aw_stg.hide = 0;
837        aw_stg.aws = aws;
838
839        AW_window_simple *awm = new AW_window_simple;
840        awm->init(aw_root, "MESSAGE_BOX", "MESSAGE BOX");
841        awm->load_xfig("message.fig");
842
843        awm->at("Message");
844        awm->create_text_field(AWAR_ERROR_MESSAGES, 10, 2);
845
846        awm->at("Hide");
847        awm->callback(AW_POPDOWN);
848        awm->create_button("HIDE", "Hide", "h");
849
850        awm->at("Clear");
851        awm->callback(aw_clear_message_cb);
852        awm->create_button("CLEAR", "Clear", "C");
853
854        awm->at("HideNClear");
855        awm->callback(aw_clear_and_hide_message_cb);
856        awm->create_button("HIDE_CLEAR", "Ok", "O");
857
858        aw_stg.awm = awm;
859
860#if defined(TRACE_STATUS)
861        fprintf(stderr, "Created status window!\n"); fflush(stderr);
862#endif // TRACE_STATUS
863
864        aw_status_wait_for_open(aw_stg.fd_to[0]);
865
866        // install callback
867        aws->get_root()->add_timed_callback_never_disabled(30, makeTimedCallback(aw_status_timer_listen_event)); // use short delay for first callback
868
869        // do NOT AWT_install_cb_guards here!
870        aw_root->main_loop();                       // never returns
871    }
872}
873
874static void status_write_cmd_and_text(StatusCommand cmd, const char *text, int textlen) {
875    aw_status_write(aw_stg.fd_to[1], cmd);
876    safe_write(aw_stg.fd_to[1], text, textlen+1);
877}
878static void status_write_cmd_and_text(StatusCommand cmd, const char *text) {
879    if (!text) text = "";
880    status_write_cmd_and_text(cmd, text, strlen(text));
881}
882
883void aw_openstatus(const char *title)
884{
885    aw_stg.mode = AW_STATUS_OK;
886    if (!aw_stg.status_initialized) {
887        aw_stg.status_initialized = true;
888        aw_status_write(aw_stg.fd_to[1], AW_STATUS_CMD_INIT);
889    }
890    status_write_cmd_and_text(AW_STATUS_CMD_OPEN, title);
891}
892
893void aw_closestatus() {
894    aw_status_write(aw_stg.fd_to[1], AW_STATUS_CMD_CLOSE);
895}
896
897bool AW_status(const char *text) {
898    status_write_cmd_and_text(AW_STATUS_CMD_TEXT, text);
899    return AW_status();
900}
901
902bool aw_status_title(const char *new_title) {
903    status_write_cmd_and_text(AW_STATUS_CMD_NEW_TITLE, new_title);
904    return AW_status();
905}
906
907bool AW_status(double gauge) {
908    static int last_val = -1;
909    int        val      = (int)(gauge*AW_GAUGE_GRANULARITY);
910
911    if (val != last_val) {
912        if (val>0 || gauge == 0.0) {            // only write 0 if gauge really is 0 (cause 0 resets the timer)
913            aw_assert(gauge <= 1.0);            // please fix the gauge calculation in caller!
914            aw_status_write(aw_stg.fd_to[1], AW_STATUS_CMD_GAUGE);
915            safe_write(aw_stg.fd_to[1], (char*)&val, sizeof(int));
916        }
917        last_val = val;
918    }
919    return AW_status();
920}
921
922bool AW_status() {
923    char *str = 0;
924    int cmd;
925    if (aw_stg.mode == AW_STATUS_ABORT) return AW_STATUS_ABORT;
926    for (cmd = 0;
927            cmd != EOF;
928            cmd = aw_status_read_command(aw_stg.fd_from[0], 1, str)) {
929        delete str;
930        if (cmd == AW_STATUS_ABORT)     aw_stg.mode = AW_STATUS_ABORT;
931    }
932
933    return aw_stg.mode;
934}
935
936// -------------------
937//      aw_message
938
939void aw_set_local_message() {
940    aw_stg.local_message = 1;
941}
942
943
944void aw_message(const char *msg) {
945    aw_assert(!RUNNING_TEST()); // aw_message hangs when called from nightly builds
946#if defined(DEBUG)
947    printf("aw_message: '%s'\n", msg);
948#endif // DEBUG
949    if (aw_stg.local_message) {
950        aw_insert_message_in_tmp_message(AW_root::SINGLETON, msg);
951    }
952    else {
953        if (!aw_stg.status_initialized) {
954            aw_stg.status_initialized = true;
955            aw_status_write(aw_stg.fd_to[1], AW_STATUS_CMD_INIT);
956        }
957        status_write_cmd_and_text(AW_STATUS_CMD_MESSAGE, msg);
958    }
959}
960
Note: See TracBrowser for help on using the repository browser.