source: trunk/packages/vizservers/geovis/RenderServer.cpp @ 4028

Last change on this file since 4028 was 4028, checked in by ldelgass, 10 years ago

Add map layer commands

File size: 14.5 KB
Line 
1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 * Copyright (C) 2013  HUBzero Foundation, LLC
4 *
5 * Author: Leif Delgass <ldelgass@purdue.edu>
6 */
7
8#include <cstdio>
9#include <cstring>
10#include <cstdlib>
11#include <cerrno>
12#include <unistd.h>
13#include <signal.h>
14#include <sys/types.h>
15#include <sys/stat.h>
16#include <fcntl.h>
17#include <sys/time.h>
18#include <sys/times.h>
19
20#include <string>
21#include <sstream>
22
23#include <tcl.h>
24
25#include "Trace.h"
26#include "ReadBuffer.h"
27#include "RenderServer.h"
28#include "RendererCmd.h"
29#include "Renderer.h"
30#include "PPMWriter.h"
31#include "TGAWriter.h"
32#ifdef USE_THREADS
33#include <pthread.h>
34#include "ResponseQueue.h"
35#ifdef USE_READ_THREAD
36#include "CommandQueue.h"
37#endif
38#endif
39#include <md5.h>
40
41using namespace GeoVis;
42
43Stats GeoVis::g_stats;
44
45int GeoVis::g_statsFile = -1; ///< Stats output file descriptor.
46int GeoVis::g_fdIn = STDIN_FILENO; ///< Input file descriptor
47int GeoVis::g_fdOut = STDOUT_FILENO; ///< Output file descriptor
48FILE *GeoVis::g_fOut = stdout; ///< Output file handle
49FILE *GeoVis::g_fLog = NULL; ///< Trace logging file handle
50Renderer *GeoVis::g_renderer = NULL; ///< Main render worker
51ReadBuffer *GeoVis::g_inBufPtr = NULL; ///< Socket read buffer
52#ifdef USE_THREADS
53ResponseQueue *GeoVis::g_outQueue = NULL;
54#ifdef USE_READER_THREAD
55CommandQueue *GeoVis::g_inQueue = NULL;
56#endif
57#endif
58
59#ifdef USE_THREADS
60static void
61queueFrame(ResponseQueue *queue, const unsigned char *imgData)
62{
63#ifdef DEBUG_WRITE_FRAME_FILE
64
65#ifdef RENDER_TARGA
66    writeTGAFile("/tmp/frame.tga",
67                 imgData,
68                 g_renderer->getWindowWidth(),
69                 g_renderer->getWindowHeight(),
70                 TARGA_BYTES_PER_PIXEL);
71#else
72    writeTGAFile("/tmp/frame.tga",
73                 imgData,
74                 g_renderer->getWindowWidth(),
75                 g_renderer->getWindowHeight(),
76                 TARGA_BYTES_PER_PIXEL,
77                 true);
78#endif  /*RENDER_TARGA*/
79
80#else
81 
82#ifdef RENDER_TARGA
83    queueTGA(queue, "nv>image -type image -bytes",
84             imgData,
85             g_renderer->getWindowWidth(),
86             g_renderer->getWindowHeight(),
87             TARGA_BYTES_PER_PIXEL);
88#else
89    queuePPM(queue, "nv>image -type image -bytes",
90             imgData,
91             g_renderer->getWindowWidth(),
92             g_renderer->getWindowHeight());
93#endif  /*RENDER_TARGA*/
94#endif  /*DEBUG_WRITE_FRAME_FILE*/
95}
96
97#else
98
99static void
100writeFrame(int fd, const unsigned char *imgData)
101{
102#ifdef DEBUG_WRITE_FRAME_FILE
103
104#ifdef RENDER_TARGA
105    writeTGAFile("/tmp/frame.tga",
106                 imgData,
107                 g_renderer->getWindowWidth(),
108                 g_renderer->getWindowHeight(),
109                 TARGA_BYTES_PER_PIXEL);
110#else
111    writeTGAFile("/tmp/frame.tga",
112                 imgData,
113                 g_renderer->getWindowWidth(),
114                 g_renderer->getWindowHeight(),
115                 TARGA_BYTES_PER_PIXEL,
116                 true);
117#endif  /*RENDER_TARGA*/
118
119#else
120
121#ifdef RENDER_TARGA
122    writeTGA(fd, "nv>image -type image -bytes",
123             imgData,
124             g_renderer->getWindowWidth(),
125             g_renderer->getWindowHeight(),
126             TARGA_BYTES_PER_PIXEL);
127#else
128    writePPM(fd, "nv>image -type image -bytes",
129             imgData,
130             g_renderer->getWindowWidth(),
131             g_renderer->getWindowHeight());
132#endif  /*RENDER_TARGA*/
133#endif  /*DEBUG_WRITE_FRAME_FILE*/
134}
135#endif /*USE_THREADS*/
136
137static int
138sendAck()
139{
140    std::ostringstream oss;
141    oss << "nv>ok -token " << g_stats.nCommands <<  "\n";
142    int nBytes = oss.str().length();
143
144    TRACE("Sending OK for commands through %lu", g_stats.nCommands);
145#ifdef USE_THREADS
146    queueResponse(oss.str().c_str(), nBytes, Response::VOLATILE, Response::OK);
147#else
148    if (write(g_fdOut, oss.str().c_str(), nBytes) < 0) {
149        ERROR("write failed: %s", strerror(errno));
150        return -1;
151    }
152#endif
153    return 0;
154}
155
156int
157GeoVis::getStatsFile(Tcl_Interp *interp, Tcl_Obj *objPtr)
158{
159    Tcl_DString ds;
160    Tcl_Obj **objv;
161    int objc;
162    int i;
163    char fileName[33];
164    const char *path;
165    md5_state_t state;
166    md5_byte_t digest[16];
167    char *string;
168    int length;
169
170    if ((objPtr == NULL) || (g_statsFile >= 0)) {
171        return g_statsFile;
172    }
173    if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) {
174        return -1;
175    }
176    Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj("pid", 3));
177    Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewIntObj(getpid()));
178    string = Tcl_GetStringFromObj(objPtr, &length);
179
180    md5_init(&state);
181    md5_append(&state, (const md5_byte_t *)string, length);
182    md5_finish(&state, digest);
183    for (i = 0; i < 16; i++) {
184        sprintf(fileName + i * 2, "%02x", digest[i]);
185    }
186    Tcl_DStringInit(&ds);
187    Tcl_DStringAppend(&ds, STATSDIR, -1);
188    Tcl_DStringAppend(&ds, "/", 1);
189    Tcl_DStringAppend(&ds, fileName, 32);
190    path = Tcl_DStringValue(&ds);
191
192    g_statsFile = open(path, O_EXCL | O_CREAT | O_WRONLY, 0600);
193    Tcl_DStringFree(&ds);
194    if (g_statsFile < 0) {
195        ERROR("can't open \"%s\": %s", fileName, strerror(errno));
196        return -1;
197    }
198    return g_statsFile;
199}
200
201int
202GeoVis::writeToStatsFile(int f, const char *s, size_t length)
203{
204    if (f >= 0) {
205        ssize_t numWritten;
206
207        numWritten = write(f, s, length);
208        if (numWritten == (ssize_t)length) {
209            close(dup(f));
210        }
211    }
212    return 0;
213}
214
215static int
216serverStats(int code)
217{
218    double start, finish;
219    char buf[BUFSIZ];
220    Tcl_DString ds;
221    int result;
222    int f;
223
224    {
225        struct timeval tv;
226
227        /* Get ending time.  */
228        gettimeofday(&tv, NULL);
229        finish = CVT2SECS(tv);
230        tv = g_stats.start;
231        start = CVT2SECS(tv);
232    }
233    /*
234     * Session information:
235     *   - Name of render server
236     *   - Process ID
237     *   - Hostname where server is running
238     *   - Start date of session
239     *   - Start date of session in seconds
240     *   - Number of data sets loaded from client
241     *   - Number of data bytes total loaded from client
242     *   - Number of frames returned
243     *   - Number of bytes total returned (in frames)
244     *   - Number of commands received
245     *   - Total elapsed time of all commands
246     *   - Total elapsed time of session
247     *   - Exit code of vizserver
248     *   - User time
249     *   - System time
250     *   - User time of children
251     *   - System time of children
252     */
253
254    Tcl_DStringInit(&ds);
255   
256    Tcl_DStringAppendElement(&ds, "render_stop");
257    /* renderer */
258    Tcl_DStringAppendElement(&ds, "renderer");
259    Tcl_DStringAppendElement(&ds, "geovis");
260    /* pid */
261    Tcl_DStringAppendElement(&ds, "pid");
262    sprintf(buf, "%d", getpid());
263    Tcl_DStringAppendElement(&ds, buf);
264    /* host */
265    Tcl_DStringAppendElement(&ds, "host");
266    gethostname(buf, BUFSIZ-1);
267    buf[BUFSIZ-1] = '\0';
268    Tcl_DStringAppendElement(&ds, buf);
269    /* date */
270    Tcl_DStringAppendElement(&ds, "date");
271    strcpy(buf, ctime(&g_stats.start.tv_sec));
272    buf[strlen(buf) - 1] = '\0';
273    Tcl_DStringAppendElement(&ds, buf);
274    /* date_secs */
275    Tcl_DStringAppendElement(&ds, "date_secs");
276    sprintf(buf, "%ld", g_stats.start.tv_sec);
277    Tcl_DStringAppendElement(&ds, buf);
278    /* num_data_sets */
279    Tcl_DStringAppendElement(&ds, "num_data_sets");
280    sprintf(buf, "%lu", (unsigned long int)g_stats.nDataSets);
281    Tcl_DStringAppendElement(&ds, buf);
282    /* data_set_bytes */
283    Tcl_DStringAppendElement(&ds, "data_set_bytes");
284    sprintf(buf, "%lu", (unsigned long int)g_stats.nDataBytes);
285    Tcl_DStringAppendElement(&ds, buf);
286    /* num_frames */
287    Tcl_DStringAppendElement(&ds, "num_frames");
288    sprintf(buf, "%lu", (unsigned long int)g_stats.nFrames);
289    Tcl_DStringAppendElement(&ds, buf);
290    /* frame_bytes */
291    Tcl_DStringAppendElement(&ds, "frame_bytes");
292    sprintf(buf, "%lu", (unsigned long int)g_stats.nFrameBytes);
293    Tcl_DStringAppendElement(&ds, buf);
294    /* num_commands */
295    Tcl_DStringAppendElement(&ds, "num_commands");
296    sprintf(buf, "%lu", (unsigned long int)g_stats.nCommands);
297    Tcl_DStringAppendElement(&ds, buf);
298    /* cmd_time */
299    Tcl_DStringAppendElement(&ds, "cmd_time");
300    sprintf(buf, "%g", g_stats.cmdTime);
301    Tcl_DStringAppendElement(&ds, buf);
302    /* session_time */
303    Tcl_DStringAppendElement(&ds, "session_time");
304    sprintf(buf, "%g", finish - start);
305    Tcl_DStringAppendElement(&ds, buf);
306    /* status */
307    Tcl_DStringAppendElement(&ds, "status");
308    sprintf(buf, "%d", code);
309    Tcl_DStringAppendElement(&ds, buf);
310    {
311        long clocksPerSec = sysconf(_SC_CLK_TCK);
312        double clockRes = 1.0 / clocksPerSec;
313        struct tms tms;
314
315        memset(&tms, 0, sizeof(tms));
316        times(&tms);
317        /* utime */
318        Tcl_DStringAppendElement(&ds, "utime");
319        sprintf(buf, "%g", tms.tms_utime * clockRes);
320        Tcl_DStringAppendElement(&ds, buf);
321        /* stime */
322        Tcl_DStringAppendElement(&ds, "stime");
323        sprintf(buf, "%g", tms.tms_stime * clockRes);
324        Tcl_DStringAppendElement(&ds, buf);
325        /* cutime */
326        Tcl_DStringAppendElement(&ds, "cutime");
327        sprintf(buf, "%g", tms.tms_cutime * clockRes);
328        Tcl_DStringAppendElement(&ds, buf);
329        /* cstime */
330        Tcl_DStringAppendElement(&ds, "cstime");
331        sprintf(buf, "%g", tms.tms_cstime * clockRes);
332        Tcl_DStringAppendElement(&ds, buf);
333    }
334    Tcl_DStringAppend(&ds, "\n", -1);
335    f = getStatsFile(NULL, NULL);
336    result = writeToStatsFile(f, Tcl_DStringValue(&ds),
337                              Tcl_DStringLength(&ds));
338    close(f);
339    Tcl_DStringFree(&ds);
340    return result;
341}
342
343static void
344initService()
345{
346    TRACE("Enter");
347
348    const char *user = getenv("USER");
349    char *logName = NULL;
350    int logNameLen = 0;
351
352    if (user == NULL) {
353        logNameLen = 19+1;
354        logName = (char *)calloc(logNameLen, sizeof(char));
355        strncpy(logName, "/tmp/geovis_log.txt", logNameLen);
356    } else {
357        logNameLen = 16+strlen(user)+4+1;
358        logName = (char *)calloc(logNameLen, sizeof(char));
359        strncpy(logName, "/tmp/geovis_log_", logNameLen);
360        strncat(logName, user, strlen(user));
361        strncat(logName, ".txt", 4);
362    }
363
364    // open log and map stderr to log file
365    g_fLog = fopen(logName, "w");
366    close(STDERR_FILENO);
367    dup2(fileno(g_fLog), STDERR_FILENO);
368    // flush junk
369    fflush(stderr);
370
371    // clean up malloc'd memory
372    if (logName != NULL) {
373        free(logName);
374    }
375
376    TRACE("Leave");
377}
378
379static void
380exitService()
381{
382    TRACE("Enter");
383
384    serverStats(0);
385
386    // close log file
387    if (g_fLog != NULL) {
388        fclose(g_fLog);
389        g_fLog = NULL;
390    }
391}
392
393#ifdef USE_THREADS
394
395#ifdef USE_READ_THREAD
396static void *
397readerThread(void *clientData)
398{
399    Tcl_Interp *interp = (Tcl_Interp *)clientData;
400
401    TRACE("Starting reader thread");
402
403    queueCommands(interp, NULL, g_inBufPtr);
404
405    return NULL;
406}
407#endif
408
409static void *
410writerThread(void *clientData)
411{
412    ResponseQueue *queue = (ResponseQueue *)clientData;
413
414    TRACE("Starting writer thread");
415    for (;;) {
416        Response *response = queue->dequeue();
417        if (response == NULL)
418            continue;
419        if (fwrite(response->message(), sizeof(unsigned char), response->length(),
420                   g_fOut) != response->length()) {
421            ERROR("short write while trying to write %ld bytes",
422                  response->length());
423        }
424        fflush(g_fOut);
425        TRACE("Wrote response of type %d", response->type());
426        delete response;
427        if (feof(g_fOut))
428            break;
429    }   
430    return NULL;
431}
432
433#endif  /*USE_THREADS*/
434
435int
436main(int argc, char *argv[])
437{
438    // Ignore SIGPIPE.  **Is this needed? **
439    signal(SIGPIPE, SIG_IGN);
440    initService();
441    initLog();
442
443    memset(&g_stats, 0, sizeof(g_stats));
444    gettimeofday(&g_stats.start, NULL);
445
446    TRACE("Starting GeoVis Server");
447
448    /* This synchronizes the client with the server, so that the client
449     * doesn't start writing commands before the server is ready. It could
450     * also be used to supply information about the server (version, memory
451     * size, etc). */
452    fprintf(g_fOut, "GeoVis %s (build %s)\n", GEOVIS_VERSION_STRING, SVN_VERSION);
453    fflush(g_fOut);
454
455    g_renderer = new Renderer();
456    g_inBufPtr = new ReadBuffer(g_fdIn, 1<<12);
457
458    Tcl_Interp *interp = Tcl_CreateInterp();
459
460#ifdef USE_THREADS
461    g_outQueue = new ResponseQueue();
462
463    pthread_t writerThreadId;
464    if (pthread_create(&writerThreadId, NULL, &writerThread, g_outQueue) < 0) {
465        ERROR("Can't create writer thread: %s", strerror(errno));
466    }
467#endif
468    initTcl(interp, NULL);
469
470    osg::ref_ptr<osg::Image> imgData;
471
472    // Start main server loop
473    for (;;) {
474        long timeout = g_renderer->getTimeout();
475        int cmdStatus = processCommands(interp, NULL, g_inBufPtr, g_fdOut, timeout);
476        if (cmdStatus < 0)
477            break;
478
479        if (g_renderer->render()) {
480            TRACE("Rendered new frame");
481            imgData = g_renderer->getRenderedFrame();
482            if (imgData == NULL) {
483                ERROR("Empty image");
484            } else {
485                TRACE("Image: %d x %d", imgData->s(), imgData->t());
486            }
487            if (imgData->s() == g_renderer->getWindowWidth() &&
488                imgData->t() == g_renderer->getWindowHeight()) {
489#ifdef USE_THREADS
490                queueFrame(g_outQueue, imgData->data());
491#else
492                writeFrame(g_fdOut, imgData->data());
493#endif
494            }
495            g_stats.nFrames++;
496            g_stats.nFrameBytes += imgData->s() * imgData->t() * 3;
497        } else {
498            //TRACE("No render required");
499            if (cmdStatus > 1) {
500                sendAck();
501            }
502        }
503
504        double x, y, z;
505        if (g_renderer->getMousePoint(&x, &y, &z)) {
506            // send coords to client
507            size_t length;
508            char mesg[256];
509
510            length = snprintf(mesg, sizeof(mesg),
511                              "nv>dataset coords %g %g %g\n", x, y, z);
512
513            queueResponse(mesg, length, Response::VOLATILE);
514        }
515
516        if (g_inBufPtr->status() == ReadBuffer::ENDFILE)
517            break;
518    }
519#ifdef USE_THREADS
520    // Writer thread is probably blocked on sem_wait, so cancel instead
521    // of joining
522    if (pthread_cancel(writerThreadId) < 0) {
523        ERROR("Can't cancel writer thread: %s", strerror(errno));
524    } else {
525        TRACE("Cancelled writer thread");
526    }
527
528    TRACE("Deleting ResponseQueue");
529    delete g_outQueue;
530    g_outQueue = NULL;
531#endif
532
533    TRACE("Stopping Tcl interpreter");
534    exitTcl(interp);
535    interp = NULL;
536
537    TRACE("Deleting ReadBuffer");
538    delete g_inBufPtr;
539    g_inBufPtr = NULL;
540
541    TRACE("Deleting renderer");
542    delete g_renderer;
543    g_renderer = NULL;
544
545    TRACE("Exiting GeoVis Server");
546
547    closeLog();
548    exitService();
549
550    return 0;
551}
Note: See TracBrowser for help on using the repository browser.