1
0
Fork 0

Compare commits

...

6 Commits

Author SHA1 Message Date
Craig Oates c0340c93ba Squashed commit of the following: 3 years ago
Craig Oates c6157c58df 2021-6-11 snapshot. 3 years ago
Craig Oates d7f58fee84 update .csproj file. 3 years ago
Craig Oates c617416402 change 'Ross' label to 'Andy'. 3 years ago
Craig Oates 99177b0e39 26-05-2021 snapshot. 3 years ago
Craig Oates 21ead57d15 2021-05-03 Snapshot. 3 years ago
  1. 7
      README.md
  2. 7
      src/EyesAndEars.UWP/EyesAndEars.UWP/EyesAndEars.UWP.csproj
  3. 96
      src/EyesAndEars.UWP/EyesAndEars.UWP/MainPage.xaml
  4. 27
      src/EyesAndEars.UWP/EyesAndEars.UWP/MainPage.xaml.cs
  5. 2
      src/EyesAndEars.UWP/EyesAndEars.UWP/Package.appxmanifest
  6. 129
      src/EyesAndEars.UWP/EyesAndEars.UWP/Services/DataServices.cs
  7. 2
      src/EyesAndEars.UWP/EyesAndEars.UWP/ViewModels/MainPageVM.cs

7
README.md

@ -1,3 +1,6 @@
# eyes-and-ears
# Return to Ritherdon: Eyes and Ears
A dashboard which provides a live overview of the, as of yet, unnamed project.
Eyes and Ears is a Windows UWP 'Dashboard App.' which monitors the overall artwork, 'Personal Flash in Real-Time' -- which is one of several artworks in the 'Return to Ritherdon' project. For more information on the 'Return to Ritherdon' project and 'Eyes and Ears' specifically, please use the following linsk:
- [Return to Ritherdon Overview](https://git.abbether.net/return-to-ritherdon/rtr-docs)
- [Eyes and Ears Documentation](https://git.abbether.net/return-to-ritherdon/rtr-docs/src/branch/master/eyes-and-ears/rtr-eyes-and-ears.md)

7
src/EyesAndEars.UWP/EyesAndEars.UWP/EyesAndEars.UWP.csproj

@ -19,15 +19,16 @@
<WindowsXamlEnableOverview>true</WindowsXamlEnableOverview>
<AppxPackageSigningEnabled>True</AppxPackageSigningEnabled>
<GenerateAppInstallerFile>False</GenerateAppInstallerFile>
<PackageCertificateKeyFile>EyesAndEars.UWP_TemporaryKey.pfx</PackageCertificateKeyFile>
<PackageCertificateThumbprint>
</PackageCertificateThumbprint>
<PackageCertificateKeyFile>
</PackageCertificateKeyFile>
<PackageCertificateThumbprint>DAE20EE5240091F7E98C03948A35781171CB1005</PackageCertificateThumbprint>
<AppxPackageSigningTimestampDigestAlgorithm>SHA256</AppxPackageSigningTimestampDigestAlgorithm>
<AppxAutoIncrementPackageRevision>False</AppxAutoIncrementPackageRevision>
<GenerateTestArtifacts>True</GenerateTestArtifacts>
<AppxBundle>Always</AppxBundle>
<AppxBundlePlatforms>x64</AppxBundlePlatforms>
<HoursBetweenUpdateChecks>0</HoursBetweenUpdateChecks>
<AppxSymbolPackageEnabled>False</AppxSymbolPackageEnabled>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>

96
src/EyesAndEars.UWP/EyesAndEars.UWP/MainPage.xaml

@ -12,6 +12,7 @@
<Grid.RowDefinitions>
<RowDefinition Height="96"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
@ -25,14 +26,21 @@
Text="{x:Bind _vm.CurrentTime, Mode=OneWay, FallbackValue=00}"
Margin="0" Padding="0"/>
<StackPanel Grid.Row="0" Orientation="Horizontal"
<Rectangle Fill="White" Width="92" Height="92" HorizontalAlignment="Left"
RadiusX="46" RadiusY="46"
/>
<StackPanel Grid.Row="0" Orientation="Horizontal" Margin="2,0,0,0"
VerticalAlignment="Top">
<MediaElement Name="AudioUpdater" AutoPlay="True"/>
<Image Grid.Column="0" Width="88" Height="88" VerticalAlignment="Center"
Source="Images\logo.png"/>
<StackPanel Padding="8,0">
<TextBlock Text="Return to Ritherdon Project" FontSize="40"/>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Unnamed Project" Padding="0" FontSize="24"
<TextBlock Text="Personal Flash in Real-Time" Padding="0" FontSize="24"
VerticalAlignment="Bottom"/>
<TextBlock Text="by Nicola Ellis" Padding="0" Margin="12,0"
FontSize="18" VerticalAlignment="Bottom"/>
@ -40,7 +48,7 @@
</StackPanel>
</StackPanel>
<Grid Grid.Row="1" Margin="0,12">
<Grid Grid.Row="1" Margin="0,12,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
@ -55,7 +63,8 @@
<Grid x:Name="Factory1Grid" Grid.Row="0" Grid.Column="0"
Background="{x:Bind _vm.Device1.StatusColour, Mode=OneWay,
FallbackValue=Pink}" Margin="0,0,0,12" Width="300" Height="300">
FallbackValue=Pink}" Margin="0,0,0,12" Width="300" Height="300"
CornerRadius="10">
<StackPanel Margin="12" VerticalAlignment="Stretch">
<TextBlock x:Name="Factory1Time"
Text="{x:Bind _vm.Device1.LatestReading.time,
@ -65,14 +74,15 @@
Text="{x:Bind _vm.Device1.LatestReading.reading,
Mode=OneWay, FallbackValue=404}" Foreground="White"
VerticalAlignment="Bottom" FontSize="144"/>
<TextBlock Text="Factory 1" Foreground="White" FontSize="36"
<TextBlock Text="Factory 1 (Andy)" Foreground="White" FontSize="36"
VerticalAlignment="Bottom"/>
</StackPanel>
</Grid>
<Grid x:Name="Factory2Grid" Grid.Row="0" Grid.Column="1"
Background="{x:Bind _vm.Device2.StatusColour, Mode=OneWay,
FallbackValue=Orange}" Margin="12,0,12,12" Width="300" Height="300">
FallbackValue=Orange}" Margin="12,0,12,12" Width="300" Height="300"
CornerRadius="10">
<StackPanel Margin="12" VerticalAlignment="Stretch">
<TextBlock x:Name="Factory2Time" Foreground="White" FontSize="24"
Text="{x:Bind _vm.Device2.LatestReading.time,
@ -81,35 +91,40 @@
Text="{x:Bind _vm.Device2.LatestReading.reading,
Mode=OneWay, FallbackValue=404}" Foreground="White"
VerticalAlignment="Bottom" FontSize="144"/>
<TextBlock Text="Factory 2" Foreground="White" FontSize="36"
<TextBlock Text="Factory 2 (Tony)" Foreground="White" FontSize="36"
VerticalAlignment="Bottom"/>
</StackPanel>
</Grid>
<Grid Grid.Row="0" Grid.Column="2" Grid.RowSpan="2">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Rectangle Fill="LightSeaGreen" Width="92" Height="52"/>
<StackPanel Orientation="Horizontal" Margin="0,0,0,5">
<Rectangle Fill="LightSeaGreen" Width="92" Height="52"
RadiusX="5" RadiusY="5"/>
<TextBlock Text="Device is on" FontSize="42"
VerticalAlignment="Center" Margin="12,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="DarkSeaGreen" Width="92" Height="52"/>
<StackPanel Orientation="Horizontal" Margin="0,0,0,5">
<Rectangle Fill="DarkSeaGreen" Width="92" Height="52"
RadiusX="5" RadiusY="5"/>
<TextBlock Text="Welding detected" FontSize="42"
VerticalAlignment="Center" Margin="12,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="LightSkyBlue" Width="92" Height="52"/>
<StackPanel Orientation="Horizontal" Margin="0,0,0,5">
<Rectangle Fill="LightSkyBlue" Width="92" Height="52"
RadiusX="5" RadiusY="5"/>
<TextBlock Text="Low light in factory" FontSize="42"
VerticalAlignment="Center" Margin="12,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="DarkOrange" Width="92" Height="52"/>
<StackPanel Orientation="Horizontal" Margin="0,0,0,5">
<Rectangle Fill="DarkOrange" Width="92" Height="52"
RadiusX="5" RadiusY="5"/>
<TextBlock Text="Unable to retrieve data" FontSize="42"
VerticalAlignment="Center" Margin="12,0"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Rectangle Fill="DarkRed" Width="92" Height="52"/>
<StackPanel Orientation="Horizontal" Margin="0,0,0,5">
<Rectangle Fill="DarkRed" Width="92" Height="52"
RadiusX="5" RadiusY="5"/>
<TextBlock Text="Device is off" FontSize="42"
VerticalAlignment="Center" Margin="12,0"/>
</StackPanel>
@ -120,8 +135,8 @@
<TextBlock Text="Dashboard updates at slower rate than devices."
FontSize="18" Margin="0" Foreground="SlateGray"
VerticalAlignment="Center"/>
</StackPanel>
</StackPanel>
</Grid>
<!-- This device is not in use. Uncomment if becomes available. -->
<!--<Grid x:Name="Factory3Grid" Grid.Row="0" Grid.Column="2"
@ -143,7 +158,8 @@
<Grid x:Name="Gallery1Grid" Grid.Row="1" Grid.Column="0"
Background="{x:Bind _vm.Device4.StatusColour, Mode=OneWay,
FallbackValue=Pink}" Margin="0,0,0,12" Width="300" Height="150">
FallbackValue=Pink}" Margin="0,0,0,12" Width="300" Height="150"
CornerRadius="10">
<StackPanel Margin="12" VerticalAlignment="Bottom">
<TextBlock x:Name="Gallery1Time" Foreground="White"
Text="{x:Bind _vm.Device4.LatestStatus.time,
@ -159,7 +175,8 @@
<Grid x:Name="Gallery2Grid" Grid.Row="1" Grid.Column="1"
Background="{x:Bind _vm.Device5.StatusColour, Mode=OneWay,
FallbackValue=Pink}" Margin="0,0,0,12" Width="300" Height="150">
FallbackValue=Pink}" Margin="0,0,0,12" Width="300" Height="150"
CornerRadius="10">
<StackPanel Margin="12" VerticalAlignment="Bottom">
<TextBlock x:Name="Gallery2Time" Foreground="White" FontSize="24"
Text="{x:Bind _vm.Device5.LatestStatus.time,
@ -193,33 +210,52 @@
</Grid>
<Grid Grid.Row="2" Margin="0">
<Grid Grid.Row="2" Margin="0,0,0,8">
<ScrollViewer x:Name="LogViewer" VerticalScrollBarVisibility="Visible"
HorizontalScrollMode="Disabled" Margin="0"
VerticalScrollMode="Enabled">
<RichTextBlock x:Name="Logs" FontSize="16" FontFamily="Consolas">
</RichTextBlock>
</ScrollViewer>
</Grid>
<Grid Grid.Row="3" Margin="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="113"/>
<ColumnDefinition Width="45"/>
<ColumnDefinition Width="*" MinWidth="350"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<HyperlinkButton x:Name="InfoButton" Grid.Column="0"
<StackPanel Orientation="Horizontal" Grid.Column="0" Grid.ColumnSpan="2">
<ToggleButton x:Name="LogToggle" FontFamily="Segoe MDL2 Assets"
Content="&#xE8BC;" HorizontalAlignment="Right"
VerticalAlignment="Top" FontSize="28"
Height="79" Width="79"
Unchecked="LogToggle_Unchecked"
Checked="LogToggle_Checked"
IsChecked="False"/>
<HyperlinkButton x:Name="InfoButton" Grid.Column="0"
NavigateUri="http://www.nicolaellis.com"
FontFamily="Segoe MDL2 Assets" Content="&#xE946;"
Height="79" Width="79" VerticalAlignment="Center"
HorizontalAlignment="Left" FontSize="28"
ToolTipService.Placement="Mouse"
ToolTipService.ToolTip="This is a link to a webpage with information about Nicola and the project."/>
</StackPanel>
<TextBox x:Name="WebAddressBox" Grid.Column="1" FontSize="26"
Text="{x:Bind _vm.BaseURL , Mode=TwoWay}"
Padding="20" TextAlignment="Left"
HorizontalAlignment="Stretch" TextWrapping="NoWrap"
Margin="0" VerticalAlignment="Center"
<TextBox x:Name="WebAddressBox" Grid.Column="2" FontSize="26"
Text="{x:Bind _vm.BaseURL, Mode=TwoWay}"
Padding="20" TextAlignment="Left" TextWrapping="NoWrap" VerticalAlignment="Center"
HorizontalContentAlignment="Left"
ToolTipService.Placement="Mouse"
ToolTipService.ToolTip="The base U.R.L. used to form the A.P.I. queries."
PlaceholderText="Enter the base U.R.L. here..." />
<StackPanel Orientation="Horizontal" Grid.Column="2">
<StackPanel Orientation="Horizontal" Grid.Column="3">
<ToggleButton x:Name="PlayButton" FontFamily="Segoe MDL2 Assets"
Content="&#xE71A;" Height="79" Width="79"
VerticalAlignment="Center" FontSize="28"

27
src/EyesAndEars.UWP/EyesAndEars.UWP/MainPage.xaml.cs

@ -2,6 +2,7 @@
using EyesAndEars.UWP.ViewModels;
using System;
using Windows.ApplicationModel;
using Windows.Media.SpeechSynthesis;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
@ -11,6 +12,7 @@ namespace EyesAndEars.UWP {
MainPageVM _vm = new MainPageVM();
DispatcherTimer _dispatcherTimer = new DispatcherTimer();
bool _logData;
public MainPage() {
InitializeComponent();
@ -35,7 +37,7 @@ namespace EyesAndEars.UWP {
_vm.Device4 = DataServices.CreateFallBackDevice(4);
_vm.Device5 = DataServices.CreateFallBackDevice(5);
// _vm.Device6 = DataServices.CreateFallBackDevice(6);
_vm.CurrentTime = DateTime.UtcNow.ToShortTimeString();
_vm.CurrentTime = DataServices.SetCurrentTime();
DataContext = _vm;
}
@ -50,13 +52,17 @@ namespace EyesAndEars.UWP {
var url = _vm.BaseURL;
if (!string.IsNullOrEmpty(url)) {
// Devices 3 and 6 are not in use.
_vm.Device1 = await DataServices.UpdateDevice(url, 1);
_vm.Device2 = await DataServices.UpdateDevice(url, 2);
_vm.Device1 = await DataServices.UpdateDevice
(url, 1, Logs, _vm.Device1.LatestStatus.status, AudioUpdater, _logData);
_vm.Device2 = await DataServices.UpdateDevice
(url, 2, Logs, _vm.Device2.LatestStatus.status, AudioUpdater, _logData);
// _vm.Device3 = await DataServices.UpdateDevice(url, 3);
_vm.Device4 = await DataServices.UpdateDevice(url, 4);
_vm.Device5 = await DataServices.UpdateDevice(url, 5);
_vm.Device4 = await DataServices.UpdateDevice
(url, 4, Logs, _vm.Device4.LatestStatus.status, AudioUpdater, _logData);
_vm.Device5 = await DataServices.UpdateDevice
(url, 5, Logs, _vm.Device5.LatestStatus.status, AudioUpdater, _logData);
// _vm.Device6 = await DataServices.UpdateDevice(url, 6);
_vm.CurrentTime = DateTime.UtcNow.ToShortTimeString();
_vm.CurrentTime = DataServices.SetCurrentTime();
}
else {
IntialiseDataContext();
@ -76,5 +82,14 @@ namespace EyesAndEars.UWP {
PlayButton.Content = "\uE71A";
_dispatcherTimer.Start();
}
private void LogToggle_Unchecked(object sender, RoutedEventArgs e) {
_logData = false;
Logs.Blocks.Clear();
}
private void LogToggle_Checked(object sender, RoutedEventArgs e) {
_logData = true;
}
}
}

2
src/EyesAndEars.UWP/EyesAndEars.UWP/Package.appxmanifest

@ -9,7 +9,7 @@
<Identity
Name="55acd946-60d4-4776-b6c6-03fef750e3da"
Publisher="CN=Craig Oates"
Version="2020.1.14.0" />
Version="2021.6.11.0" />
<mp:PhoneIdentity PhoneProductId="55acd946-60d4-4776-b6c6-03fef750e3da" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>

129
src/EyesAndEars.UWP/EyesAndEars.UWP/Services/DataServices.cs

@ -6,11 +6,25 @@ using System.Threading.Tasks;
using Windows.Storage;
using Windows.UI;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Documents;
using Windows.UI.Text;
using Windows.Media.SpeechSynthesis;
namespace EyesAndEars.UWP.Services {
public static class DataServices {
public static async Task<Device> UpdateDevice(string url, int deviceId) {
static SpeechSynthesizer _synth = new SpeechSynthesizer();
public static string SetCurrentTime() {
var zone = TimeZoneInfo.Local;
var britishZone = TimeZoneInfo.FindSystemTimeZoneById(zone.Id);
var newDate = TimeZoneInfo.ConvertTime(DateTime.Now, TimeZoneInfo.Local, britishZone);
return newDate.ToShortTimeString();
}
public static async Task<Device> UpdateDevice(string url, int deviceId,
RichTextBlock logs, string currentStatus, MediaElement audioUpdater, bool _logData) {
try {
var id = MapFactoryDeviceToGalleyDevice(deviceId); // Has note.
var readingAPI = $"{url}/api/readings/latest/{id}";
@ -19,8 +33,11 @@ namespace EyesAndEars.UWP.Services {
var statusJSON = await WebServices.GetJSON(statusAPI);
var r = MapToLightReading(readingJSON);
var s = MapToDeviceStatus(statusJSON);
var c = UpdateStatusColour(s.status, r.reading);
return new Device(deviceId, r, s, c);
var c = UpdateStatusColour(deviceId, s.status, r.reading);
var dev = new Device(deviceId, r, s, c);
if(_logData == true) LogUpdate(logs, dev);
NotifyAnyStatusChanges(currentStatus, dev, audioUpdater);
return dev;
}
catch (Exception e) {
Debug.WriteLine(e.Message);
@ -28,38 +45,90 @@ namespace EyesAndEars.UWP.Services {
}
}
static SolidColorBrush UpdateStatusColour(string status, int reading) {
private static async void NotifyAnyStatusChanges(string currentStatus,
Device dev, MediaElement audioUpdater) {
if (currentStatus.Equals
(dev.LatestStatus.status, StringComparison.InvariantCulture)
!= true) {
var stream = await _synth.SynthesizeTextToStreamAsync
($"{TranslateDeviceIdToDeviceName(dev)} {dev.LatestStatus.status}");
audioUpdater.SetSource(stream, stream.ContentType);
audioUpdater.Play();
}
}
private static string TranslateDeviceIdToDeviceName(Device dev) {
if (dev.Id == 1) return "factory1";
else if (dev.Id == 2) return "factory2";
else if (dev.Id == 4) return "gallery1";
else return "gallery2";
}
private static void LogUpdate(RichTextBlock logs, Device dev) {
// Devices 3 and 6 are not in use.
// string device = TranslateDeviceIdToDeviceName(dev);
Paragraph paragraph = new Paragraph();
var run1 = new Run {
FontWeight = FontWeights.Bold,
Text = $"{DateTime.Now} | "
};
var run2 = new Run {
Foreground = dev.StatusColour,
FontWeight = FontWeights.Bold,
Text = TranslateDeviceIdToDeviceName(dev)
};
var run3 = new Run {
Foreground = dev.StatusColour,
Text = $" | {dev.LatestReading.id} | {dev.LatestReading.reading} | " +
$"{dev.LatestStatus.status} | {dev.LatestReading.time}"
};
paragraph.Inlines.Add(run1);
paragraph.Inlines.Add(run2);
paragraph.Inlines.Add(run3);
logs.Blocks.Insert(0, paragraph);
}
private static SolidColorBrush UpdateStatusColour(int device, string status, int reading) {
try {
if (status.Equals("on", StringComparison.OrdinalIgnoreCase)) {
/* Note: Reading values breakdown.
* =======================================
* 1. When testing the light meters, the base line for normal
* light is 48 or below. Anything above this is when one of
* the guys in the factory was welding. Becuase of this,
* I have set 0 to 48 as the 'default' status on the
* dashboard.
*
* 2. During testing we noticed the light meters would
* return 'negative light' values. There is a reason for
* this but that is out of the scope of this project. This
* project just needs to process the data. The negative
* light values are when the lights in the factory are off.
* The light meter is still running but there is no light
* to measure. So, everything is fine but it looks broken.
* The 'LightSkyBlue' colour is used to help relay this
* bit of information.
*/
if (reading > 0 && reading < 48) // On but no weld detected.
return new SolidColorBrush(Colors.LightSeaGreen);
if (reading > 48) // On and weld detected.
return new SolidColorBrush(Colors.DarkSeaGreen);
else {
// On but factory lights are off.
return new SolidColorBrush(Colors.LightSkyBlue);
/* Note: 'Negative Light' Levels.
* ========================================================
* The light meters will record 'negative light' values
* when the factory lights are off. This does not mean
* the light meters are not functioning properly. With that
* said, this dashboard make it look like they are. Because
* of this, the blocks in the dashboard will change to
* 'LightSkyBlue' to help indicate the change is something
* the system knows about and everything is fine -- nothing
* is broken.
*/
if (device == 1 || device == 2) {
if (device == 1) {
if (reading > 0 && reading <= 39) // No weld detected.
return new SolidColorBrush(Colors.LightSeaGreen);
if (reading > 38) // Weld detected.
return new SolidColorBrush(Colors.DarkSeaGreen);
}
else if (device == 2) {
if (reading > 0 && reading <= 48) // No weld detected.
return new SolidColorBrush(Colors.LightSeaGreen);
if (reading > 48) // Weld detected.
return new SolidColorBrush(Colors.DarkSeaGreen);
}
else if (reading < 0) {
// The device is on but factory lights are off.
return new SolidColorBrush(Colors.LightSkyBlue);
}
}
// Indicate the gallery relays are on.
return new SolidColorBrush(Colors.LightSeaGreen);
}
else return new SolidColorBrush(Colors.DarkRed); }
// The device is off.
return new SolidColorBrush(Colors.DarkRed);
}
catch (Exception e) {
// Extra protection to if-check at top of this try block.
Debug.WriteLine(e.Message);
return new SolidColorBrush(Colors.DarkOrange);
}

2
src/EyesAndEars.UWP/EyesAndEars.UWP/ViewModels/MainPageVM.cs

@ -26,7 +26,7 @@ namespace EyesAndEars.UWP.ViewModels {
Device4 = g1;
Device5 = g2;
Device6 = g3;
CurrentTime = DateTime.Now.ToShortTimeString();
CurrentTime = "Not Set";
}
string _currentTime;