Fixes for new service

This commit is contained in:
2023-03-31 20:43:02 -04:00
parent 66f5b7ee0a
commit 2697521949
8 changed files with 220 additions and 74 deletions

View File

@@ -1,12 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?> <?xml version="1.0" encoding="utf-8"?>
<configuration> <configuration>
<configSections> <configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" > <sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<section name="HomeStatusWindow.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" /> <section name="HomeStatusWindow.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false"/>
</sectionGroup>
<sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="HomeStatusWindow.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</sectionGroup> </sectionGroup>
</configSections> </configSections>
<startup> <startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" /> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
</startup> </startup>
<userSettings> <userSettings>
<HomeStatusWindow.Properties.Settings> <HomeStatusWindow.Properties.Settings>
@@ -16,9 +19,25 @@
<setting name="WindowSettings" serializeAs="String"> <setting name="WindowSettings" serializeAs="String">
<value /> <value />
</setting> </setting>
<setting name="ServerAddress" serializeAs="String">
<value>http://chip</value>
</setting>
</HomeStatusWindow.Properties.Settings> </HomeStatusWindow.Properties.Settings>
</userSettings> </userSettings>
<applicationSettings>
<HomeStatusWindow.Properties.Settings>
<setting name="ReconnectTimerInterval" serializeAs="String">
<value>00:00:30</value>
</setting>
<setting name="ReconnectTimeout" serializeAs="String">
<value>00:01:00</value>
</setting>
<setting name="ServerUri" serializeAs="String">
<value>http://{0}:{1}/api/hub/device-status</value>
</setting>
<setting name="ServerName" serializeAs="String">
<value>172.23.10.3</value>
</setting>
<setting name="ServerPort" serializeAs="String">
<value>80</value>
</setting>
</HomeStatusWindow.Properties.Settings>
</applicationSettings>
</configuration> </configuration>

View File

@@ -9,11 +9,12 @@
<AppDesignerFolder>Properties</AppDesignerFolder> <AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>HomeStatusWindow</RootNamespace> <RootNamespace>HomeStatusWindow</RootNamespace>
<AssemblyName>HomeStatusWindow</AssemblyName> <AssemblyName>HomeStatusWindow</AssemblyName>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion> <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<TargetFrameworkProfile />
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
@@ -53,6 +54,7 @@
<SubType>Designer</SubType> <SubType>Designer</SubType>
</ApplicationDefinition> </ApplicationDefinition>
<Compile Include="Status.cs" /> <Compile Include="Status.cs" />
<Compile Include="StatusMessage.cs" />
<Compile Include="WindowSource.cs" /> <Compile Include="WindowSource.cs" />
<Compile Include="App.xaml.cs"> <Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon> <DependentUpon>App.xaml</DependentUpon>
@@ -98,8 +100,11 @@
<PackageReference Include="FloatingStatusWindow"> <PackageReference Include="FloatingStatusWindow">
<Version>1.0.0.9</Version> <Version>1.0.0.9</Version>
</PackageReference> </PackageReference>
<PackageReference Include="SocketIoClientDotNet"> <PackageReference Include="Microsoft.AspNetCore.SignalR.Client">
<Version>1.0.5</Version> <Version>6.0.8</Version>
</PackageReference>
<PackageReference Include="Newtonsoft.Json">
<Version>13.0.1</Version>
</PackageReference> </PackageReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -19,7 +19,7 @@ namespace HomeStatusWindow.Properties {
// class via a tool like ResGen or Visual Studio. // class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen // To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project. // with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources { internal class Resources {

View File

@@ -12,7 +12,7 @@ namespace HomeStatusWindow.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.3.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@@ -47,15 +47,48 @@ namespace HomeStatusWindow.Properties {
} }
} }
[global::System.Configuration.UserScopedSettingAttribute()] [global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("http://chip")] [global::System.Configuration.DefaultSettingValueAttribute("00:00:30")]
public string ServerAddress { public global::System.TimeSpan ReconnectTimerInterval {
get { get {
return ((string)(this["ServerAddress"])); return ((global::System.TimeSpan)(this["ReconnectTimerInterval"]));
} }
set { }
this["ServerAddress"] = value;
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("00:01:00")]
public global::System.TimeSpan ReconnectTimeout {
get {
return ((global::System.TimeSpan)(this["ReconnectTimeout"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("http://{0}:{1}/api/hub/device-status")]
public string ServerUri {
get {
return ((string)(this["ServerUri"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("172.23.10.3")]
public string ServerName {
get {
return ((string)(this["ServerName"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("80")]
public int ServerPort {
get {
return ((int)(this["ServerPort"]));
} }
} }
} }

View File

@@ -8,8 +8,20 @@
<Setting Name="WindowSettings" Type="System.String" Scope="User"> <Setting Name="WindowSettings" Type="System.String" Scope="User">
<Value Profile="(Default)" /> <Value Profile="(Default)" />
</Setting> </Setting>
<Setting Name="ServerAddress" Type="System.String" Scope="User"> <Setting Name="ReconnectTimerInterval" Type="System.TimeSpan" Scope="Application">
<Value Profile="(Default)">http://chip</Value> <Value Profile="(Default)">00:00:30</Value>
</Setting>
<Setting Name="ReconnectTimeout" Type="System.TimeSpan" Scope="Application">
<Value Profile="(Default)">00:01:00</Value>
</Setting>
<Setting Name="ServerUri" Type="System.String" Scope="Application">
<Value Profile="(Default)">http://{0}:{1}/api/hub/device-status</Value>
</Setting>
<Setting Name="ServerName" Type="System.String" Scope="Application">
<Value Profile="(Default)">172.23.10.3</Value>
</Setting>
<Setting Name="ServerPort" Type="System.Int32" Scope="Application">
<Value Profile="(Default)">80</Value>
</Setting> </Setting>
</Settings> </Settings>
</SettingsFile> </SettingsFile>

View File

@@ -4,9 +4,9 @@
public class Status public class Status
{ {
// ReSharper disable once UnusedAutoPropertyAccessor.Global // ReSharper disable once UnusedAutoPropertyAccessor.Global
public bool? Washer { get; set; } public bool Washer { get; set; }
// ReSharper disable once UnusedAutoPropertyAccessor.Global // ReSharper disable once UnusedAutoPropertyAccessor.Global
public bool? Dryer { get; set; } public bool Dryer { get; set; }
} }
} }

8
StatusMessage.cs Normal file
View File

@@ -0,0 +1,8 @@
namespace HomeStatusWindow
{
public class StatusMessage
{
public string Name { get; set; }
public bool Status { get; set; }
}
}

View File

@@ -1,10 +1,11 @@
using FloatingStatusWindowLibrary; using FloatingStatusWindowLibrary;
using HomeStatusWindow.Properties; using HomeStatusWindow.Properties;
using Microsoft.AspNetCore.SignalR.Client;
using Newtonsoft.Json; using Newtonsoft.Json;
using Quobject.SocketIoClientDotNet.Client;
using System; using System;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Timers;
using System.Windows.Threading; using System.Windows.Threading;
namespace HomeStatusWindow namespace HomeStatusWindow
@@ -14,21 +15,30 @@ namespace HomeStatusWindow
private readonly FloatingStatusWindow _floatingStatusWindow; private readonly FloatingStatusWindow _floatingStatusWindow;
private readonly Dispatcher _dispatcher; private readonly Dispatcher _dispatcher;
private Socket _socket; private HubConnection _hubConnection;
private DateTime _lastUpdate;
private Timer _reconnectTimer;
private readonly Status _lastStatus = new Status();
internal WindowSource() internal WindowSource()
{ {
_dispatcher = Dispatcher.CurrentDispatcher; _dispatcher = Dispatcher.CurrentDispatcher;
_floatingStatusWindow = new FloatingStatusWindow(this); _floatingStatusWindow = new FloatingStatusWindow(this);
_floatingStatusWindow.SetText(Resources.Loading); UpdateText(Resources.Loading);
Task.Factory.StartNew(Initialize); // Initialize the connection
Task.Factory.StartNew(InitializeConnection);
} }
public void Dispose() public async void Dispose()
{ {
Terminate(); // Stop the reconnection timer (if any)
StopReconnectionTimer();
// Terminate the connection
await TerminateConnection();
_floatingStatusWindow.Save(); _floatingStatusWindow.Save();
_floatingStatusWindow.Dispose(); _floatingStatusWindow.Dispose();
@@ -48,12 +58,14 @@ namespace HomeStatusWindow
public string Name => Resources.Name; public string Name => Resources.Name;
public bool HasSettingsMenu => false;
public bool HasRefreshMenu => false;
public bool HasAboutMenu => false;
public System.Drawing.Icon Icon => Resources.ApplicationIcon; public System.Drawing.Icon Icon => Resources.ApplicationIcon;
public bool HasSettingsMenu => false;
public bool HasRefreshMenu => false;
public bool HasAboutMenu => false;
public string WindowSettings public string WindowSettings
{ {
get => Settings.Default.WindowSettings; get => Settings.Default.WindowSettings;
@@ -64,64 +76,121 @@ namespace HomeStatusWindow
} }
} }
private readonly Status _fullStatus = new Status(); private void StartReconnectionTimer()
private void Initialize()
{ {
// Create the socket // Stop the current timer (if any)
_socket = IO.Socket(Settings.Default.ServerAddress); StopReconnectionTimer();
// Setup for status events // Create and start the reconnection timer
_socket.On("status", UpdateText); _reconnectTimer = new Timer(Settings.Default.ReconnectTimerInterval.TotalMilliseconds);
_reconnectTimer.Elapsed += HandleReconnectTimerElapsed;
_socket.On(Socket.EVENT_CONNECT, () => _socket.Emit("getStatus")); _reconnectTimer.Start();
_socket.On(Socket.EVENT_DISCONNECT, () => SetText(Resources.Disconnected));
} }
private void Terminate() private void StopReconnectionTimer()
{ {
_socket?.Disconnect(); // Get rid of the reconnection timer
_reconnectTimer?.Dispose();
_reconnectTimer = null;
} }
private void UpdateText(object data) private void HandleReconnectTimerElapsed(object sender, ElapsedEventArgs e)
{ {
var json = (string)data; // See if we haven't heard from the server within the timeout and reconnect if needed
if (DateTime.Now - _lastUpdate >= Settings.Default.ReconnectTimeout)
var status = JsonConvert.DeserializeObject<Status>(json); InitializeConnection();
if (status.Dryer.HasValue)
_fullStatus.Dryer = status.Dryer;
if (status.Washer.HasValue)
_fullStatus.Washer = status.Washer;
var text = GetText(_fullStatus);
SetText(text);
} }
private void SetText(string text) private void InitializeConnection()
{
// Update the window on the main thread
_dispatcher.Invoke(() => _floatingStatusWindow.SetText(text));
}
private static string GetText(Status status)
{ {
try try
{ {
var output = new StringBuilder(); // Stop the reconnection timer (if any)
StopReconnectionTimer();
output.AppendFormat(Resources.DryerStatus, status.Dryer.GetValueOrDefault() ? Resources.On : Resources.Off); // Create the URI for the server
output.AppendLine(); var serverUri = string.Format(Settings.Default.ServerUri, Settings.Default.ServerName, Settings.Default.ServerPort);
output.AppendFormat(Resources.WasherStatus, status.Washer.GetValueOrDefault() ? Resources.On : Resources.Off);
return output.ToString(); _hubConnection = new HubConnectionBuilder()
.WithUrl(serverUri)
.Build();
_hubConnection.Closed += error =>
{
StartReconnectionTimer();
return Task.CompletedTask;
};
_hubConnection.On<string>("LatestStatus", (message) =>
{
try
{
var statusMessage = JsonConvert.DeserializeObject<StatusMessage>(message);
switch (statusMessage?.Name)
{
case "washer":
_lastStatus.Washer = statusMessage.Status;
break;
case "dryer":
_lastStatus.Dryer = statusMessage.Status;
break;
}
UpdateDisplay(_lastStatus);
}
catch (Exception e)
{
Console.WriteLine(e);
}
});
// Open the connection
_hubConnection.StartAsync().Wait();
_hubConnection.InvokeAsync("RequestLatestStatus").Wait();
} }
catch (Exception ex) catch (Exception exception)
{ {
return ex.Message; UpdateText($"Connection error: {exception.Message}");
} }
finally
{
// Start the reconnection check timer
StartReconnectionTimer();
}
}
private async Task TerminateConnection()
{
// If the client doesn't exist or isn't open then there's nothing to do
if (_hubConnection == null || _hubConnection.State != HubConnectionState.Connected)
return;
// Close the connection
await _hubConnection.DisposeAsync();
}
private void UpdateDisplay(Status status)
{
// Last update was now
_lastUpdate = DateTime.Now;
// Create a string builder
var text = new StringBuilder();
text.AppendFormat(Resources.DryerStatus, status.Dryer ? Resources.On : Resources.Off);
text.AppendLine();
text.AppendFormat(Resources.WasherStatus, status.Washer ? Resources.On : Resources.Off);
// Set the text
UpdateText(text.ToString());
}
private void UpdateText(string text)
{
_dispatcher.InvokeAsync(() => _floatingStatusWindow.SetText(text));
} }
} }
} }