diff --git a/App.config b/App.config index ce42248..5df4b6f 100644 --- a/App.config +++ b/App.config @@ -1,95 +1,146 @@  - - -
- - - - - - - - - False - - - - - - 00:00:01 - - - C:\WeatherCenter\WeatherCenter.sdf - - - http://*:9090 - - - http://localhost:9090 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +
+ + +
+ + + + + + + + False + + + 00:00:01 + + + http://*:9090 + + + http://localhost:9090 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Data/Database.cs b/Data/Database.cs deleted file mode 100644 index e6d01c0..0000000 --- a/Data/Database.cs +++ /dev/null @@ -1,99 +0,0 @@ -using Common.Debug; -using System.Collections.Generic; -using System.Data.Linq; -using System.Data.SqlServerCe; -using System.Linq; -using WeatherService.Values; - -namespace WeatherService.Data -{ - /// - /// This class manages database connectivity and logging - /// - internal static class Database - { - private static DatabaseContext _databaseContext; - private static SqlCeConnection _connection; - - public static void Connect(string databasePath) - { - _connection = new SqlCeConnection(string.Format("Data Source={0}", databasePath)); - _connection.Open(); - } - - public static SqlCeConnection Connection - { - get - { - return _connection; - } - } - - public static void Initialize(string databasePath) - { - Connect(databasePath); - - // Create a database context - _databaseContext = new DatabaseContext(databasePath); - - // Turn on logging if requested - if (Settings.Default.LogDatabase) - _databaseContext.Log = Tracer.Writer; - - // Create the database engine - SqlCeEngine engine = new SqlCeEngine(string.Format("Data Source={0}", databasePath)); - - // Check to see if the database exists - if (!_databaseContext.DatabaseExists()) - { - // Create the database itself - engine.CreateDatabase(); - - // Run the creation script - // executeScript(Resources.CreateDatabase); - } - - // Compact the database - engine.Shrink(); - } - - public static Table DeviceTable - { - get { return _databaseContext.DeviceTable; } - } - - public static Table ReadingTable - { - get { return _databaseContext.ReadingTable; } - } - - public static IEnumerable DeviceList - { - get { return from device in DeviceTable select device; } - } - - public static IEnumerable ReadingList(int deviceId) - { - return from reading in ReadingTable where reading.DeviceId == deviceId select reading; - } - - public static IEnumerable ReadingList(int deviceId, WeatherValueType valueType) - { - return from reading in ReadingTable where reading.DeviceId == deviceId && reading.Type == valueType select reading; - } - - public static void SaveChanges() - { - _databaseContext.SubmitChanges(ConflictMode.ContinueOnConflict); - } - - public static void DiscardChanges() - { - ChangeSet changeSet = _databaseContext.GetChangeSet(); - - _databaseContext.Refresh(RefreshMode.OverwriteCurrentValues, changeSet.Inserts); - _databaseContext.Refresh(RefreshMode.OverwriteCurrentValues, changeSet.Deletes); - _databaseContext.Refresh(RefreshMode.OverwriteCurrentValues, changeSet.Updates); - } - } -} \ No newline at end of file diff --git a/Data/DatabaseContext.cs b/Data/DatabaseContext.cs deleted file mode 100644 index a47b210..0000000 --- a/Data/DatabaseContext.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Data.Linq; - -namespace WeatherService.Data -{ - public class DatabaseContext : DataContext - { - public Table DeviceTable; - public Table ReadingTable; - - public DatabaseContext(string databaseFile) : base(databaseFile) { } - } -} diff --git a/Data/Device.cs b/Data/Device.cs new file mode 100644 index 0000000..590dea5 --- /dev/null +++ b/Data/Device.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace WeatherService.Data +{ + [Table("Device")] + public class Device + { + public int Id { get; set; } + + [Required] + [StringLength(100)] + public string Address { get; set; } + + [Required] + [StringLength(100)] + public string Name { get; set; } + + public int ReadInterval { get; set; } + } +} diff --git a/Data/DeviceData.cs b/Data/DeviceData.cs deleted file mode 100644 index cb39c9f..0000000 --- a/Data/DeviceData.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Data.Linq.Mapping; - -namespace WeatherService.Data -{ - [Table(Name = "Device")] - public class DeviceData - { - [Column(UpdateCheck = UpdateCheck.Never, IsDbGenerated = true, IsPrimaryKey = true)] - public int Id; - - [Column(UpdateCheck = UpdateCheck.Never)] - public string Address; - - [Column(UpdateCheck = UpdateCheck.Never)] - public string Name; - - [Column(UpdateCheck = UpdateCheck.Never)] - public int ReadInterval; - - //private readonly EntitySet _readingSet = new EntitySet(); - //[Association(Storage = "_readingSet", OtherKey = "DeviceId", ThisKey = "Id")] - //public EntitySet Readings - //{ - // get { return _readingSet; } - // set { _readingSet.Assign(value); } - //} - } -} diff --git a/Data/Reading.cs b/Data/Reading.cs new file mode 100644 index 0000000..a9ed9fd --- /dev/null +++ b/Data/Reading.cs @@ -0,0 +1,19 @@ +using System; +using System.ComponentModel.DataAnnotations.Schema; + +namespace WeatherService.Data +{ + [Table("Reading")] + public class Reading + { + public int Id { get; set; } + + public int DeviceId { get; set; } + + public int Type { get; set; } + + public double Value { get; set; } + + public DateTimeOffset ReadTime { get; set; } + } +} diff --git a/Data/ReadingData.cs b/Data/ReadingData.cs deleted file mode 100644 index 152f841..0000000 --- a/Data/ReadingData.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.Data.Linq.Mapping; -using System.Data.SqlTypes; -using WeatherService.Values; - -namespace WeatherService.Data -{ - [Table(Name = "Reading")] - public class ReadingData - { - public ReadingData() - { - ReadTime = SqlDateTime.MinValue.Value; - } - - [Column(UpdateCheck = UpdateCheck.Never, IsDbGenerated = true, IsPrimaryKey = true)] - public int Id - { - get; - internal set; - } - - [Column(UpdateCheck = UpdateCheck.Never)] - public int DeviceId - { - get; - internal set; - } - - [Column(UpdateCheck = UpdateCheck.Never)] - public WeatherValueType Type - { - get; - internal set; - } - - [Column(UpdateCheck = UpdateCheck.Never)] - public double Value - { - get; - internal set; - } - - [Column(UpdateCheck = UpdateCheck.Never)] - public DateTime ReadTime - { - get; - internal set; - } - - //private EntityRef _device; - //[Association(Storage = "_device", ThisKey = "DeviceId", OtherKey = "Id")] - //public DeviceData Device - //{ - // get { return _device.Entity; } - // set { _device.Entity = value; } - //} - - } -} diff --git a/Data/Setting.cs b/Data/Setting.cs new file mode 100644 index 0000000..8d60865 --- /dev/null +++ b/Data/Setting.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace WeatherService.Data +{ + [Table("Setting")] + public class Setting + { + [Key] + [StringLength(500)] + public string Name { get; set; } + + [Required] + [StringLength(3500)] + public string Value { get; set; } + } +} diff --git a/Data/WeatherArchiveData.cs b/Data/WeatherArchiveData.cs new file mode 100644 index 0000000..4842cb4 --- /dev/null +++ b/Data/WeatherArchiveData.cs @@ -0,0 +1,48 @@ +using System.Collections.Generic; +using System.Configuration; +using System.Data.Entity; +using System.Data.SqlClient; + +namespace WeatherService.Data +{ + public class WeatherArchiveData : DbContext + { + private const string ConnectionStringTemplateName = "WeatherArchiveData"; + private const string DatabaseNameYearTemplate = "WeatherData{0}"; + + private static readonly Dictionary DatabaseExists = new Dictionary(); + + private static string BuildConnectionString(int year) + { + var databaseName = string.Format(DatabaseNameYearTemplate, year); + + var connectionString = ConfigurationManager.ConnectionStrings[ConnectionStringTemplateName].ConnectionString; + + var builder = new SqlConnectionStringBuilder(connectionString) { InitialCatalog = databaseName }; + + return builder.ConnectionString; + } + + public WeatherArchiveData(int year) + : base(BuildConnectionString(year)) + { + if (DatabaseExists.ContainsKey(year)) + return; + + DatabaseExists[year] = Database.Exists(); + + if (DatabaseExists[year]) + return; + + Database.Create(); + + DatabaseExists[year] = true; + } + + public virtual DbSet Readings { get; set; } + + protected override void OnModelCreating(DbModelBuilder modelBuilder) + { + } + } +} diff --git a/Data/WeatherData.cs b/Data/WeatherData.cs new file mode 100644 index 0000000..8e0fd54 --- /dev/null +++ b/Data/WeatherData.cs @@ -0,0 +1,19 @@ +using System.Data.Entity; + +namespace WeatherService.Data +{ + public class WeatherData : DbContext + { + public WeatherData() + : base("name=WeatherData") + { + } + + public virtual DbSet Devices { get; set; } + public virtual DbSet Settings { get; set; } + + protected override void OnModelCreating(DbModelBuilder modelBuilder) + { + } + } +} diff --git a/Devices/DeviceBase.cs b/Devices/DeviceBase.cs index 719c5ef..5d7d4ba 100644 --- a/Devices/DeviceBase.cs +++ b/Devices/DeviceBase.cs @@ -100,13 +100,19 @@ namespace WeatherService.Devices { try { - // Get the device data from the database - DeviceData deviceData = (from device in Database.DeviceTable where device.Address == _deviceAddress select device).First(); + using (var weatherData = new WeatherData()) + { + // Get the device data from the database + var deviceData = weatherData.Devices.FirstOrDefault(d => d.Address == _deviceAddress); - // Load the device data - _deviceId = deviceData.Id; - _displayName = deviceData.Name; - _refreshFrequency = deviceData.ReadInterval; + if (deviceData == null) + return false; + + // Load the device data + _deviceId = deviceData.Id; + _displayName = deviceData.Name; + _refreshFrequency = deviceData.ReadInterval; + } return true; } @@ -118,12 +124,20 @@ namespace WeatherService.Devices internal bool Save() { - // Get the device data from the database - DeviceData deviceData = (from device in Database.DeviceTable where device.Address == _deviceAddress select device).First(); + using (var weatherData = new WeatherData()) + { + // Get the device data from the database + var deviceData = weatherData.Devices.FirstOrDefault(d => d.Address == _deviceAddress); - // Save device data - deviceData.Name = _displayName; - deviceData.ReadInterval = _refreshFrequency; + if (deviceData == null) + return false; + + // Save device data + deviceData.Name = _displayName; + deviceData.ReadInterval = _refreshFrequency; + + weatherData.SaveChanges(); + } return true; } @@ -250,7 +264,7 @@ namespace WeatherService.Devices [DataMember] public List SupportedValues { - get { return _valueList.Keys.ToList(); } + get { return _valueList.Keys.ToList(); } } #endregion diff --git a/EventArguments.cs b/EventArguments.cs deleted file mode 100644 index b16f6b1..0000000 --- a/EventArguments.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using WeatherService.Devices; - -namespace WeatherService -{ - #region DeviceRefreshed - - [Serializable] - public class DeviceRefreshedEventArgs : EventArgs - { - private readonly DeviceBase _device; - - public DeviceRefreshedEventArgs(DeviceBase Device) - { - _device = Device; - } - - public DeviceBase Device - { - get - { - return _device; - } - } - } - - #endregion -} diff --git a/Framework/WindowsServiceInstaller.cs b/Framework/WindowsServiceInstaller.cs index 4ca8e04..03bd5de 100644 --- a/Framework/WindowsServiceInstaller.cs +++ b/Framework/WindowsServiceInstaller.cs @@ -204,6 +204,7 @@ namespace WeatherService.Framework DisplayName = Configuration.DisplayName, Description = Configuration.Description, StartType = Configuration.StartMode, + ServicesDependedOn = new[] { "MSSQL$WEATHER" } }; return result; diff --git a/Program.cs b/Program.cs index 99f5201..e93c0cc 100644 --- a/Program.cs +++ b/Program.cs @@ -1,4 +1,5 @@ -using Common.Debug; +using System.Globalization; +using Common.Debug; using System; using System.Diagnostics; using System.Linq; @@ -13,7 +14,7 @@ namespace WeatherService static void Main(string[] args) { - Tracer.Initialize(@"C:\WeatherCenter\Logs", "WeatherService", Process.GetCurrentProcess().Id.ToString(), Environment.UserInteractive); + Tracer.Initialize(@"C:\WeatherCenter\Logs", "WeatherService", Process.GetCurrentProcess().Id.ToString(CultureInfo.InvariantCulture), Environment.UserInteractive); if (args.Contains("-install", StringComparer.InvariantCultureIgnoreCase)) { diff --git a/ServiceImplementation.cs b/ServiceImplementation.cs index 200e9a6..634f464 100644 --- a/ServiceImplementation.cs +++ b/ServiceImplementation.cs @@ -10,20 +10,23 @@ using WeatherService.SignalR; namespace WeatherService { - [WindowsService("WeatherService", DisplayName = "Weather Reporting Service", Description = "", StartMode = ServiceStartMode.Automatic, ServiceAccount = ServiceAccount.LocalSystem)] + [WindowsService("WeatherReporting", DisplayName = "Weather Reporting", Description = "", StartMode = ServiceStartMode.Automatic, ServiceAccount = ServiceAccount.LocalSystem)] public class ServiceImplementation : IWindowsService { - private List _serviceHosts = new List(); + private List _serviceHosts; private IDisposable _signalR; public void OnStart(string[] args) { - using (new BeginEndTracer(GetType().Name, "OnStart")) + using (new BeginEndTracer(GetType().Name)) { try { - _serviceHosts.Add(new WebServiceHost(typeof(WeatherService))); - _serviceHosts.Add(new WebServiceHost(typeof(WeatherServiceDuplex))); + _serviceHosts = new List + { + new WebServiceHost(typeof (WeatherService)), + new WebServiceHost(typeof (WeatherServiceDuplex)) + }; _serviceHosts.ForEach(h => h.Open()); @@ -31,6 +34,7 @@ namespace WeatherService Trace.Listeners.Remove("HostingTraceListener"); Program.Session = new Session(); + Program.Session.Initialize(); Program.Session.StartRefresh(); } @@ -44,15 +48,15 @@ namespace WeatherService public void OnStop() { - using (new BeginEndTracer(GetType().Name, "OnStop")) + using (new BeginEndTracer(GetType().Name)) { try { - _signalR.Dispose(); - Program.Session.StopRefresh(); Program.Session.Terminate(); + _signalR.Dispose(); + _serviceHosts.ForEach(h => h.Close()); _serviceHosts.Clear(); } diff --git a/Session.cs b/Session.cs index 374e06a..46a87af 100644 --- a/Session.cs +++ b/Session.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading; -using WeatherService.Data; using WeatherService.Devices; using WeatherService.SignalR; @@ -39,7 +38,7 @@ namespace WeatherService public Session() { // Initialize the database - Database.Initialize(Settings.Default.DatabaseFile); + // DB CHANGE - Database.Initialize(Settings.Default.DatabaseFile); // Create the list of devices Devices = new List(); diff --git a/Settings.Designer.cs b/Settings.Designer.cs index 97e0dec..1dcef02 100644 --- a/Settings.Designer.cs +++ b/Settings.Designer.cs @@ -1,7 +1,7 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.18033 +// Runtime Version:4.0.30319.34209 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -12,7 +12,7 @@ namespace WeatherService { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -32,15 +32,6 @@ namespace WeatherService { } } - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] - public string LcdTemplate { - get { - return ((string)(this["LcdTemplate"])); - } - } - [global::System.Configuration.ApplicationScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("00:00:01")] @@ -50,15 +41,6 @@ namespace WeatherService { } } - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("C:\\WeatherCenter\\WeatherCenter.sdf")] - public string DatabaseFile { - get { - return ((string)(this["DatabaseFile"])); - } - } - [global::System.Configuration.ApplicationScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Configuration.DefaultSettingValueAttribute("http://*:9090")] diff --git a/Settings.settings b/Settings.settings index 824d1b0..c88e5d6 100644 --- a/Settings.settings +++ b/Settings.settings @@ -5,15 +5,9 @@ False - - - 00:00:01 - - C:\WeatherCenter\WeatherCenter.sdf - http://*:9090 diff --git a/Values/Value.cs b/Values/Value.cs index ff4a852..b66c230 100644 --- a/Values/Value.cs +++ b/Values/Value.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Data.SqlServerCe; using System.Linq; using System.Runtime.Serialization; using WeatherService.Data; @@ -71,23 +70,48 @@ namespace WeatherService.Values // Figure out the minimum time we want to load into history DateTime minimumTime = DateTime.Now.AddHours(-MaximumHours); - // Build a list for this device using the right value type and limit to the maximum number of hours - var readingList = from reading in Database.ReadingTable - where reading.DeviceId == ownerDevice.Id && reading.Type == valueType && reading.ReadTime >= minimumTime - select reading; - - // Loop over all readings and reload them into the history - foreach (ReadingData readingData in readingList) + using (var weatherArchiveData = new WeatherArchiveData(minimumTime.Year)) { - // Get the value from the reading - double dValue = readingData.Value; + var readingList = + weatherArchiveData.Readings.Where( + reading => + reading.DeviceId == ownerDevice.Id && reading.Type == (int) valueType && + reading.ReadTime >= minimumTime); - // Get the timestamp from the reading - DateTime dtTimestamp = readingData.ReadTime; + // Loop over all readings and reload them into the history + foreach (var readingData in readingList) + { + // Get the value from the reading + double dValue = readingData.Value; - // Set the value into the history - SetValue(dValue, dtTimestamp, false); + // Get the timestamp from the reading + DateTime dtTimestamp = readingData.ReadTime.LocalDateTime; + + // Set the value into the history + SetValue(dValue, dtTimestamp, false); + } } + + /* + + // Build a list for this device using the right value type and limit to the maximum number of hours + var readingList = from reading in Database.ReadingTable + where reading.DeviceId == ownerDevice.Id && reading.Type == valueType && reading.ReadTime >= minimumTime + select reading; + + // Loop over all readings and reload them into the history + foreach (ReadingData readingData in readingList) + { + // Get the value from the reading + double dValue = readingData.Value; + + // Get the timestamp from the reading + DateTime dtTimestamp = readingData.ReadTime; + + // Set the value into the history + SetValue(dValue, dtTimestamp, false); + } + */ } #endregion @@ -151,7 +175,7 @@ namespace WeatherService.Values if ((Minimum.ReadTime == DateTime.MinValue) || (value < Minimum.Value) || (Minimum.Value.Equals(0) && bNoZero)) { bool bSetMinimum; // Is this value the new minimum? - + if (bNoZero) bSetMinimum = (value > 0) || (Minimum.ReadTime == DateTime.MinValue); else @@ -217,24 +241,19 @@ namespace WeatherService.Values if (save) { // Save the reading - /* - ReadingData readingData = new ReadingData - { - DeviceId = _ownerDevice.Id, - Value = value, - Type = _valueType, - ReadTime = timeStamp - }; - Database.ReadingTable.InsertOnSubmit(readingData); - Database.SaveChanges(); - */ + using (var weatherArchiveData = new WeatherArchiveData(timeStamp.Year)) + { + var reading = new Data.Reading + { + DeviceId = _ownerDevice.Id, + Type = (int) ValueType, + Value = value, + ReadTime = timeStamp + }; - string query = - string.Format( - "INSERT INTO Reading (DeviceID, Type, Value, ReadTime) VALUES ({0}, {1:d}, {2}, '{3}')", - _ownerDevice.Id, ValueType, value, timeStamp); - SqlCeCommand command = new SqlCeCommand(query, Database.Connection); - command.ExecuteScalar(); + weatherArchiveData.Readings.Add(reading); + weatherArchiveData.SaveChanges(); + } // Update the minimum value CheckMinimumValue(value, timeStamp); diff --git a/WeatherService.csproj b/WeatherService.csproj index 0a034e3..977dea8 100644 --- a/WeatherService.csproj +++ b/WeatherService.csproj @@ -50,7 +50,14 @@ + + + packages\EntityFramework.6.1.2\lib\net45\EntityFramework.dll + + + packages\EntityFramework.6.1.2\lib\net45\EntityFramework.SqlServer.dll + packages\Microsoft.AspNet.SignalR.Client.2.0.3\lib\net45\Microsoft.AspNet.SignalR.Client.dll @@ -83,11 +90,14 @@ packages\Owin.1.0\lib\net40\Owin.dll + + + @@ -102,10 +112,11 @@ - - - - + + + + + @@ -113,7 +124,6 @@ - @@ -193,12 +203,11 @@ false + + + + - - - - -