source: trunk/packages/vizservers/vtkvis/RpVtkRenderServer.cpp @ 3615

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

Remove enclosing Rappture namespace from vtkvis. Remove outline and outline
color subcommands from dataset in vtkvis protocol -- they are replaced by the
outline object. Translate heightmap to dataset z plane (client is doing this
right now).

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