diff --git a/CumulusMX/Api.cs b/CumulusMX/Api.cs index 877f1bd6..afebfe1d 100644 --- a/CumulusMX/Api.cs +++ b/CumulusMX/Api.cs @@ -14,118 +14,118 @@ namespace CumulusMX { - public static class Api - { - private const string RelativePath = "/api/"; - internal static WeatherStation Station; - public static StationSettings stationSettings; - public static InternetSettings internetSettings; - public static CalibrationSettings calibrationSettings; - public static NOAASettings noaaSettings; - public static MysqlSettings mySqlSettings; - internal static DataEditor dataEditor; - - public static string Utf16ToUtf8(string utf16String) - { - /************************************************************** - * Every .NET string will store text with the UTF16 encoding, * - * known as Encoding.Unicode. Other encodings may exist as * - * Byte-Array or incorrectly stored with the UTF16 encoding. * - * * - * UTF8 = 1 bytes per char * - * ["100" for the ansi 'd'] * - * ["206" and "186" for the russian 'κ'] * - * * - * UTF16 = 2 bytes per char * - * ["100, 0" for the ansi 'd'] * - * ["186, 3" for the russian 'κ'] * - * * - * UTF8 inside UTF16 * - * ["100, 0" for the ansi 'd'] * - * ["206, 0" and "186, 0" for the russian 'κ'] * - * * - * We can use the convert encoding function to convert an * - * UTF16 Byte-Array to an UTF8 Byte-Array. When we use UTF8 * - * encoding to string method now, we will get a UTF16 string. * - * * - * So we imitate UTF16 by filling the second byte of a char * - * with a 0 byte (binary 0) while creating the string. * - **************************************************************/ - - // Storage for the UTF8 string - string utf8String = String.Empty; - - // Get UTF16 bytes and convert UTF16 bytes to UTF8 bytes - byte[] utf16Bytes = Encoding.Unicode.GetBytes(utf16String); - byte[] utf8Bytes = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, utf16Bytes); - - // Fill UTF8 bytes inside UTF8 string - for (int i = 0; i < utf8Bytes.Length; i++) - { - // Because char always saves 2 bytes, fill char with 0 - byte[] utf8Container = new byte[2] { utf8Bytes[i], 0 }; - utf8String += BitConverter.ToChar(utf8Container, 0); - } - - // Return UTF8 - return utf8String; - } - - private static string EscapeUnicode(string input) - { - StringBuilder sb = new StringBuilder(input.Length); - foreach (char ch in input) - { - if (ch <= 0x7f) - sb.Append(ch); - else - sb.AppendFormat(CultureInfo.InvariantCulture, "\\u{0:x4}", (int)ch); - } - return sb.ToString(); - } - - public static void Setup(WebServer server) - { - server.RegisterModule(new WebApiModule()); - - server.Module<WebApiModule>().RegisterController<GraphDataController>(); - server.Module<WebApiModule>().RegisterController<DataController>(); - server.Module<WebApiModule>().RegisterController<RecordsController>(); - server.Module<WebApiModule>().RegisterController<TodayYestDataController>(); - server.Module<WebApiModule>().RegisterController<ExtraDataController>(); - server.Module<WebApiModule>().RegisterController<GetSettingsController>(); - server.Module<WebApiModule>().RegisterController<SetSettingsController>(); + public static class Api + { + private const string RelativePath = "/api/"; + internal static WeatherStation Station; + public static StationSettings stationSettings; + public static InternetSettings internetSettings; + public static CalibrationSettings calibrationSettings; + public static NOAASettings noaaSettings; + public static MysqlSettings mySqlSettings; + internal static DataEditor dataEditor; + + public static string Utf16ToUtf8(string utf16String) + { + /************************************************************** + * Every .NET string will store text with the UTF16 encoding, * + * known as Encoding.Unicode. Other encodings may exist as * + * Byte-Array or incorrectly stored with the UTF16 encoding. * + * * + * UTF8 = 1 bytes per char * + * ["100" for the ansi 'd'] * + * ["206" and "186" for the russian 'κ'] * + * * + * UTF16 = 2 bytes per char * + * ["100, 0" for the ansi 'd'] * + * ["186, 3" for the russian 'κ'] * + * * + * UTF8 inside UTF16 * + * ["100, 0" for the ansi 'd'] * + * ["206, 0" and "186, 0" for the russian 'κ'] * + * * + * We can use the convert encoding function to convert an * + * UTF16 Byte-Array to an UTF8 Byte-Array. When we use UTF8 * + * encoding to string method now, we will get a UTF16 string. * + * * + * So we imitate UTF16 by filling the second byte of a char * + * with a 0 byte (binary 0) while creating the string. * + **************************************************************/ + + // Storage for the UTF8 string + string utf8String = String.Empty; + + // Get UTF16 bytes and convert UTF16 bytes to UTF8 bytes + byte[] utf16Bytes = Encoding.Unicode.GetBytes(utf16String); + byte[] utf8Bytes = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, utf16Bytes); + + // Fill UTF8 bytes inside UTF8 string + for (int i = 0; i < utf8Bytes.Length; i++) + { + // Because char always saves 2 bytes, fill char with 0 + byte[] utf8Container = new byte[2] { utf8Bytes[i], 0 }; + utf8String += BitConverter.ToChar(utf8Container, 0); + } + + // Return UTF8 + return utf8String; + } + + private static string EscapeUnicode(string input) + { + StringBuilder sb = new StringBuilder(input.Length); + foreach (char ch in input) + { + if (ch <= 0x7f) + sb.Append(ch); + else + sb.AppendFormat(CultureInfo.InvariantCulture, "\\u{0:x4}", (int)ch); + } + return sb.ToString(); + } + + public static void Setup(WebServer server) + { + server.RegisterModule(new WebApiModule()); + + server.Module<WebApiModule>().RegisterController<GraphDataController>(); + server.Module<WebApiModule>().RegisterController<DataController>(); + server.Module<WebApiModule>().RegisterController<RecordsController>(); + server.Module<WebApiModule>().RegisterController<TodayYestDataController>(); + server.Module<WebApiModule>().RegisterController<ExtraDataController>(); + server.Module<WebApiModule>().RegisterController<GetSettingsController>(); + server.Module<WebApiModule>().RegisterController<SetSettingsController>(); server.Module<WebApiModule>().RegisterController<EditControllerGet>(); server.Module<WebApiModule>().RegisterController<EditControllerPost>(); } public class EditControllerGet : WebApiController - { + { public EditControllerGet(IHttpContext context) : base(context) { } - [WebApiHandler(HttpVerbs.Get, RelativePath + "edit/*")] + [WebApiHandler(HttpVerbs.Get, RelativePath + "edit/*")] public bool EditData() { try - { + { // read the last segment of the URL to determine what data the caller wants string lastSegment = Request.Url.Segments.Last(); - - switch (lastSegment) - { - case "raintodayeditdata.json": - return this.JsonResponse(dataEditor.GetRainTodayEditData()); - - case "raintoday": - return this.JsonResponse(dataEditor.EditRainToday(this)); - } - - throw new KeyNotFoundException("Key Not Found: " + lastSegment); - } - catch (Exception ex) - { + + switch (lastSegment) + { + case "raintodayeditdata.json": + return this.JsonResponse(dataEditor.GetRainTodayEditData()); + + case "raintoday": + return this.JsonResponse(dataEditor.EditRainToday(this)); + } + + throw new KeyNotFoundException("Key Not Found: " + lastSegment); + } + catch (Exception ex) + { return HandleError(ex, 404); } @@ -134,19 +134,19 @@ public bool EditData() private bool HandleError(Exception ex, int statusCode) { var errorResponse = new - { - Title = "Unexpected Error", - ErrorCode = ex.GetType().Name, - Description = ex.Message, - }; + { + Title = "Unexpected Error", + ErrorCode = ex.GetType().Name, + Description = ex.Message, + }; this.Response.StatusCode = statusCode; - return this.JsonResponse(errorResponse); - } - } + return this.JsonResponse(errorResponse); + } + } - public class EditControllerPost : WebApiController - { + public class EditControllerPost : WebApiController + { public EditControllerPost(IHttpContext context) : base(context) { } @@ -155,17 +155,17 @@ public EditControllerPost(IHttpContext context) : base(context) public bool EditData() { try - { + { // read the last segment of the URL to determine what data the caller wants string lastSegment = Request.Url.Segments.Last(); - switch (lastSegment) - { - case "raintodayeditdata.json": - return this.JsonResponse(dataEditor.GetRainTodayEditData()); + switch (lastSegment) + { + case "raintodayeditdata.json": + return this.JsonResponse(dataEditor.GetRainTodayEditData()); - case "raintoday": - return this.JsonResponse(dataEditor.EditRainToday(this)); + case "raintoday": + return this.JsonResponse(dataEditor.EditRainToday(this)); case "diarydata": return this.JsonResponse(dataEditor.EditDiary(this)); @@ -175,30 +175,30 @@ public bool EditData() } throw new KeyNotFoundException("Key Not Found: " + lastSegment); - } - catch (Exception ex) - { - return HandleError(ex, 404); - } + } + catch (Exception ex) + { + return HandleError(ex, 404); + } - } + } private bool HandleError(Exception ex, int statusCode) { var errorResponse = new - { - Title = "Unexpected Error", - ErrorCode = ex.GetType().Name, - Description = ex.Message, - }; + { + Title = "Unexpected Error", + ErrorCode = ex.GetType().Name, + Description = ex.Message, + }; this.Response.StatusCode = statusCode; - return this.JsonResponse(errorResponse); - } - } + return this.JsonResponse(errorResponse); + } + } - public class DataController : WebApiController - { + public class DataController : WebApiController + { public DataController(IHttpContext context) : base(context) { } @@ -207,28 +207,28 @@ public DataController(IHttpContext context) : base(context) public bool GetData() { try - { - // read the last segment of the URL to determine what data the caller wants - var lastSegment = Request.Url.Segments.Last(); + { + // read the last segment of the URL to determine what data the caller wants + var lastSegment = Request.Url.Segments.Last(); - var query = HttpUtility.ParseQueryString(Request.Url.Query); - var date = query["date"]; + var query = HttpUtility.ParseQueryString(Request.Url.Query); + var date = query["date"]; var year = query["year"]; - var month = query["month"]; - var draw = query["draw"]; - int start = Convert.ToInt32(query["start"]); - int length = Convert.ToInt32(query["length"]); - - switch (lastSegment) - { - case "dayfile": - return this.JsonResponse(Station.GetDayfile(draw,start,length)); - case "logfile": - return this.JsonResponse(Station.GetLogfile(month,draw,start,length,false)); - case "extralogfile": - return this.JsonResponse(Station.GetLogfile(month, draw, start, length, true)); - case "currentdata": - return this.JsonResponse(Station.GetCurrentData()); + var month = query["month"]; + var draw = query["draw"]; + int start = Convert.ToInt32(query["start"]); + int length = Convert.ToInt32(query["length"]); + + switch (lastSegment) + { + case "dayfile": + return this.JsonResponse(Station.GetDayfile(draw,start,length)); + case "logfile": + return this.JsonResponse(Station.GetLogfile(month,draw,start,length,false)); + case "extralogfile": + return this.JsonResponse(Station.GetLogfile(month, draw, start, length, true)); + case "currentdata": + return this.JsonResponse(Station.GetCurrentData()); case "diarydata": return this.JsonResponse(Station.GetDiaryData(date)); case "diarysummary": @@ -237,30 +237,29 @@ public bool GetData() } throw new KeyNotFoundException("Key Not Found: " + lastSegment); - } - catch (Exception ex) - { - return HandleError(ex, 404); - } - - } + } + catch (Exception ex) + { + return HandleError(ex, 404); + } + } private bool HandleError(Exception ex, int statusCode) { var errorResponse = new - { - Title = "Unexpected Error", - ErrorCode = ex.GetType().Name, - Description = ex.Message, - }; - - this.Response.StatusCode = statusCode; - return this.JsonResponse(errorResponse); - } - } - - public class GraphDataController : WebApiController - { + { + Title = "Unexpected Error", + ErrorCode = ex.GetType().Name, + Description = ex.Message, + }; + + this.Response.StatusCode = statusCode; + return this.JsonResponse(errorResponse); + } + } + + public class GraphDataController : WebApiController + { public GraphDataController(IHttpContext context) : base(context) { } @@ -269,77 +268,76 @@ public GraphDataController(IHttpContext context) : base(context) public bool GetGraphData() { try - { - // read the last segment of the URL to determine what data the caller wants - var lastSegment = Request.Url.Segments.Last(); - - switch (lastSegment) - { - case "tempdata.json": - return this.JsonResponse(Station.GetTempGraphData()); - case "tempdatad3.json": - return this.JsonResponse(Station.GetTempGraphDataD3()); - case "winddata.json": - return this.JsonResponse(Station.GetWindGraphData()); - case "winddatad3.json": - return this.JsonResponse(Station.GetWindGraphDataD3()); - case "raindata.json": - return this.JsonResponse(Station.GetRainGraphData()); - case "raindatad3.json": - return this.JsonResponse(Station.GetRainGraphDataD3()); - case "pressdata.json": - return this.JsonResponse(Station.GetPressGraphData()); - case "pressdatad3.json": - return this.JsonResponse(Station.GetPressGraphDataD3()); - case "wdirdata.json": - return this.JsonResponse(Station.GetWindDirGraphData()); - case "wdirdatad3.json": - return this.JsonResponse(Station.GetWindDirGraphDataD3()); - case "humdata.json": - return this.JsonResponse(Station.GetHumGraphData()); - case "humdatad3.json": - return this.JsonResponse(Station.GetHumGraphDataD3()); - case "solardata.json": - return this.JsonResponse(Station.GetSolarGraphData()); - case "solardatad3.json": - return this.JsonResponse(Station.GetSolarGraphDataD3()); - case "dailyrain.json": - return this.JsonResponse(Station.GetDailyRainGraphData()); - case "sunhours.json": - return this.JsonResponse(Station.GetSunHoursGraphData()); - case "dailytemp.json": - return this.JsonResponse(Station.GetDailyTempGraphData()); - case "units.json": - return this.JsonResponse(Station.GetUnits()); - case "graphconfig.json": - return this.JsonResponse(Station.GetGraphConfig()); - } - - throw new KeyNotFoundException("Key Not Found: " + lastSegment); - } - catch (Exception ex) - { - return HandleError(ex, 404); - } - - } + { + // read the last segment of the URL to determine what data the caller wants + var lastSegment = Request.Url.Segments.Last(); + + switch (lastSegment) + { + case "tempdata.json": + return this.JsonResponse(Station.GetTempGraphData()); + case "tempdatad3.json": + return this.JsonResponse(Station.GetTempGraphDataD3()); + case "winddata.json": + return this.JsonResponse(Station.GetWindGraphData()); + case "winddatad3.json": + return this.JsonResponse(Station.GetWindGraphDataD3()); + case "raindata.json": + return this.JsonResponse(Station.GetRainGraphData()); + case "raindatad3.json": + return this.JsonResponse(Station.GetRainGraphDataD3()); + case "pressdata.json": + return this.JsonResponse(Station.GetPressGraphData()); + case "pressdatad3.json": + return this.JsonResponse(Station.GetPressGraphDataD3()); + case "wdirdata.json": + return this.JsonResponse(Station.GetWindDirGraphData()); + case "wdirdatad3.json": + return this.JsonResponse(Station.GetWindDirGraphDataD3()); + case "humdata.json": + return this.JsonResponse(Station.GetHumGraphData()); + case "humdatad3.json": + return this.JsonResponse(Station.GetHumGraphDataD3()); + case "solardata.json": + return this.JsonResponse(Station.GetSolarGraphData()); + case "solardatad3.json": + return this.JsonResponse(Station.GetSolarGraphDataD3()); + case "dailyrain.json": + return this.JsonResponse(Station.GetDailyRainGraphData()); + case "sunhours.json": + return this.JsonResponse(Station.GetSunHoursGraphData()); + case "dailytemp.json": + return this.JsonResponse(Station.GetDailyTempGraphData()); + case "units.json": + return this.JsonResponse(Station.GetUnits()); + case "graphconfig.json": + return this.JsonResponse(Station.GetGraphConfig()); + } + + throw new KeyNotFoundException("Key Not Found: " + lastSegment); + } + catch (Exception ex) + { + return HandleError(ex, 404); + } + } private bool HandleError(Exception ex, int statusCode) { var errorResponse = new - { - Title = "Unexpected Error", - ErrorCode = ex.GetType().Name, - Description = ex.Message, - }; - - this.Response.StatusCode = statusCode; - return this.JsonResponse(errorResponse); - } - } - - public class RecordsController : WebApiController - { + { + Title = "Unexpected Error", + ErrorCode = ex.GetType().Name, + Description = ex.Message, + }; + + this.Response.StatusCode = statusCode; + return this.JsonResponse(errorResponse); + } + } + + public class RecordsController : WebApiController + { public RecordsController(IHttpContext context) : base(context) { } @@ -348,309 +346,295 @@ public RecordsController(IHttpContext context) : base(context) public bool GetAlltimeData() { try - { - // read the last segment of the URL to determine what data the caller wants - var lastSegment = Request.Url.Segments.Last(); - - switch (lastSegment) - { - case "temperature.json": - return this.JsonResponse(EscapeUnicode(Station.GetTempRecords())); - case "humidity.json": - return this.JsonResponse(EscapeUnicode(Station.GetHumRecords())); - case "pressure.json": - return this.JsonResponse(EscapeUnicode(Station.GetPressRecords())); - case "wind.json": - return this.JsonResponse(EscapeUnicode(Station.GetWindRecords())); - case "rain.json": - return this.JsonResponse(EscapeUnicode(Station.GetRainRecords())); - - } - - throw new KeyNotFoundException("Key Not Found: " + lastSegment); - } - catch (Exception ex) - { - return HandleError(ex, 404); - } - - } - - [WebApiHandler(HttpVerbs.Get, RelativePath + "records/month/*")] + { + // read the last segment of the URL to determine what data the caller wants + var lastSegment = Request.Url.Segments.Last(); + + switch (lastSegment) + { + case "temperature.json": + return this.JsonResponse(EscapeUnicode(Station.GetTempRecords())); + case "humidity.json": + return this.JsonResponse(EscapeUnicode(Station.GetHumRecords())); + case "pressure.json": + return this.JsonResponse(EscapeUnicode(Station.GetPressRecords())); + case "wind.json": + return this.JsonResponse(EscapeUnicode(Station.GetWindRecords())); + case "rain.json": + return this.JsonResponse(EscapeUnicode(Station.GetRainRecords())); + } + + throw new KeyNotFoundException("Key Not Found: " + lastSegment); + } + catch (Exception ex) + { + return HandleError(ex, 404); + } + } + + [WebApiHandler(HttpVerbs.Get, RelativePath + "records/month/*")] public bool GetMonthlyRecordData() { try - { - // read the last segment of the URL to determine what data the caller wants - var lastSegment = Request.Url.Segments.Last(); - // Get penultimate segment and trim off traling slash. This gives the required month - int month = Convert.ToInt32(Request.Url.Segments[Request.Url.Segments.Length - 2].TrimEnd('/')); - - switch (lastSegment) - { - case "temperature.json": - return this.JsonResponse(EscapeUnicode(Station.GetMonthlyTempRecords(month))); - case "humidity.json": - return this.JsonResponse(EscapeUnicode(Station.GetMonthlyHumRecords(month))); - case "pressure.json": - return this.JsonResponse(EscapeUnicode(Station.GetMonthlyPressRecords(month))); - case "wind.json": - return this.JsonResponse(EscapeUnicode(Station.GetMonthlyWindRecords(month))); - case "rain.json": - return this.JsonResponse(EscapeUnicode(Station.GetMonthlyRainRecords(month))); - - } - - throw new KeyNotFoundException("Key Not Found: " + lastSegment); - } - catch (Exception ex) - { - return HandleError(ex, 404); - } - - } - - [WebApiHandler(HttpVerbs.Get, RelativePath + "records/thismonth/*")] + { + // read the last segment of the URL to determine what data the caller wants + var lastSegment = Request.Url.Segments.Last(); + // Get penultimate segment and trim off traling slash. This gives the required month + int month = Convert.ToInt32(Request.Url.Segments[Request.Url.Segments.Length - 2].TrimEnd('/')); + + switch (lastSegment) + { + case "temperature.json": + return this.JsonResponse(EscapeUnicode(Station.GetMonthlyTempRecords(month))); + case "humidity.json": + return this.JsonResponse(EscapeUnicode(Station.GetMonthlyHumRecords(month))); + case "pressure.json": + return this.JsonResponse(EscapeUnicode(Station.GetMonthlyPressRecords(month))); + case "wind.json": + return this.JsonResponse(EscapeUnicode(Station.GetMonthlyWindRecords(month))); + case "rain.json": + return this.JsonResponse(EscapeUnicode(Station.GetMonthlyRainRecords(month))); + } + + throw new KeyNotFoundException("Key Not Found: " + lastSegment); + } + catch (Exception ex) + { + return HandleError(ex, 404); + } + } + + [WebApiHandler(HttpVerbs.Get, RelativePath + "records/thismonth/*")] public bool GetThisMonthRecordData() { try - { - // read the last segment of the URL to determine what data the caller wants - var lastSegment = Request.Url.Segments.Last(); - - switch (lastSegment) - { - case "temperature.json": - return this.JsonResponse(EscapeUnicode(Station.GetThisMonthTempRecords())); - case "humidity.json": - return this.JsonResponse(EscapeUnicode(Station.GetThisMonthHumRecords())); - case "pressure.json": - return this.JsonResponse(EscapeUnicode(Station.GetThisMonthPressRecords())); - case "wind.json": - return this.JsonResponse(EscapeUnicode(Station.GetThisMonthWindRecords())); - case "rain.json": - return this.JsonResponse(EscapeUnicode(Station.GetThisMonthRainRecords())); - - } - - throw new KeyNotFoundException("Key Not Found: " + lastSegment); - } - catch (Exception ex) - { - return HandleError(ex, 404); - } - - } - - [WebApiHandler(HttpVerbs.Get, RelativePath + "records/thisyear/*")] + { + // read the last segment of the URL to determine what data the caller wants + var lastSegment = Request.Url.Segments.Last(); + + switch (lastSegment) + { + case "temperature.json": + return this.JsonResponse(EscapeUnicode(Station.GetThisMonthTempRecords())); + case "humidity.json": + return this.JsonResponse(EscapeUnicode(Station.GetThisMonthHumRecords())); + case "pressure.json": + return this.JsonResponse(EscapeUnicode(Station.GetThisMonthPressRecords())); + case "wind.json": + return this.JsonResponse(EscapeUnicode(Station.GetThisMonthWindRecords())); + case "rain.json": + return this.JsonResponse(EscapeUnicode(Station.GetThisMonthRainRecords())); + } + + throw new KeyNotFoundException("Key Not Found: " + lastSegment); + } + catch (Exception ex) + { + return HandleError(ex, 404); + } + } + + [WebApiHandler(HttpVerbs.Get, RelativePath + "records/thisyear/*")] public bool GetThisYearRecordData() { try - { - // read the last segment of the URL to determine what data the caller wants - var lastSegment = Request.Url.Segments.Last(); - - switch (lastSegment) - { - case "temperature.json": - return this.JsonResponse(EscapeUnicode(Station.GetThisYearTempRecords())); - case "humidity.json": - return this.JsonResponse(EscapeUnicode(Station.GetThisYearHumRecords())); - case "pressure.json": - return this.JsonResponse(EscapeUnicode(Station.GetThisYearPressRecords())); - case "wind.json": - return this.JsonResponse(EscapeUnicode(Station.GetThisYearWindRecords())); - case "rain.json": - return this.JsonResponse(EscapeUnicode(Station.GetThisYearRainRecords())); - - } - - throw new KeyNotFoundException("Key Not Found: " + lastSegment); - } - catch (Exception ex) - { - return HandleError(ex, 404); - } - - } + { + // read the last segment of the URL to determine what data the caller wants + var lastSegment = Request.Url.Segments.Last(); + + switch (lastSegment) + { + case "temperature.json": + return this.JsonResponse(EscapeUnicode(Station.GetThisYearTempRecords())); + case "humidity.json": + return this.JsonResponse(EscapeUnicode(Station.GetThisYearHumRecords())); + case "pressure.json": + return this.JsonResponse(EscapeUnicode(Station.GetThisYearPressRecords())); + case "wind.json": + return this.JsonResponse(EscapeUnicode(Station.GetThisYearWindRecords())); + case "rain.json": + return this.JsonResponse(EscapeUnicode(Station.GetThisYearRainRecords())); + } + + throw new KeyNotFoundException("Key Not Found: " + lastSegment); + } + catch (Exception ex) + { + return HandleError(ex, 404); + } + } private bool HandleError(Exception ex, int statusCode) { var errorResponse = new - { - Title = "Unexpected Error", - ErrorCode = ex.GetType().Name, - Description = ex.Message, - }; - - this.Response.StatusCode = statusCode; - return this.JsonResponse(errorResponse); - } - } - - public class TodayYestDataController : WebApiController - { + { + Title = "Unexpected Error", + ErrorCode = ex.GetType().Name, + Description = ex.Message, + }; + + this.Response.StatusCode = statusCode; + return this.JsonResponse(errorResponse); + } + } + + public class TodayYestDataController : WebApiController + { public TodayYestDataController(IHttpContext context) : base(context) {} [WebApiHandler(HttpVerbs.Get, RelativePath + "todayyest/*")] public bool GetYesterdayData() { try - { - // read the last segment of the URL to determine what data the caller wants - var lastSegment = Request.Url.Segments.Last(); - - switch (lastSegment) - { - case "temp.json": - return this.JsonResponse(Station.GetTodayYestTemp()); - case "hum.json": - return this.JsonResponse(Station.GetTodayYestHum()); - case "rain.json": - return this.JsonResponse(Station.GetTodayYestRain()); - case "wind.json": - return this.JsonResponse(Station.GetTodayYestWind()); - case "pressure.json": - return this.JsonResponse(Station.GetTodayYestPressure()); - case "solar.json": - return this.JsonResponse(Station.GetTodayYestSolar()); - - } - - throw new KeyNotFoundException("Key Not Found: " + lastSegment); - } - catch (Exception ex) - { - return HandleError(ex, 404); - } - - } + { + // read the last segment of the URL to determine what data the caller wants + var lastSegment = Request.Url.Segments.Last(); + + switch (lastSegment) + { + case "temp.json": + return this.JsonResponse(Station.GetTodayYestTemp()); + case "hum.json": + return this.JsonResponse(Station.GetTodayYestHum()); + case "rain.json": + return this.JsonResponse(Station.GetTodayYestRain()); + case "wind.json": + return this.JsonResponse(Station.GetTodayYestWind()); + case "pressure.json": + return this.JsonResponse(Station.GetTodayYestPressure()); + case "solar.json": + return this.JsonResponse(Station.GetTodayYestSolar()); + } + + throw new KeyNotFoundException("Key Not Found: " + lastSegment); + } + catch (Exception ex) + { + return HandleError(ex, 404); + } + } private bool HandleError(Exception ex, int statusCode) { var errorResponse = new - { - Title = "Unexpected Error", - ErrorCode = ex.GetType().Name, - Description = ex.Message, - }; - - this.Response.StatusCode = statusCode; - return this.JsonResponse(errorResponse); - } - } - - public class ExtraDataController : WebApiController - { + { + Title = "Unexpected Error", + ErrorCode = ex.GetType().Name, + Description = ex.Message, + }; + + this.Response.StatusCode = statusCode; + return this.JsonResponse(errorResponse); + } + } + + public class ExtraDataController : WebApiController + { public ExtraDataController(IHttpContext context) : base(context) { } [WebApiHandler(HttpVerbs.Get, RelativePath + "extra/*")] public bool GetExtraData() { try - { - // read the last segment of the URL to determine what data the caller wants - var lastSegment = Request.Url.Segments.Last(); - - switch (lastSegment) - { - case "temp.json": - return this.JsonResponse(Station.GetExtraTemp()); - case "hum.json": - return this.JsonResponse(Station.GetExtraHum()); - case "dew.json": - return this.JsonResponse(Station.GetExtraDew()); - case "soiltemp.json": - return this.JsonResponse(Station.GetSoilTemp()); - case "soilmoisture.json": - return this.JsonResponse(Station.GetSoilMoisture()); - case "leaf.json": - return this.JsonResponse(Station.GetLeaf()); - case "leaf4.json": - return this.JsonResponse(Station.GetLeaf4()); - - } - - throw new KeyNotFoundException("Key Not Found: " + lastSegment); - } - catch (Exception ex) - { - return HandleError(ex, 404); - } - - } + { + // read the last segment of the URL to determine what data the caller wants + var lastSegment = Request.Url.Segments.Last(); + + switch (lastSegment) + { + case "temp.json": + return this.JsonResponse(Station.GetExtraTemp()); + case "hum.json": + return this.JsonResponse(Station.GetExtraHum()); + case "dew.json": + return this.JsonResponse(Station.GetExtraDew()); + case "soiltemp.json": + return this.JsonResponse(Station.GetSoilTemp()); + case "soilmoisture.json": + return this.JsonResponse(Station.GetSoilMoisture()); + case "leaf.json": + return this.JsonResponse(Station.GetLeaf()); + case "leaf4.json": + return this.JsonResponse(Station.GetLeaf4()); + } + + throw new KeyNotFoundException("Key Not Found: " + lastSegment); + } + catch (Exception ex) + { + return HandleError(ex, 404); + } + } private bool HandleError(Exception ex, int statusCode) { var errorResponse = new - { - Title = "Unexpected Error", - ErrorCode = ex.GetType().Name, - Description = ex.Message, - }; - - this.Response.StatusCode = statusCode; - return this.JsonResponse(errorResponse); - } - } - - public class SetSettingsController : WebApiController - { + { + Title = "Unexpected Error", + ErrorCode = ex.GetType().Name, + Description = ex.Message, + }; + + this.Response.StatusCode = statusCode; + return this.JsonResponse(errorResponse); + } + } + + public class SetSettingsController : WebApiController + { public SetSettingsController(IHttpContext context) : base(context) { } [WebApiHandler(HttpVerbs.Post, RelativePath + "setsettings/*")] public bool SettingsSet() { try - { - // read the last segment of the URL to determine what data the caller wants - var lastSegment = Request.Url.Segments.Last(); - - switch (lastSegment) - { - - case "updatestationconfig.json": - return this.JsonResponse(stationSettings.UpdateStationConfig(this)); - case "updateinternetconfig.json": - return this.JsonResponse(internetSettings.UpdateInternetConfig(this)); - case "updatecalibrationconfig.json": - return this.JsonResponse(calibrationSettings.UpdateCalibrationConfig(this)); - case "updatenoaaconfig.json": - return this.JsonResponse(noaaSettings.UpdateNoaaConfig(this)); - case "updateextrawebfiles.html": - return this.JsonResponse(internetSettings.UpdateExtraWebFiles(this)); - case "updatemysqlconfig.json": - return this.JsonResponse(mySqlSettings.UpdateMysqlConfig(this)); - case "createmonthlysql.json": - return this.JsonResponse(mySqlSettings.CreateMonthlySQL(this)); - case "createdayfilesql.json": - return this.JsonResponse(mySqlSettings.CreateDayfileSQL(this)); - case "createrealtimesql.json": - return this.JsonResponse(mySqlSettings.CreateRealtimeSQL(this)); - } - - throw new KeyNotFoundException("Key Not Found: " + lastSegment); - } - catch (Exception ex) - { - return HandleError(ex, 404); - } - - } + { + // read the last segment of the URL to determine what data the caller wants + var lastSegment = Request.Url.Segments.Last(); + + switch (lastSegment) + { + case "updatestationconfig.json": + return this.JsonResponse(stationSettings.UpdateStationConfig(this)); + case "updateinternetconfig.json": + return this.JsonResponse(internetSettings.UpdateInternetConfig(this)); + case "updatecalibrationconfig.json": + return this.JsonResponse(calibrationSettings.UpdateCalibrationConfig(this)); + case "updatenoaaconfig.json": + return this.JsonResponse(noaaSettings.UpdateNoaaConfig(this)); + case "updateextrawebfiles.html": + return this.JsonResponse(internetSettings.UpdateExtraWebFiles(this)); + case "updatemysqlconfig.json": + return this.JsonResponse(mySqlSettings.UpdateMysqlConfig(this)); + case "createmonthlysql.json": + return this.JsonResponse(mySqlSettings.CreateMonthlySQL(this)); + case "createdayfilesql.json": + return this.JsonResponse(mySqlSettings.CreateDayfileSQL(this)); + case "createrealtimesql.json": + return this.JsonResponse(mySqlSettings.CreateRealtimeSQL(this)); + } + + throw new KeyNotFoundException("Key Not Found: " + lastSegment); + } + catch (Exception ex) + { + return HandleError(ex, 404); + } + } private bool HandleError(Exception ex, int statusCode) { var errorResponse = new - { - Title = "Unexpected Error", - ErrorCode = ex.GetType().Name, - Description = ex.Message, - }; + { + Title = "Unexpected Error", + ErrorCode = ex.GetType().Name, + Description = ex.Message, + }; - this.Response.StatusCode = statusCode; - return this.JsonResponse(errorResponse); - } - } + this.Response.StatusCode = statusCode; + return this.JsonResponse(errorResponse); + } + } public class GetSettingsController : WebApiController { @@ -686,77 +670,76 @@ public bool SettingsGet() }*/ try - { - // read the last segment of the URL to determine what data the caller wants - var lastSegment = Request.Url.Segments.Last(); - - switch (lastSegment) - { - case "stationdata.json": - return this.JsonResponse(stationSettings.GetStationAlpacaFormData()); - case "stationoptions.json": - return this.JsonResponse(stationSettings.GetStationAlpacaFormOptions()); - case "stationschema.json": - return this.JsonResponse(stationSettings.GetStationAlpacaFormSchema()); - - case "internetdata.json": - return this.JsonResponse(internetSettings.GetInternetAlpacaFormData()); - case "internetoptions.json": - return this.JsonResponse(internetSettings.GetInternetAlpacaFormOptions()); - case "internetschema.json": - return this.JsonResponse(internetSettings.GetInternetAlpacaFormSchema()); - - case "extrawebfiles.json": - return this.JsonResponse(internetSettings.GetExtraWebFilesData()); - - case "calibrationdata.json": - return this.JsonResponse(calibrationSettings.GetCalibrationAlpacaFormData()); - case "calibrationoptions.json": - return this.JsonResponse(calibrationSettings.GetCalibrationAlpacaFormOptions()); - case "calibrationschema.json": - return this.JsonResponse(calibrationSettings.GetCalibrationAlpacaFormSchema()); - - case "noaadata.json": - return this.JsonResponse(noaaSettings.GetNoaaAlpacaFormData()); - case "noaaoptions.json": - return this.JsonResponse(noaaSettings.GetNoaaAlpacaFormOptions()); - case "noaaschema.json": - return this.JsonResponse(noaaSettings.GetNoaaAlpacaFormSchema()); - - case "wsport.json": - return this.JsonResponse(stationSettings.GetWSport()); - case "version.json": - return this.JsonResponse(stationSettings.GetVersion()); - - case "mysqldata.json": - return this.JsonResponse(mySqlSettings.GetMySqlAlpacaFormData()); - case "mysqloptions.json": - return this.JsonResponse(mySqlSettings.GetMySqAlpacaFormOptions()); - case "mysqlschema.json": - return this.JsonResponse(mySqlSettings.GetMySqAlpacaFormSchema()); - } - - throw new KeyNotFoundException("Key Not Found: " + lastSegment); - } - catch (Exception ex) - { - return HandleError(ex, 404); - } - - } + { + // read the last segment of the URL to determine what data the caller wants + var lastSegment = Request.Url.Segments.Last(); + + switch (lastSegment) + { + case "stationdata.json": + return this.JsonResponse(stationSettings.GetStationAlpacaFormData()); + case "stationoptions.json": + return this.JsonResponse(stationSettings.GetStationAlpacaFormOptions()); + case "stationschema.json": + return this.JsonResponse(stationSettings.GetStationAlpacaFormSchema()); + + case "internetdata.json": + return this.JsonResponse(internetSettings.GetInternetAlpacaFormData()); + case "internetoptions.json": + return this.JsonResponse(internetSettings.GetInternetAlpacaFormOptions()); + case "internetschema.json": + return this.JsonResponse(internetSettings.GetInternetAlpacaFormSchema()); + + case "extrawebfiles.json": + return this.JsonResponse(internetSettings.GetExtraWebFilesData()); + + case "calibrationdata.json": + return this.JsonResponse(calibrationSettings.GetCalibrationAlpacaFormData()); + case "calibrationoptions.json": + return this.JsonResponse(calibrationSettings.GetCalibrationAlpacaFormOptions()); + case "calibrationschema.json": + return this.JsonResponse(calibrationSettings.GetCalibrationAlpacaFormSchema()); + + case "noaadata.json": + return this.JsonResponse(noaaSettings.GetNoaaAlpacaFormData()); + case "noaaoptions.json": + return this.JsonResponse(noaaSettings.GetNoaaAlpacaFormOptions()); + case "noaaschema.json": + return this.JsonResponse(noaaSettings.GetNoaaAlpacaFormSchema()); + + case "wsport.json": + return this.JsonResponse(stationSettings.GetWSport()); + case "version.json": + return this.JsonResponse(stationSettings.GetVersion()); + + case "mysqldata.json": + return this.JsonResponse(mySqlSettings.GetMySqlAlpacaFormData()); + case "mysqloptions.json": + return this.JsonResponse(mySqlSettings.GetMySqAlpacaFormOptions()); + case "mysqlschema.json": + return this.JsonResponse(mySqlSettings.GetMySqAlpacaFormSchema()); + } + + throw new KeyNotFoundException("Key Not Found: " + lastSegment); + } + catch (Exception ex) + { + return HandleError(ex, 404); + } + } private bool HandleError(Exception ex, int statusCode) { var errorResponse = new - { - Title = "Unexpected Error", - ErrorCode = ex.GetType().Name, - Description = ex.Message, - }; - - this.Response.StatusCode = statusCode; - return this.JsonResponse(errorResponse); - } - } - } + { + Title = "Unexpected Error", + ErrorCode = ex.GetType().Name, + Description = ex.Message, + }; + + this.Response.StatusCode = statusCode; + return this.JsonResponse(errorResponse); + } + } + } } diff --git a/CumulusMX/Cumulus.cs b/CumulusMX/Cumulus.cs index e6dec641..daaf6e1d 100644 --- a/CumulusMX/Cumulus.cs +++ b/CumulusMX/Cumulus.cs @@ -30,7 +30,7 @@ public class Cumulus { ///////////////////////////////// public string Version = "3.0.0"; - public string Build = "3048beta"; + public string Build = "3048"; ///////////////////////////////// private static string appGuid = "57190d2e-7e45-4efb-8c09-06a176cef3f3"; @@ -76,11 +76,11 @@ public enum rainunits IN } - public enum solarcalcTypes - { - RyanStolzenbach = 0, - Bras = 1 - } + public enum solarcalcTypes + { + RyanStolzenbach = 0, + Bras = 1 + } public struct Dataunits { @@ -201,7 +201,7 @@ public struct TExtraFiles public string WindRunUnitText; - public bool WebUpdating = false; + public volatile bool WebUpdating = false; public double WindRoseAngle { get; set; } @@ -355,9 +355,12 @@ public struct TExtraFiles public string RainFormat; internal int PressDPlaces = 1; - internal bool DavisIncrementPressureDP; + internal bool DavisIncrementPressureDP; public string PressFormat; + internal int SunshineDPlaces = 1; + public string SunFormat; + internal int UVDPlaces = 1; public string UVFormat; @@ -365,9 +368,9 @@ public struct TExtraFiles public int VPrainGaugeType = -1; - public string ComportName; - public string DefaultComportName; - public int ImetBaudRate; + public string ComportName; + public string DefaultComportName; + public int ImetBaudRate; public int DavisBaudRate; public int VendorID; @@ -513,7 +516,7 @@ public struct TExtraFiles public bool RealtimeEnabled; // The timer is to be started public bool RealtimeFTPEnabled; // The FTP connection is to be established public bool RealtimeTxtFTP; // The realtime.txt file is to be uploaded - public bool RealtimeGaugesTxtFTP; // The realtimegauges.txt file is to be uploaded + public bool RealtimeGaugesTxtFTP; // The realtimegauges.txt file is to be uploaded // Twitter settings public string Twitteruser = " "; @@ -782,7 +785,7 @@ public struct TExtraFiles public string[] APRSstationtype = { "DsVP", "DsVP", "WMR928", "WM918", "EW", "FO", "WS2300", "FOs", "WMR100", "WMR200", "Instromet" }; - /* + /* CryptoLicense lic = new CryptoLicense(); @@ -886,11 +889,11 @@ private void DoLicenseCheck() } */ - public Cumulus(int HTTPport, int WSport) - { - //DoLicenseCheck(); + public Cumulus(int HTTPport, int WSport) + { + //DoLicenseCheck(); - /*lic.ValidationKey = "AMAAMACrfxYrYEOGd+D5ypZ32bnLCvviBrTlejReXNRdvgWzSgyvdfkLvNDvDX1WuMh2JIEDAAEAAQ=="; + /*lic.ValidationKey = "AMAAMACrfxYrYEOGd+D5ypZ32bnLCvviBrTlejReXNRdvgWzSgyvdfkLvNDvDX1WuMh2JIEDAAEAAQ=="; // Load license from the file lic.StorageMode = LicenseStorageMode.ToFile; @@ -902,10 +905,10 @@ public Cumulus(int HTTPport, int WSport) throw new Exception("license validation failed"); */ - string serial = CalculateMD5Hash(Environment.MachineName); - Console.WriteLine("Serial: " + serial); - File.WriteAllText("serial.txt", serial); - /* + string serial = CalculateMD5Hash(Environment.MachineName); + Console.WriteLine("Serial: " + serial); + File.WriteAllText("serial.txt", serial); + /* try { using (TextReader reader = File.OpenText(@"licence.lic")) @@ -954,71 +957,71 @@ public Cumulus(int HTTPport, int WSport) Environment.Exit(0); } */ - DirectorySeparator = Path.DirectorySeparatorChar; + DirectorySeparator = Path.DirectorySeparatorChar; - AppDir = AppDomain.CurrentDomain.BaseDirectory; + AppDir = AppDomain.CurrentDomain.BaseDirectory; - TwitterTxtFile = AppDir + "twitter.txt"; - WebTagFile = AppDir + "WebTags.txt"; + TwitterTxtFile = AppDir + "twitter.txt"; + WebTagFile = AppDir + "WebTags.txt"; - // interface port passed as param - HttpPort = HTTPport; + // interface port passed as param + HttpPort = HTTPport; //b3045, use smae port for WS... WS port = HTTPS port //wsPort = WSport; wsPort = HTTPport; - // Set up the diagnostic tracing - string loggingfile = GetLoggingFileName("MXdiags" + DirectorySeparator); + // Set up the diagnostic tracing + string loggingfile = GetLoggingFileName("MXdiags" + DirectorySeparator); - TextWriterTraceListener myTextListener = new TextWriterTraceListener(loggingfile); + TextWriterTraceListener myTextListener = new TextWriterTraceListener(loggingfile); - Trace.Listeners.Add(myTextListener); - Trace.AutoFlush = true; + Trace.Listeners.Add(myTextListener); + Trace.AutoFlush = true; - // Read the configuration file + // Read the configuration file - LogMessage(" ========================== Cumulus MX starting =========================="); + LogMessage(" ========================== Cumulus MX starting =========================="); - LogMessage("Command line: " + Environment.CommandLine); + LogMessage("Command line: " + Environment.CommandLine); - Assembly thisAssembly = this.GetType().Assembly; - //Version = thisAssembly.GetName().Version.ToString(); - //VersionLabel.Content = "Cumulus v." + thisAssembly.GetName().Version; - LogMessage("Cumulus MX v." + Version + " build " + Build); - Console.WriteLine("Cumulus MX v." + Version + " build " + Build); - //Console.WriteLine("This is pre-release beta software"); + Assembly thisAssembly = this.GetType().Assembly; + //Version = thisAssembly.GetName().Version.ToString(); + //VersionLabel.Content = "Cumulus v." + thisAssembly.GetName().Version; + LogMessage("Cumulus MX v." + Version + " build " + Build); + Console.WriteLine("Cumulus MX v." + Version + " build " + Build); + //Console.WriteLine("This is pre-release beta software"); - IsOSX = IsRunningOnMac(); + IsOSX = IsRunningOnMac(); - Platform = IsOSX ? "Mac OS X" : Environment.OSVersion.Platform.ToString(); + Platform = IsOSX ? "Mac OS X" : Environment.OSVersion.Platform.ToString(); - // Set the default comport name depending on platform - if (Platform.Substring(0, 3) == "Win") - { - DefaultComportName = "COM1"; - } - else - { - DefaultComportName = "/dev/ttyUSB0"; - } + // Set the default comport name depending on platform + if (Platform.Substring(0, 3) == "Win") + { + DefaultComportName = "COM1"; + } + else + { + DefaultComportName = "/dev/ttyUSB0"; + } - LogMessage("Platform: " + Platform); + LogMessage("Platform: " + Platform); LogMessage("OS version: " + Environment.OSVersion.ToString()); - Type type = Type.GetType("Mono.Runtime"); - if (type != null) - { - MethodInfo displayName = type.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static); - if (displayName != null) - LogMessage("Mono version: "+displayName.Invoke(null, null)); - } + Type type = Type.GetType("Mono.Runtime"); + if (type != null) + { + MethodInfo displayName = type.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static); + if (displayName != null) + LogMessage("Mono version: "+displayName.Invoke(null, null)); + } - LogMessage("Current culture: " + CultureInfo.CurrentCulture.DisplayName); + LogMessage("Current culture: " + CultureInfo.CurrentCulture.DisplayName); ListSeparator = CultureInfo.CurrentCulture.TextInfo.ListSeparator; DecimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator; @@ -1066,7 +1069,7 @@ public Cumulus(int HTTPport, int WSport) ThisMonthTFile = "web" + DirectorySeparator + "thismonthT.htm"; ThisYearTFile = "web" + DirectorySeparator + "thisyearT.htm"; MonthlyRecordTFile = "web" + DirectorySeparator + "monthlyrecordT.htm"; - RealtimeGaugesTxtTFile = "web" + DirectorySeparator + "realtimegaugesT.txt"; + RealtimeGaugesTxtTFile = "web" + DirectorySeparator + "realtimegaugesT.txt"; Indexfile = "web" + DirectorySeparator + "index.htm"; Todayfile = "web" + DirectorySeparator + "today.htm"; @@ -1077,9 +1080,9 @@ public Cumulus(int HTTPport, int WSport) ThisMonthfile = "web" + DirectorySeparator + "thismonth.htm"; ThisYearfile = "web" + DirectorySeparator + "thisyear.htm"; MonthlyRecordfile = "web" + DirectorySeparator + "monthlyrecord.htm"; - RealtimeGaugesTxtFile = "web" + DirectorySeparator + "realtimegauges.txt"; + RealtimeGaugesTxtFile = "web" + DirectorySeparator + "realtimegauges.txt"; - localwebtextfiles = new[] { Indexfile, Todayfile, Yesterfile, Recordfile, Trendsfile, Gaugesfile, ThisMonthfile, ThisYearfile, MonthlyRecordfile }; + localwebtextfiles = new[] { Indexfile, Todayfile, Yesterfile, Recordfile, Trendsfile, Gaugesfile, ThisMonthfile, ThisYearfile, MonthlyRecordfile }; remotewebtextfiles = new[] { "index.htm", "today.htm", "yesterday.htm", "record.htm", "trends.htm", "gauges.htm", "thismonth.htm", "thisyear.htm", "monthlyrecord.htm" }; //localgraphdatafiles = new[] {"units.json","tempdatad3.json", "pressdatad3.json", "winddatad3.json", "wdirdatad3.json", "humdatad3.json", "raindatad3.json", "solardatad3.json"}; @@ -1101,10 +1104,10 @@ public Cumulus(int HTTPport, int WSport) }; remotegraphdatafiles = new[] - { - "graphconfig.json", "tempdata.json", "pressdata.json", "winddata.json", "wdirdata.json", "humdata.json", "raindata.json", "solardata.json", - "dailyrain.json", "sunhours.json", "dailytemp.json" - }; + { + "graphconfig.json", "tempdata.json", "pressdata.json", "winddata.json", "wdirdata.json", "humdata.json", "raindata.json", "solardata.json", + "dailyrain.json", "sunhours.json", "dailytemp.json" + }; LogMessage("Data path = " + Datapath); @@ -1143,14 +1146,15 @@ public Cumulus(int HTTPport, int WSport) LogMessage("Debug logging is " + (logging ? "enabled" : "disabled")); LogMessage("Data logging is " + (DataLogging ? "enabled" : "disabled")); LogMessage("Logging interval = " + logints[DataLogInterval]); - LogMessage("NoSensorCheck = " + (NoSensorCheck ? "1" : "0")); + LogMessage("NoSensorCheck = " + (NoSensorCheck ? "1" : "0")); - TempFormat = "F" + TempDPlaces; + TempFormat = "F" + TempDPlaces; WindFormat = "F" + WindDPlaces; RainFormat = "F" + RainDPlaces; PressFormat = "F" + PressDPlaces; HumFormat = "F" + HumDPlaces; UVFormat = "F" + UVDPlaces; + SunFormat = "F" + SunshineDPlaces; ETFormat = "F" + (RainDPlaces + 1); WindRunFormat = "F" + WindRunDPlaces; TempTrendFormat = "+0.0;-0.0;0"; @@ -1255,12 +1259,12 @@ public Cumulus(int HTTPport, int WSport) LogMessage("RainDayThreshold=" + RainDayThreshold.ToString("F3")); LogMessage("Offsets and Multipliers:"); LogMessage("PO=" + PressOffset.ToString("F3") + " TO=" + TempOffset.ToString("F3") + " HO=" + HumOffset + " WDO=" + WindDirOffset + " ITO=" + - InTempoffset.ToString("F3") + " UVO=" + UVOffset.ToString("F3")); + InTempoffset.ToString("F3") + " UVO=" + UVOffset.ToString("F3")); LogMessage("WSM=" + WindSpeedMult.ToString("F3") + " WGM=" + WindGustMult.ToString("F3") + " TM=" + TempMult.ToString("F3") + " TM2=" + TempMult2.ToString("F3") + - " HM=" + HumMult.ToString("F3") + " HM2=" + HumMult2.ToString("F3") + " RM=" + RainMult.ToString("F3") + " UVM=" + UVMult.ToString("F3")); + " HM=" + HumMult.ToString("F3") + " HM2=" + HumMult2.ToString("F3") + " RM=" + RainMult.ToString("F3") + " UVM=" + UVMult.ToString("F3")); LogMessage("Spike removal:"); LogMessage("TD=" + EWtempdiff.ToString("F3") + " GD=" + EWgustdiff.ToString("F3") + " WD=" + EWwinddiff.ToString("F3") + " HD=" + EWhumiditydiff.ToString("F3") + " PD=" + - EWpressurediff.ToString("F3")); + EWpressurediff.ToString("F3")); LogMessage("MR=" + EWmaxRainRate.ToString("F3") + " MH=" + EWmaxHourlyRain.ToString("F3")); LogMessage("Cumulus Starting"); @@ -1460,7 +1464,7 @@ private void CustomHttpSecondsTimerTick(object sender, ElapsedEventArgs e) internal void SetStartOfRealtimeInsertSQL() { StartOfRealtimeInsertSQL = "INSERT IGNORE INTO " + MySqlRealtimeTable + - " (LogDateTime,temp,hum,dew,wspeed,wlatest,bearing,rrate,rfall,press,currentwdir,beaufortnumber,windunit,tempunitnodeg,pressunit,rainunit,windrun,presstrendval,rmonth,ryear,rfallY,intemp,inhum,wchill,temptrend,tempTH,TtempTH,tempTL,TtempTL,windTM,TwindTM,wgustTM,TwgustTM,pressTH,TpressTH,pressTL,TpressTL,version,build,wgust,heatindex,humidex,UV,ET,SolarRad,avgbearing,rhour,forecastnumber,isdaylight,SensorContactLost,wdir,cloudbasevalue,cloudbaseunit,apptemp,SunshineHours,CurrentSolarMax,IsSunny)"; + " (LogDateTime,temp,hum,dew,wspeed,wlatest,bearing,rrate,rfall,press,currentwdir,beaufortnumber,windunit,tempunitnodeg,pressunit,rainunit,windrun,presstrendval,rmonth,ryear,rfallY,intemp,inhum,wchill,temptrend,tempTH,TtempTH,tempTL,TtempTL,windTM,TwindTM,wgustTM,TwgustTM,pressTH,TpressTH,pressTL,TpressTL,version,build,wgust,heatindex,humidex,UV,ET,SolarRad,avgbearing,rhour,forecastnumber,isdaylight,SensorContactLost,wdir,cloudbasevalue,cloudbaseunit,apptemp,SunshineHours,CurrentSolarMax,IsSunny)"; } internal void SetRealtimeSqlCreateString() @@ -1481,19 +1485,19 @@ internal void SetRealtimeSqlCreateString() ") NOT NULL,ET decimal(4," + RainDPlaces + ") NOT NULL,SolarRad decimal(5,1) NOT NULL,avgbearing varchar(3) NOT NULL,rhour decimal(4," + RainDPlaces + ") NOT NULL,forecastnumber varchar(2) NOT NULL,isdaylight varchar(1) NOT NULL,SensorContactLost varchar(1) NOT NULL,wdir varchar(3) NOT NULL,cloudbasevalue varchar(5) NOT NULL,cloudbaseunit varchar(2) NOT NULL,apptemp decimal(4," + TempDPlaces + - ") NOT NULL,SunshineHours decimal(3,1) NOT NULL,CurrentSolarMax decimal(5,1) NOT NULL,IsSunny varchar(1) NOT NULL,PRIMARY KEY (LogDateTime)) COMMENT = \"Realtime log\""; + ") NOT NULL,SunshineHours decimal(3," + SunshineDPlaces + ") NOT NULL,CurrentSolarMax decimal(5,1) NOT NULL,IsSunny varchar(1) NOT NULL,PRIMARY KEY (LogDateTime)) COMMENT = \"Realtime log\""; } internal void SetStartOfDayfileInsertSQL() { StartOfDayfileInsertSQL = "INSERT IGNORE INTO " + MySqlDayfileTable + - " (LogDate,HighWindGust,HWindGBear,THWindG,MinTemp,TMinTemp,MaxTemp,TMaxTemp,MinPress,TMinPress,MaxPress,TMaxPress,MaxRainRate,TMaxRR,TotRainFall,AvgTemp,TotWindRun,HighAvgWSpeed,THAvgWSpeed,LowHum,TLowHum,HighHum,THighHum,TotalEvap,HoursSun,HighHeatInd,THighHeatInd,HighAppTemp,THighAppTemp,LowAppTemp,TLowAppTemp,HighHourRain,THighHourRain,LowWindChill,TLowWindChill,HighDewPoint,THighDewPoint,LowDewPoint,TLowDewPoint,DomWindDir,HeatDegDays,CoolDegDays,HighSolarRad,THighSolarRad,HighUV,THighUV,HWindGBearSym,DomWindDirSym)"; + " (LogDate,HighWindGust,HWindGBear,THWindG,MinTemp,TMinTemp,MaxTemp,TMaxTemp,MinPress,TMinPress,MaxPress,TMaxPress,MaxRainRate,TMaxRR,TotRainFall,AvgTemp,TotWindRun,HighAvgWSpeed,THAvgWSpeed,LowHum,TLowHum,HighHum,THighHum,TotalEvap,HoursSun,HighHeatInd,THighHeatInd,HighAppTemp,THighAppTemp,LowAppTemp,TLowAppTemp,HighHourRain,THighHourRain,LowWindChill,TLowWindChill,HighDewPoint,THighDewPoint,LowDewPoint,TLowDewPoint,DomWindDir,HeatDegDays,CoolDegDays,HighSolarRad,THighSolarRad,HighUV,THighUV,HWindGBearSym,DomWindDirSym)"; } internal void SetStartOfMonthlyInsertSQL() { StartOfMonthlyInsertSQL = "INSERT IGNORE INTO " + MySqlMonthlyTable + - " (LogDateTime,Temp,Humidity,Dewpoint,Windspeed,Windgust,Windbearing,RainRate,TodayRainSoFar,Pressure,Raincounter,InsideTemp,InsideHumidity,LatestWindGust,WindChill,HeatIndex,UVindex,SolarRad,Evapotrans,AnnualEvapTran,ApparentTemp,MaxSolarRad,HrsSunShine,CurrWindBearing,RG11rain,RainSinceMidnight,WindbearingSym,CurrWindBearingSym)"; + " (LogDateTime,Temp,Humidity,Dewpoint,Windspeed,Windgust,Windbearing,RainRate,TodayRainSoFar,Pressure,Raincounter,InsideTemp,InsideHumidity,LatestWindGust,WindChill,HeatIndex,UVindex,SolarRad,Evapotrans,AnnualEvapTran,ApparentTemp,MaxSolarRad,HrsSunShine,CurrWindBearing,RG11rain,RainSinceMidnight,WindbearingSym,CurrWindBearingSym)"; } @@ -1626,7 +1630,7 @@ private void OnDisconnect(UserContext context) private void OnConnected(UserContext context) { - LogDebugMessage("Connected From : " + context.ClientAddress.ToString()); + LogDebugMessage("Connected From : " + context.ClientAddress.ToString()); } private void OnConnect(UserContext context) @@ -1639,12 +1643,12 @@ private void OnConnect(UserContext context) private void OnSend(UserContext context) { - LogDebugMessage("OnSend From : " + context.ClientAddress.ToString()); + LogDebugMessage("OnSend From : " + context.ClientAddress.ToString()); } private void OnReceive(UserContext context) { - LogDebugMessage("WS receive : " + context.DataFrame.ToString()); + LogDebugMessage("WS receive : " + context.DataFrame.ToString()); } */ private void InitialiseRG11() @@ -1720,7 +1724,11 @@ private void APRSTimerTick(object sender, ElapsedEventArgs e) private void WebTimerTick(object sender, ElapsedEventArgs e) { - if (!WebUpdating) + if (WebUpdating) + { + LogMessage("Warning, previous web update is still in progress, skipping this interval"); + } + else { WebUpdating = true; ftpThread = new Thread(DoHTMLFiles); @@ -1739,8 +1747,7 @@ internal async void UpdateTwitter() LogDebugMessage("Starting Twitter update"); var auth = new XAuthAuthorizer { - CredentialStore = - new XAuthCredentials { ConsumerKey = twitterKey, ConsumerSecret = twitterSecret, UserName = Twitteruser, Password = TwitterPW } + CredentialStore = new XAuthCredentials { ConsumerKey = twitterKey, ConsumerSecret = twitterSecret, UserName = Twitteruser, Password = TwitterPW } }; if (TwitterOauthToken == "unknown") @@ -1789,7 +1796,7 @@ internal async void UpdateTwitter() LogDebugMessage("Updating Twitter: " + status); - Status tweet; + Status tweet; try { @@ -1963,60 +1970,88 @@ internal async void UpdateWCloud(DateTime timestamp) internal void RealtimeTimerTick(object sender, ElapsedEventArgs elapsedEventArgs) { - if (!RealtimeInProgress) - { - try - { - RealtimeInProgress = true; - if (RealtimeFTPEnabled) - { - if (!RealtimeFTP.IsConnected) - { - try - { - LogDebugMessage("Realtime ftp not connected - reconnecting"); - RealtimeFTP.Connect(); - } - catch (Exception ex) - { - LogMessage("Error connecting ftp - " + ex.Message); - } - - //RealtimeFTP.EnableThreadSafeDataConnections = false; // use same connection for all transfers - } - - try - { - //LogDebugMessage("Create realtime file"); - CreateRealtimeFile(); - //LogDebugMessage("Create extra realtime files"); - CreateRealtimeHTMLfiles(); - //LogDebugMessage("Upload realtime files"); - RealtimeFTPUpload(); - } - catch (Exception ex) - { - LogMessage("Error during realtime update: " + ex.Message); - } - } - else - { - // No FTP, just process files - CreateRealtimeFile(); - CreateRealtimeHTMLfiles(); - } - - if (!string.IsNullOrEmpty(RealtimeProgram)) - { - //LogDebugMessage("Execute realtime program"); - ExecuteProgram(RealtimeProgram, RealtimeParams); - } - } - finally - { - RealtimeInProgress = false; - } - } + bool connectionFailed = false; + // We are not overly fussed about locking as the thread start times are sufficently far apart + if (RealtimeInProgress) + { + LogMessage("Warning, previous realtime ftp still in progress, skipping this period."); + } + else + { + RealtimeInProgress = true; + try + { + // Process any files + CreateRealtimeFile(); + CreateRealtimeHTMLfiles(); + + if (RealtimeFTPEnabled) + { + if (RealtimeFTP.Host == null) + { + // This only happens if the user enables realtime FTP after starting Cumulus + RealtimeFTPLogin(); + } + // Force a test of the connection, IsConnected is not always reliable + try + { + string pwd = RealtimeFTP.GetWorkingDirectory(); + if (pwd.Length == 0) + { + connectionFailed = true; + } + } + catch (Exception ex) + { + LogDebugMessage("Test of realtime FTP connection failed: " + ex.Message); + connectionFailed = true; + } + if (!(RealtimeFTP.IsConnected) || connectionFailed) + { + LogDebugMessage("Realtime ftp not connected - reconnecting"); + try + { + RealtimeFTP.Disconnect(); + } + catch + { + // do nothing on any disconnect error + } + try + { + RealtimeFTP.Connect(); + } + catch (Exception ex) + { + LogMessage("Error connecting ftp - " + ex.Message); + } + } + + try + { + RealtimeFTPUpload(); + } + catch (Exception ex) + { + LogMessage("Error during realtime FTP update: " + ex.Message); + } + } + + if (!string.IsNullOrEmpty(RealtimeProgram)) + { + //LogDebugMessage("Execute realtime program"); + ExecuteProgram(RealtimeProgram, RealtimeParams); + } + } + catch (Exception ex) + { + LogMessage("Error during realtime update: " + ex.Message); + } + finally + { + RealtimeInProgress = false; + } + } } private void RealtimeFTPUpload() @@ -2027,12 +2062,12 @@ private void RealtimeFTPUpload() if (ftp_directory == "") { filepath = "realtime.txt"; - gaugesfilepath = "realtimegauges.txt"; + gaugesfilepath = "realtimegauges.txt"; } else { filepath = ftp_directory + "/realtime.txt"; - gaugesfilepath = ftp_directory + "/realtimegauges.txt"; + gaugesfilepath = ftp_directory + "/realtimegauges.txt"; } if (RealtimeTxtFTP) @@ -2040,11 +2075,11 @@ private void RealtimeFTPUpload() UploadFile(RealtimeFTP, RealtimeFile, filepath); } - if (RealtimeGaugesTxtFTP) - { - ProcessTemplateFile(RealtimeGaugesTxtTFile,RealtimeGaugesTxtFile, realtimeTokenParser); - UploadFile(RealtimeFTP, RealtimeGaugesTxtFile,gaugesfilepath); - } + if (RealtimeGaugesTxtFTP) + { + ProcessTemplateFile(RealtimeGaugesTxtTFile, RealtimeGaugesTxtFile, realtimeTokenParser); + UploadFile(RealtimeFTP, RealtimeGaugesTxtFile, gaugesfilepath); + } // Extra files for (int i = 0; i < numextrafiles; i++) @@ -2154,11 +2189,11 @@ private List<string> ParseParams(string line) { if (Char.IsWhiteSpace(line[i])) { - if (!insideQuotes && start != -1) - { - parts.Add(line.Substring(start, i - start)); - start = -1; - } + if (!insideQuotes && start != -1) + { + parts.Add(line.Substring(start, i - start)); + start = -1; + } } else if (line[i] == '"') { @@ -2676,7 +2711,7 @@ private void ReadIniFile() DavisInitWaitTime = ini.GetValue("Station", "DavisInitWaitTime", 200); DavisIPResponseTime = ini.GetValue("Station", "DavisIPResponseTime", 1000); DavisReadTimeout = ini.GetValue("Station", "DavisReadTimeout", 1000); - DavisIncrementPressureDP = ini.GetValue("Station", "DavisIncrementPressureDP", true); + DavisIncrementPressureDP = ini.GetValue("Station", "DavisIncrementPressureDP", true); if (StationType == StationTypes.VantagePro) { UseDavisLoop2 = false; @@ -2862,7 +2897,7 @@ private void ReadIniFile() LogMessage("Cumulus start date: " + RecordsBeganDate); ImetWaitTime = ini.GetValue("Station", "ImetWaitTime", 500); - ImetUpdateLogPointer = ini.GetValue("Station", "ImetUpdateLogPointer", true); + ImetUpdateLogPointer = ini.GetValue("Station", "ImetUpdateLogPointer", true); UseDataLogger = ini.GetValue("Station", "UseDataLogger", true); UseCumulusForecast = ini.GetValue("Station", "UseCumulusForecast", false); @@ -2962,7 +2997,7 @@ private void ReadIniFile() RealtimeEnabled = ini.GetValue("FTP site", "EnableRealtime", false); RealtimeFTPEnabled = ini.GetValue("FTP site", "RealtimeFTPEnabled", false); RealtimeTxtFTP = ini.GetValue("FTP site", "RealtimeTxtFTP", false); - RealtimeGaugesTxtFTP = ini.GetValue("FTP site", "RealtimeGaugesTxtFTP", false); + RealtimeGaugesTxtFTP = ini.GetValue("FTP site", "RealtimeGaugesTxtFTP", false); RealtimeInterval = ini.GetValue("FTP site", "RealtimeInterval", 30000); if (RealtimeInterval < 1) { RealtimeInterval = 1; } //RealtimeTimer.Change(0,RealtimeInterval); @@ -3192,8 +3227,8 @@ private void ReadIniFile() SolarMinimum = ini.GetValue("Solar", "SolarMinimum", 0); LuxToWM2 = ini.GetValue("Solar", "LuxToWM2", 0.0079); UseBlakeLarsen = ini.GetValue("Solar", "UseBlakeLarsen", false); - SolarCalc = ini.GetValue("Solar", "SolarCalc", 0); - BrasTurbidity = ini.GetValue("Solar", "BrasTurbidity", 2.0); + SolarCalc = ini.GetValue("Solar", "SolarCalc", 0); + BrasTurbidity = ini.GetValue("Solar", "BrasTurbidity", 2.0); NOAAname = ini.GetValue("NOAA", "Name", " "); NOAAcity = ini.GetValue("NOAA", "City", " "); @@ -3489,7 +3524,7 @@ internal void WriteIniFile() ini.SetValue("FTP site", "EnableRealtime", RealtimeEnabled); ini.SetValue("FTP site", "RealtimeFTPEnabled", RealtimeFTPEnabled); ini.SetValue("FTP site", "RealtimeTxtFTP", RealtimeTxtFTP); - ini.SetValue("FTP site", "RealtimeGaugesTxtFTP", RealtimeGaugesTxtFTP); + ini.SetValue("FTP site", "RealtimeGaugesTxtFTP", RealtimeGaugesTxtFTP); ini.SetValue("FTP site", "RealtimeInterval", RealtimeInterval); ini.SetValue("FTP site", "UpdateInterval", UpdateInterval); ini.SetValue("FTP site", "IncludeSTD", IncludeStandardFiles); @@ -3675,11 +3710,11 @@ internal void WriteIniFile() ini.SetValue("Solar", "RStransfactor", RStransfactor); ini.SetValue("Solar", "SolarMinimum", SolarMinimum); ini.SetValue("Solar", "UseBlakeLarsen", UseBlakeLarsen); - ini.SetValue("Solar", "SolarCalc", SolarCalc); - ini.SetValue("Solar", "BrasTurbidity", BrasTurbidity); + ini.SetValue("Solar", "SolarCalc", SolarCalc); + ini.SetValue("Solar", "BrasTurbidity", BrasTurbidity); - ini.SetValue("NOAA", "Name", NOAAname); + ini.SetValue("NOAA", "Name", NOAAname); ini.SetValue("NOAA", "City", NOAAcity); ini.SetValue("NOAA", "State", NOAAstate); ini.SetValue("NOAA", "12hourformat", NOAA12hourformat); @@ -4121,11 +4156,11 @@ private void ReadStringsFile() public int SunThreshold { get; set; } - public int SolarCalc { get; set; } + public int SolarCalc { get; set; } - public double BrasTurbidity { get; set; } + public double BrasTurbidity { get; set; } - public int xapPort { get; set; } + public int xapPort { get; set; } public string xapUID { get; set; } @@ -4263,7 +4298,7 @@ private void ReadStringsFile() public int ImetWaitTime { get; set; } - public bool ImetUpdateLogPointer { get; set; } + public bool ImetUpdateLogPointer { get; set; } public bool DavisConsoleHighGust { get; set; } @@ -4457,15 +4492,15 @@ private WeatherStation Station private readonly string ThisYearTFile; private readonly string GaugesTFile; private readonly string RealtimeFile = "realtime.txt"; - private readonly string RealtimeGaugesTxtTFile; - private readonly string RealtimeGaugesTxtFile; - private readonly string TwitterTxtFile; + private readonly string RealtimeGaugesTxtTFile; + private readonly string RealtimeGaugesTxtFile; + private readonly string TwitterTxtFile; public bool IncludeStandardFiles = true; public bool IncludeGraphDataFiles; public bool TwitterSendLocation; private const int numwebtextfiles = 9; private FtpClient RealtimeFTP = new FtpClient(); - private bool RealtimeInProgress = false; + private volatile bool RealtimeInProgress = false; public bool SendSoilTemp1ToWund; public bool SendSoilTemp2ToWund; public bool SendSoilTemp3ToWund; @@ -4682,7 +4717,7 @@ public void DoLogFile(DateTime timestamp, bool live) // Writes an entry to the n file.Write(station.AnnualETTotal.ToString(ETFormat) + ListSeparator); file.Write(station.ApparentTemperature.ToString(TempFormat) + ListSeparator); file.Write((Math.Round(station.CurrentSolarMax)) + ListSeparator); - file.Write(station.SunshineHours.ToString("N1") + ListSeparator); + file.Write(station.SunshineHours.ToString(SunFormat) + ListSeparator); file.Write(station.Bearing + ListSeparator); file.Write(station.RG11RainToday.ToString(RainFormat) + ListSeparator); file.WriteLine(station.RainSinceMidnight.ToString(RainFormat)); @@ -4713,7 +4748,7 @@ public void DoLogFile(DateTime timestamp, bool live) // Writes an entry to the n station.HeatIndex.ToString(TempFormat, InvC) + "," + station.UV.ToString(UVFormat, InvC) + "," + station.SolarRad + "," + station.ET.ToString(ETFormat, InvC) + "," + station.AnnualETTotal.ToString(ETFormat, InvC) + "," + station.ApparentTemperature.ToString(TempFormat, InvC) + "," + (Math.Round(station.CurrentSolarMax)) + "," + - station.SunshineHours.ToString("N1", InvC) + "," + station.Bearing + "," + station.RG11RainToday.ToString(RainFormat, InvC) + "," + + station.SunshineHours.ToString(SunFormat, InvC) + "," + station.Bearing + "," + station.RG11RainToday.ToString(RainFormat, InvC) + "," + station.RainSinceMidnight.ToString(RainFormat, InvC) + ",'" + station.CompassPoint(station.AvgBearing) + "','" + station.CompassPoint(station.Bearing) + "')"; @@ -5381,111 +5416,116 @@ public void ExecuteProgram(string externalProgram, string externalParams) public void DoHTMLFiles() { - if (!RealtimeEnabled) + try { - CreateRealtimeFile(); - } + if (!RealtimeEnabled) + { + CreateRealtimeFile(); + } - //LogDebugMessage("Creating standard HTML files"); - ProcessTemplateFile(IndexTFile, Indexfile,tokenParser); - ProcessTemplateFile(TodayTFile, Todayfile,tokenParser); - ProcessTemplateFile(YesterdayTFile, Yesterfile,tokenParser); - ProcessTemplateFile(RecordTFile, Recordfile, tokenParser); - ProcessTemplateFile(MonthlyRecordTFile, MonthlyRecordfile,tokenParser); - ProcessTemplateFile(TrendsTFile, Trendsfile, tokenParser); - ProcessTemplateFile(ThisMonthTFile, ThisMonthfile, tokenParser); - ProcessTemplateFile(ThisYearTFile, ThisYearfile, tokenParser); - ProcessTemplateFile(GaugesTFile, Gaugesfile,tokenParser); - //LogDebugMessage("Done creating standard HTML files"); - if (IncludeGraphDataFiles) - { - //LogDebugMessage("Creating graph data files"); - station.CreateGraphDataFiles(); - //LogDebugMessage("Done creating graph data files"); - } - //LogDebugMessage("Creating extra files"); - // handle any extra files - for (int i = 0; i < numextrafiles; i++) - { - if (!ExtraFiles[i].realtime && !ExtraFiles[i].endofday) + //LogDebugMessage("Creating standard HTML files"); + ProcessTemplateFile(IndexTFile, Indexfile, tokenParser); + ProcessTemplateFile(TodayTFile, Todayfile, tokenParser); + ProcessTemplateFile(YesterdayTFile, Yesterfile, tokenParser); + ProcessTemplateFile(RecordTFile, Recordfile, tokenParser); + ProcessTemplateFile(MonthlyRecordTFile, MonthlyRecordfile, tokenParser); + ProcessTemplateFile(TrendsTFile, Trendsfile, tokenParser); + ProcessTemplateFile(ThisMonthTFile, ThisMonthfile, tokenParser); + ProcessTemplateFile(ThisYearTFile, ThisYearfile, tokenParser); + ProcessTemplateFile(GaugesTFile, Gaugesfile, tokenParser); + //LogDebugMessage("Done creating standard HTML files"); + if (IncludeGraphDataFiles) { - var uploadfile = ExtraFiles[i].local; - if (uploadfile == "<currentlogfile>") + //LogDebugMessage("Creating graph data files"); + station.CreateGraphDataFiles(); + //LogDebugMessage("Done creating graph data files"); + } + //LogDebugMessage("Creating extra files"); + // handle any extra files + for (int i = 0; i < numextrafiles; i++) + { + if (!ExtraFiles[i].realtime && !ExtraFiles[i].endofday) { - uploadfile = GetLogFileName(DateTime.Now); - } - var remotefile = ExtraFiles[i].remote; - remotefile = remotefile.Replace("<currentlogfile>", Path.GetFileName(GetLogFileName(DateTime.Now))); + var uploadfile = ExtraFiles[i].local; + if (uploadfile == "<currentlogfile>") + { + uploadfile = GetLogFileName(DateTime.Now); + } + var remotefile = ExtraFiles[i].remote; + remotefile = remotefile.Replace("<currentlogfile>", Path.GetFileName(GetLogFileName(DateTime.Now))); - if ((uploadfile != "") && (File.Exists(uploadfile)) && (remotefile != "")) - { - if (ExtraFiles[i].process) + if ((uploadfile != "") && (File.Exists(uploadfile)) && (remotefile != "")) { - //LogDebugMessage("Processing extra file "+uploadfile); - // process the file - var utf8WithoutBom = new System.Text.UTF8Encoding(false); - var encoding = UTF8encode ? utf8WithoutBom : System.Text.Encoding.GetEncoding("iso-8859-1"); - tokenParser.encoding = encoding; - tokenParser.SourceFile = uploadfile; - var output = tokenParser.ToString(); - uploadfile += "tmp"; - try + if (ExtraFiles[i].process) { - using (StreamWriter file = new StreamWriter(uploadfile, false, encoding)) + //LogDebugMessage("Processing extra file "+uploadfile); + // process the file + var utf8WithoutBom = new System.Text.UTF8Encoding(false); + var encoding = UTF8encode ? utf8WithoutBom : System.Text.Encoding.GetEncoding("iso-8859-1"); + tokenParser.encoding = encoding; + tokenParser.SourceFile = uploadfile; + var output = tokenParser.ToString(); + uploadfile += "tmp"; + try { - file.Write(output); + using (StreamWriter file = new StreamWriter(uploadfile, false, encoding)) + { + file.Write(output); - file.Close(); + file.Close(); + } } + catch (Exception ex) + { + LogDebugMessage("Error writing file " + uploadfile); + LogDebugMessage(ex.Message); + } + //LogDebugMessage("Finished processing extra file " + uploadfile); } - catch (Exception ex) - { - LogDebugMessage("Error writing file " + uploadfile); - LogDebugMessage(ex.Message); - } - //LogDebugMessage("Finished processing extra file " + uploadfile); - } - if (!ExtraFiles[i].FTP) - { - // just copy the file - //LogDebugMessage("Copying extra file " + uploadfile); - try + if (!ExtraFiles[i].FTP) { - File.Copy(uploadfile, remotefile, true); - } - catch (Exception ex) - { - LogDebugMessage("Error copying extra file: " + ex.Message); + // just copy the file + //LogDebugMessage("Copying extra file " + uploadfile); + try + { + File.Copy(uploadfile, remotefile, true); + } + catch (Exception ex) + { + LogDebugMessage("Error copying extra file: " + ex.Message); + } + //LogDebugMessage("Finished copying extra file " + uploadfile); } - //LogDebugMessage("Finished copying extra file " + uploadfile); } } } - } - if (!string.IsNullOrEmpty(ExternalProgram)) - { - LogDebugMessage("Executing program " + ExternalProgram + " " + ExternalParams); - try + if (!string.IsNullOrEmpty(ExternalProgram)) { - ExecuteProgram(ExternalProgram, ExternalParams); - LogDebugMessage("External program started"); + LogDebugMessage("Executing program " + ExternalProgram + " " + ExternalParams); + try + { + ExecuteProgram(ExternalProgram, ExternalParams); + LogDebugMessage("External program started"); + } + catch (Exception ex) + { + LogMessage("Error starting external program: " + ex.Message); + } } - catch (Exception ex) + + //LogDebugMessage("Done creating extra files"); + + if (!String.IsNullOrEmpty(ftp_host)) { - LogMessage("Error starting external program: " + ex.Message); + DoFTPLogin(); } } - - //LogDebugMessage("Done creating extra files"); - - if (!String.IsNullOrEmpty(ftp_host)) + finally { - DoFTPLogin(); + WebUpdating = false; } - - WebUpdating = false; } void Client_ValidateCertificate(FtpClient control, FtpSslValidationEventArgs e) @@ -5508,11 +5548,11 @@ private void DoFTPLogin() conn.EncryptionMode = FtpEncryptionMode.Explicit; conn.DataConnectionEncryption = true; conn.ValidateCertificate += Client_ValidateCertificate; - // b3045 - switch from System.Net.Ftp.Client to FluentFTP allows us to specifiy protocols - conn.SslProtocols = SslProtocols.Default | SslProtocols.Tls11 | SslProtocols.Tls12; - } + // b3045 - switch from System.Net.Ftp.Client to FluentFTP allows us to specifiy protocols + conn.SslProtocols = SslProtocols.Default | SslProtocols.Tls11 | SslProtocols.Tls12; + } - if (ActiveFTPMode) + if (ActiveFTPMode) { conn.DataConnectionType = FtpDataConnectionType.PORT; } @@ -5562,6 +5602,7 @@ private void DoFTPLogin() //LogDebugMessage("Uploading extra files"); // Extra files + FtpTrace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + " Uploading Extra files"); for (int i = 0; i < numextrafiles; i++) { var uploadfile = ExtraFiles[i].local; @@ -5647,8 +5688,8 @@ private void DoFTPLogin() } } - // b3045 - dispose of connection - conn.Disconnect(); + // b3045 - dispose of connection + conn.Disconnect(); FtpTrace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + " Disconnected from " + ftp_host); } } @@ -5664,15 +5705,15 @@ private void UploadFile(FtpClient conn, string localfile, string remotefile) { if (DeleteBeforeUpload) { - // delete the existing file - try - { - conn.DeleteFile(remotefile); - } - catch (Exception ex) - { - LogMessage("FTP error deleting " + remotefile + " : " + ex.Message); - } + // delete the existing file + try + { + conn.DeleteFile(remotefile); + } + catch (Exception ex) + { + LogMessage("FTP error deleting " + remotefile + " : " + ex.Message); + } } using (Stream ostream = conn.OpenWrite(remotefilename)) @@ -5699,11 +5740,17 @@ private void UploadFile(FtpClient conn, string localfile, string remotefile) } } - if (FTPRename) { // rename the file - conn.Rename(remotefilename, remotefile); + try + { + conn.Rename(remotefilename, remotefile); + } + catch (Exception ex) + { + LogMessage("FTP error renaming " + remotefilename + " to " + remotefile + " : " + ex.Message); + } } FtpTrace.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + " Completed uploading " + localfile + " to " + remotefile); @@ -5867,7 +5914,7 @@ 58 1 Is sunny? file.Write((Convert.ToInt32(station.CloudBase)).ToString() + ' '); // 53 file.Write(CloudBaseInFeet ? "ft " : "m "); file.Write(ReplaceCommas(station.ApparentTemperature.ToString(TempFormat)) + ' '); // 55 - file.Write(ReplaceCommas(station.SunshineHours.ToString("F1")) + ' '); // 56 + file.Write(ReplaceCommas(station.SunshineHours.ToString(SunFormat)) + ' '); // 56 file.Write((Convert.ToInt32(station.CurrentSolarMax)).ToString() + ' '); // 57 file.WriteLine(station.IsSunny ? "1 " : "0 "); @@ -5899,7 +5946,7 @@ 58 1 Is sunny? ((int)station.SolarRad).ToString() + ',' + station.AvgBearing.ToString() + ',' + station.RainLastHour.ToString(RainFormat, InvC) + ',' + station.Forecastnumber.ToString() + ",'" + (IsDaylight() ? "1" : "0") + "','" + (station.SensorContactLost ? "1" : "0") + "','" + station.CompassPoint(station.AvgBearing) + "'," + ((int)station.CloudBase).ToString() + ",'" + (CloudBaseInFeet ? "ft" : "m") + "'," + - station.ApparentTemperature.ToString(TempFormat, InvC) + ',' + station.SunshineHours.ToString("F1", InvC) + ',' + + station.ApparentTemperature.ToString(TempFormat, InvC) + ',' + station.SunshineHours.ToString(SunFormat, InvC) + ',' + ((int) Math.Round(station.CurrentSolarMax)).ToString() + ",'" + (station.IsSunny ? "1" : "0") + "')"; @@ -5931,8 +5978,7 @@ 58 1 Is sunny? if (!string.IsNullOrEmpty(MySqlRealtimeRetention)) { // delete old entries - cmd.CommandText = "DELETE IGNORE FROM " + MySqlRealtimeTable + " WHERE LogDateTime < DATE_SUB('" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "', INTERVAL " + - MySqlRealtimeRetention + ")"; + cmd.CommandText = "DELETE IGNORE FROM " + MySqlRealtimeTable + " WHERE LogDateTime < DATE_SUB('" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "', INTERVAL " + MySqlRealtimeRetention + ")"; //LogMessage(queryString); try @@ -6329,9 +6375,9 @@ private void RealtimeFTPLogin() RealtimeFTP.Host = ftp_host; RealtimeFTP.Port = ftp_port; RealtimeFTP.Credentials = new NetworkCredential(ftp_user, ftp_password); - // b3045 - Reduce the default polling interval to try and keep the session alive - RealtimeFTP.SocketKeepAlive = true; - //RealtimeFTP.SocketPollInterval = 2000; // 2 seconds, defaults to 15 seconds + // b3045 - Reduce the default polling interval to try and keep the session alive + RealtimeFTP.SocketKeepAlive = true; + //RealtimeFTP.SocketPollInterval = 2000; // 2 seconds, defaults to 15 seconds if (Sslftp) @@ -6339,10 +6385,10 @@ private void RealtimeFTPLogin() RealtimeFTP.EncryptionMode = FtpEncryptionMode.Explicit; RealtimeFTP.DataConnectionEncryption = true; RealtimeFTP.ValidateCertificate += Client_ValidateCertificate; - // b3045 - switch from System.Net.Ftp.Client to FluentFTP allows us to specifiy protocols - RealtimeFTP.SslProtocols = SslProtocols.Default | SslProtocols.Tls11 | SslProtocols.Tls12; + // b3045 - switch from System.Net.Ftp.Client to FluentFTP allows us to specifiy protocols + RealtimeFTP.SslProtocols = SslProtocols.Default | SslProtocols.Tls11 | SslProtocols.Tls12; - } + } if (ftp_host != "" && ftp_host != " ") @@ -6355,6 +6401,7 @@ private void RealtimeFTPLogin() catch (Exception ex) { LogMessage("Error connecting ftp - " + ex.Message); + RealtimeFTP.Disconnect(); } RealtimeFTP.EnableThreadSafeDataConnections = false; // use same connection for all transfers @@ -6466,7 +6513,7 @@ private async void WBCatchup() } catch (Exception ex) { - LogMessage("Wbug update: " + ex.Message); + LogMessage("Wbug update: " + ex.Message); } } @@ -6758,33 +6805,33 @@ private void AddToWeatherbugList(DateTime timestamp) public void SetMonthlySqlCreateString() { CreateMonthlySQL = "CREATE TABLE " + MySqlMonthlyTable + " (LogDateTime DATETIME NOT NULL,Temp decimal(4," + TempDPlaces + ") NOT NULL,Humidity decimal(4," + HumDPlaces + - ") NOT NULL,Dewpoint decimal(4," + TempDPlaces + ") NOT NULL,Windspeed decimal(4," + WindDPlaces + ") NOT NULL,Windgust decimal(4," + WindDPlaces + - ") NOT NULL,Windbearing VARCHAR(3) NOT NULL,RainRate decimal(4," + RainDPlaces + ") NOT NULL,TodayRainSoFar decimal(4," + RainDPlaces + - ") NOT NULL,Pressure decimal(6," + PressDPlaces + ") NOT NULL,Raincounter decimal(6," + RainDPlaces + ") NOT NULL,InsideTemp decimal(4," + - TempDPlaces + ") NOT NULL,InsideHumidity decimal(4," + HumDPlaces + ") NOT NULL,LatestWindGust decimal(5," + WindDPlaces + - ") NOT NULL,WindChill decimal(4," + TempDPlaces + ") NOT NULL,HeatIndex decimal(4," + TempDPlaces + ") NOT NULL,UVindex decimal(4," + UVDPlaces + - "),SolarRad decimal(5,1),Evapotrans decimal(4," + RainDPlaces + "),AnnualEvapTran decimal(5," + RainDPlaces + "),ApparentTemp decimal(4," + - TempDPlaces + "),MaxSolarRad decimal(5,1),HrsSunShine decimal(3,1),CurrWindBearing varchar(3),RG11rain decimal(4," + RainDPlaces + - "),RainSinceMidnight decimal(4," + RainDPlaces + - "), WindbearingSym varchar(3),CurrWindBearingSym varchar(3),PRIMARY KEY (LogDateTime)) COMMENT = \"Monthly logs from Cumulus\""; + ") NOT NULL,Dewpoint decimal(4," + TempDPlaces + ") NOT NULL,Windspeed decimal(4," + WindDPlaces + ") NOT NULL,Windgust decimal(4," + WindDPlaces + + ") NOT NULL,Windbearing VARCHAR(3) NOT NULL,RainRate decimal(4," + RainDPlaces + ") NOT NULL,TodayRainSoFar decimal(4," + RainDPlaces + + ") NOT NULL,Pressure decimal(6," + PressDPlaces + ") NOT NULL,Raincounter decimal(6," + RainDPlaces + ") NOT NULL,InsideTemp decimal(4," + + TempDPlaces + ") NOT NULL,InsideHumidity decimal(4," + HumDPlaces + ") NOT NULL,LatestWindGust decimal(5," + WindDPlaces + + ") NOT NULL,WindChill decimal(4," + TempDPlaces + ") NOT NULL,HeatIndex decimal(4," + TempDPlaces + ") NOT NULL,UVindex decimal(4," + UVDPlaces + + "),SolarRad decimal(5,1),Evapotrans decimal(4," + RainDPlaces + "),AnnualEvapTran decimal(5," + RainDPlaces + "),ApparentTemp decimal(4," + + TempDPlaces + "),MaxSolarRad decimal(5,1),HrsSunShine decimal(3," + SunshineDPlaces + "),CurrWindBearing varchar(3),RG11rain decimal(4," + RainDPlaces + + "),RainSinceMidnight decimal(4," + RainDPlaces + + "), WindbearingSym varchar(3),CurrWindBearingSym varchar(3),PRIMARY KEY (LogDateTime)) COMMENT = \"Monthly logs from Cumulus\""; } internal void SetDayfileSqlCreateString() { CreateDayfileSQL = "CREATE TABLE " + MySqlDayfileTable + " (LogDate date NOT NULL ,HighWindGust decimal(4," + WindDPlaces + - ") NOT NULL,HWindGBear varchar(3) NOT NULL,THWindG varchar(5) NOT NULL,MinTemp decimal(5," + TempDPlaces + - ") NOT NULL,TMinTemp varchar(5) NOT NULL,MaxTemp decimal(5," + TempDPlaces + ") NOT NULL,TMaxTemp varchar(5) NOT NULL,MinPress decimal(6," + - PressDPlaces + ") NOT NULL,TMinPress varchar(5) NOT NULL,MaxPress decimal(6," + PressDPlaces + - ") NOT NULL,TMaxPress varchar(5) NOT NULL,MaxRainRate decimal(4," + RainDPlaces + ") NOT NULL,TMaxRR varchar(5) NOT NULL,TotRainFall decimal(6," + - RainDPlaces + ") NOT NULL,AvgTemp decimal(4," + TempDPlaces + ") NOT NULL,TotWindRun decimal(5," + WindRunDPlaces + - ") NOT NULL,HighAvgWSpeed decimal(3," + WindDPlaces + "),THAvgWSpeed varchar(5),LowHum decimal(4," + HumDPlaces + - "),TLowHum varchar(5),HighHum decimal(4," + HumDPlaces + "),THighHum varchar(5),TotalEvap decimal(5," + RainDPlaces + - "),HoursSun decimal(3,1),HighHeatInd decimal(4," + TempDPlaces + "),THighHeatInd varchar(5),HighAppTemp decimal(4," + TempDPlaces + - "),THighAppTemp varchar(5),LowAppTemp decimal(4," + TempDPlaces + "),TLowAppTemp varchar(5),HighHourRain decimal(4," + RainDPlaces + - "),THighHourRain varchar(5),LowWindChill decimal(4," + TempDPlaces + "),TLowWindChill varchar(5),HighDewPoint decimal(4," + TempDPlaces + - "),THighDewPoint varchar(5),LowDewPoint decimal(4," + TempDPlaces + - "),TLowDewPoint varchar(5),DomWindDir varchar(3),HeatDegDays decimal(4,1),CoolDegDays decimal(4,1),HighSolarRad decimal(5,1),THighSolarRad varchar(5),HighUV decimal(3," + - UVDPlaces + "),THighUV varchar(5),HWindGBearSym varchar(3),DomWindDirSym varchar(3),PRIMARY KEY(LogDate)) COMMENT = \"Dayfile from Cumulus\""; + ") NOT NULL,HWindGBear varchar(3) NOT NULL,THWindG varchar(5) NOT NULL,MinTemp decimal(5," + TempDPlaces + + ") NOT NULL,TMinTemp varchar(5) NOT NULL,MaxTemp decimal(5," + TempDPlaces + ") NOT NULL,TMaxTemp varchar(5) NOT NULL,MinPress decimal(6," + + PressDPlaces + ") NOT NULL,TMinPress varchar(5) NOT NULL,MaxPress decimal(6," + PressDPlaces + + ") NOT NULL,TMaxPress varchar(5) NOT NULL,MaxRainRate decimal(4," + RainDPlaces + ") NOT NULL,TMaxRR varchar(5) NOT NULL,TotRainFall decimal(6," + + RainDPlaces + ") NOT NULL,AvgTemp decimal(4," + TempDPlaces + ") NOT NULL,TotWindRun decimal(5," + WindRunDPlaces + + ") NOT NULL,HighAvgWSpeed decimal(3," + WindDPlaces + "),THAvgWSpeed varchar(5),LowHum decimal(4," + HumDPlaces + + "),TLowHum varchar(5),HighHum decimal(4," + HumDPlaces + "),THighHum varchar(5),TotalEvap decimal(5," + RainDPlaces + + "),HoursSun decimal(3," + SunshineDPlaces + "),HighHeatInd decimal(4," + TempDPlaces + "),THighHeatInd varchar(5),HighAppTemp decimal(4," + TempDPlaces + + "),THighAppTemp varchar(5),LowAppTemp decimal(4," + TempDPlaces + "),TLowAppTemp varchar(5),HighHourRain decimal(4," + RainDPlaces + + "),THighHourRain varchar(5),LowWindChill decimal(4," + TempDPlaces + "),TLowWindChill varchar(5),HighDewPoint decimal(4," + TempDPlaces + + "),THighDewPoint varchar(5),LowDewPoint decimal(4," + TempDPlaces + + "),TLowDewPoint varchar(5),DomWindDir varchar(3),HeatDegDays decimal(4,1),CoolDegDays decimal(4,1),HighSolarRad decimal(5,1),THighSolarRad varchar(5),HighUV decimal(3," + + UVDPlaces + "),THighUV varchar(5),HWindGBearSym varchar(3),DomWindDirSym varchar(3),PRIMARY KEY(LogDate)) COMMENT = \"Dayfile from Cumulus\""; } } diff --git a/CumulusMX/CumulusMX.csproj b/CumulusMX/CumulusMX.csproj index cfb9c8fd..3940f926 100644 --- a/CumulusMX/CumulusMX.csproj +++ b/CumulusMX/CumulusMX.csproj @@ -80,8 +80,8 @@ <Reference Include="Devart.Data.MySql"> <HintPath>..\packages\Devart.Data.MySql.dll</HintPath> </Reference> - <Reference Include="FluentFTP, Version=19.2.3.0, Culture=neutral, PublicKeyToken=f4af092b1d8df44f, processorArchitecture=MSIL"> - <HintPath>..\packages\FluentFTP.19.2.3\lib\net45\FluentFTP.dll</HintPath> + <Reference Include="FluentFTP, Version=21.0.0.0, Culture=neutral, PublicKeyToken=f4af092b1d8df44f, processorArchitecture=MSIL"> + <HintPath>..\packages\FluentFTP.21.0.0\lib\net45\FluentFTP.dll</HintPath> </Reference> <Reference Include="HidSharp, Version=2.0.8.0, Culture=neutral, processorArchitecture=MSIL"> <HintPath>..\packages\HidSharp.2.0.8\lib\net35\HidSharp.dll</HintPath> diff --git a/CumulusMX/DataStruct.cs b/CumulusMX/DataStruct.cs index 53911712..58be8938 100644 --- a/CumulusMX/DataStruct.cs +++ b/CumulusMX/DataStruct.cs @@ -640,7 +640,7 @@ public string PressTrendRounded [DataMember(Name = "SunshineHours")] public string SunshineHoursRounded { - get { return SunshineHours.ToString("F1"); } + get { return SunshineHours.ToString(cumulus.SunFormat); } set { } } diff --git a/CumulusMX/DavisStation.cs b/CumulusMX/DavisStation.cs index b11e40d9..87706e38 100644 --- a/CumulusMX/DavisStation.cs +++ b/CumulusMX/DavisStation.cs @@ -22,10 +22,13 @@ internal class DavisStation : WeatherStation //private int min; private int previousMinuteDisconnect = 60; private const int ACK = 6; + private const int NACK = 33; + private const int CANCEL = 24; private bool clockSetNeeded = false; private int previousMinuteSetClock = 60; private const string newline = "\n"; private DateTime lastRecepStatsTime; + private int commWaitTimeMs = 200; private TcpClient socket; @@ -181,7 +184,7 @@ private string GetFirmwareVersion() { comport.WriteLine(commandString); - Thread.Sleep(200); + Thread.Sleep(commWaitTimeMs); // Read the response var bytesRead = 0; @@ -264,7 +267,7 @@ private string GetReceptionStats() { comport.WriteLine(commandString); - Thread.Sleep(200); + Thread.Sleep(commWaitTimeMs); // Read the response var bytesRead = 0; @@ -666,6 +669,7 @@ private void SendBarRead() cumulus.LogDebugMessage("Sending BARREAD"); string response = ""; + // Expected response = "\n\rOK\n\rNNNNN\n\r" - Where NNNNN = ASCII pressure, inHg * 1000 if (IsSerial) { @@ -674,7 +678,7 @@ private void SendBarRead() { comport.WriteLine(commandString); - Thread.Sleep(200); + Thread.Sleep(commWaitTimeMs); // Read the response var bytesRead = 0; @@ -693,10 +697,10 @@ private void SendBarRead() } catch (Exception ex) { - cumulus.LogMessage(ex.Message); + cumulus.LogDebugMessage("SendBarRead: Error - " + ex.Message); } - cumulus.LogDebugMessage(BitConverter.ToString(buffer)); + cumulus.LogDataMessage("BARREAD Recieved 0x" + BitConverter.ToString(buffer)); } } else @@ -724,7 +728,7 @@ private void SendBarRead() //cumulus.LogMessage("Received " + ch.ToString("X2")); } - cumulus.LogDataMessage("Recieved 0x" + BitConverter.ToString(buffer)); + cumulus.LogDataMessage("BARREAD Recieved 0x" + BitConverter.ToString(buffer)); } catch (Exception ex) { @@ -762,19 +766,9 @@ private bool SendLoopCommand(SerialPort serialPort, string commandString) Thread.Sleep(500); - // Wait for the VP to acknowledge the the receipt of the command - sometimes we get a '\n\r' - // in the buffer first or no response is given. If all else fails, try again. - cumulus.LogDebugMessage("Wait for ACK"); - while (serialPort.BytesToRead > 0 && !Found_ACK) - { - // Read the current character - if (serialPort.ReadChar() == ACK) - { - Found_ACK = true; - cumulus.LogDebugMessage("ACK received"); - } - } - + // Wait for the VP to acknowledge the the receipt of the command - sometimes we get a '\n\r' + // in the buffer first or no response is given. If all else fails, try again. + Found_ACK = WaitForACK(serialPort); passCount++; } @@ -796,7 +790,6 @@ private bool SendLoopCommand(TcpClient tcpPort, string commandString) { bool Found_ACK = false; - const int ACK = 6; // ASCII 6 int passCount = 1; const int maxPasses = 4; @@ -814,22 +807,10 @@ private bool SendLoopCommand(TcpClient tcpPort, string commandString) stream.Write(Encoding.ASCII.GetBytes(commandString), 0, commandString.Length); Thread.Sleep(cumulus.DavisIPResponseTime); cumulus.LogDebugMessage("Wait for ACK"); - // Wait for the VP to acknowledge the the receipt of the command - sometimes we get a '\n\r' - // in the buffer first or no response is given. If all else fails, try again. - while (stream.DataAvailable && !Found_ACK) - { - - // Read the current character - int data = stream.ReadByte(); - cumulus.LogDataMessage("Received 0x" + data.ToString("X2")); - if (data == ACK) - { - cumulus.LogDebugMessage("Received ACK"); - Found_ACK = true; - } - } - - passCount++; + // Wait for the VP to acknowledge the the receipt of the command - sometimes we get a '\n\r' + // in the buffer first or no response is given. If all else fails, try again. + Found_ACK = WaitForACK(stream); + passCount++; } } catch (Exception ex) @@ -876,7 +857,7 @@ private void GetAndProcessLoopData(int number) while (loopcount < 20 && comport.BytesToRead < loopDataLength) { // Wait a short period to allow more data into the buffer - Thread.Sleep(250); + Thread.Sleep(commWaitTimeMs); loopcount++; } @@ -964,7 +945,8 @@ private void GetAndProcessLoopData(int number) cumulus.LogMessage("Data " + (i + 1) + ": " + BitConverter.ToString(loopString)); } - if (!(loopString[0] == 'L' && loopString[1] == 'O' && loopString[2] == 'O')) + // Check it is a LOOP packet, starts with "LOO" and 5th byte == 0: LOOP1 + if (!(loopString[0] == 'L' && loopString[1] == 'O' && loopString[2] == 'O' && Convert.ToByte(loopString[4]) == 0)) { cumulus.LogDebugMessage("invalid LOOP packet"); // Stop the sending of LOOP packets so we can resynch @@ -1044,8 +1026,8 @@ private void GetAndProcessLoopData(int number) double wind = ConvertWindMPHToUser(loopData.CurrentWindSpeed); double avgwind = ConvertWindMPHToUser(loopData.AvgWindSpeed); - // Check for sensible figures (spec says max for large cups is 175mph) - if (loopData.CurrentWindSpeed < 175 && loopData.AvgWindSpeed < 175) + // Check for sensible figures (spec says max for large cups is 175 mph, but up to 200 mph) + if (loopData.CurrentWindSpeed < 200 && loopData.AvgWindSpeed < 200) { int winddir = loopData.WindDirection; @@ -1361,7 +1343,7 @@ private void GetAndProcessLoop2Data(int number) while (loopcount < 100 && comport.BytesToRead < loopDataLength) { // Wait a short period to allow more data into the buffer - Thread.Sleep(200); + Thread.Sleep(commWaitTimeMs); loopcount++; } @@ -1409,7 +1391,8 @@ private void GetAndProcessLoop2Data(int number) } } - if (!(loopString[0] == 'L' && loopString[1] == 'O' && loopString[2] == 'O')) + // Check it is a LOOP packet, starts with "LOO" and 5th byte == 1: LOOP2 + if (!(loopString[0] == 'L' && loopString[1] == 'O' && loopString[2] == 'O' && Convert.ToByte(loopString[4]) == 1)) { cumulus.LogDebugMessage("invalid LOOP2 packet"); continue; @@ -1559,8 +1542,6 @@ private void GetArchiveData() cumulus.LogMessage("Date: " + vantageDateStamp); cumulus.LogMessage("Time: " + vantageTimeStamp); - int currChar; - if (IsSerial) { comport.DiscardInBuffer(); @@ -1574,12 +1555,12 @@ private void GetArchiveData() cumulus.LogMessage("Sending DMPAFT"); comport.WriteLine("DMPAFT"); - // wait for the ACK - currChar = comport.ReadChar(); + Thread.Sleep(commWaitTimeMs); - if (currChar != ACK) + // wait for the ACK + if (!WaitForACK(comport)) { - cumulus.LogMessage("No Ack in response to DMPAFT, received 0x" + currChar.ToString("X2")); + cumulus.LogMessage("No Ack in response to DMPAFT"); return; } } @@ -1588,25 +1569,17 @@ private void GetArchiveData() WakeVP(socket); string dmpaft = "DMPAFT\n"; stream.Write(Encoding.ASCII.GetBytes(dmpaft), 0, dmpaft.Length); - Thread.Sleep(cumulus.DavisIPResponseTime); - bool Found_ACK = false; - - while (stream.DataAvailable && !Found_ACK) - { - // Read the current character - if (stream.ReadByte() == ACK) - Found_ACK = true; - } + Thread.Sleep(cumulus.DavisIPResponseTime); - if (!Found_ACK) - { - cumulus.LogMessage("No Ack in response to DMPAFT"); + if (!WaitForACK(stream)) + { + cumulus.LogMessage("No Ack in response to DMPAFT"); return; } } - cumulus.LogMessage("Received response to DMPAFT, sending date and time"); + cumulus.LogMessage("Received response to DMPAFT, sending start date and time"); Trace.Flush(); // Construct date time string to send next @@ -1622,33 +1595,28 @@ private void GetArchiveData() data[4] = (byte) (crc/256); data[5] = (byte) (crc%256); - cumulus.LogMessage(BitConverter.ToString(data)); + cumulus.LogMessage("Sending: " + BitConverter.ToString(data)); if (IsSerial) { // send the data comport.Write(data, 0, 6); - // wait for the ACK - cumulus.LogMessage("Wait for ACK..."); - currChar = comport.ReadChar(); + Thread.Sleep(commWaitTimeMs); - if (currChar != ACK) + // wait for the ACK + if (!WaitForACK(comport)) { - cumulus.LogMessage("No ACK, received: 0x" + currChar.ToString("X2")); + cumulus.LogMessage("No ACK in response to sending date and time"); return; } - else - { - cumulus.LogMessage("ACK received"); - } cumulus.LogMessage("Waiting for response"); // wait for the response while (comport.BytesToRead < 6) { // Wait a short period to let more data load into the buffer - Thread.Sleep(200); + Thread.Sleep(commWaitTimeMs); } // Read the response @@ -1668,30 +1636,17 @@ private void GetArchiveData() Thread.Sleep(cumulus.DavisIPResponseTime); - bool Found_ACK = false; - - while (stream.DataAvailable && !Found_ACK) + if (!WaitForACK(stream)) { - // Read the current character - currChar = stream.ReadByte(); - cumulus.LogMessage("Received 0x" + currChar.ToString("X2")); - if (currChar == ACK) - Found_ACK = true; + cumulus.LogMessage("No ACK in response to sending date and time"); + return; } - if (!Found_ACK) - { - cumulus.LogMessage("No ACK"); - return; - } - - cumulus.LogMessage("ACK received"); - // Wait until the buffer is full while (socket.Available < 6) { // Wait a short period to let more data load into the buffer - Thread.Sleep(200); + Thread.Sleep(cumulus.DavisIPResponseTime); } // Read the response @@ -1741,7 +1696,7 @@ private void GetArchiveData() while (comport.BytesToRead < pageSize && responsePasses < 20) { // Wait a short period to let more data load into the buffer - Thread.Sleep(200); + Thread.Sleep(commWaitTimeMs); responsePasses++; } @@ -2201,7 +2156,7 @@ private byte[] GetData(SerialPort serialPort, string commandString, int returnLe while (serialPort.BytesToRead < loopString.Length) { // Wait a short period to let more data load into the buffer - Thread.Sleep(200); + Thread.Sleep(commWaitTimeMs); } // Read the first returnLength bytes of the buffer into the array @@ -2237,7 +2192,7 @@ private byte[] GetData(SerialPort serialPort, string commandString, int returnLe private bool WakeVP(SerialPort serialPort) { int passCount = 1, maxPasses = 4; - + int currChar, newLine = 10; try { @@ -2246,26 +2201,28 @@ private bool WakeVP(SerialPort serialPort) serialPort.DiscardInBuffer(); serialPort.DiscardOutBuffer(); - //cumulus.LogMessage("Waking VP"); + cumulus.LogDebugMessage("Waking VP"); // Put a newline character ('\n') out the serial port - the Writeline method terminates with a '\n' of its own serialPort.WriteLine(""); - //Thread.Sleep(1200); - //serialPort.WriteLine(""); - // Wait for 1.2 seconds - //Thread.Sleep(1200); + // Wait for 0.2 second for a response + Thread.Sleep(commWaitTimeMs); bool woken = false; - - for (int i = 0; i < 5; i++) + int i = 0; + while (!woken && i < 5) { - if (serialPort.BytesToRead != 0) + while (serialPort.BytesToRead != 0) { - woken = true; - //cumulus.LogMessage("Woken: i="+i); - break; + currChar = comport.ReadChar(); + if (currChar == newLine) + { + woken = true; + //cumulus.LogMessage("Woken: i="+i); + break; + } } - - Thread.Sleep(100); + i++; + Thread.Sleep(commWaitTimeMs); } // Now check and see if anything's been returned. If nothing, try again with another newline. @@ -2273,16 +2230,21 @@ private bool WakeVP(SerialPort serialPort) { //cumulus.LogMessage("No response, retry wake"); serialPort.WriteLine(""); - for (int i = 0; i < 12; i++) - { - if (serialPort.BytesToRead != 0) - { - woken = true; - //cumulus.LogMessage("Woken: i=" + i); - break; + i = 0; + while (!woken && i < 12) + { + while (serialPort.BytesToRead != 0) + { + currChar = comport.ReadChar(); + if (currChar == newLine) + { + woken = true; + //cumulus.LogMessage("Woken: i=" + i); + break; + } } - - Thread.Sleep(100); + i++; + Thread.Sleep(commWaitTimeMs); } passCount++; } @@ -2302,12 +2264,12 @@ private bool WakeVP(SerialPort serialPort) //} //cumulus.LogMessage(str); serialPort.DiscardInBuffer(); - //cumulus.LogMessage("Woken"); + cumulus.LogDebugMessage("Woken"); return (true); } else { - cumulus.LogDebugMessage("!!! Not woken"); + cumulus.LogMessage("!!! VP2 Not woken"); return (false); } } @@ -2457,304 +2419,295 @@ private bool WakeVP() } } - private DateTime getTime() - { - cumulus.LogMessage("Reading console time"); + private bool WaitForACK(SerialPort serialPort) + { + int currChar; - if (IsSerial) - { - string commandString = "GETTIME"; - if (WakeVP(comport)) - { - comport.WriteLine(commandString); - - Thread.Sleep(200); - - // Read the time - var bytesRead = 0; - byte[] buffer = new byte[8]; - while (comport.BytesToRead > 0 && bytesRead < 9) - { - // Read the current character - var ch = comport.ReadChar(); - if (bytesRead > 0) - { - buffer[bytesRead - 1] = (byte) ch; - } - bytesRead++; - //cumulus.LogMessage("Received " + ch.ToString("X2")); - } - - cumulus.LogDataMessage("Recieved 0x" + BitConverter.ToString(buffer)); - - if (bytesRead != 9) - { - cumulus.LogMessage("Expected 9 bytes, got " + bytesRead); - } - /*else if (!crcOK(buffer)) - { - cumulus.LogMessage("Invalid CRC"); - }*/ - else - { - try - { - return new DateTime(buffer[5] + 1900, buffer[4], buffer[3], buffer[2], buffer[1], buffer[0]); - } - catch (Exception) - { - cumulus.LogMessage("Error in time format"); - } - } - } - } - else - { - string commandString = "GETTIME\n"; - if (WakeVP(socket)) - { - try - { - NetworkStream stream = socket.GetStream(); - stream.Write(Encoding.ASCII.GetBytes(commandString), 0, commandString.Length); - - Thread.Sleep(cumulus.DavisIPResponseTime); - - bool Found_ACK = false; - int ch; - - while (stream.DataAvailable && !Found_ACK) - { - // Read the current character - ch = stream.ReadByte(); - //cumulus.LogMessage("Received 0x" + ch.ToString("X2")); - if (ch == ACK) - Found_ACK = true; - } - - if (!Found_ACK) - { - cumulus.LogMessage("No ACK - wait a little longer"); - // wait a little longer - Thread.Sleep(500); - while (stream.DataAvailable && !Found_ACK) - { - // Read the current character - ch = stream.ReadByte(); - cumulus.LogMessage("Received 0x" + ch.ToString("X2")); - if (ch == ACK) - Found_ACK = true; - } - if (!Found_ACK) - { - cumulus.LogMessage("No ACK"); - return DateTime.MinValue; - } - } - - cumulus.LogMessage("ACK received"); - - // Read the time - var bytesRead = 0; - byte[] buffer = new byte[8]; - while (stream.DataAvailable && bytesRead < 8) - { - // Read the current character - ch = stream.ReadByte(); - buffer[bytesRead] = (byte)ch; - - bytesRead++; - //cumulus.LogMessage("Received " + ch.ToString("X2")); - } + // Wait for the VP to acknowledge the the receipt of the command - sometimes we get a '\n\r' + // in the buffer first or no response is given. If all else fails, try again. + cumulus.LogDebugMessage("Wait for ACK"); + while (serialPort.BytesToRead > 0) + { + // Read the current character + currChar = serialPort.ReadChar(); + cumulus.LogDataMessage("WaitForACK received 0x" + currChar.ToString("X2")); + if (currChar == ACK) + { + cumulus.LogDebugMessage("ACK received"); + return true; + } + else if (currChar == NACK) + { + cumulus.LogDebugMessage("NACK received"); + return false; + } else if (currChar == CANCEL) + { + cumulus.LogDebugMessage("CANCEL received"); + return false; + } + } + return false; + } - cumulus.LogDataMessage("Recieved 0x" + BitConverter.ToString(buffer)); + private bool WaitForACK(NetworkStream stream) + { + int currChar; - if (bytesRead != 8) - { - cumulus.LogMessage("Expected 8 bytes, got " + bytesRead); - } - /*else if (!crcOK(buffer)) - { - cumulus.LogMessage("Invalid CRC"); - }*/ - else - { - try - { - return new DateTime(buffer[5] + 1900, buffer[4], buffer[3], buffer[2], buffer[1], buffer[0]); - } - catch (Exception) - { - cumulus.LogMessage("Error in time format"); - } - } - } - catch (Exception ex) - { - cumulus.LogDebugMessage("Get date time: Error - " + ex.Message); - } - } - } + // Wait for the VP to acknowledge the the receipt of the command - sometimes we get a '\n\r' + // in the buffer first or no response is given. If all else fails, try again. + cumulus.LogDebugMessage("Wait for ACK"); + while (stream.DataAvailable) + { + // Read the current character + currChar = stream.ReadByte(); + cumulus.LogDataMessage("WaitForACK received 0x" + currChar.ToString("X2")); + if (currChar == ACK) + { + cumulus.LogDebugMessage("ACK received"); + return true; + } + else if (currChar == NACK) + { + cumulus.LogDebugMessage("NACK received"); + return false; + } + else if (currChar == CANCEL) + { + cumulus.LogDebugMessage("CANCEL received"); + return false; + } + } + return false; + } - return DateTime.MinValue; - } - private void setTime() + private DateTime getTime() { - cumulus.LogMessage("Setting console time"); + byte[] buffer = new byte[8]; + var bytesRead = 0; - if (IsSerial) - { - string commandString = "SETTIME"; - if (WakeVP(comport)) - { - comport.WriteLine(commandString); - - //Thread.Sleep(200); + cumulus.LogMessage("Reading console time"); - // wait for the ACK - cumulus.LogMessage("Wait for ACK..."); - var ch = comport.ReadChar(); + if (IsSerial) + { + string commandString = "GETTIME"; + if (WakeVP(comport)) + { + comport.WriteLine(commandString); - if (ch != ACK) - { - cumulus.LogMessage("No ACK, received: 0x" + ch.ToString("X2")); - return; - } - else - { - cumulus.LogMessage("ACK received"); - } + Thread.Sleep(commWaitTimeMs); - DateTime now = DateTime.Now; + if (!WaitForACK(comport)) + { + cumulus.LogMessage("No ACK"); + return DateTime.MinValue; + } - byte[] buffer = new byte[8]; + // Read the time + while (comport.BytesToRead > 0 && bytesRead < 8) + { + // Read the current character + var ch = comport.ReadChar(); + if (bytesRead > 0) + { + buffer[bytesRead] = (byte)ch; + } + bytesRead++; + //cumulus.LogMessage("Received " + ch.ToString("X2")); + } + } + } + else + { + string commandString = "GETTIME\n"; + if (WakeVP(socket)) + { + try + { + NetworkStream stream = socket.GetStream(); + stream.Write(Encoding.ASCII.GetBytes(commandString), 0, commandString.Length); - buffer[0] = (byte) now.Second; - buffer[1] = (byte) now.Minute; - buffer[2] = (byte) now.Hour; - buffer[3] = (byte) now.Day; - buffer[4] = (byte) now.Month; - buffer[5] = (byte) (now.Year - 1900); + Thread.Sleep(cumulus.DavisIPResponseTime); - // calculate and insert CRC + int ch; - byte[] datacopy = new byte[6]; + if (!WaitForACK(stream)) + { + cumulus.LogMessage("No ACK - wait a little longer"); + // wait a little longer + Thread.Sleep(500); + if (!WaitForACK(stream)) + { + cumulus.LogMessage("No ACK"); + return DateTime.MinValue; + } + } - Array.Copy(buffer, datacopy, 6); - int crc = calculateCRC(datacopy); + cumulus.LogMessage("ACK received"); - buffer[6] = (byte) (crc/256); - buffer[7] = (byte) (crc%256); + // Read the time + while (stream.DataAvailable && bytesRead < 8) + { + // Read the current character + ch = stream.ReadByte(); + buffer[bytesRead] = (byte)ch; - // send the data - comport.Write(buffer, 0, 8); + bytesRead++; + //cumulus.LogMessage("Received " + ch.ToString("X2")); + } + } + catch (Exception ex) + { + cumulus.LogDebugMessage("Get date time: Error - " + ex.Message); + } + } + } - // wait for the ACK - cumulus.LogMessage("Wait for ACK..."); - ch = comport.ReadChar(); + cumulus.LogDataMessage("Recieved 0x" + BitConverter.ToString(buffer)); - if (ch != ACK) - { - cumulus.LogMessage("No ACK, received: 0x" + ch.ToString("X2")); - return; - } - else - { - cumulus.LogMessage("ACK received"); - } - } + if (bytesRead != 8) + { + cumulus.LogMessage("Expected 8 bytes, got " + bytesRead); } + /* Always seems to return a fixed CRC :( + else if (!crcOK(buffer)) + { + cumulus.LogMessage("Invalid CRC!"); + } + */ else { - string commandString = "SETTIME\n"; - if (WakeVP(socket)) + try { - try - { - NetworkStream stream = socket.GetStream(); - stream.Write(Encoding.ASCII.GetBytes(commandString), 0, commandString.Length); + return new DateTime(buffer[5] + 1900, buffer[4], buffer[3], buffer[2], buffer[1], buffer[0]); + } + catch (Exception) + { + cumulus.LogMessage("Error in time format"); + } + } + return DateTime.MinValue; + } - Thread.Sleep(cumulus.DavisIPResponseTime); + private void setTime() + { + NetworkStream stream = null; + + cumulus.LogMessage("Setting console time"); - bool Found_ACK = false; - int ch; + try + { + if (IsSerial) + { + string commandString = "SETTIME"; + if (WakeVP(comport)) + { + comport.WriteLine(commandString); - while (stream.DataAvailable && !Found_ACK) - { - // Read the current character - ch = stream.ReadByte(); - cumulus.LogMessage("Received 0x" + ch.ToString("X2")); - if (ch == ACK) - Found_ACK = true; - } + Thread.Sleep(commWaitTimeMs); - if (!Found_ACK) - { - cumulus.LogMessage("No ACK"); - return; - } + // wait for the ACK + if (!WaitForACK(comport)) + { + cumulus.LogMessage("No ACK to SETTIME - Not setting the time"); + return; + } + } + } + else + { + string commandString = "SETTIME\n"; + if (WakeVP(socket)) + { + stream = socket.GetStream(); + stream.Write(Encoding.ASCII.GetBytes(commandString), 0, commandString.Length); - cumulus.LogMessage("ACK received"); + Thread.Sleep(cumulus.DavisIPResponseTime); - DateTime now = DateTime.Now; + // wait for the ACK + if (!WaitForACK(stream)) + { + cumulus.LogMessage("No ACK to SETTIME - Not setting the time"); + return; + } + } + } + } + catch (Exception ex) + { + cumulus.LogDebugMessage("setTime Error - " + ex.Message); + return; + } - byte[] buffer = new byte[8]; + DateTime now = DateTime.Now; - buffer[0] = (byte)now.Second; - buffer[1] = (byte)now.Minute; - buffer[2] = (byte)now.Hour; - buffer[3] = (byte)now.Day; - buffer[4] = (byte)now.Month; - buffer[5] = (byte)(now.Year - 1900); + byte[] buffer = new byte[8]; - // calculate and insert CRC + buffer[0] = (byte)now.Second; + buffer[1] = (byte)now.Minute; + buffer[2] = (byte)now.Hour; + buffer[3] = (byte)now.Day; + buffer[4] = (byte)now.Month; + buffer[5] = (byte)(now.Year - 1900); - byte[] datacopy = new byte[6]; + // calculate and insert CRC - Array.Copy(buffer, datacopy, 6); - int crc = calculateCRC(datacopy); + byte[] datacopy = new byte[6]; - buffer[6] = (byte)(crc / 256); - buffer[7] = (byte)(crc % 256); + Array.Copy(buffer, datacopy, 6); + int crc = calculateCRC(datacopy); - stream.Write(buffer, 0, buffer.Length); + buffer[6] = (byte)(crc / 256); + buffer[7] = (byte)(crc % 256); - Thread.Sleep(cumulus.DavisIPResponseTime); + try + { + if (IsSerial) + { - Found_ACK = false; + // send the data + comport.Write(buffer, 0, 8); - while (stream.DataAvailable && !Found_ACK) - { - // Read the current character - ch = stream.ReadByte(); - cumulus.LogMessage("Received 0x" + ch.ToString("X2")); - if (ch == ACK) - Found_ACK = true; - } + Thread.Sleep(commWaitTimeMs); - if (!Found_ACK) - { - cumulus.LogMessage("No ACK"); - return; - } + // wait for the ACK + if (WaitForACK(comport)) + { + cumulus.LogMessage("Console time set OK"); + } + else + { + cumulus.LogMessage("Error, console time set failed"); + } + } + else if (stream != null) + { + stream.Write(buffer, 0, buffer.Length); - cumulus.LogMessage("ACK received"); - } - catch (Exception ex) - { - cumulus.LogDebugMessage("setTime Error - " + ex.Message); - } - } - } - } + Thread.Sleep(cumulus.DavisIPResponseTime); - /// <summary> - /// Converts rain from VP standard (in) to internal standard (mm) - /// </summary> - /// <param name="VPrain">rain in inches</param> - /// <returns>rain in mm</returns> - internal double ConvertRainToInternal(double VPrain) + if (WaitForACK(stream)) + { + cumulus.LogMessage("Console time set OK"); + } + else + { + cumulus.LogMessage("Error, console time set failed"); + } + } + } + catch (Exception ex) + { + cumulus.LogDebugMessage("setTime Error - " + ex.Message); + } + } + + /// <summary> + /// Converts rain from VP standard (in) to internal standard (mm) + /// </summary> + /// <param name="VPrain">rain in inches</param> + /// <returns>rain in mm</returns> + internal double ConvertRainToInternal(double VPrain) { if (cumulus.VPrainGaugeType == 1) return VPrain*25.4F; diff --git a/CumulusMX/EasyWeather.cs b/CumulusMX/EasyWeather.cs index b8da9822..91aefb74 100644 --- a/CumulusMX/EasyWeather.cs +++ b/CumulusMX/EasyWeather.cs @@ -10,6 +10,47 @@ namespace CumulusMX { + /* + * EasyWeather.dat file format (* indicates fields used by Cumulus) + * 1 Record number int + * 2 Transfer Date yyy-mmm-dd hh:mm:ss + * 3* Reading Date yyy-mmm-dd hh:mm:ss + * 4 Reading interval int (minutes since previous reading) + * 5* Indoor humidity int + * 6* Indoor temp float Celcius + * 7* Outdoor humidity int + * 8* Outdoor temp float Celcius + * 9* Dew point float Celcius + * 10* Wind chill float Celcius + * 11 Absolute press float mB/hPa + * 12* Relative press float mB/hPa + * 13* Wind average float m/s + * 14 Wind average int Beaufort + * 15* Wind gust float m/s + * 16 Wind gust int Beaufort + * 17 Wind direction int 0 - 15. 0 = North, 1 = NNE etc + * 18* Wind direction str Oddly ENE appears as NEE, and ESE appears as SEE + * 19 Rain ticks int + * 20 Rain total float mm + * 21 Rain since last float mm + * 22* Rain in last hour float mm + * 23 Rain in last 24h float mm + * 24 Rain in last 7d float mm + * 25 Rain in last 30d float mm + * 26* Rain total float mm + * 27* Light reading int Lux + * 28* UV Index int + * 29 Status bit #0 int 0|1 + * 30 Status bit #1 int 0|1 + * 31 Status bit #2 int 0|1 + * 32 Status bit #3 int 0|1 + * 33 Status bit #4 int 0|1 + * 34 Status bit #5 int 0|1 + * 35 Status bit #6 int 0|1 - Outdoor readings invalid = 1 + * 36 Status bit #7 int 0|1 + * 37 Data address 6 digit hex + * 38 Raw data 16x 2-digit hex + */ internal class EasyWeather : WeatherStation { private Timer tmrDataRead; diff --git a/CumulusMX/ImetStation.cs b/CumulusMX/ImetStation.cs index 477c3ba0..dfe5cf9e 100644 --- a/CumulusMX/ImetStation.cs +++ b/CumulusMX/ImetStation.cs @@ -31,6 +31,12 @@ public ImetStation(Cumulus cumulus) : base(cumulus) calculaterainrate = true; + // Change the default dps for rain and sunshine from 1 to 2 for IMet stations + cumulus.RainDPlaces = cumulus.SunshineDPlaces = 2; + cumulus.RainDPlace[0] = 2; // mm + cumulus.RainDPlace[1] = 3; // in + cumulus.RainFormat = cumulus.SunFormat = "F2"; + comport = new SerialPort(cumulus.ComportName, cumulus.ImetBaudRate, Parity.None, 8, StopBits.One) {Handshake = Handshake.None, RtsEnable = true, DtrEnable = true}; try diff --git a/CumulusMX/Properties/AssemblyInfo.cs b/CumulusMX/Properties/AssemblyInfo.cs index 41cf47f8..c19500c2 100644 --- a/CumulusMX/Properties/AssemblyInfo.cs +++ b/CumulusMX/Properties/AssemblyInfo.cs @@ -5,11 +5,11 @@ // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle("CumulusMX - beta")] +[assembly: AssemblyTitle("CumulusMX")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("CumulusMX - beta")] +[assembly: AssemblyProduct("CumulusMX")] [assembly: AssemblyCopyright("Copyright © 2015")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/CumulusMX/TokenParser.cs b/CumulusMX/TokenParser.cs index b5e20fec..8966189e 100644 --- a/CumulusMX/TokenParser.cs +++ b/CumulusMX/TokenParser.cs @@ -237,7 +237,8 @@ private String Parse3() Trace.WriteLine(e.ToString()); //cumulus.LogMessage(InputText); Console.WriteLine("*** web tag error - see MXdiags file ***"); - outText += e.Message; + //outText += e.Message; + outText += "**Web tag error, tag starting: #" + token.Substring(0, token.Length > 40 ? 39 : token.Length - 1) + "**"; } i = match.Index + match.Length; } diff --git a/CumulusMX/WeatherLink.cs b/CumulusMX/WeatherLink.cs index ce74e904..b5a0d508 100644 --- a/CumulusMX/WeatherLink.cs +++ b/CumulusMX/WeatherLink.cs @@ -23,8 +23,7 @@ namespace CumulusMX // 80 = ASCII "P" = Rev A firmware, no trend info is available. // Any other value means that the Vantage does not have the 3 hours of bar data needed // to determine the bar trend. - // Packet Type 4 1 Has the value zero. In the future we may define new LOOP packet formats and assign a different - // value to this field. + // Packet Type 4 1 Has the value zero. LOOP2 packets are set to 1. // Next Record 5 2 Location in the archive memory where the next data packet will be written. This can be // monitored to detect when a new record is created. // Pressure 7 2 Current Pressure. Units are (in Hg / 1000). The barometric value should be between 20 inches @@ -255,7 +254,14 @@ public void Load(Byte[] byteArray) var srMonth = (stormRainStart & 0xF000) >> 12; var srDay = (stormRainStart & 0x0F80) >> 7; var srYear = (stormRainStart & 0x007F) + 2000; - StormRainStart = new DateTime(srYear, srMonth, srDay); + if (srMonth < 13 && srDay < 32) // Exception suppression! + { + StormRainStart = new DateTime(srYear, srMonth, srDay); + } + else + { + StormRainStart = DateTime.MinValue; + } } catch (Exception) { diff --git a/CumulusMX/WeatherStation.cs b/CumulusMX/WeatherStation.cs index 1bbcc808..bdf53964 100644 --- a/CumulusMX/WeatherStation.cs +++ b/CumulusMX/WeatherStation.cs @@ -1220,11 +1220,12 @@ public void UpdateDegreeDays(int interval) public double SunshineHours { get; set; } public double YestSunshineHours { get; set; } + public double SunshineToMidnight { get; set; } - public double SunHourCounter { get; set; } - public double StartOfDaySunHourCounter { get; set; } + public double SunHourCounter { get; set; } + public double StartOfDaySunHourCounter { get; set; } public double CurrentSolarMax { get; set; } @@ -1309,11 +1310,11 @@ public void SecondTimer(object sender, ElapsedEventArgs e) // send current data to websocket every 3 seconds for now try { - String windRoseData = (windcounts[0] * cumulus.WindGustMult).ToString(cumulus.WindFormat).Replace(",", "."); + String windRoseData = (windcounts[0] * cumulus.WindGustMult).ToString(cumulus.WindFormat, CultureInfo.InvariantCulture); for (var i = 1; i < cumulus.NumWindRosePoints; i++) { - windRoseData = windRoseData + "," + (windcounts[i] * cumulus.WindGustMult).ToString(cumulus.WindFormat).Replace(",", "."); + windRoseData = windRoseData + "," + (windcounts[i] * cumulus.WindGustMult).ToString(cumulus.WindFormat, CultureInfo.InvariantCulture); } string stormRainStart = StartOfStorm == DateTime.MinValue ? "-----" : StartOfStorm.ToString("d"); @@ -4823,12 +4824,12 @@ private void DoDayfile(DateTime timestamp) if (cumulus.RolloverHour == 0) { // use existing current sunshinehour count - str += SunshineHours.ToString("F1") + cumulus.ListSeparator; + str += SunshineHours.ToString(cumulus.SunFormat) + cumulus.ListSeparator; } else { // for non-midnight rollover, use new item - str += SunshineToMidnight.ToString("F1") + cumulus.ListSeparator; + str += SunshineToMidnight.ToString(cumulus.SunFormat) + cumulus.ListSeparator; } str += HighHeatIndexToday.ToString(cumulus.TempFormat) + cumulus.ListSeparator; str += highheatindextodaytime.ToString("HH:mm") + cumulus.ListSeparator; @@ -4899,12 +4900,12 @@ private void DoDayfile(DateTime timestamp) if (cumulus.RolloverHour == 0) { // use existing current sunshinehour count to minimise risk - file.Write(SunshineHours.ToString("F1") + cumulus.ListSeparator); + file.Write(SunshineHours.ToString(cumulus.SunFormat) + cumulus.ListSeparator); } else { // for non-midnight rollover, use new item - file.Write(SunshineToMidnight.ToString("F1") + cumulus.ListSeparator); + file.Write(SunshineToMidnight.ToString(cumulus.SunFormat) + cumulus.ListSeparator); } file.Write(HighHeatIndexToday.ToString(cumulus.TempFormat) + cumulus.ListSeparator); file.Write(highheatindextodaytime.ToString("HH:mm") + cumulus.ListSeparator); @@ -4972,7 +4973,7 @@ private void DoDayfile(DateTime timestamp) highhumiditytoday + "," + highhumiditytodaytime.ToString("\\'HH:mm\\'") + "," + ET.ToString(cumulus.ETFormat, InvC) + "," + - (cumulus.RolloverHour == 0 ? SunshineHours.ToString("F1", InvC) : SunshineToMidnight.ToString("F1", InvC)) + "," + + (cumulus.RolloverHour == 0 ? SunshineHours.ToString(cumulus.SunFormat, InvC) : SunshineToMidnight.ToString(cumulus.SunFormat, InvC)) + "," + HighHeatIndexToday.ToString(cumulus.TempFormat, InvC) + "," + highheatindextodaytime.ToString("\\'HH:mm\\'") + "," + HighAppTempToday.ToString(cumulus.TempFormat, InvC) + "," + @@ -8537,7 +8538,7 @@ public string GetTodayYestSolar() json += "[\"" + "High Solar Radiation" + "\",\"" + HighSolarToday.ToString("F0") + " " + "W/m2" + "\",\"" + highsolartodaytime.ToShortTimeString() + "\",\"" + HighSolarYesterday.ToString("F0") + " " + "W/m2" + "\",\"" + highsolaryesterdaytime.ToShortTimeString() + "\"],"; - json += "[\"" + "Hours of Sunshine" + "\",\"" + SunshineHours.ToString("F1") + " hrs" + "\",\"" + " " + "\",\"" + YestSunshineHours.ToString("F1") + + json += "[\"" + "Hours of Sunshine" + "\",\"" + SunshineHours.ToString(cumulus.SunFormat) + " hrs" + "\",\"" + " " + "\",\"" + YestSunshineHours.ToString(cumulus.SunFormat) + " hrs" + "\",\"" + " " + "\"]"; json += "]}"; @@ -8757,7 +8758,7 @@ public string GetSunHoursGraphData() for (var i = 0; i < RecentDailyDataList.Count; i++) { - sb.Append("[" + DateTimeToUnix(RecentDailyDataList[i].timestamp) * 1000 + "," + RecentDailyDataList[i].sunhours.ToString("F1", InvC) + "]"); + sb.Append("[" + DateTimeToUnix(RecentDailyDataList[i].timestamp) * 1000 + "," + RecentDailyDataList[i].sunhours.ToString(cumulus.SunFormat, InvC) + "]"); if (i < RecentDailyDataList.Count - 1) sb.Append(","); @@ -8801,11 +8802,11 @@ public string GetDailyTempGraphData() internal string GetCurrentData() { - String windRoseData = (windcounts[0] * cumulus.WindGustMult).ToString(cumulus.WindFormat).Replace(",", "."); + String windRoseData = (windcounts[0] * cumulus.WindGustMult).ToString(cumulus.WindFormat, CultureInfo.InvariantCulture); for (var i = 1; i < cumulus.NumWindRosePoints; i++) { - windRoseData = windRoseData + "," + (windcounts[i] * cumulus.WindGustMult).ToString(cumulus.WindFormat).Replace(",", "."); + windRoseData = windRoseData + "," + (windcounts[i] * cumulus.WindGustMult).ToString(cumulus.WindFormat, CultureInfo.InvariantCulture); } string stormRainStart = StartOfStorm == DateTime.MinValue ? "-----" : StartOfStorm.ToString("d"); diff --git a/CumulusMX/packages.config b/CumulusMX/packages.config index ee0384f7..71491240 100644 --- a/CumulusMX/packages.config +++ b/CumulusMX/packages.config @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <packages> <package id="EmbedIO" version="2.1.1" targetFramework="net452" /> - <package id="FluentFTP" version="19.2.3" targetFramework="net452" /> + <package id="FluentFTP" version="21.0.0" targetFramework="net452" /> <package id="HidSharp" version="2.0.8" targetFramework="net452" /> <package id="linqtotwitter" version="3.1.1" targetFramework="net45" /> <package id="Microsoft.Bcl" version="1.1.10" targetFramework="net452" /> diff --git a/CumulusMX/webtags.cs b/CumulusMX/webtags.cs index b9b35501..c639e54c 100644 --- a/CumulusMX/webtags.cs +++ b/CumulusMX/webtags.cs @@ -836,10 +836,10 @@ private string Tagwdirdata(Dictionary<string,string> TagParams) private string Tagwspddata(Dictionary<string,string> TagParams) { //String s = (station.windspeeds[0]*cumulus.WindGustMult).ToString(cumulus.WindFormat).Replace(",", "."); - var sb = new StringBuilder((station.windspeeds[0]*cumulus.WindGustMult).ToString(cumulus.WindFormat).Replace(",", ".")); + var sb = new StringBuilder((station.windspeeds[0]*cumulus.WindGustMult).ToString(cumulus.WindFormat, CultureInfo.InvariantCulture)); for (var i = 1; i < station.numwindvalues; i++) { - sb.Append("," + (station.windspeeds[i]*cumulus.WindGustMult).ToString(cumulus.WindFormat).Replace(",", ".")); + sb.Append("," + (station.windspeeds[i]*cumulus.WindGustMult).ToString(cumulus.WindFormat, CultureInfo.InvariantCulture)); //s = s + "," + (station.windspeeds[i]*cumulus.WindGustMult).ToString(cumulus.WindFormat).Replace(",", "."); } @@ -859,11 +859,11 @@ private string Tagnextwindindex(Dictionary<string,string> TagParams) private string TagWindRoseData(Dictionary<string,string> TagParams) { - String s = (station.windcounts[0]*cumulus.WindGustMult).ToString(cumulus.WindFormat).Replace(",", "."); + String s = (station.windcounts[0]*cumulus.WindGustMult).ToString(cumulus.WindFormat, CultureInfo.InvariantCulture); for (var i = 1; i < cumulus.NumWindRosePoints; i++) { - s = s + "," + (station.windcounts[i]*cumulus.WindGustMult).ToString(cumulus.WindFormat).Replace(",", "."); + s = s + "," + (station.windcounts[i]*cumulus.WindGustMult).ToString(cumulus.WindFormat, CultureInfo.InvariantCulture); } return s; @@ -2684,7 +2684,7 @@ private string TagCurrentSolarMax(Dictionary<string,string> TagParams) private string TagSunshineHours(Dictionary<string,string> TagParams) { - return station.SunshineHours.ToString("F1"); + return station.SunshineHours.ToString(cumulus.SunFormat); } private string TagTHWIndex(Dictionary<string,string> TagParams) @@ -2704,7 +2704,7 @@ private string TagChillHours(Dictionary<string,string> TagParams) private string TagYSunshineHours(Dictionary<string,string> TagParams) { - return station.YestSunshineHours.ToString("F1"); + return station.YestSunshineHours.ToString(cumulus.SunFormat); } private string TagIsSunny(Dictionary<string,string> TagParams) diff --git a/Updates.txt b/Updates.txt index 0613e926..f094caa4 100644 --- a/Updates.txt +++ b/Updates.txt @@ -1,6 +1,24 @@ -3048beta -======== -- Fix web tag <#YearLowDailyTempRangeD> +3048 +==== +- You can now first time enable/disable Realtime FTP without having to restart CMX + +- Instromet stations now record and report rainfall (mm) and sunshine hours to 2 decimal places + +- Improved realtime FTP error handling + +- Improved Davis protocol handling + +- Fix Davis protocol mixing up LOOP1 and LOOP2 packets and consequently providing invalid rain and wind data. + +- Fix web tag <#YearLowDailyTempRangeD> broken in b3047 + +- Bug fixes to FTP Component, and internal changes to FTP transfer mechanism + +- Updated files + \CumulusMX.exe + \CumulusMX.exe.pdb + \FluentFTP.dll + 3047