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

Last change on this file since 4009 was 4009, checked in by ldelgass, 11 years ago

Updates to geovis server: image capture, load earth file

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