static int daemonStationInitComplete (void *eventData) { struct tm locTime; time_t nowtime; ARCHIVE_PKT newestRecord; if (eventData != 0) { // failed startup! radMsgLog (PRI_HIGH, "daemonStationInitComplete: station startup failed!"); return ERROR; } if (!wviewdWork.runningFlag) { wviewdWork.runningFlag = TRUE; // set the positional data: if (stationGetPosition (&wviewdWork) == ERROR) { radMsgLog (PRI_HIGH, "daemonStationInitComplete: stationGetPosition failed!"); emailAlertSend(ALERT_TYPE_STATION_READ); return ERROR; } // !!! the order of these calls is very important !!! // Initialize the HILOW database interface: computedDataInit (&wviewdWork); stormRainInit (wviewdWork.stationRainStormTrigger, wviewdWork.stationRainStormIdleHours); computedDataClearInterval (&wviewdWork, 0, 0); // we know it just finished an initial sensor readings, it is a // REQUIREMENT of the stationInit API... daemonStationLoopComplete (); // do an initial update to propogate the initial readings // (so we have some data to start with) computedDataUpdate (&wviewdWork, NULL); // start the timers... stationStartArchiveTimerUniform (&wviewdWork); stationStartCDataTimerUniform (&wviewdWork); radProcessTimerStart (wviewdWork.pushTimer, 30000L); // first run stationStartSyncTimerUniform (&wviewdWork, TRUE); // first run radMsgLog (PRI_STATUS, "-- Station Init Complete --"); // get the newest archive file record date/time wviewdWork.archiveDateTime = dbsqliteArchiveGetNewestTime(&newestRecord); if ((int)wviewdWork.archiveDateTime == ERROR) { wviewdWork.archiveDateTime = time(NULL); radMsgLog (PRI_STATUS, "no archive records found in database!"); } else { radMsgLog (PRI_STATUS, "newest archive record: %4.4d-%2.2d-%2.2d %2.2d:%2.2d", wvutilsGetYear(wviewdWork.archiveDateTime), wvutilsGetMonth(wviewdWork.archiveDateTime), wvutilsGetDay(wviewdWork.archiveDateTime), wvutilsGetHour(wviewdWork.archiveDateTime), wvutilsGetMin(wviewdWork.archiveDateTime)); } // finally, answer all the WVIEW_RQST_TYPE_STATION_INFO requestors // so they can continue initialization - our data is ready: stationProcessInfoResponses(&wviewdWork); } return OK; }
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; }
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; }