Beispiel #1
0
Datei: ftp.c Projekt: breu/wview
static void timerHandler (void *parm)
{
    ULONGLONG   msOffset = radTimeGetMSSinceEpoch ();
    int         rules;
    long long   netOffset;

    //  ... allow for timer latency
    if (ftpWork.msOffset == 0)
    {
        // first time through
        ftpWork.msOffset = msOffset;
        netOffset = 0;
    }
    else
    {
        netOffset = msOffset - ftpWork.msOffset;
        while (netOffset >= 60000LL)
        {
            netOffset -= 60000LL;
        }
    }

    while (ftpWork.msOffset < msOffset)
    {
        ftpWork.msOffset += 60000ULL;
    }

    radProcessTimerStart(ftpWork.timer, (ULONG)(60000LL - netOffset));


    //  ... process the ftp rules
    rules = ftpUtilsSendFiles(ftpWork.ftpId, ftpWork.wviewdir);

    if (rules > 0)
    {
        wvutilsLogEvent (PRI_STATUS, "FTP: %d rules processed", rules);
    }
    else if (rules < 0)
    {
        wvutilsLogEvent (PRI_MEDIUM, "FTP: ftpUtilsSendFiles failed!");
    }

    return;
}
Beispiel #2
0
Datei: cwop.c Projekt: breu/wview
static void processCWOP ()
{
    RADSOCK_ID                          socket;
    time_t                              ntime;
    struct tm                           *gmTime;
    char                                cwopBuffer[256], login[64];
    int                                 length = 0;
    char                                *serv;
    int                                 port;
    volatile WVIEW_MSG_ARCHIVE_NOTIFY   Notify;

    memcpy ((void*)&Notify, (void*)&cwopWork.ArchiveMsg, sizeof(WVIEW_MSG_ARCHIVE_NOTIFY));

    // format the CWOP data
    ntime = time (NULL);
    gmTime = gmtime (&ntime);
    length = sprintf (cwopBuffer, "%s>APRS,TCPXX*,qAX,%s:@", 
                      cwopWork.callSign, cwopWork.callSign);
    length += sprintf (&cwopBuffer[length], "%2.2d%2.2d%2.2dz",
                       gmTime->tm_mday, gmTime->tm_hour, gmTime->tm_min);
    length += sprintf (&cwopBuffer[length], "%s/%s",
                       cwopWork.latitude, cwopWork.longitude);

    // check for any wind registered
    if (Notify.wspeed < 0)
    {
        length += sprintf (&cwopBuffer[length], "_...");
    }
    else
    {
        length += sprintf (&cwopBuffer[length], "_%3.3d", Notify.winddir);
    }
    
    length += sprintf (&cwopBuffer[length], "/%3.3d", Notify.wspeed);
    length += sprintf (&cwopBuffer[length], "g%3.3d", Notify.hiwspeed);

    if (Notify.temp < 0)
    {
        if (((Notify.temp * (-1)) % 10) >= 5)
        {
            Notify.temp -= 10;
        }

        length += sprintf (&cwopBuffer[length], "t-%2.2d", 
                           (Notify.temp * (-1))/10);
    }
    else
    {
        if ((Notify.temp % 10) >= 5)
        {
            Notify.temp += 10;
        }

        length += sprintf (&cwopBuffer[length], "t%3.3d", Notify.temp/10);
    }
    
    length += sprintf (&cwopBuffer[length], "P%3.3d", Notify.rainDay);
    
    if (Notify.humidity >= 0 && Notify.humidity <= 100)
    {
        length += sprintf (&cwopBuffer[length], "h%2.2d", Notify.humidity % 100);
    }
    
    length += sprintf (&cwopBuffer[length], "b%5.5d", 
                       (int)(10 * wvutilsConvertINHGToHPA((float)Notify.altimeter/1000.0)));
    sprintf (&cwopBuffer[length], ".%s", wvutilsCreateCWOPVersion(globalWviewVersionStr));


    // connect to the CWOP server - try the primary then secondary then tertiary
    socket = radSocketClientCreate (cwopWork.server1, cwopWork.portNo1);
    if (socket == NULL)
    {
        statusUpdateMessage("CWOP-connect: failed to connect to server");
        statusIncrementStat(CWOP_STATS_CONNECT_ERRORS);
        wvutilsLogEvent (PRI_MEDIUM, "CWOP-connect: failed to connect to %s:%d",
                         cwopWork.server1, cwopWork.portNo1);
        
        // try the secondary server
        socket = radSocketClientCreate (cwopWork.server2, cwopWork.portNo2);
        if (socket == NULL)
        {
            statusUpdateMessage("CWOP-connect: failed to connect to server");
            statusIncrementStat(CWOP_STATS_CONNECT_ERRORS);
            wvutilsLogEvent (PRI_MEDIUM, "CWOP-connect: failed to connect to %s:%d",
                             cwopWork.server2, cwopWork.portNo2);
        
            // try the tertiary server
            socket = radSocketClientCreate (cwopWork.server3, cwopWork.portNo3);
            if (socket == NULL)
            {
                // we are all out of luck this time!
                statusUpdateMessage("CWOP-connect: failed to connect to server");
                statusIncrementStat(CWOP_STATS_CONNECT_ERRORS);
                wvutilsLogEvent (PRI_MEDIUM, "CWOP-connect: failed to connect to %s:%d",
                                 cwopWork.server3, cwopWork.portNo3);
                radMsgLog (PRI_HIGH, 
                           "CWOP-connect: failed to connect to all 3 APRS servers!");
                return;
            }
            else
            {
                serv = cwopWork.server3;
                port = cwopWork.portNo3;
            }
        }
        else
        {
            serv = cwopWork.server2;
            port = cwopWork.portNo2;
        }
    }
    else
    {
        serv = cwopWork.server1;
        port = cwopWork.portNo1;
    }
    
    // wait 1 second ...
    radUtilsSleep (1000);
    
    // transmit the data 
    sprintf (login, "user %6s pass %d vers %s", 
             cwopWork.callSign, 
             (int)getPasscode(cwopWork.callSign), 
             globalWviewVersionStr);
    length = strlen (login);
    login[length] = 0x0D;           // tack on the CR and LF
    login[length+1] = 0x0A;
    login[length+2] = 0;
    
    if (radSocketWriteExact (socket, login, length + 2) != (length + 2))
    {
        statusUpdateMessage("CWOP-error: failed to login to server");
        radMsgLog (PRI_HIGH, "CWOP-error: %d: failed to login to %s:%d!",
                   errno, serv, port);
        radSocketDestroy (socket);
        return;
    }
    
    // wait 3 seconds ...
    radUtilsSleep (3000);
    
    // write the data record
    wvutilsLogEvent (PRI_STATUS, "CWOP-send: %s", cwopBuffer);

    length = strlen (cwopBuffer);
    cwopBuffer[length] = 0x0D;       // tack on the CR and LF
    cwopBuffer[length+1] = 0x0A;
    cwopBuffer[length+2] = 0;
    if (radSocketWriteExact (socket, cwopBuffer, length + 2) != (length + 2))
    {
        statusUpdateMessage("CWOP-error: failed to write to server");
        radMsgLog (PRI_HIGH, "CWOP-error: %d: failed to write to %s:%d!",
                   errno, serv, port);
        radSocketDestroy (socket);
        return;
    }

    statusIncrementStat(CWOP_STATS_PKTS_SENT);

    // wait 3 more seconds ...
    radUtilsSleep (3000);

    // close connection and cleanup
    radSocketDestroy (socket);

    return;
}
Beispiel #3
0
static void daemonStoreArchiveRecord (ARCHIVE_PKT *newRecord)
{
    float           carryOverRain, carryOverET, sampleRain, tempf;
    int             deltaTime, tempInt;
    USHORT          tempRainBits;

    if (newRecord == NULL)
    {
        radMsgLog (PRI_MEDIUM, "daemonStoreArchiveRecord: record is NULL!");
        return;
    }

    deltaTime = newRecord->dateTime - wviewdWork.archiveDateTime;
    if (deltaTime == 0)
    {
        // discard it, same as previous record
        radMsgLog (PRI_MEDIUM,
                   "daemonStoreArchiveRecord: record has same timestamp as previous!");
        return;
    }
    else if (deltaTime < 0)
    {
        // chunk it, it is just wrong
        radMsgLog (PRI_MEDIUM,
                   "StoreArchiveRecord: record has earlier timestamp than previous (DST change?)");
        return;
    }

    wviewdWork.archiveDateTime = newRecord->dateTime;

    wvutilsLogEvent (PRI_STATUS, "storing record for %4.4d-%2.2d-%2.2d %2.2d:%2.2d",
                     wvutilsGetYear(newRecord->dateTime),
                     wvutilsGetMonth(newRecord->dateTime),
                     wvutilsGetDay(newRecord->dateTime),
                     wvutilsGetHour(newRecord->dateTime),
                     wvutilsGetMin(newRecord->dateTime));

    if (dbsqliteArchiveStoreRecord(newRecord) == ERROR)
    {
        radMsgLog (PRI_MEDIUM, "daemonStoreArchiveRecord: dbsqliteArchiveStoreRecord failed!!!");
        emailAlertSend(ALERT_TYPE_FILE_IO);
        return;
    }

    // if we are running normally (out of init), do normal activities:
    if (wviewdWork.runningFlag)
    {
        // Check to see if a DST change has occured:
        // Note: wvutilsDetectDSTChange can only be called once per process per
        //       DST event.
        if (wvutilsDetectDSTChange() != WVUTILS_DST_NO_CHANGE)
        {
            radMsgLog (PRI_STATUS, 
                       "DST change: scheduling station time update (if supported)");

            // Update the time zone info:
            tzset();

            // Adjust station time:
            stationSyncTime(&wviewdWork);
        }

        // save trace accumulator amounts:
        if (newRecord->value[DATA_INDEX_rain] > ARCHIVE_VALUE_NULL)
        {
            sampleRain = (float)newRecord->value[DATA_INDEX_rain];
            carryOverRain = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_INTERVAL][SENSOR_RAIN]);
            carryOverRain -= sampleRain;
            if (carryOverRain < 0)
            {
                carryOverRain = 0;
            }
        }

        if (newRecord->value[DATA_INDEX_ET] > ARCHIVE_VALUE_NULL)
        {
            carryOverET = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_INTERVAL][SENSOR_ET]);
            carryOverET -= (float)newRecord->value[DATA_INDEX_ET];
            if (carryOverET < 0)
            {
                carryOverET = 0;
            }
        }

        // compute HILOW data:
        computedDataUpdate (&wviewdWork, newRecord);

        // clear for the next archive period (saving trace amounts):
        computedDataClearInterval (&wviewdWork, carryOverRain, carryOverET);

        // compute storm rain:
        if (newRecord->value[DATA_INDEX_rain] > ARCHIVE_VALUE_NULL &&
            newRecord->value[DATA_INDEX_rainRate] > ARCHIVE_VALUE_NULL)
        {
            stormRainUpdate ((float)newRecord->value[DATA_INDEX_rainRate],
                             (float)newRecord->value[DATA_INDEX_rain]);
        }

        // sync to sensors:
        wviewdWork.loopPkt.stormRain  = stormRainGet();
        wviewdWork.loopPkt.stormStart = stormRainGetStartTimeT();
        wviewdWork.loopPkt.dayRain    = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_DAY][SENSOR_RAIN]);
        wviewdWork.loopPkt.monthRain  = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_MONTH][SENSOR_RAIN]);
        wviewdWork.loopPkt.yearRain   = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_YEAR][SENSOR_RAIN]);
        wviewdWork.loopPkt.dayET      = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_DAY][SENSOR_ET]);
        wviewdWork.loopPkt.monthET    = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_MONTH][SENSOR_ET]);
        wviewdWork.loopPkt.yearET     = sensorGetCumulative(&wviewdWork.sensors.sensor[STF_YEAR][SENSOR_ET]);

        // send archive notification:
        stationSendArchiveNotifications (&wviewdWork, sampleRain);
    }

    statusIncrementStat(WVIEW_STATS_ARCHIVE_PKTS_RX);
    return;
}
Beispiel #4
0
static void processNewArchiveRecord (HTML_WORK *work, WVIEW_MSG_ARCHIVE_NOTIFY *armsg)
{
    HISTORY_DATA        data;
    ARCHIVE_PKT         arcRecord;
    int                 currHour, currDay, currMonth, currYear;
    float               gmtOffsetHours;
    int                 startmin, starthour, startday, startmonth, startyear;
    int                 i, DSTFlag;
    int16_t             tempShort;
    time_t              ntime, baseTime;
    struct tm           locTime;
    int                 deltaArchiveIntervals;

    //  ... generate the mesonet file
    htmlgenMesonetFile (work->mgrId, armsg);

    //  ... check to see if a DST change has occured
    DSTFlag = wvutilsDetectDSTChange();
    if (DSTFlag != WVUTILS_DST_NO_CHANGE)
    {
        radMsgLog (PRI_STATUS, "DST change: updating astronomical times for new local time...");

        ntime = time (NULL);
        localtime_r (&ntime, &locTime);
        currDay     = locTime.tm_mday;
        currMonth   = locTime.tm_mon + 1;
        currYear    = locTime.tm_year + 1900;

#ifdef HAVE_TM_GMTOFF
        gmtOffsetHours = locTime.tm_gmtoff/(60.0*60.0);
#else
        gmtOffsetHours = gmtoffset()/(60.0*60.0);
#endif

        //  ... update the sun times:
        sunTimesGetSunRiseSet (currYear, currMonth, currDay,
                               (float)work->mgrId->stationLatitude/10,
                               (float)work->mgrId->stationLongitude/10,
                               RS_TYPE_SUN,
                               &work->mgrId->sunrise,
                               &work->mgrId->sunset);
        sunTimesGetSunRiseSet (currYear, currMonth, currDay,
                               (float)work->mgrId->stationLatitude/10,
                               (float)work->mgrId->stationLongitude/10,
                               RS_TYPE_CIVIL,
                               &work->mgrId->civilrise,
                               &work->mgrId->civilset);
        sunTimesGetSunRiseSet (currYear, currMonth, currDay,
                               (float)work->mgrId->stationLatitude/10,
                               (float)work->mgrId->stationLongitude/10,
                               RS_TYPE_ASTRO,
                               &work->mgrId->astrorise,
                               &work->mgrId->astroset);
        sunTimesGetSunRiseSet (currYear, currMonth, currDay,
                               (float)work->mgrId->stationLatitude/10,
                               (float)work->mgrId->stationLongitude/10,
                               RS_TYPE_MIDDAY,
                               &work->mgrId->midday,
                               &tempShort);
        work->mgrId->dayLength = 
                sunTimesGetDayLength (currYear, currMonth, currDay,
                                      (float)work->mgrId->stationLatitude/10,
                                      (float)work->mgrId->stationLongitude/10);

        GetMoonRiseSetTimes (currYear, currMonth, currDay,
                             (float)gmtOffsetHours,
                             (float)work->mgrId->stationLatitude/10,
                             (float)work->mgrId->stationLongitude/10,
                             &work->mgrId->moonrise,
                             NULL,
                             &work->mgrId->moonset,
                             NULL);

        // restart the generation timer as the change may leave it in limbo
        radProcessTimerStart (work->timer, 5000L);
    }

    baseTime = time (NULL);

    //  ... update our data by adding the new sample

    //// Interval change:

    //  ... determine time to start for last sample
    startmin    = wvutilsGetMin(armsg->dateTime);
    starthour   = wvutilsGetHour(armsg->dateTime);
    startday    = wvutilsGetDay(armsg->dateTime);
    startmonth  = wvutilsGetMonth(armsg->dateTime);
    startyear   = wvutilsGetYear(armsg->dateTime);

    currHour    = starthour;
    currDay     = startday;
    currMonth   = startmonth;
    currYear    = startyear;

    if (work->LastArchiveDateTime != 0)
    {
        // compute the number of archive intervals since the last good record
        deltaArchiveIntervals = armsg->dateTime - work->LastArchiveDateTime;
        deltaArchiveIntervals /= 60;
        if (deltaArchiveIntervals < work->archiveInterval)
        {
            // this is odd, report and bail out
            radMsgLog (PRI_MEDIUM, "processNewArchiveRecord: "
                                   "minutes since last archive record (%d) "
                                   "less than archive interval (%d) - "
                                   "ignoring record...",
                                   deltaArchiveIntervals,
                                   work->archiveInterval);
            return;
        }

        deltaArchiveIntervals /= work->archiveInterval;
    }
    else
    {
        deltaArchiveIntervals = 1;
    }

    work->LastArchiveDateTime = armsg->dateTime;

    if (dbsqliteArchiveGetAverages (work->mgrId->isMetricUnits,
                                    work->archiveInterval,
                                    &data,
                                    armsg->dateTime,
                                    1)
        <= 0)
    {
        // populate history data with ARCHIVE_VALUE_NULL
        for (i = 0; i < DATA_INDEX_MAX(work->isExtendedData); i ++)
        {
            data.values[i] = ARCHIVE_VALUE_NULL;
            data.samples[i] = 0;
        }
        radMsgLog (PRI_MEDIUM, "no data found for sample...");
    }

    wvutilsLogEvent (PRI_STATUS, "Adding %d minute sample for %4.4d-%2.2d-%2.2d %2.2d:%2.2d...", 
                     work->archiveInterval, startyear, startmonth, startday, starthour, startmin);

    //  ... set the archiveAvailable flag for generation...
    work->mgrId->newArchiveMask |= NEW_ARCHIVE_SAMPLE;

    // add the RX check value to the data here
    data.values[DATA_INDEX_rxCheckPercent] = armsg->rxPercent;

    htmlmgrAddSampleValue (work->mgrId, &data, deltaArchiveIntervals);
        
    // generate an ASCII entry in the day's browser file (if enabled)
    if (dbsqliteArchiveGetRecord(armsg->dateTime, &arcRecord) == OK)
    {
        arcrecGenerate(&arcRecord, work->mgrId->isMetricUnits);
    }


    //// Hour change?
    if (work->histLastHour != currHour)
    {
        // determine times to start for last hour:
        ntime = time (NULL);
        localtime_r (&ntime, &locTime);
        locTime.tm_sec   = 0;
        locTime.tm_min   = work->archiveInterval;
        locTime.tm_hour  = currHour;
        locTime.tm_mday  = currDay;
        locTime.tm_mon   = currMonth - 1;
        locTime.tm_year  = currYear - 1900;
        locTime.tm_isdst = -1;

        ntime = mktime (&locTime);
        ntime -= WV_SECONDS_IN_HOUR;
        localtime_r (&ntime, &locTime);

        starthour = locTime.tm_hour;

        if (dbsqliteArchiveGetAverages (work->mgrId->isMetricUnits,
                                        work->archiveInterval,
                                        &data,
                                        ntime,
                                        WV_SECONDS_IN_HOUR/SECONDS_IN_INTERVAL(work->archiveInterval))
            <= 0)
        {
            // populate history data with ARCHIVE_VALUE_NULL
            for (i = 0; i < DATA_INDEX_MAX(work->isExtendedData); i ++)
            {
                data.values[i] = ARCHIVE_VALUE_NULL;
                data.samples[i] = 0;
            }
            radMsgLog (PRI_MEDIUM, "no data found for last hour...");
        }

        wvutilsLogEvent (PRI_STATUS, "Adding hour sample...");

        //  ... set the archiveAvailable flag for generation...
        work->mgrId->newArchiveMask |= NEW_ARCHIVE_HOUR;

        htmlmgrAddHourValue (work->mgrId, &data);

        work->histLastHour = currHour;
    }

    //// Day change?
    if (work->histLastDay != currDay)
    {
        // determine times to start for last day:
        // normalize packed time:
        ntime = time (NULL);
        localtime_r (&ntime, &locTime);
        locTime.tm_sec   = 0;
        locTime.tm_min   = work->archiveInterval;
        locTime.tm_hour  = 0;
        locTime.tm_mday  = currDay;
        locTime.tm_mon   = currMonth - 1;
        locTime.tm_year  = currYear - 1900;
        locTime.tm_isdst = -1;
#ifdef HAVE_TM_GMTOFF
        gmtOffsetHours = locTime.tm_gmtoff/(60.0*60.0);
#else   
        gmtOffsetHours = gmtoffset()/(60.0*60.0);
#endif

        ntime = mktime (&locTime);
        ntime -= WV_SECONDS_IN_DAY;
        localtime_r (&ntime, &locTime);

        if (dbsqliteArchiveGetAverages (work->mgrId->isMetricUnits,
                                        work->archiveInterval,
                                        &data,
                                        ntime,
                                        WV_SECONDS_IN_DAY/SECONDS_IN_INTERVAL(work->archiveInterval))
            <= 0)
        {
            // populate history data with ARCHIVE_VALUE_NULL
            for (i = 0; i < DATA_INDEX_MAX(work->isExtendedData); i ++)
            {
                data.values[i] = ARCHIVE_VALUE_NULL;
                data.samples[i] = 0;
            }
            radMsgLog (PRI_MEDIUM, "no data found for last day...");
        }
        else
        {
            // Add a day history record:
            wvutilsLogEvent(PRI_STATUS, "Storing day history for %s", ctime(&ntime));
            dbsqliteHistoryInsertDay(&data);
        }

        //  ... set the archiveAvailable flag for generation...
        work->mgrId->newArchiveMask |= NEW_ARCHIVE_DAY;

        wvutilsLogEvent(PRI_STATUS, "Adding day sample...");
        htmlmgrAddDayValue (work->mgrId, &data);

        work->histLastDay = currDay;

        //  Start the timeout to do NOAA updates:
        radProcessTimerStart(work->noaaTimer, HTML_NOAA_UPDATE_DELAY);

        //  ... update the sun times
        sunTimesGetSunRiseSet (currYear, currMonth, currDay,
                               (float)work->mgrId->stationLatitude/10,
                               (float)work->mgrId->stationLongitude/10,
                               RS_TYPE_SUN,
                               &work->mgrId->sunrise,
                               &work->mgrId->sunset);
        sunTimesGetSunRiseSet (currYear, currMonth, currDay,
                               (float)work->mgrId->stationLatitude/10,
                               (float)work->mgrId->stationLongitude/10,
                               RS_TYPE_CIVIL,
                               &work->mgrId->civilrise,
                               &work->mgrId->civilset);
        sunTimesGetSunRiseSet (currYear, currMonth, currDay,
                               (float)work->mgrId->stationLatitude/10,
                               (float)work->mgrId->stationLongitude/10,
                               RS_TYPE_ASTRO,
                               &work->mgrId->astrorise,
                               &work->mgrId->astroset);
        sunTimesGetSunRiseSet (currYear, currMonth, currDay,
                               (float)work->mgrId->stationLatitude/10,
                               (float)work->mgrId->stationLongitude/10,
                               RS_TYPE_MIDDAY,
                               &work->mgrId->midday,
                               &tempShort);
        work->mgrId->dayLength = 
                sunTimesGetDayLength (currYear, currMonth, currDay,
                                      (float)work->mgrId->stationLatitude/10,
                                      (float)work->mgrId->stationLongitude/10);

        GetMoonRiseSetTimes (currYear, currMonth, currDay,
                             (float)gmtOffsetHours,
                             (float)work->mgrId->stationLatitude/10,
                             (float)work->mgrId->stationLongitude/10,
                             &work->mgrId->moonrise,
                             NULL,
                             &work->mgrId->moonset,
                             NULL);

    }

    return;
}
Beispiel #5
0
int htmlmgrGenerate
(
    HTML_MGR_ID         id
)
{
    register HTML_IMG   *img;
    int                 retVal, imgs = 0, htmls = 0;
    char                temp[256];
    struct stat         fileData;

    GenerateTime = radTimeGetMSSinceEpoch ();

#if __DEBUG_BUFFERS
    radMsgLog (PRI_STATUS, "DBG BFRS: HTML BEGIN: %u of %u available",
               buffersGetAvailable (),
               buffersGetTotal ());
#endif

    //  ... compute the Barometric Pressure trend
    computeBPTrend (id);

#if DEBUG_GENERATION
    radMsgLog (PRI_MEDIUM, "GENERATE: images");
#endif

    //  ... generate the weather images
    for (img = (HTML_IMG *)radListGetFirst (&id->imgList);
         img != NULL;
         img = (HTML_IMG *)radListGetNext (&id->imgList, (NODE_PTR)img))
    {
        retVal = (*img->generator) (img);
        if (retVal == OK)
        {
            imgs ++;
        }
        else if (retVal != ERROR_ABORT)
        {
            sprintf (temp, "%s/%s", id->imagePath, img->fname);
            radMsgLog (PRI_HIGH, "%s generation failed - must be local to the wview server!",
                       temp);
            radMsgLog (PRI_HIGH, "Otherwise you may be including data in "
                                 "images.conf for which you do not have sensors?!?");
        }
    }

    //  ... clear the archiveAvailable flag (must be after generator loop)
    id->newArchiveMask = 0;

#if DEBUG_GENERATION
    radMsgLog (PRI_MEDIUM, "GENERATE: pre-generate script");
#endif

    // If the wview pre-generation script exists, run it now
    sprintf (temp, "%s/%s", WVIEW_CONFIG_DIR, HTML_PRE_GEN_SCRIPT);
    if (stat (temp, &fileData) == 0)
    {
        // File exists, run it
        radStartProcess (newProcessEntryPoint, temp);
    }

#if DEBUG_GENERATION
    radMsgLog (PRI_MEDIUM, "GENERATE: templates");
#endif

    //  ... now generate the HTML
    if ((htmls = htmlgenOutputFiles(id, GenerateTime)) == ERROR)
    {
        return ERROR;
    }

    wvutilsLogEvent(PRI_STATUS, "Generated: %u ms: %d images, %d template files",
                    (uint32_t)(radTimeGetMSSinceEpoch() - GenerateTime), imgs, htmls);

    id->imagesGenerated += imgs;
    id->templatesGenerated += htmls;
    statusUpdateStat(HTML_STATS_IMAGES_GENERATED, id->imagesGenerated);
    statusUpdateStat(HTML_STATS_TEMPLATES_GENERATED, id->templatesGenerated);

#if __DEBUG_BUFFERS
    radMsgLog (PRI_STATUS, "DBG BFRS: HTML END: %u of %u available",
               buffersGetAvailable (),
               buffersGetTotal ());
#endif

#if DEBUG_GENERATION
    radMsgLog (PRI_MEDIUM, "GENERATE: post-generate script");
#endif

    // Finally, if the wview post-generation script exists, run it now
    sprintf (temp, "%s/%s", WVIEW_CONFIG_DIR, HTML_POST_GEN_SCRIPT);
    if (stat (temp, &fileData) == 0)
    {
        // File exists, run it
        radStartProcess (newProcessEntryPoint, temp);
    }

#if DEBUG_GENERATION
    radMsgLog (PRI_MEDIUM, "GENERATE: DONE");
#endif

    return OK;
}