From a2587fe837118504bbe750769e6c4b7d8f60f755 Mon Sep 17 00:00:00 2001 From: hezy0426 Date: Wed, 9 Aug 2023 11:00:52 -0400 Subject: [PATCH] Logging is supported --- components/SDCard/CMakeLists.txt | 4 +- components/SDCard/SDCard.cpp | 50 ++++-- components/SDCard/dataLogging.cpp | 159 +++++++++++++++++++ components/SDCard/include/SDCard.h | 9 +- components/SDCard/include/dataLogging.h | 18 +++ components/webServer/Data2Web.cpp | 8 - components/webServer/include/Data2Web.h | 9 ++ components/webServer/include/supporting_js.h | 2 +- main/ethernet_example_main.cpp | 4 +- partitions.csv | 5 + sdkconfig | 14 +- 11 files changed, 252 insertions(+), 30 deletions(-) create mode 100644 components/SDCard/dataLogging.cpp create mode 100644 components/SDCard/include/dataLogging.h create mode 100644 partitions.csv diff --git a/components/SDCard/CMakeLists.txt b/components/SDCard/CMakeLists.txt index bf11caa..01951ee 100644 --- a/components/SDCard/CMakeLists.txt +++ b/components/SDCard/CMakeLists.txt @@ -1,3 +1,3 @@ -idf_component_register(SRCS "SDCard.cpp" +idf_component_register(SRCS "dataLogging.cpp" "SDCard.cpp" INCLUDE_DIRS "include" - REQUIRES "sdmmc" "fatfs" "vfs") + REQUIRES "sdmmc" "fatfs" "vfs" "webServer") diff --git a/components/SDCard/SDCard.cpp b/components/SDCard/SDCard.cpp index 46008b8..fd9996f 100644 --- a/components/SDCard/SDCard.cpp +++ b/components/SDCard/SDCard.cpp @@ -1,9 +1,14 @@ -#include +/** + * 08/04/2023 - Ruizhe He, this source file contains code to mount and unmount + * a SD card from the ESP 32 board. + */ #include "SDCard.h" -#include "esp_log.h" #define MOUNT_POINT "/sdcard" +/** + * This function will mount the SD card + */ esp_err_t initi_sd_card(void) { sdmmc_host_t host = SDMMC_HOST_DEFAULT(); @@ -25,6 +30,9 @@ esp_err_t initi_sd_card(void) return err; } +/** + * This function will unmount the sd card + */ esp_err_t unmount_sd_card(void) { esp_err_t err = esp_vfs_fat_sdcard_unmount(MOUNT_POINT, card); @@ -38,29 +46,43 @@ esp_err_t unmount_sd_card(void) return err; } -void testingCode(char *jsonString) +/** + * This function will write the parameter string into a txt file + */ +int logStringToFile(const char *formattedString, char *fileName) { if (!SDCARDMOUNTED) - return; - const char *file_hello = MOUNT_POINT "/hello.txt"; + return 0; - ESP_LOGI(TAG_SD, "Opening file %s", file_hello); - FILE *f = fopen(file_hello, "w"); + std::string fullPath; + fullPath.append(MOUNT_POINT).append("/").append(fileName); + ESP_LOGI(TAG_SD, "Opening file %s", fullPath.c_str()); + + FILE *f = fopen(fullPath.c_str(), "a"); if (f == NULL) { ESP_LOGE(TAG_SD, "Failed to open file for writing"); - return; + return 0; } - fprintf(f, "The Data is:\n%s", jsonString); + fprintf(f, "%s\n", formattedString); fclose(f); ESP_LOGI(TAG_SD, "SUCCESS"); + return 1; } +/** + * Retrun a bool value whether the SD card is mounted or not + */ bool isMounted(void) { return SDCARDMOUNTED; } +/** + * This function will return a bool value whether we retrieved the value + * successfully or not. Two parameters will be used to pass in and out + * the free space and total space. + */ bool SD_getFreeSpace(uint32_t *tot, uint32_t *free) { FATFS *fs; @@ -82,4 +104,14 @@ bool SD_getFreeSpace(uint32_t *tot, uint32_t *free) return true; } return false; +} + +int hasFile(char *fileName) +{ + struct stat st; + std::string filePath; + filePath.append(MOUNT_POINT).append("/").append(fileName); + if (stat(filePath.c_str(), &st) == 0) + return 1; + return 0; } \ No newline at end of file diff --git a/components/SDCard/dataLogging.cpp b/components/SDCard/dataLogging.cpp new file mode 100644 index 0000000..f1c4653 --- /dev/null +++ b/components/SDCard/dataLogging.cpp @@ -0,0 +1,159 @@ +#include "dataLogging.h" + +#define HEADER1 "Data Date (UTC),Milliseconds,b_dc_power,i_dc_power,calc_add_power,gen_power,load_power,b_dc_volts,b_dc_amps,i_ac_volts_in,b_amph_in_out,b_state_of_charge,i_dc_volts,i_dc_amps,i_ac_volts_out,i_amps_out,i_amps_in,i_ac_hz,i_status,i_fault,i_temp_transformer,i_temp_fet,i_temp_battery,a_gen_run_hours_since_boot,a_gen_runtime_decihours,age_inverter,a_temperature" +#define HEADER2 "Data Date (UTC),Milliseconds,DC Power (RMK),DC Power (Inverter),Calculated Renewable Power,AC In Power,AC Out Power,VDC (BMK),DC Amps (BMK),VAC In (Inverter),Ah In/Out (BMK),SOC (BMK),VDC (Inveter),DC Amps (Inverter),VAC Out (Inverter),AC Amps Out,AC Amps IN,AC Out Hz,Inverter Status,Fault Code,Transformer Temp (C ),FET Temp (C ),Battery Temp (C ),Total Generator Runtime (Since AGS Boot),Generator Runtime (Current Cycle),Inverter Age (255 indicates old data),AGS Temp (C )" + +time_t epoch_time = 1691547163; +const TickType_t xDelay = 10000; +TaskHandle_t task_handle; + +// int generateHeadlines(char **headlineA, char **headlineB) +// { +// std::string ha, hb; +// ha += "Data Date (UTC),Milliseconds"; +// hb += "Data Date (UTC),Milliseconds"; +// dataNowString = getDataNow(); +// cJSON *dataNowJson = cJSON_Parse(dataNowString); +// cJSON *channelJson = cJSON_Parse(channel_string); + +// if (!dataNowJson || !channelJson) +// { +// cJSON_Delete(dataNowJson); +// cJSON_Delete(channelJson); +// return 0; +// } + +// cJSON *dataNowObj = cJSON_GetObjectItem(dataNowJson, "data"); +// cJSON *channelObj = cJSON_GetObjectItem(channelJson, "data"); + +// if (!dataNowObj || !channelObj) +// { +// cJSON_Delete(dataNowJson); +// cJSON_Delete(channelJson); +// return 0; +// } + +// cJSON *dataNowSubObj = NULL; +// cJSON *channelSubObj = NULL; +// cJSON_ArrayForEach(dataNowSubObj, dataNowObj) +// { +// cJSON *channel = cJSON_GetObjectItemCaseSensitive(dataNowSubObj, "channel"); +// ha += channel->valueString; + +// cJSON_ArrayForEach(channelSubObj, channelObj) +// { +// cJSON *id = cJSON_GetObjectItemCaseSensitive(channelSubObj, "id"); +// if (strcmp(id->valuestring, channel->valuestring) == 0) +// { +// cJSON *descriptionA = cJSON_GetObjectItemCaseSensitive(channelSubObj, "description"); +// hb += descriptionA->valueString; +// break; +// } +// } +// } + +// return 1; +// } + +void dataNowLog(void *pv_args) +{ + std::string tempString; + struct tm *currentTime; + + for (;;) + { + // Move into the loop so when a new day comes, it will generate a new file + char fileName[15]; + char time_str[25]; + currentTime = localtime(&epoch_time); + strftime(fileName, sizeof fileName, "%Y%m%d.csv", currentTime); + std::mt19937_64 rng(epoch_time); + std::uniform_real_distribution dist(0.0, 1.0); + + // Create the file if it doesn't exist. Add the headers to the file + if (!hasFile(fileName)) + { + std::string headers; + headers.append(HEADER1).append("\n").append(HEADER2); + if (logStringToFile(headers.c_str(), fileName)) + ; + } + + strftime(time_str, sizeof(time_str), "%Y/%m/%d %H:%M:%S", currentTime); + currentTime = NULL; + tempString.append(time_str).append(",0,"); + // b_dc_power + tempString.append(std::to_string(25.0 + 5.0 * dist(rng))).append(","); + // i_dc_power + tempString.append(std::to_string(25.0 + 5.0 * dist(rng))).append(","); + // calc_add_power + tempString.append(std::to_string(25.0 + 5.0 * dist(rng))).append(","); + // gen_power + tempString.append(std::to_string(25.0 + 5.0 * dist(rng))).append(","); + // load_power + tempString.append(std::to_string(25.0 + 5.0 * dist(rng))).append(","); + tempString.append(std::to_string(25.0 + 5.0 * dist(rng))).append(","); + tempString.append(std::to_string(25.0 + 5.0 * dist(rng))).append(","); + tempString.append(std::to_string(25.0 + 5.0 * dist(rng))).append(","); + tempString.append(std::to_string(25.0 + 5.0 * dist(rng))).append(","); + tempString.append(std::to_string(25.0 + 5.0 * dist(rng))).append(","); + tempString.append(std::to_string(25.0 + 5.0 * dist(rng))).append(","); + tempString.append(std::to_string(25.0 + 5.0 * dist(rng))).append(","); + tempString.append(std::to_string(25.0 + 5.0 * dist(rng))).append(","); + tempString.append(std::to_string(25.0 + 5.0 * dist(rng))).append(","); + tempString.append(std::to_string(25.0 + 5.0 * dist(rng))).append(","); + tempString.append(std::to_string(25.0 + 5.0 * dist(rng))).append(","); + tempString.append(std::to_string(25.0 + 5.0 * dist(rng))).append(","); + tempString.append(std::to_string(25.0 + 5.0 * dist(rng))).append(","); + tempString.append(std::to_string(25.0 + 5.0 * dist(rng))).append(","); + tempString.append(std::to_string(25.0 + 5.0 * dist(rng))).append(","); + tempString.append(std::to_string(25.0 + 5.0 * dist(rng))).append(","); + tempString.append(std::to_string(25.0 + 5.0 * dist(rng))).append(","); + tempString.append(std::to_string(25.0 + 5.0 * dist(rng))).append(","); + // age_inverter + tempString.append("1,").append(std::to_string(25.0 + 5.0 * dist(rng))); + + if (logStringToFile(tempString.c_str(), fileName)) + { + ESP_LOGI("Logging", "Successfully did one"); + } + tempString.clear(); + epoch_time += 10; + // TODO: Need the format of the file + vTaskDelay(xDelay); + } +} + +/** + * This function will mount the SD card and then start the logging of live data. + */ +void startLogging() +{ + if (!isMounted()) + { + initi_sd_card(); + BaseType_t ret = xTaskCreate(dataNowLog, "dataNow_Logging_Task", 8192, NULL, 5, &task_handle); + if (ret == pdPASS) + { + ESP_LOGI("Logging", "Successfully started the task"); + } + } + // TODO: confirm the priority with BA +} + +/** + * Stop the logging of live data when the function is called. + */ +void stopLogging() +{ + if (task_handle) + { + vTaskDelete(task_handle); + task_handle = NULL; + } +} + +void starterFunction() +{ + startLogging(); +} \ No newline at end of file diff --git a/components/SDCard/include/SDCard.h b/components/SDCard/include/SDCard.h index fc30345..3138919 100644 --- a/components/SDCard/include/SDCard.h +++ b/components/SDCard/include/SDCard.h @@ -1,6 +1,12 @@ +#pragma once + #ifndef SDCARD_H #define SDCARD_H +#include +#include +#include +#include "esp_log.h" #include "driver/sdmmc_host.h" #include "driver/sdmmc_defs.h" #include "sdmmc_cmd.h" @@ -11,8 +17,9 @@ static sdmmc_card_t *card; static bool SDCARDMOUNTED = false; esp_err_t initi_sd_card(void); esp_err_t unmount_sd_card(void); -void testingCode(char *jsonString); +int logStringToFile(const char *formattedString, char *fileName); bool isMounted(void); bool SD_getFreeSpace(uint32_t *tot, uint32_t *free); +int hasFile(char *fileName); #endif // SDCARD_H \ No newline at end of file diff --git a/components/SDCard/include/dataLogging.h b/components/SDCard/include/dataLogging.h new file mode 100644 index 0000000..d857442 --- /dev/null +++ b/components/SDCard/include/dataLogging.h @@ -0,0 +1,18 @@ +#pragma once + +#include "Data2Web.h" +#include "SDCard.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" + +#include +#include +#include +#include +#include + +void startLogging(); +void stopLogging(); +void starterFunction(); \ No newline at end of file diff --git a/components/webServer/Data2Web.cpp b/components/webServer/Data2Web.cpp index 5cadfd4..49898c9 100644 --- a/components/webServer/Data2Web.cpp +++ b/components/webServer/Data2Web.cpp @@ -4,12 +4,6 @@ */ #include "Data2Web.h" -#include "cJSON.h" -#include -#include -#include -#include -#include #include "SDCard.h" /** @@ -123,9 +117,7 @@ char *getHostInfoJson() char *JSONString = NULL; cJSON *JSONObj = NULL; cJSON *drives = NULL; - cJSON *hostName = NULL; cJSON *sd_card = NULL; - cJSON *media_usb = NULL; uint32_t tot, free; bool ret; if (!isMounted()) diff --git a/components/webServer/include/Data2Web.h b/components/webServer/include/Data2Web.h index beeead3..8fac11f 100644 --- a/components/webServer/include/Data2Web.h +++ b/components/webServer/include/Data2Web.h @@ -1,2 +1,11 @@ +#pragma once + +#include "cJSON.h" +#include +#include +#include +#include +#include + char *getDataNow(long long time); char *getHostInfoJson(); \ No newline at end of file diff --git a/components/webServer/include/supporting_js.h b/components/webServer/include/supporting_js.h index 4dab3ac..56a3187 100644 --- a/components/webServer/include/supporting_js.h +++ b/components/webServer/include/supporting_js.h @@ -9,7 +9,7 @@ const char *jqueryfloat = "/* Javascript plotting library for jQuery, version 0. const char *howler = "/*!\n * howler.js v1.1.25\n * howlerjs.com\n *\n * (c) 2013-2014, James Simpson of GoldFire Studios\n * goldfirestudios.com\n *\n * MIT License\n */\n!function(){var e={},t=null,n=!0,r=!1;try{\"undefined\"!=typeof AudioContext?t=new AudioContext:\"undefined\"!=typeof webkitAudioContext?t=new webkitAudioContext:n=!1}catch(i){n=!1}if(!n)if(\"undefined\"!=typeof Audio)try{new Audio}catch(i){r=!0}else r=!0;if(n){var s=void 0===t.createGain?t.createGainNode():t.createGain();s.gain.value=1,s.connect(t.destination)}var o=function(e){this._volume=1,this._muted=!1,this.usingWebAudio=n,this.ctx=t,this.noAudio=r,this._howls=[],this._codecs=e,this.iOSAutoEnable=!0};o.prototype={volume:function(e){var t=this;if(e=parseFloat(e),e>=0&&1>=e){t._volume=e,n&&(s.gain.value=e);for(var r in t._howls)if(t._howls.hasOwnProperty(r)&&t._howls[r]._webAudio===!1)for(var i=0;i0?i._pos:r._sprite[e][0]/1e3,o=0;r._webAudio?(o=r._sprite[e][1]/1e3-i._pos,i._pos>0&&(s=r._sprite[e][0]/1e3+s)):o=r._sprite[e][1]/1e3-(s-r._sprite[e][0]/1e3);var u,a=!(!r._loop&&!r._sprite[e][2]),l=\"string\"==typeof n?n:Math.round(Date.now()*Math.random())+\"\";if(function(){var t={id:l,sprite:e,loop:a};u=setTimeout(function(){!r._webAudio&&a&&r.stop(t.id).play(e,t.id),r._webAudio&&!a&&(r._nodeById(t.id).paused=!0,r._nodeById(t.id)._pos=0,r._clearEndTimer(t.id)),r._webAudio||a||r.stop(t.id),r.on(\"end\",l)},1e3*o),r._onendTimer.push({timer:u,id:t.id})}(),r._webAudio){var c=r._sprite[e][0]/1e3,h=r._sprite[e][1]/1e3;i.id=l,i.paused=!1,d(r,[a,c,h],l),r._playStart=t.currentTime,i.gain.value=r._volume,void 0===i.bufferSource.start?i.bufferSource.noteGrainOn(0,s,o):i.bufferSource.start(0,s,o)}else{if(4!==i.readyState&&(i.readyState||!navigator.isCocoonJS))return r._clearEndTimer(l),function(){var t=r,s=e,o=n,u=i,a=function(){t.play(s,o),u.removeEventListener(\"canplaythrough\",a,!1)};u.addEventListener(\"canplaythrough\",a,!1)}(),r;i.readyState=4,i.id=l,i.currentTime=s,i.muted=f._muted||i.muted,i.volume=r._volume*f.volume(),setTimeout(function(){i.play()},0)}return r.on(\"play\"),\"function\"==typeof n&&n(l),r}),r):(\"function\"==typeof n&&n(),r):(r.on(\"load\",function(){r.play(e,n)}),r)},pause:function(e){var t=this;if(!t._loaded)return t.on(\"play\",function(){t.pause(e)}),t;t._clearEndTimer(e);var n=e?t._nodeById(e):t._activeNode();if(n)if(n._pos=t.pos(null,e),t._webAudio){if(!n.bufferSource||n.paused)return t;n.paused=!0,void 0===n.bufferSource.stop?n.bufferSource.noteOff(0):n.bufferSource.stop(0)}else n.pause();return t.on(\"pause\"),t},stop:function(e){var t=this;if(!t._loaded)return t.on(\"play\",function(){t.stop(e)}),t;t._clearEndTimer(e);var n=e?t._nodeById(e):t._activeNode();if(n)if(n._pos=0,t._webAudio){if(!n.bufferSource||n.paused)return t;n.paused=!0,void 0===n.bufferSource.stop?n.bufferSource.noteOff(0):n.bufferSource.stop(0)}else isNaN(n.duration)||(n.pause(),n.currentTime=0);return t},mute:function(e){var t=this;if(!t._loaded)return t.on(\"play\",function(){t.mute(e)}),t;var n=e?t._nodeById(e):t._activeNode();return n&&(t._webAudio?n.gain.value=0:n.muted=!0),t},unmute:function(e){var t=this;if(!t._loaded)return t.on(\"play\",function(){t.unmute(e)}),t;var n=e?t._nodeById(e):t._activeNode();return n&&(t._webAudio?n.gain.value=t._volume:n.muted=!1),t},volume:function(e,t){var n=this;if(e=parseFloat(e),e>=0&&1>=e){if(n._volume=e,!n._loaded)return n.on(\"play\",function(){n.volume(e,t)}),n;var r=t?n._nodeById(t):n._activeNode();return r&&(n._webAudio?r.gain.value=e:r.volume=e*f.volume()),n}return n._volume},loop:function(e){var t=this;return\"boolean\"==typeof e?(t._loop=e,t):t._loop},sprite:function(e){var t=this;return\"object\"==typeof e?(t._sprite=e,t):t._sprite},pos:function(e,n){var r=this;if(!r._loaded)return r.on(\"load\",function(){r.pos(e)}),\"number\"==typeof e?r:r._pos||0;e=parseFloat(e);var i=n?r._nodeById(n):r._activeNode();if(i)return e>=0?(r.pause(n),i._pos=e,r.play(i._sprite,n),r):r._webAudio?i._pos+(t.currentTime-r._playStart):i.currentTime;if(e>=0)return r;for(var s=0;s=0||0>e))return i._pos3d;if(i._webAudio){var s=r?i._nodeById(r):i._activeNode();s&&(i._pos3d=[e,t,n],s.panner.setPosition(e,t,n),s.panner.panningModel=i._model||\"HRTF\")}return i},fade:function(e,t,n,r,i){var s=this,o=Math.abs(e-t),u=e>t?\"down\":\"up\",a=o/.01,f=n/a;if(!s._loaded)return s.on(\"load\",function(){s.fade(e,t,n,r,i)}),s;s.volume(e,i);for(var l=1;a>=l;l++)!function(){var e=s._volume+(\"up\"===u?.01:-.01)*l,n=Math.round(1e3*e)/1e3,o=t;setTimeout(function(){s.volume(n,i),n===o&&r&&r()},f*l)}()},fadeIn:function(e,t,n){return this.volume(0).play().fade(0,e,t,n)},fadeOut:function(e,t,n,r){var i=this;return i.fade(i._volume,e,t,function(){n&&n(),i.pause(r),i.on(\"end\")},r)},_nodeById:function(e){for(var t=this,n=t._audioNode[0],r=0;r=0&&!(5>=n);e--)t._audioNode[e].paused&&(t._webAudio&&t._audioNode[e].disconnect(0),n--,t._audioNode.splice(e,1))},_clearEndTimer:function(e){for(var t=this,n=0,r=0;r=0&&f._howls.splice(i,1),delete e[t._src],t=null}},n)var c=function(t,n){if(n in e)return t._duration=e[n].duration,void p(t);if(/^data:[^;]+;base64,/.test(n)){for(var r=atob(n.split(\",\")[1]),i=new Uint8Array(r.length),s=0;s\",s+=\"INVERTER FAULT: \"+magnumInverterFault(parseInt(a.i_fault.sampleValue))),-1!=AGSStatus(parseInt(a.a_status.sampleValue)).indexOf(\"Fault\")&&(s+=\"
\",s+=\"AGS FAULT: \"+magnumAGSStatus(parseInt(a.a_status.sampleValue))),parseInt(a.age_bmk.sampleValue)<100?(a.b_dc_volts.avg-1<=parseFloat(a.r_low_batt_cut_out.sampleValue)&&8!=parseInt(a.i_fault.sampleValue)&&(s+=\"
\",s+=faultMessages(0)),1!=parseInt(a.b_fault.sampleValue)&&(s+=\"
\",s+=faultMessages(6))):parseInt(a.age_inverter.sampleValue)<100&&a.i_dc_volts.avg-1<=parseFloat(a.r_low_batt_cut_out.sampleValue)&&8!=parseInt(a.i_fault.sampleValue)&&(s+=\"
\",s+=faultMessages(1)),parseInt(a.age_inverter.sampleValue)>100&&(s+=\"
\",s+=faultMessages(9))),testAlr&&(s+=\"
\",s+=faultMessages(3)),noResponse&&(s+=\"
\",s+=faultMessages(4)),oldData&&(s+=\"
\",s+=faultMessages(5)),noData&&(s+=\"
\",s+=faultMessages(2)),\"\"==s?(dismiss(),!1):(warn(s),alarmUp(),location.href=\"#alarm\",!0)}"; +const char *alarm_js = "var alarmSound,flashCount=0,booFlash=!0,booFault=!1,testAlr=!1,noResponse=!1,oldData=!1,noData=!1,faultMsg=\"The inverter has faulted!\";function warn(a){flashCount=0,null==a?(faultMsg=\"The inverter has faulted!\",$(\"#alarmMessage\").html(\"The inverter has faulted!\")):(faultMsg=a,$(\"#alarmMessage\").html(\"FAULTS:\"+a)),$(\"#alarm\").show(),booFault||(booFault=!0,screenFlash(),soundAlarm())}function testAlarm(){testAlr||(testAlr=!0,customFaults(null))}function screenFlash(){(booFlash=!booFlash)?$(\"#alarm\").css(\"background\",\"orange\"):$(\"#alarm\").css(\"background\",\"red\"),booFault&&setTimeout(screenFlash,500)}(alarmSound=new Howl({urls:[\"\"],loop:!0})).isPlaying=!1;var sound_file_url=\"\",silence=!1;function soundAlarm(){isIE}function silenceAlarm(a){a?(silence=!0,$(\"#controlSilence\").html(\"true\"),alarmSound.stop(),alarmSound.isPlaying=!1):(silence=!1,$(\"#controlSilence\").html(\"false\"),!alarmSound.isPlaying&&booFault&&(alarmSound.isPlaying=!0,alarmSound.play()))}function dismiss(){testAlr=!1,booFault=!1,$(\"#alarm\").hide(),alarmSound.stop()}function faultMessages(a){var s=\"\";switch(a){case 0:case 1:s=\"LOW BATTERY, ATTACH TO CHARGING SOURCE IMEDIATELY\";break;case 2:s='LOST COMMUNICATION WITH MAGWEB PRO
MAGWEB PRO APPEARS TO BE DISCONNECTED';break;case 3:s='TRIGGERED BY TEST BUTTON
CLICK DISMISS TO CLEAR';break;case 4:s='NO RESPONSE FROM SERVER!
PLEASE CHECK TO MAKE SURE YOU ARE STILL CONNECTED TO NETWORK!';break;case 5:s='MAGWEB PRO INTERNAL ERROR: DATA NOT UPDATING
CONTACT SUPPORT OR POWER CYCLE MAGWEB PRO';break;case 6:s=\"BMK IN FAULT MODE!\";break;case 7:s=\"GENERATOR START FAULT - CHECK FUEL LEVEL\";break;case 8:s=\"CHARGER FAULT - OVER TEMPERATURE - CLEAN MAGWEB PRO VENTS AND/OR REDUCE CHARGE RATE\";break;case 9:s='MAGWEB PRO NOT CONNECTED TO THE MAGNUM NETWORK
OR MAGNUM DEVICES ARE POWERED OFF';break;default:s=\"UNKNOWN FAULT\"}return s}function customFaults(a){var s=\"\";return null!=a&&(0!=parseInt(a.i_fault.sampleValue)&&(s+=\"
\",s+=\"INVERTER FAULT: \"+magnumInverterFault(parseInt(a.i_fault.sampleValue))),-1!=AGSStatus(parseInt(a.a_status.sampleValue)).indexOf(\"Fault\")&&(s+=\"
\",s+=\"AGS FAULT: \"+magnumAGSStatus(parseInt(a.a_status.sampleValue))),parseInt(a.age_bmk.sampleValue)<100?(a.b_dc_volts.avg-1<=parseFloat(a.r_low_batt_cut_out.sampleValue)&&8!=parseInt(a.i_fault.sampleValue)&&(s+=\"
\",s+=faultMessages(0)),1!=parseInt(a.b_fault.sampleValue)&&(s+=\"
\",s+=faultMessages(6))):parseInt(a.age_inverter.sampleValue)<100&&a.i_dc_volts.avg-1<=parseFloat(a.r_low_batt_cut_out.sampleValue)&&8!=parseInt(a.i_fault.sampleValue)&&(s+=\"
\",s+=faultMessages(1)),parseInt(a.age_inverter.sampleValue)>100&&(s+=\"
\",s+=faultMessages(9))),testAlr&&(s+=\"
\",s+=faultMessages(3)),noResponse&&(s+=\"
\",s+=faultMessages(4)),oldData&&(s+=\"
\",s+=faultMessages(5)),noData&&(s+=\"
\",s+=faultMessages(2)),\"\"==s?(dismiss(),!1):(warn(s),alarmUp(),location.href=\"#alarm\",!0)}"; const char *date_js = "/**\n * Version: 1.0 Alpha-1 \n * Build Date: 13-Nov-2007\n * Copyright (c) 2006-2007, Coolite Inc. (http://www.coolite.com/). All rights reserved.\n * License: Licensed under The MIT License. See license.txt and http://www.datejs.com/license/. \n * Website: http://www.datejs.com/ or http://www.coolite.com/datejs/\n */\nDate.CultureInfo={name:\"en-US\",englishName:\"English (United States)\",nativeName:\"English (United States)\",dayNames:[\"Sunday\",\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\",\"Saturday\"],abbreviatedDayNames:[\"Sun\",\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\"],shortestDayNames:[\"Su\",\"Mo\",\"Tu\",\"We\",\"Th\",\"Fr\",\"Sa\"],firstLetterDayNames:[\"S\",\"M\",\"T\",\"W\",\"T\",\"F\",\"S\"],monthNames:[\"January\",\"February\",\"March\",\"April\",\"May\",\"June\",\"July\",\"August\",\"September\",\"October\",\"November\",\"December\"],abbreviatedMonthNames:[\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"],amDesignator:\"AM\",pmDesignator:\"PM\",firstDayOfWeek:0,twoDigitYearMax:2029,dateElementOrder:\"mdy\",formatPatterns:{shortDate:\"M/d/yyyy\",longDate:\"dddd, MMMM dd, yyyy\",shortTime:\"h:mm tt\",longTime:\"h:mm:ss tt\",fullDateTime:\"dddd, MMMM dd, yyyy h:mm:ss tt\",sortableDateTime:\"yyyy-MM-ddTHH:mm:ss\",universalSortableDateTime:\"yyyy-MM-dd HH:mm:ssZ\",rfc1123:\"ddd, dd MMM yyyy HH:mm:ss GMT\",monthDay:\"MMMM dd\",yearMonth:\"MMMM, yyyy\"},regexPatterns:{jan:/^jan(uary)?/i,feb:/^feb(ruary)?/i,mar:/^mar(ch)?/i,apr:/^apr(il)?/i,may:/^may/i,jun:/^jun(e)?/i,jul:/^jul(y)?/i,aug:/^aug(ust)?/i,sep:/^sep(t(ember)?)?/i,oct:/^oct(ober)?/i,nov:/^nov(ember)?/i,dec:/^dec(ember)?/i,sun:/^su(n(day)?)?/i,mon:/^mo(n(day)?)?/i,tue:/^tu(e(s(day)?)?)?/i,wed:/^we(d(nesday)?)?/i,thu:/^th(u(r(s(day)?)?)?)?/i,fri:/^fr(i(day)?)?/i,sat:/^sa(t(urday)?)?/i,future:/^next/i,past:/^last|past|prev(ious)?/i,add:/^(\\+|after|from)/i,subtract:/^(\\-|before|ago)/i,yesterday:/^yesterday/i,today:/^t(oday)?/i,tomorrow:/^tomorrow/i,now:/^n(ow)?/i,millisecond:/^ms|milli(second)?s?/i,second:/^sec(ond)?s?/i,minute:/^min(ute)?s?/i,hour:/^h(ou)?rs?/i,week:/^w(ee)?k/i,month:/^m(o(nth)?s?)?/i,day:/^d(ays?)?/i,year:/^y((ea)?rs?)?/i,shortMeridian:/^(a|p)/i,longMeridian:/^(a\\.?m?\\.?|p\\.?m?\\.?)/i,timezone:/^((e(s|d)t|c(s|d)t|m(s|d)t|p(s|d)t)|((gmt)?\\s*(\\+|\\-)\\s*\\d\\d\\d\\d?)|gmt)/i,ordinalSuffix:/^\\s*(st|nd|rd|th)/i,timeContext:/^\\s*(\\:|a|p)/i},abbreviatedTimeZoneStandard:{GMT:\"-000\",EST:\"-0400\",CST:\"-0500\",MST:\"-0600\",PST:\"-0700\"},abbreviatedTimeZoneDST:{GMT:\"-000\",EDT:\"-0500\",CDT:\"-0600\",MDT:\"-0700\",PDT:\"-0800\"}};\nDate.getMonthNumberFromName=function(name){var n=Date.CultureInfo.monthNames,m=Date.CultureInfo.abbreviatedMonthNames,s=name.toLowerCase();for(var i=0;idate)?1:(this=start.getTime()&&t<=end.getTime();};Date.prototype.addMilliseconds=function(value){this.setMilliseconds(this.getMilliseconds()+value);return this;};Date.prototype.addSeconds=function(value){return this.addMilliseconds(value*1000);};Date.prototype.addMinutes=function(value){return this.addMilliseconds(value*60000);};Date.prototype.addHours=function(value){return this.addMilliseconds(value*3600000);};Date.prototype.addDays=function(value){return this.addMilliseconds(value*86400000);};Date.prototype.addWeeks=function(value){return this.addMilliseconds(value*604800000);};Date.prototype.addMonths=function(value){var n=this.getDate();this.setDate(1);this.setMonth(this.getMonth()+value);this.setDate(Math.min(n,this.getDaysInMonth()));return this;};Date.prototype.addYears=function(value){return this.addMonths(value*12);};Date.prototype.add=function(config){if(typeof config==\"number\"){this._orient=config;return this;}\nvar x=config;if(x.millisecond||x.milliseconds){this.addMilliseconds(x.millisecond||x.milliseconds);}\nif(x.second||x.seconds){this.addSeconds(x.second||x.seconds);}\nif(x.minute||x.minutes){this.addMinutes(x.minute||x.minutes);}\nif(x.hour||x.hours){this.addHours(x.hour||x.hours);}\nif(x.month||x.months){this.addMonths(x.month||x.months);}\nif(x.year||x.years){this.addYears(x.year||x.years);}\nif(x.day||x.days){this.addDays(x.day||x.days);}\nreturn this;};Date._validate=function(value,min,max,name){if(typeof value!=\"number\"){throw new TypeError(value+\" is not a Number.\");}else if(valuemax){throw new RangeError(value+\" is not a valid value for \"+name+\".\");}\nreturn true;};Date.validateMillisecond=function(n){return Date._validate(n,0,999,\"milliseconds\");};Date.validateSecond=function(n){return Date._validate(n,0,59,\"seconds\");};Date.validateMinute=function(n){return Date._validate(n,0,59,\"minutes\");};Date.validateHour=function(n){return Date._validate(n,0,23,\"hours\");};Date.validateDay=function(n,year,month){return Date._validate(n,1,Date.getDaysInMonth(year,month),\"days\");};Date.validateMonth=function(n){return Date._validate(n,0,11,\"months\");};Date.validateYear=function(n){return Date._validate(n,1,9999,\"seconds\");};Date.prototype.set=function(config){var x=config;if(!x.millisecond&&x.millisecond!==0){x.millisecond=-1;}\nif(!x.second&&x.second!==0){x.second=-1;}\nif(!x.minute&&x.minute!==0){x.minute=-1;}\nif(!x.hour&&x.hour!==0){x.hour=-1;}\nif(!x.day&&x.day!==0){x.day=-1;}\nif(!x.month&&x.month!==0){x.month=-1;}\nif(!x.year&&x.year!==0){x.year=-1;}\nif(x.millisecond!=-1&&Date.validateMillisecond(x.millisecond)){this.addMilliseconds(x.millisecond-this.getMilliseconds());}\nif(x.second!=-1&&Date.validateSecond(x.second)){this.addSeconds(x.second-this.getSeconds());}\nif(x.minute!=-1&&Date.validateMinute(x.minute)){this.addMinutes(x.minute-this.getMinutes());}\nif(x.hour!=-1&&Date.validateHour(x.hour)){this.addHours(x.hour-this.getHours());}\nif(x.month!==-1&&Date.validateMonth(x.month)){this.addMonths(x.month-this.getMonth());}\nif(x.year!=-1&&Date.validateYear(x.year)){this.addYears(x.year-this.getFullYear());}\nif(x.day!=-1&&Date.validateDay(x.day,this.getFullYear(),this.getMonth())){this.addDays(x.day-this.getDate());}\nif(x.timezone){this.setTimezone(x.timezone);}\nif(x.timezoneOffset){this.setTimezoneOffset(x.timezoneOffset);}\nreturn this;};Date.prototype.clearTime=function(){this.setHours(0);this.setMinutes(0);this.setSeconds(0);this.setMilliseconds(0);return this;};Date.prototype.isLeapYear=function(){var y=this.getFullYear();return(((y%4===0)&&(y%100!==0))||(y%400===0));};Date.prototype.isWeekday=function(){return!(this.is().sat()||this.is().sun());};Date.prototype.getDaysInMonth=function(){return Date.getDaysInMonth(this.getFullYear(),this.getMonth());};Date.prototype.moveToFirstDayOfMonth=function(){return this.set({day:1});};Date.prototype.moveToLastDayOfMonth=function(){return this.set({day:this.getDaysInMonth()});};Date.prototype.moveToDayOfWeek=function(day,orient){var diff=(day-this.getDay()+7*(orient||+1))%7;return this.addDays((diff===0)?diff+=7*(orient||+1):diff);};Date.prototype.moveToMonth=function(month,orient){var diff=(month-this.getMonth()+12*(orient||+1))%12;return this.addMonths((diff===0)?diff+=12*(orient||+1):diff);};Date.prototype.getDayOfYear=function(){return Math.floor((this-new Date(this.getFullYear(),0,1))/86400000);};Date.prototype.getWeekOfYear=function(firstDayOfWeek){var y=this.getFullYear(),m=this.getMonth(),d=this.getDate();var dow=firstDayOfWeek||Date.CultureInfo.firstDayOfWeek;var offset=7+1-new Date(y,0,1).getDay();if(offset==8){offset=1;}\nvar daynum=((Date.UTC(y,m,d,0,0,0)-Date.UTC(y,0,1,0,0,0))/86400000)+1;var w=Math.floor((daynum-offset+7)/7);if(w===dow){y--;var prevOffset=7+1-new Date(y,0,1).getDay();if(prevOffset==2||prevOffset==8){w=53;}else{w=52;}}\nreturn w;};Date.prototype.isDST=function(){console.log('isDST');return this.toString().match(/(E|C|M|P)(S|D)T/)[2]==\"D\";};Date.prototype.getTimezone=function(){return Date.getTimezoneAbbreviation(this.getUTCOffset,this.isDST());};Date.prototype.setTimezoneOffset=function(s){var here=this.getTimezoneOffset(),there=Number(s)*-6/10;this.addMinutes(there-here);return this;};Date.prototype.setTimezone=function(s){return this.setTimezoneOffset(Date.getTimezoneOffset(s));};Date.prototype.getUTCOffset=function(){var n=this.getTimezoneOffset()*-10/6,r;if(n<0){r=(n-10000).toString();return r[0]+r.substr(2);}else{r=(n+10000).toString();return\"+\"+r.substr(1);}};Date.prototype.getDayName=function(abbrev){return abbrev?Date.CultureInfo.abbreviatedDayNames[this.getDay()]:Date.CultureInfo.dayNames[this.getDay()];};Date.prototype.getMonthName=function(abbrev){return abbrev?Date.CultureInfo.abbreviatedMonthNames[this.getMonth()]:Date.CultureInfo.monthNames[this.getMonth()];};Date.prototype._toString=Date.prototype.toString;Date.prototype.toString=function(format){var self=this;var p=function p(s){return(s.toString().length==1)?\"0\"+s:s;};return format?format.replace(/dd?d?d?|MM?M?M?|yy?y?y?|hh?|HH?|mm?|ss?|tt?|zz?z?/g,function(format){switch(format){case\"hh\":return p(self.getHours()<13?self.getHours():(self.getHours()-12));case\"h\":return self.getHours()<13?self.getHours():(self.getHours()-12);case\"HH\":return p(self.getHours());case\"H\":return self.getHours();case\"mm\":return p(self.getMinutes());case\"m\":return self.getMinutes();case\"ss\":return p(self.getSeconds());case\"s\":return self.getSeconds();case\"yyyy\":return self.getFullYear();case\"yy\":return self.getFullYear().toString().substring(2,4);case\"dddd\":return self.getDayName();case\"ddd\":return self.getDayName(true);case\"dd\":return p(self.getDate());case\"d\":return self.getDate().toString();case\"MMMM\":return self.getMonthName();case\"MMM\":return self.getMonthName(true);case\"MM\":return p((self.getMonth()+1));case\"M\":return self.getMonth()+1;case\"t\":return self.getHours()<12?Date.CultureInfo.amDesignator.substring(0,1):Date.CultureInfo.pmDesignator.substring(0,1);case\"tt\":return self.getHours()<12?Date.CultureInfo.amDesignator:Date.CultureInfo.pmDesignator;case\"zzz\":case\"zz\":case\"z\":return\"\";}}):this._toString();};\nDate.now=function(){return new Date();};Date.today=function(){return Date.now().clearTime();};Date.prototype._orient=+1;Date.prototype.next=function(){this._orient=+1;return this;};Date.prototype.last=Date.prototype.prev=Date.prototype.previous=function(){this._orient=-1;return this;};Date.prototype._is=false;Date.prototype.is=function(){this._is=true;return this;};Number.prototype._dateElement=\"day\";Number.prototype.fromNow=function(){var c={};c[this._dateElement]=this;return Date.now().add(c);};Number.prototype.ago=function(){var c={};c[this._dateElement]=this*-1;return Date.now().add(c);};(function(){var $D=Date.prototype,$N=Number.prototype;var dx=(\"sunday monday tuesday wednesday thursday friday saturday\").split(/\\s/),mx=(\"january february march april may june july august september october november december\").split(/\\s/),px=(\"Millisecond Second Minute Hour Day Week Month Year\").split(/\\s/),de;var df=function(n){return function(){if(this._is){this._is=false;return this.getDay()==n;}\nreturn this.moveToDayOfWeek(n,this._orient);};};for(var i=0;i0&&!last){try{q=d.call(this,r[1]);}catch(ex){last=true;}}else{last=true;}\nif(!last&&q[1].length===0){last=true;}\nif(!last){var qx=[];for(var j=0;j0){rx[0]=rx[0].concat(p[0]);rx[1]=p[1];}}\nif(rx[1].length1){args=Array.prototype.slice.call(arguments);}else if(arguments[0]instanceof Array){args=arguments[0];}\nif(args){for(var i=0,px=args.shift();i2)?n:(n+(((n+2000)Date.getDaysInMonth(this.year,this.month)){throw new RangeError(this.day+\" is not a valid value for days.\");}\nvar r=new Date(this.year,this.month,this.day,this.hour,this.minute,this.second);if(this.timezone){r.set({timezone:this.timezone});}else if(this.timezoneOffset){r.set({timezoneOffset:this.timezoneOffset});}\nreturn r;},finish:function(x){x=(x instanceof Array)?flattenAndCompact(x):[x];if(x.length===0){return null;}\nfor(var i=0;i