source: tags/ms_r16q3/WINDOW/AW_status.cxx

Last change on this file was 15176, checked in by westram, 8 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.7 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 msgs(AW_MESSAGE_LINES*60);
472
473    for (int i = AW_MESSAGE_LINES-1; i>=0; i--) {
474        if (aw_stg.lines[i]) {
475            msgs.cat(aw_stg.lines[i]);
476            msgs.put('\n');
477        }
478    };
479
480    awr->awar(AWAR_ERROR_MESSAGES)->write_string(msgs.get_data());
481
482    aw_stg.need_refresh      = false;
483    aw_stg.last_refresh_time = aw_stg.last_message_time;
484}
485
486static void aw_insert_message_in_tmp_message_delayed(const char *message) {
487    free(aw_stg.lines[0]);
488    for (int i = 1; i< AW_MESSAGE_LINES; i++) {
489        aw_stg.lines[i-1] = aw_stg.lines[i];
490    };
491
492    time_t      t    = time(0);
493    struct tm  *lt   = localtime(&t);
494    const char *lf   = strchr(message, '\n');
495    char       *copy = 0;
496
497    if (lf) { // contains linefeeds
498        const int indentation = 10;
499        int       count       = 1;
500
501        while (lf) { lf = strchr(lf+1, '\n'); ++count; }
502
503        int newsize = strlen(message)+count*indentation+1;
504        ARB_alloc(copy, newsize);
505
506        lf       = strchr(message, '\n');
507        char *cp = copy;
508        while (lf) {
509            int len  = lf-message;
510            memcpy(cp, message, len+1);
511            cp      += len+1;
512            memset(cp, ' ', indentation);
513            cp      += indentation;
514
515            message = lf+1;
516            lf      = strchr(message, '\n');
517        }
518
519        strcpy(cp, message);
520
521        message = copy;
522    }
523
524    aw_stg.lines[AW_MESSAGE_LINES-1] = GBS_global_string_copy("%02i:%02i.%02i  %s",
525                                                              lt->tm_hour, lt->tm_min, lt->tm_sec,
526                                                              message);
527    aw_stg.last_message_time         = t;
528    free(copy);
529
530    aw_stg.need_refresh = true;
531}
532
533static void aw_insert_message_in_tmp_message(AW_root *awr, const char *message) {
534    aw_insert_message_in_tmp_message_delayed(message);
535    aw_refresh_tmp_message_display(awr);
536}
537
538inline const char *sec2disp(long seconds) {
539    static char buffer[50];
540
541    if (seconds<0) seconds = 0;
542    if (seconds<100) {
543        sprintf(buffer, "%li sec", seconds);
544    }
545    else {
546        long minutes = (seconds+30)/60;
547
548        if (minutes<60) {
549            sprintf(buffer, "%li min", minutes);
550        }
551        else {
552            long hours = minutes/60;
553            minutes    = minutes%60;
554
555            sprintf(buffer, "%lih:%02li min", hours, minutes);
556        }
557    }
558    return buffer;
559}
560
561#ifdef ARB_LOGGING
562static void aw_status_append_to_log(const char* str)
563{
564    static const char *logname = 0;
565    if (!logname) {
566        logname = GBS_global_string_copy("%s/arb.log", GB_getenvHOME());
567    }
568
569    int fd           = open(logname, O_WRONLY|O_APPEND);
570    if (fd == -1) fd = open(logname, O_CREAT|O_WRONLY|O_APPEND, S_IWUSR | S_IRUSR);
571    if (fd == -1) return;
572
573    write(fd, str, strlen(str));
574    write(fd, "\n", 1);
575    close(fd);
576}
577#endif
578
579
580static unsigned aw_status_timer_listen_event(AW_root *awr)
581{
582    static int  delay      = AW_STATUS_LISTEN_DELAY;
583    int         cmd;
584    char       *str        = 0;
585    int         gaugeValue = 0;
586
587#if defined(TRACE_STATUS_MORE)
588    fprintf(stderr, "in aw_status_timer_listen_event (aw_stg.is_child=%i)\n", (int)aw_stg.is_child); fflush(stdout);
589#endif // TRACE_STATUS_MORE
590
591    if (aw_stg.need_refresh && aw_stg.last_refresh_time != aw_stg.last_message_time) {
592        aw_refresh_tmp_message_display(awr); // force refresh each second
593    }
594
595    cmd = aw_status_read_command(aw_stg.fd_to[0], 1, str, &gaugeValue);
596    if (cmd == EOF) {
597        aw_status_check_pipe();
598        delay = delay*3/2+1;      // wait a longer time
599        if (aw_stg.need_refresh) aw_refresh_tmp_message_display(awr); // and do the refresh here
600    }
601    else {
602        delay = delay*2/3+1;       // shorten time  (was *2/3+1)
603    }
604    char *gauge = 0;
605    while (cmd != EOF) {
606        switch (cmd) {
607            case AW_STATUS_CMD_OPEN:
608#if defined(TRACE_STATUS)
609                fprintf(stderr, "received AW_STATUS_CMD_OPEN\n"); fflush(stdout);
610#endif // TRACE_STATUS
611                aw_stg.mode           = AW_STATUS_OK;
612                aw_stg.last_start     = time(0);
613                aw_stg.last_est_count = 0;
614                aw_stg.last_used_est  = -1;
615                aw_stg.aws->show();
616                aw_stg.hide           = 0;
617                aw_stg.hide_delay     = AW_STATUS_HIDE_DELAY;
618
619#if defined(ARB_LOGGING)
620                aw_status_append_to_log("----------------------------");
621#endif // ARB_LOGGING
622                awr->awar(AWAR_STATUS_TITLE)->write_string(str);
623                awr->awar(AWAR_STATUS_GAUGE)->write_string("----------------------------");
624                awr->awar(AWAR_STATUS_TEXT)->write_string("");
625                awr->awar(AWAR_STATUS_ELAPSED)->write_string("");
626                cmd = EOF;
627                free(str);
628                continue;       // break while loop
629
630            case AW_STATUS_CMD_CLOSE:
631#if defined(TRACE_STATUS)
632                fprintf(stderr, "received AW_STATUS_CMD_CLOSE\n"); fflush(stdout);
633#endif // TRACE_STATUS
634                aw_stg.mode = AW_STATUS_OK;
635                aw_stg.aws->hide();
636                break;
637
638            case AW_STATUS_CMD_TEXT:
639#if defined(TRACE_STATUS)
640                fprintf(stderr, "received AW_STATUS_CMD_TEXT\n"); fflush(stdout);
641#endif // TRACE_STATUS
642#if defined(ARB_LOGGING)
643                aw_status_append_to_log(str);
644#endif // ARB_LOGGING
645                awr->awar(AWAR_STATUS_TEXT)->write_string(str);
646                break;
647
648            case AW_STATUS_CMD_NEW_TITLE:
649#if defined(TRACE_STATUS)
650                fprintf(stderr, "received AW_STATUS_CMD_NEW_TITLE\n"); fflush(stdout);
651#endif // TRACE_STATUS
652#if defined(ARB_LOGGING)
653                aw_status_append_to_log(str);
654#endif // ARB_LOGGING
655                awr->awar(AWAR_STATUS_TITLE)->write_string(str);
656                break;
657
658            case AW_STATUS_CMD_GAUGE: {
659#if defined(TRACE_STATUS)
660                fprintf(stderr, "received AW_STATUS_CMD_GAUGE\n"); fflush(stdout);
661#endif // TRACE_STATUS
662
663                reassign(gauge, str);
664
665                if (gaugeValue>0) {
666                    long sec_elapsed   = time(0)-aw_stg.last_start;
667                    long sec_estimated = (sec_elapsed*AW_GAUGE_GRANULARITY)/gaugeValue; // guess overall time
668
669#define PRINT_MIN_GAUGE AW_GAUGE_PERCENT(2)
670
671                    char buffer[200];
672                    int  off  = 0;
673                    off      += sprintf(buffer+off, "%i%%  ", gaugeValue*100/AW_GAUGE_GRANULARITY);
674                    off      += sprintf(buffer+off, "Elapsed: %s  ", sec2disp(sec_elapsed));
675
676                    // rotate estimations
677                    memmove(aw_stg.last_estimation, aw_stg.last_estimation+1, sizeof(aw_stg.last_estimation[0])*(AW_EST_BUFFER-1));
678                    aw_stg.last_estimation[AW_EST_BUFFER-1] = sec_estimated;
679
680                    if (aw_stg.last_est_count == AW_EST_BUFFER) { // now we can estimate!
681                        long used_estimation = 0;
682                        int  i;
683
684                        for (i = 0; i<AW_EST_BUFFER; ++i) {
685                            used_estimation += aw_stg.last_estimation[i];
686                        }
687                        used_estimation /= AW_EST_BUFFER;
688
689                        if (aw_stg.last_used_est != -1) {
690                            long diff = labs(aw_stg.last_used_est-used_estimation);
691                            if (diff <= 1) { used_estimation = aw_stg.last_used_est; } // fake away low frequency changes
692                        }
693
694                        long sec_rest         = used_estimation-sec_elapsed;
695                        off                  += sprintf(buffer+off, "Rest: %s", sec2disp(sec_rest));
696                        aw_stg.last_used_est  = used_estimation;
697                    }
698                    else {
699                        aw_stg.last_est_count++;
700                        off += sprintf(buffer+off, "Rest: ???");
701                    }
702
703                    awr->awar(AWAR_STATUS_ELAPSED)->write_string(buffer);
704
705#if defined(TRACE_STATUS)
706                    fprintf(stderr, "gauge=%i\n", gaugeValue); fflush(stdout);
707#endif // TRACE_STATUS
708                }
709                else if (gaugeValue == 0) { // restart timer
710                    aw_stg.last_start     = time(0);
711                    aw_stg.last_est_count = 0;
712                    aw_stg.last_used_est  = 0;
713#if defined(TRACE_STATUS)
714                    fprintf(stderr, "reset status timer (gauge=0)\n"); fflush(stdout);
715#endif // TRACE_STATUS
716                }
717                break;
718            }
719            case AW_STATUS_CMD_MESSAGE:
720#if defined(TRACE_STATUS)
721                fprintf(stderr, "received AW_STATUS_CMD_MESSAGE\n"); fflush(stdout);
722#endif // TRACE_STATUS
723#if defined(ARB_LOGGING)
724                aw_status_append_to_log(str);
725#endif // ARB_LOGGING
726                aw_stg.awm->activate();
727                aw_insert_message_in_tmp_message_delayed(str);
728                break;
729
730            default:
731                break;
732        }
733        free(str);
734        cmd = aw_status_read_command(aw_stg.fd_to[0], 1, str, &gaugeValue);
735    }
736
737#if defined(TRACE_STATUS_MORE)
738    fprintf(stderr, "exited while loop\n"); fflush(stdout);
739#endif // TRACE_STATUS_MORE
740
741    if (gauge) {
742        awr->awar(AWAR_STATUS_GAUGE)->write_string(gauge);
743        free(gauge);
744    }
745    if (delay>AW_STATUS_LISTEN_DELAY) delay = AW_STATUS_LISTEN_DELAY;
746    else if (delay<0) delay                 = 0;
747#if defined(TRACE_STATUS_MORE)
748    fprintf(stderr, "add aw_status_timer_listen_event with delay = %i\n", delay); fflush(stdout);
749#endif // TRACE_STATUS_MORE
750    aw_assert(delay>0);
751    return delay;
752}
753
754void aw_clear_message_cb(AW_window *aww) {
755    AW_root *awr = aww->get_root();
756    for (int i = 0; i< AW_MESSAGE_LINES; i++) freenull(aw_stg.lines[i]);
757    awr->awar(AWAR_ERROR_MESSAGES)->write_string("");
758}
759
760static void aw_clear_and_hide_message_cb(AW_window *aww) {
761    aw_clear_message_cb(aww);
762    AW_POPDOWN(aww);
763}
764
765static void create_status_awars(AW_root *aw_root) {
766    aw_root->awar_string(AWAR_STATUS_TITLE,   "------------------------------------");
767    aw_root->awar_string(AWAR_STATUS_TEXT,    "");
768    aw_root->awar_string(AWAR_STATUS_GAUGE,   "------------------------------------");
769    aw_root->awar_string(AWAR_STATUS_ELAPSED, "");
770    aw_root->awar_string(AWAR_ERROR_MESSAGES, "");
771}
772
773void aw_initstatus() {
774    // fork status window.
775    // Note: call this function once as early as possible
776   
777    aw_assert(aw_stg.pid == 0);                     // do not init status twice!
778    aw_assert(!AW_root::SINGLETON);                 // aw_initstatus has to be called before constructing AW_root
779    aw_assert(GB_open_DBs() == 0);                  // aw_initstatus has to be called before opening the first ARB-DB
780
781    int error = pipe(aw_stg.fd_to);
782    if (error) GBK_terminate("Cannot create socketpair [1]");
783    error = pipe(aw_stg.fd_from);
784    if (error) GBK_terminate("Cannot create socketpair [2]");
785
786    aw_stg.pid = getpid();
787    GB_install_pid(1);
788    pid_t clientid = fork();
789
790    if (clientid) { // i am the father
791#if defined(TRACE_STATUS)
792        fprintf(stderr, "Forked status! (i am the father)\n"); fflush(stderr);
793#endif // TRACE_STATUS
794        return;
795    }
796    else {
797        GB_install_pid(1);
798
799#if defined(TRACE_STATUS)
800        fprintf(stderr, "Forked status! (i am the child)\n"); fflush(stderr);
801#endif // TRACE_STATUS
802
803        aw_stg.is_child = true; // mark as child
804
805        GB_shell shell;
806        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_resources.pl@uses_pix_res
807        create_status_awars(aw_root);
808
809        AW_window_simple *aws = new AW_window_simple;
810        aws->init(aw_root, "STATUS_BOX", "STATUS BOX");
811        aws->load_xfig("status.fig");
812
813        aws->button_length(AW_GAUGE_SIZE+4);
814        aws->at("Titel");
815        aws->create_button(0, AWAR_STATUS_TITLE);
816
817        aws->at("Text");
818        aws->create_button(0, AWAR_STATUS_TEXT);
819
820        aws->at("Gauge");
821        aws->create_button(0, AWAR_STATUS_GAUGE);
822
823        aws->at("elapsed");
824        aws->create_button(0, AWAR_STATUS_ELAPSED);
825
826        aws->at("Hide");
827        aws->callback(aw_status_hide);
828        aws->create_button("HIDE", "Hide", "h");
829
830        aws->at("Kill");
831        aws->callback(aw_status_kill);
832        aws->create_button("ABORT", "Abort", "k");
833
834        aw_stg.hide = 0;
835        aw_stg.aws = aws;
836
837        AW_window_simple *awm = new AW_window_simple;
838        awm->init(aw_root, "MESSAGE_BOX", "MESSAGE BOX");
839        awm->load_xfig("message.fig");
840
841        awm->at("Message");
842        awm->create_text_field(AWAR_ERROR_MESSAGES, 10, 2);
843
844        awm->at("Hide");
845        awm->callback(AW_POPDOWN);
846        awm->create_button("HIDE", "Hide", "h");
847
848        awm->at("Clear");
849        awm->callback(aw_clear_message_cb);
850        awm->create_button("CLEAR", "Clear", "C");
851
852        awm->at("HideNClear");
853        awm->callback(aw_clear_and_hide_message_cb);
854        awm->create_button("HIDE_CLEAR", "Ok", "O");
855
856        aw_stg.awm = awm;
857
858#if defined(TRACE_STATUS)
859        fprintf(stderr, "Created status window!\n"); fflush(stderr);
860#endif // TRACE_STATUS
861
862        aw_status_wait_for_open(aw_stg.fd_to[0]);
863
864        // install callback
865        aws->get_root()->add_timed_callback_never_disabled(30, makeTimedCallback(aw_status_timer_listen_event)); // use short delay for first callback
866
867        // do NOT AWT_install_cb_guards here!
868        aw_root->main_loop();                       // never returns
869    }
870}
871
872static void status_write_cmd_and_text(StatusCommand cmd, const char *text, int textlen) {
873    aw_status_write(aw_stg.fd_to[1], cmd);
874    safe_write(aw_stg.fd_to[1], text, textlen+1);
875}
876static void status_write_cmd_and_text(StatusCommand cmd, const char *text) {
877    if (!text) text = "";
878    status_write_cmd_and_text(cmd, text, strlen(text));
879}
880
881void aw_openstatus(const char *title)
882{
883    aw_stg.mode = AW_STATUS_OK;
884    if (!aw_stg.status_initialized) {
885        aw_stg.status_initialized = true;
886        aw_status_write(aw_stg.fd_to[1], AW_STATUS_CMD_INIT);
887    }
888    status_write_cmd_and_text(AW_STATUS_CMD_OPEN, title);
889}
890
891void aw_closestatus() {
892    aw_status_write(aw_stg.fd_to[1], AW_STATUS_CMD_CLOSE);
893}
894
895bool AW_status(const char *text) {
896    status_write_cmd_and_text(AW_STATUS_CMD_TEXT, text);
897    return AW_status();
898}
899
900bool aw_status_title(const char *new_title) {
901    status_write_cmd_and_text(AW_STATUS_CMD_NEW_TITLE, new_title);
902    return AW_status();
903}
904
905bool AW_status(double gauge) {
906    static int last_val = -1;
907    int        val      = (int)(gauge*AW_GAUGE_GRANULARITY);
908
909    if (val != last_val) {
910        if (val>0 || gauge == 0.0) {            // only write 0 if gauge really is 0 (cause 0 resets the timer)
911            aw_assert(gauge <= 1.0);            // please fix the gauge calculation in caller!
912            aw_status_write(aw_stg.fd_to[1], AW_STATUS_CMD_GAUGE);
913            safe_write(aw_stg.fd_to[1], (char*)&val, sizeof(int));
914        }
915        last_val = val;
916    }
917    return AW_status();
918}
919
920bool AW_status() {
921    char *str = 0;
922    int cmd;
923    if (aw_stg.mode == AW_STATUS_ABORT) return AW_STATUS_ABORT;
924    for (cmd = 0;
925            cmd != EOF;
926            cmd = aw_status_read_command(aw_stg.fd_from[0], 1, str)) {
927        delete str;
928        if (cmd == AW_STATUS_ABORT)     aw_stg.mode = AW_STATUS_ABORT;
929    }
930
931    return aw_stg.mode;
932}
933
934// -------------------
935//      aw_message
936
937void aw_set_local_message() {
938    aw_stg.local_message = 1;
939}
940
941
942void aw_message(const char *msg) {
943    aw_assert(!RUNNING_TEST()); // aw_message hangs when called from nightly builds
944#if defined(DEBUG)
945    printf("aw_message: '%s'\n", msg);
946#endif // DEBUG
947    if (aw_stg.local_message) {
948        aw_insert_message_in_tmp_message(AW_root::SINGLETON, msg);
949    }
950    else {
951        if (!aw_stg.status_initialized) {
952            aw_stg.status_initialized = true;
953            aw_status_write(aw_stg.fd_to[1], AW_STATUS_CMD_INIT);
954        }
955        status_write_cmd_and_text(AW_STATUS_CMD_MESSAGE, msg);
956    }
957}
958
Note: See TracBrowser for help on using the repository browser.