source: vtkvis/trunk/RenderServer.cpp @ 6226

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

drop int from long int

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