source: branches/stable/WINDOW/AW_status.cxx

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