source: trunk/packages/vizservers/vtkvis/VtkRenderServer.cpp @ 3616

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

Remove Rp from vtkvis filenames

  • 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 "VtkRenderServer.h"
28#include "VtkRendererCmd.h"
29#include "VtkRenderer.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.