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

Last change on this file since 3330 was 3330, checked in by gah, 12 years ago

merge (by hand) with Rappture1.2 branch

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