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; }
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; }
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; }
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; }