Ignore:
Timestamp:
Dec 18, 2014, 8:45:38 PM (5 years ago)
Author:
ldelgass
Message:

Add command line options to set I/O file descriptors, merge some refactoring to
prep for merging threading support.

Location:
nanovis/branches/1.1
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • nanovis/branches/1.1

  • nanovis/branches/1.1/Command.cpp

    r4830 r4874  
    3333
    3434#include <assert.h>
     35#include <errno.h>
    3536#include <stdlib.h>
    3637#include <unistd.h>                     /* Needed for getpid, gethostname,
     
    4445#include <vrmath/Vector3f.h>
    4546
     47#include "nanovisServer.h"
    4648#include "nanovis.h"
     49#include "ReadBuffer.h"
     50#include "Command.h"
    4751#include "CmdProc.h"
    4852#include "FlowCmd.h"
     
    5862#include "Trace.h"
    5963
     64using namespace nv;
    6065using namespace nv::graphics;
    6166using namespace vrmath;
     
    100105static int lastCmdStatus;
    101106
     107ssize_t
     108nv::SocketWrite(const void *bytes, size_t len)
     109{
     110    size_t ofs = 0;
     111    ssize_t bytesWritten;
     112    while ((bytesWritten = write(g_fdOut, (const char *)bytes + ofs, len - ofs)) > 0) {
     113        ofs += bytesWritten;
     114        if (ofs == len)
     115            break;
     116    }
     117    if (bytesWritten < 0) {
     118        ERROR("write: %s", strerror(errno));
     119    }
     120    return bytesWritten;
     121}
     122
    102123bool
    103 GetBooleanFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, bool *boolPtr)
     124nv::SocketRead(char *bytes, size_t len)
     125{
     126    ReadBuffer::BufferStatus status;
     127    status = g_inBufPtr->followingData((unsigned char *)bytes, len);
     128    TRACE("followingData status: %d", status);
     129    return (status == ReadBuffer::OK);
     130}
     131
     132static int
     133ExecuteCommand(Tcl_Interp *interp, Tcl_DString *dsPtr)
     134{
     135    int result;
     136#ifdef WANT_TRACE
     137    char *str = Tcl_DStringValue(dsPtr);
     138    std::string cmd(str);
     139    cmd.erase(cmd.find_last_not_of(" \n\r\t")+1);
     140    TRACE("command %lu: '%s'", g_stats.nCommands+1, cmd.c_str());
     141#endif
     142
     143    result = Tcl_EvalEx(interp, Tcl_DStringValue(dsPtr),
     144                        Tcl_DStringLength(dsPtr),
     145                        TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL);
     146    Tcl_DStringSetLength(dsPtr, 0);
     147
     148    if (result != TCL_OK) {
     149        TRACE("Error: %d", result);
     150    }
     151    return result;
     152}
     153
     154int
     155nv::GetBooleanFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, bool *boolPtr)
    104156{
    105157    int value;
     
    113165
    114166int
    115 GetFloatFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, float *valuePtr)
     167nv::GetFloatFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, float *valuePtr)
    116168{
    117169    double value;
     
    392444 */
    393445int
    394 GetAxisFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *indexPtr)
     446nv::GetAxisFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *indexPtr)
    395447{
    396448    return GetAxis(interp, Tcl_GetString(objPtr), indexPtr);
     
    449501 */
    450502int
    451 GetDataStream(Tcl_Interp *interp, Rappture::Buffer &buf, int nBytes)
    452 {
     503nv::GetDataStream(Tcl_Interp *interp, Rappture::Buffer &buf, int nBytes)
     504{
     505#ifdef USE_NEW_EVENT_LOOP
     506    if (!SocketRead((char *)buf.bytes(), nBytes)) {
     507        return TCL_ERROR;
     508    }
     509    buf.count(nBytes);
     510#else
    453511    char buffer[8096];
    454512
    455     clearerr(NanoVis::stdin);
     513    clearerr(g_fIn);
    456514    while (nBytes > 0) {
    457515        unsigned int chunk;
     
    460518        chunk = (sizeof(buffer) < (unsigned int) nBytes) ?
    461519            sizeof(buffer) : nBytes;
    462         nRead = fread(buffer, sizeof(char), chunk, NanoVis::stdin);
    463         if (ferror(NanoVis::stdin)) {
     520        nRead = fread(buffer, sizeof(char), chunk, g_fIn);
     521        if (ferror(g_fIn)) {
    464522            Tcl_AppendResult(interp, "while reading data stream: ",
    465523                             Tcl_PosixError(interp), (char*)NULL);
    466524            return TCL_ERROR;
    467525        }
    468         if (feof(NanoVis::stdin)) {
     526        if (feof(g_fIn)) {
    469527            Tcl_AppendResult(interp, "premature EOF while reading data stream",
    470528                             (char*)NULL);
     
    474532        nBytes -= nRead;
    475533    }
    476     if (NanoVis::recfile != NULL) {
    477         ssize_t nWritten;
    478 
    479         nWritten = fwrite(buf.bytes(), sizeof(char), buf.size(),
    480                           NanoVis::recfile);
    481         assert(nWritten == (ssize_t)buf.size());
    482         fflush(NanoVis::recfile);
    483     }
     534#endif
    484535    Rappture::Outcome err;
    485536    TRACE("Checking header[%.13s]", buf.bytes());
     
    785836    /* Use the initial client key value pairs as the parts for a generating
    786837     * a unique file name. */
    787     int f = NanoVis::getStatsFile(objv[1]);
     838    int f = getStatsFile(objv[1]);
    788839    if (f < 0) {
    789840        Tcl_AppendResult(interp, "can't open stats file: ",
     
    806857        objPtr = Tcl_NewStringObj("pid", 3);
    807858        Tcl_ListObjAppendElement(interp, listObjPtr, objPtr);
    808         Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewLongObj((long)NanoVis::stats.pid));
     859        Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewLongObj((long)g_stats.pid));
    809860        /* host */
    810861        objPtr = Tcl_NewStringObj("host", 4);
     
    821872    /* date */
    822873    Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj("date", 4));
    823     strcpy(buf, ctime(&NanoVis::stats.start.tv_sec));
     874    strcpy(buf, ctime(&g_stats.start.tv_sec));
    824875    buf[strlen(buf) - 1] = '\0';
    825876    Tcl_ListObjAppendElement(interp, listObjPtr, Tcl_NewStringObj(buf, -1));
     
    828879                             Tcl_NewStringObj("date_secs", 9));
    829880    Tcl_ListObjAppendElement(interp, listObjPtr,
    830                              Tcl_NewLongObj(NanoVis::stats.start.tv_sec));
     881                             Tcl_NewLongObj(g_stats.start.tv_sec));
    831882    /* Client arguments. */
    832883    if (Tcl_ListObjGetElements(interp, objv[1], &numItems, &items) != TCL_OK) {
     
    841892    Tcl_DStringAppend(&ds, "\n", 1);
    842893#ifdef KEEPSTATS
    843     result = NanoVis::writeToStatsFile(f, Tcl_DStringValue(&ds),
    844                                        Tcl_DStringLength(&ds));
     894    result = writeToStatsFile(f, Tcl_DStringValue(&ds),
     895                              Tcl_DStringLength(&ds));
    845896#else
    846897    TRACE("clientinfo: %s", Tcl_DStringValue(&ds));
     
    12991350                    volume->wAxis.min(), volume->wAxis.max(),
    13001351                    Volume::valueMin, Volume::valueMax);
    1301         ssize_t nWritten = write(1, info, (size_t)cmdLength);
    1302         if (nWritten != (ssize_t)cmdLength) {
    1303             ERROR("Short write");
     1352        if (SocketWrite(info, (size_t)cmdLength) != (ssize_t)cmdLength) {
    13041353            return TCL_ERROR;
    13051354        }
     
    21352184}
    21362185
    2137 Tcl_Interp *
    2138 initTcl()
    2139 {
    2140     Tcl_Interp *interp = Tcl_CreateInterp();
     2186/**
     2187 * \brief Execute commands from client in Tcl interpreter
     2188 *
     2189 * In this threaded model, the select call is for event compression.  We
     2190 * want to execute render server commands as long as they keep coming. 
     2191 * This lets us execute a stream of many commands but render once.  This
     2192 * benefits camera movements, screen resizing, and opacity changes
     2193 * (using a slider on the client).  The down side is you don't render
     2194 * until there's a lull in the command stream.  If the client needs an
     2195 * image, it can issue an "imgflush" command.  That breaks us out of the
     2196 * read loop.
     2197 */
     2198int
     2199nv::processCommands(Tcl_Interp *interp,
     2200                    ReadBuffer *inBufPtr, int fdOut)
     2201{
     2202    int ret = 1;
     2203    int status = TCL_OK;
     2204
     2205    Tcl_DString command;
     2206    Tcl_DStringInit(&command);
     2207    fd_set readFds;
     2208    struct timeval tv, *tvPtr;
     2209
     2210    FD_ZERO(&readFds);
     2211    FD_SET(inBufPtr->file(), &readFds);
     2212    tvPtr = NULL;                       /* Wait for the first read. This is so
     2213                                         * that we don't spin when no data is
     2214                                         * available. */
     2215    while (inBufPtr->isLineAvailable() ||
     2216           (select(inBufPtr->file()+1, &readFds, NULL, NULL, tvPtr) > 0)) {
     2217        size_t numBytes;
     2218        unsigned char *buffer;
     2219
     2220        /* A short read is treated as an error here because we assume that we
     2221         * will always get commands line by line. */
     2222        if (inBufPtr->getLine(&numBytes, &buffer) != ReadBuffer::OK) {
     2223            /* Terminate the server if we can't communicate with the client
     2224             * anymore. */
     2225            if (inBufPtr->status() == ReadBuffer::ENDFILE) {
     2226                TRACE("Exiting server on EOF from client");
     2227                return -1;
     2228            } else {
     2229                ERROR("Exiting server, failed to read from client: %s",
     2230                      strerror(errno));
     2231                return -1;
     2232            }
     2233        }
     2234        Tcl_DStringAppend(&command, (char *)buffer, numBytes);
     2235        if (Tcl_CommandComplete(Tcl_DStringValue(&command))) {
     2236            struct timeval start, finish;
     2237            gettimeofday(&start, NULL);
     2238            status = ExecuteCommand(interp, &command);
     2239            gettimeofday(&finish, NULL);
     2240            g_stats.cmdTime += (MSECS_ELAPSED(start, finish) / 1.0e+3);
     2241            g_stats.nCommands++;
     2242            if (status == TCL_BREAK) {
     2243                return 1;               /* This was caused by a "imgflush"
     2244                                         * command. Break out of the read loop
     2245                                         * and allow a new image to be
     2246                                         * rendered. */
     2247            } else { //if (status != TCL_OK) {
     2248                ret = 0;
     2249                if (handleError(interp, status, fdOut) < 0) {
     2250                    return -1;
     2251                }
     2252            }
     2253        }
     2254
     2255        tv.tv_sec = tv.tv_usec = 0L;    /* On successive reads, we break out
     2256                                         * if no data is available. */
     2257        FD_SET(inBufPtr->file(), &readFds);
     2258        tvPtr = &tv;
     2259    }
     2260
     2261    return ret;
     2262}
     2263
     2264/**
     2265 * \brief Send error message to client socket
     2266 */
     2267int
     2268nv::handleError(Tcl_Interp *interp, int status, int fdOut)
     2269{
     2270    const char *string;
     2271    int nBytes;
     2272
     2273    if (status != TCL_OK) {
     2274        string = Tcl_GetVar(interp, "errorInfo", TCL_GLOBAL_ONLY);
     2275        nBytes = strlen(string);
     2276        if (nBytes > 0) {
     2277            TRACE("status=%d errorInfo=(%s)", status, string);
     2278
     2279            std::ostringstream oss;
     2280            oss << "nv>viserror -type internal_error -token " << g_stats.nCommands << " -bytes " << nBytes << "\n" << string;
     2281            std::string ostr = oss.str();
     2282            nBytes = ostr.length();
     2283
     2284            if (write(fdOut, oss.str().c_str(), nBytes) < 0) {
     2285                ERROR("write failed: %s", strerror(errno));
     2286                return -1;
     2287            }
     2288        }
     2289    }
     2290
     2291    std::string msg = getUserMessages();
     2292    nBytes = msg.length();
     2293    if (nBytes > 0) {
     2294        string = msg.c_str();
     2295        TRACE("userError=(%s)", string);
     2296
     2297        std::ostringstream oss;
     2298        oss << "nv>viserror -type error -token " << g_stats.nCommands << " -bytes " << nBytes << "\n" << string;
     2299        std::string ostr = oss.str();
     2300        nBytes = ostr.length();
     2301
     2302        if (write(fdOut, ostr.c_str(), nBytes) < 0) {
     2303            ERROR("write failed: %s", strerror(errno));
     2304            return -1;
     2305        }
     2306        clearUserMessages();
     2307    }
     2308
     2309    return 0;
     2310}
     2311
     2312void
     2313nv::initTcl(Tcl_Interp *interp, ClientData clientData)
     2314{
    21412315    Tcl_MakeSafe(interp);
    21422316
    2143     Tcl_CreateObjCommand(interp, "axis",        AxisCmd,        NULL, NULL);
    2144     Tcl_CreateObjCommand(interp, "camera",      CameraCmd,      NULL, NULL);
    2145     Tcl_CreateObjCommand(interp, "clientinfo",  ClientInfoCmd,  NULL, NULL);
    2146     Tcl_CreateObjCommand(interp, "cutplane",    CutplaneCmd,    NULL, NULL);
    2147     if (FlowCmdInitProc(interp) != TCL_OK) {
    2148         return NULL;
    2149     }
    2150     Tcl_CreateObjCommand(interp, "grid",        GridCmd,        NULL, NULL);
    2151     Tcl_CreateObjCommand(interp, "heightmap",   HeightMapCmd,   NULL, NULL);
    2152     Tcl_CreateObjCommand(interp, "imgflush",    ImageFlushCmd,  NULL, NULL);
    2153     Tcl_CreateObjCommand(interp, "legend",      LegendCmd,      NULL, NULL);
    2154     Tcl_CreateObjCommand(interp, "screen",      ScreenCmd,      NULL, NULL);
    2155     Tcl_CreateObjCommand(interp, "snapshot",    SnapshotCmd,    NULL, NULL);
    2156     Tcl_CreateObjCommand(interp, "transfunc",   TransfuncCmd,   NULL, NULL);
    2157     Tcl_CreateObjCommand(interp, "up",          UpCmd,          NULL, NULL);
    2158     Tcl_CreateObjCommand(interp, "volume",      VolumeCmd,      NULL, NULL);
     2317    Tcl_CreateObjCommand(interp, "axis",        AxisCmd,        clientData, NULL);
     2318    Tcl_CreateObjCommand(interp, "camera",      CameraCmd,      clientData, NULL);
     2319    Tcl_CreateObjCommand(interp, "clientinfo",  ClientInfoCmd,  clientData, NULL);
     2320    Tcl_CreateObjCommand(interp, "cutplane",    CutplaneCmd,    clientData, NULL);
     2321    FlowCmdInitProc(interp, clientData);
     2322    Tcl_CreateObjCommand(interp, "grid",        GridCmd,        clientData, NULL);
     2323    Tcl_CreateObjCommand(interp, "heightmap",   HeightMapCmd,   clientData, NULL);
     2324    Tcl_CreateObjCommand(interp, "imgflush",    ImageFlushCmd,  clientData, NULL);
     2325    Tcl_CreateObjCommand(interp, "legend",      LegendCmd,      clientData, NULL);
     2326    Tcl_CreateObjCommand(interp, "screen",      ScreenCmd,      clientData, NULL);
     2327    Tcl_CreateObjCommand(interp, "snapshot",    SnapshotCmd,    clientData, NULL);
     2328    Tcl_CreateObjCommand(interp, "transfunc",   TransfuncCmd,   clientData, NULL);
     2329    Tcl_CreateObjCommand(interp, "up",          UpCmd,          clientData, NULL);
     2330    Tcl_CreateObjCommand(interp, "volume",      VolumeCmd,      clientData, NULL);
    21592331
    21602332    // create a default transfer function
     
    21632335             Tcl_GetStringResult(interp));
    21642336    }
    2165     return interp;
    2166 }
     2337}
Note: See TracChangeset for help on using the changeset viewer.