Added NightAlert project for travel kit

This commit is contained in:
2021-06-10 14:39:06 -04:00
commit d38d9e3b7e
308 changed files with 35922 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NightScout
{
public abstract class Alerter : IAlerter
{
public abstract void StartAlert();
public abstract void StartStaleDataAlert();
public abstract void StartUrgentAlert();
public abstract void StopAlerts();
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NightScout
{
public class ConsoleAlerter : IAlerter
{
public void StartAlert()
{
Console.WriteLine("Alerting");
}
public void StartUrgentAlert()
{
Console.WriteLine("Urgent Alerting");
}
public void StartStaleDataAlert()
{
Console.WriteLine("Stale Data Alerting");
}
public void StopAlerts()
{
Console.WriteLine("Alerts stopped");
}
}
}

View File

@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime;
using System.Text;
using System.Threading.Tasks;
namespace NightScout
{
public class MultiAlerter : IAlerter
{
public List<IAlerter> Alerters { get; set; }
public MultiAlerter(IEnumerable<IAlerter> alerters)
{
Alerters = new List<IAlerter>(alerters);
}
public void StartAlert()
{
Alerters.ForEach(a =>
{
try
{
a.StartAlert();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
});
}
public void StartUrgentAlert()
{
Alerters.ForEach(a =>
{
try
{
a.StartUrgentAlert();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
});
}
public void StartStaleDataAlert()
{
Alerters.ForEach(a =>
{
try
{
a.StartStaleDataAlert();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
});
}
public void StopAlerts()
{
Alerters.ForEach(a =>
{
try
{
a.StopAlerts();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
});
}
}
}

View File

@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NightScout.Interfaces;
namespace NightScout
{
public class RaspberryPiAudioAlert : IAlerter
{
public String PlayerCommandLine { get; set; }
public String KillCommand { get; set; }
public String AlertFileName { get; set; }
protected IRaspberryPiButtonSource ButtonSource;
private Boolean playing = false;
public RaspberryPiAudioAlert()
{
this.PlayerCommandLine = "\"mpg123 -q {0}\"";
this.KillCommand = "pkill mpg123";
this.AlertFileName = "~/ratm.mp3";
}
public RaspberryPiAudioAlert(String playerCommandline)
: base()
{
this.PlayerCommandLine = playerCommandline;
}
public RaspberryPiAudioAlert(String playerCommandline, IRaspberryPiButtonSource buttonSource)
: this(playerCommandline)
{
this.ButtonSource = buttonSource;
this.ButtonSource.ButtonStateChange += ButtonSource_ButtonStateChange;
}
public RaspberryPiAudioAlert(IRaspberryPiButtonSource buttonSource)
: this()
{
this.ButtonSource = buttonSource;
this.ButtonSource.ButtonStateChange += ButtonSource_ButtonStateChange;
}
private void ButtonSource_ButtonStateChange(bool obj)
{
Console.WriteLine("Button state change: {0}", obj);
if(obj)
StopAlerts();
}
public void StartAlert()
{
if (!playing)
{
playing = true;
Process.Start("/bin/bash", String.Format("-c {0}", String.Format(PlayerCommandLine,AlertFileName )));
}
}
public void StartUrgentAlert()
{
StartAlert();
}
public void StartStaleDataAlert()
{
StartAlert();
}
public void StopAlerts()
{
Process.Start("/bin/bash", "-c \"pkill mpg123\"");
playing = false;
}
}
}

67
NightScout/App.config Normal file
View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0" />
</startup>
<appSettings>
<add key="ButtonSourceEnabled" value="false"/>
<add key="NightscoutEndpoint" value="" />
<add key="NightscoutCredentials" value="" />
</appSettings>
<log4net>
<appender name="RootRollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<param name="File" value="NightScout.log" />
<param name="AppendToFile" value="true" />
<param name="MaxSizeRollBackups" value="62" />
<param name="RollingStyle" value="Date" />
<datePattern value="yyyy-MM-dd" />
<param name="StaticLogFileName" value="true" />
<layout type="log4net.Layout.PatternLayout">
<param name="Header" value="*****************************************************&#xD;&#xA;* NightScout Alerter&#xD;&#xA;* &#xD;&#xA;* started...&#xD;&#xA;" />
<param name="Footer" value="...End of server logging.&#xD;&#xA;" />
<param name="ConversionPattern" value="%d [%t] %-5p %-30.37c{2} - %m%n" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="RootRollingLogFileAppender" />
</root>
</log4net>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="WebSocket4Net" publicKeyToken="eb4e154b696bf72a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-0.15.2.11" newVersion="0.15.2.11" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SuperSocket.ClientEngine" publicKeyToken="ee9af13f57f00acc" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-0.10.0.0" newVersion="0.10.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-11.0.0.0" newVersion="11.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View File

@@ -0,0 +1,10 @@
namespace NightScout
{
public interface IAlerter
{
void StartAlert();
void StartUrgentAlert();
void StartStaleDataAlert();
void StopAlerts();
}
}

View File

@@ -0,0 +1,19 @@
using System;
using Newtonsoft.Json.Linq;
namespace NightScout
{
public interface INightscoutDataSource
{
event Action Connected;
event Action<Object> DataUpdate;
event Action<Object> SocketError;
event Action<Object> Alarm;
event Action<Object> UrgentAlarm;
event Action<Object> ClearAlarm;
event Action StaleDataAlarm;
void Emit(String eventName, String data);
void Emit(String eventName, JObject data);
void Authorize();
}
}

View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace NightScout.Interfaces
{
public interface IRaspberryPiButtonSource
{
event Action<Boolean> ButtonStateChange;
}
}

View File

@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{D74FA0D0-8487-47F3-AB25-A65D221A2F47}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>NightScout</RootNamespace>
<AssemblyName>NightScout</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\packages\log4net.2.0.8\lib\net40-full\log4net.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.11.0.1\lib\net40\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Raspberry.IO, Version=2.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\Libraries\Raspberry.IO.dll</HintPath>
</Reference>
<Reference Include="Raspberry.IO.GeneralPurpose, Version=2.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\Libraries\Raspberry.IO.GeneralPurpose.dll</HintPath>
</Reference>
<Reference Include="Raspberry.IO.Interop, Version=2.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\Libraries\Raspberry.IO.Interop.dll</HintPath>
</Reference>
<Reference Include="Raspberry.System, Version=1.2.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\Libraries\Raspberry.System.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Alerters\Alerter.cs" />
<Compile Include="Alerters\ConsoleAlerter.cs" />
<Compile Include="Interfaces\IAlerter.cs" />
<Compile Include="Interfaces\INightscoutDataSource.cs" />
<Compile Include="Interfaces\IRaspberryPiButtonSource.cs" />
<Compile Include="Alerters\MultiAlerter.cs" />
<Compile Include="NightscoutAlerter.cs" />
<Compile Include="Alerters\RaspberryPiAudioAlert.cs" />
<Compile Include="RaspberryPIButtonSource.cs" />
<Compile Include="SocketIONightscoutDataSource.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\NightscoutLibrary\NightscoutLibrary.csproj">
<Project>{59dc2b0b-2d94-4841-a181-d3e1124ba5f2}</Project>
<Name>NightscoutLibrary</Name>
</ProjectReference>
<ProjectReference Include="..\ThirdParty\EngineIoClientDotNet\Src\EngineIoClientDotNet.net40\EngineIoClientDotNet.net40.csproj">
<Project>{833dbca1-0b01-4477-9937-a8e50e426ee5}</Project>
<Name>EngineIoClientDotNet.net40</Name>
</ProjectReference>
<ProjectReference Include="..\ThirdParty\SocketIoClientDotNet\Src\SocketIoClientDotNet.net40\SocketIoClientDotNet.net40.csproj">
<Project>{53ae4914-a35b-406c-91ed-7e3d9c950f45}</Project>
<Name>SocketIoClientDotNet.net40</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NightScout
{
public class NightscoutAlerter
{
public INightscoutDataSource DataSource { get; set; }
public IAlerter Alerter { get; set; }
public DateTime LastDataUpdateReceived { get; set; }
public NightscoutAlerter(INightscoutDataSource dataSource, IAlerter alerter)
{
DataSource = dataSource;
Alerter = alerter;
SetupEvents();
}
private void SetupEvents()
{
DataSource.Connected += () => DataSource.Authorize();
DataSource.Alarm += (alarm) => Alerter.StartAlert();
DataSource.UrgentAlarm += (alarm) => Alerter.StartUrgentAlert();
DataSource.ClearAlarm += (alarm) => Alerter.StopAlerts();
DataSource.StaleDataAlarm += () => Alerter.StartStaleDataAlert();
//DataSource.DataUpdate += DataSource_DataUpdate;
}
void DataSource_DataUpdate(object obj)
{
Console.WriteLine(obj);
}
}
}

136
NightScout/Program.cs Normal file
View File

@@ -0,0 +1,136 @@
using System;
using System.Configuration;
using System.Linq;
using System.Threading;
using log4net;
using NightScout;
using NightscoutLibrary.Configuration;
using Quobject.SocketIoClientDotNet.Client;
namespace NightScoutAlert
{
internal class Program
{
private static void Main(string[] args)
{
if (args.Any(arg => arg.Equals("--test-audio")))
{
Console.WriteLine("Starting audio test...");
RaspberryPiAudioAlert alerter = new RaspberryPiAudioAlert();
alerter.StartAlert();
Thread.Sleep(3000);
alerter.StopAlerts();
return;
}
Quobject.EngineIoClientDotNet.Modules.LogManager.Enabled = true;
//NightscoutTestConfiguration configuration = new NightscoutTestConfiguration();
//MonoCompatibleNightscoutAlertConfiguration configuration = new MonoCompatibleNightscoutAlertConfiguration("http://nightscout.chrispr.org:8082", "ThisIsATerribleAuthSystem");
String nightscoutEndpoint = ConfigurationManager.AppSettings["NightscoutEndpoint"];
String nightscoutCredentials = ConfigurationManager.AppSettings["NightscoutCredentials"];
if (String.IsNullOrEmpty(nightscoutEndpoint) || String.IsNullOrEmpty(nightscoutCredentials))
{
Console.WriteLine("The nightscout endpoint and credentials must be filled into the App.config");
return;
}
MonoCompatibleNightscoutAlertConfiguration configuration = new MonoCompatibleNightscoutAlertConfiguration(nightscoutEndpoint, nightscoutCredentials);
RaspberryPIButtonSource buttonSource = null;
try
{
if (!String.IsNullOrEmpty(ConfigurationManager.AppSettings["ButtonSourceEnabled"]) &&
Boolean.Parse(ConfigurationManager.AppSettings["ButtonSourceEnabled"]))
{
buttonSource = new RaspberryPIButtonSource(1);
Console.WriteLine("Button source made");
}
}
catch (Exception)
{}
NightscoutDataSource dataSource =null;
RaspberryPiAudioAlert audioAlerter = null;
if (buttonSource != null)
{
dataSource = new NightscoutDataSource(configuration,
log4net.LogManager.GetLogger("NightscoutAlert"), buttonSource);
audioAlerter = new RaspberryPiAudioAlert(buttonSource);
}
else
{
dataSource = new NightscoutDataSource(configuration,
log4net.LogManager.GetLogger("NightscoutAlert"));
audioAlerter = new RaspberryPiAudioAlert();
}
ILog log = log4net.LogManager.GetLogger("NightscoutAlertMain");
log.Debug("Log started");
ConsoleAlerter consoleAlerter = new ConsoleAlerter();
//
NightscoutAlerter nightscoutAlerter = new NightscoutAlerter(dataSource,
new MultiAlerter(new IAlerter[] {consoleAlerter, audioAlerter, buttonSource}));
//var socket = IO.Socket(configuration.NightscoutBaseURL);
//socket.On(Socket.EVENT_CONNECT, () =>
//{
// Console.WriteLine("Connected...");
// JObject authRequest = new JObject();
// authRequest.Add("client", "web");
// socket.Emit("authorize", authRequest);
//});
//socket.On(Socket.EVENT_ERROR, (obj) =>
//{
// Console.WriteLine(obj.ToString());
//});
//socket.On("dataUpdate", (data) =>
//{
// Console.WriteLine(data);
//});
//socket.On("alarm", (data) =>
//{
// Console.WriteLine(data.ToString());
//});
//socket.On(Socket.EVENT_MESSAGE, (data) =>
//{
// Console.WriteLine(data.ToString());
//});
//socket.On("urgent_alarm", (data) =>
//{
// Console.WriteLine(data.ToString());
//});
//socket.On("clear_alarm", (data) =>
//{
// Console.WriteLine(data.ToString());
//});
Console.WriteLine("Started");
Console.ReadLine();
}
}
class NightscoutTestConfiguration : INightscoutAlertConfiguration
{
public String NightscoutBaseURL
{
//get { return "http://straylight.chrispr.lan:8082"; }
get { return "http://192.168.1.100:8082"; }
set
{throw new NotImplementedException();}
}
public string APISecretKey
{
get { return "ThisIsATerribleAuthSystem"; }
set
{throw new NotImplementedException();}
}
}
}

View File

@@ -0,0 +1,38 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// 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("NightScout")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("NightScout")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("5d91c91b-21c0-428c-9937-74115daa2861")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: log4net.Config.XmlConfigurator(Watch = true)]

View File

@@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using NightScout.Interfaces;
using Raspberry.IO.GeneralPurpose;
using Raspberry.IO.GeneralPurpose.Behaviors;
namespace NightScout
{
public class RaspberryPIButtonSource : IRaspberryPiButtonSource, IAlerter
{
public event Action<bool> ButtonStateChange;
protected IGpioConnectionDriver PinDriver;
protected GpioOutputBinaryPin ButtonLEDPin;
private static readonly object ledLock = new object();
public RaspberryPIButtonSource(Int32 pinNumber)
: this(pinNumber, new FileGpioConnectionDriver())
{ }
public RaspberryPIButtonSource(Int32 pinNumber, IGpioConnectionDriver driver)
{
//Attempt to monitor the pin
//pinConfiguration = ConnectorPin.P1Pin11.Input().Name("Switch");
//pinConfiguration.Reversed = true;
//pinConfiguration.OnStatusChanged(PinChangeHandler);
//if(pinConfiguration == null)
// Console.WriteLine("null");
//PinDriver = new FileGpioConnectionDriver();
PinDriver = driver;
//var settings = new GpioConnectionSettings { Driver = pinConnectionDriver };
Console.WriteLine("Making new GPIO connection");
//pinConnection = new GpioConnection(settings,pinConfiguration);
//pinConnectionDriver.Allocate(ProcessorPin.Pin17, PinDirection.Input);
//var pinDriver = pinConnectionDriver.In(ConnectorPin.P1Pin11);
var buttonPin = PinDriver.In(ConnectorPin.P1Pin16);
ButtonLEDPin = PinDriver.Out(ConnectorPin.P1Pin22);
Task.Factory.StartNew(() =>
{
Boolean savedState = false;
while (true)
{
//Pin 11 is inverse (always high unless button is pressed)
Boolean state = !(buttonPin.Read());
if (savedState != state)
{
PinChangeHandler(state);
savedState = state;
}
Thread.Sleep(100);
}
});
//Console.WriteLine("Made");
//pinConnection.PinStatusChanged += pinConnection_PinStatusChanged;
}
private void PinChangeHandler(Boolean state)
{
if (ButtonStateChange != null)
ButtonStateChange(state);
}
public void StartAlert()
{
lock (ledLock)
{
ButtonLEDPin.Write(true);
}
}
public void StartUrgentAlert()
{
lock (ledLock)
{
ButtonLEDPin.Write(true);
}
}
public void StartStaleDataAlert()
{
lock (ledLock)
{
ButtonLEDPin.Write(true);
}
}
public void StopAlerts()
{
lock (ledLock)
{
ButtonLEDPin.Write(false);
}
}
}
}

View File

@@ -0,0 +1,219 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.Odbc;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using NightScout.Interfaces;
using NightscoutLibrary.Configuration;
using Quobject.EngineIoClientDotNet.Client.Transports;
using Quobject.SocketIoClientDotNet.Client;
namespace NightScout
{
public class NightscoutDataSource : INightscoutDataSource
{
public Socket Socket { get; set; }
public Manager Manager { get; set; }
public log4net.ILog Logger { get; set; }
public IRaspberryPiButtonSource ButtonSource { get; set; }
public INightscoutAlertConfiguration NightscoutAlertConfiguration { get; set; }
public event Action Connected;
public event Action<Object> DataUpdate;
public event Action<Object> SocketError;
public event Action<Object> Alarm;
public event Action<Object> UrgentAlarm;
public event Action<Object> ClearAlarm;
public event Action StaleDataAlarm;
private DateTime lastDataUpdate = DateTime.Now;
private DateTime lastSgvReading = DateTime.Now;
public NightscoutDataSource(INightscoutAlertConfiguration configuration, log4net.ILog logger)
{
Logger = logger;
NightscoutAlertConfiguration = configuration;
ResetManagerAndConnect();
PrepareSocketEvents();
}
public NightscoutDataSource(INightscoutAlertConfiguration configuration, log4net.ILog logger, IRaspberryPiButtonSource buttonSource)
: this(configuration, logger)
{
ButtonSource = buttonSource;
ButtonSource.ButtonStateChange += ButtonSource_ButtonStateChange;
}
public void Emit(String eventName, String data)
{
Socket.Emit(eventName, data);
}
public void Emit(String eventName, JObject data)
{
Socket.Emit(eventName,data);
}
public virtual void Authorize()
{
JObject authRequest = new JObject {{"client", "web"}};
Socket.Emit("authorize", authRequest);
}
public virtual void ClearAlerts()
{
Logger.Info("Sending Clear Alerts Message");
Socket.Emit("ack", 2, "default", 45 * 60 * 1000);
Logger.Debug("Sent ack");
}
protected virtual void OnConnect()
{
Logger.Info("Connected to Socket.IO endpoint");
if (Connected != null)
Connected();
}
protected virtual void OnDataUpdate(object data)
{
Logger.DebugFormat("Data Update: {0}", data);
if (DataUpdate != null)
DataUpdate(data);
//Update lastUpdated
JObject dataUpdate = JObject.Parse(data.ToString());
if (dataUpdate["lastUpdated"] != null)
{ //lastUpdated is in ms
Int32 lastUpdatedUnixTime = (Int32)(dataUpdate["lastUpdated"].ToObject<Int64>() / 1000);
lastDataUpdate = UnixTimeStampToDateTime(lastUpdatedUnixTime);
Logger.InfoFormat("Last data update reset to {0}", lastDataUpdate);
}
if (dataUpdate["sgvs"] != null)
{
var sgvs = dataUpdate["sgvs"].Children();
var newestSSgv =
sgvs.Where(sgv => sgv["mills"] != null).OrderByDescending(sgv => sgv["mills"].Value<Int64>()).First();
Int64 sgvReadTime = newestSSgv["mills"].Value<Int64>();
DateTime reading = UnixTimeStampToDateTime(sgvReadTime / 1000);;
if (reading > lastSgvReading)
lastSgvReading = reading;
if(newestSSgv["mgdl"] != null)
Logger.InfoFormat("New sgv read of {0} mg/dl on {1}", newestSSgv["mgdl"], lastSgvReading);
}
}
protected virtual void OnSocketError(object error)
{
//Socket.Close();
Logger.InfoFormat("Socket Error: {0}", error);
if (SocketError != null)
SocketError(error);
}
protected virtual void OnAlarm(object alarm)
{
Logger.InfoFormat("Alarm: {0}", alarm);
if (Alarm != null)
Alarm(alarm);
}
protected virtual void OnUrgentAlarm(object alarm)
{
Logger.InfoFormat("Urgent Alarm: {0}", alarm);
if (UrgentAlarm != null)
UrgentAlarm(alarm);
}
protected virtual void OnClearAlarm(object alarm)
{
Logger.InfoFormat("Clear Alarm: {0}", alarm);
if (ClearAlarm != null)
ClearAlarm(alarm);
}
protected virtual void OnStaleDataAlarm()
{
if (StaleDataAlarm != null)
StaleDataAlarm();
}
protected void ResetManagerAndConnect()
{
//, DisablePing = true, Transports = new List<string>() { "polling" }
Options options;
options = new IO.Options();
Manager = new Manager(new Uri(NightscoutAlertConfiguration.NightscoutBaseURL), options);
Socket = Manager.Socket("/"); //IO.Socket(configuration.NightscoutBaseURL, options);
}
private void HeartbeatThread()
{
while (true)
{
//30 minutes - (currentTime - lastDataUpdate)
const double millisecondsToSleep = (5*60*1000); //(30*60*1000) - (DateTime.Now - lastDataUpdate).TotalMilliseconds;
Thread.Sleep((Int32)millisecondsToSleep);
if ((DateTime.Now - lastDataUpdate).TotalMinutes >= 20)
{ //forcefully reset data connection
Logger.Info("Data update timeout, closing socket");
Manager.EngineSocket.Close();
lastDataUpdate = DateTime.Now;
}
if ((DateTime.Now - lastSgvReading).TotalMinutes >= 30)
{ //Stale data alarm
OnStaleDataAlarm();
Logger.Info("Data stale alarm");
lastSgvReading = DateTime.Now;
}
}
}
private void PrepareSocketEvents()
{
Socket.On(Socket.EVENT_CONNECT, OnConnect);
Socket.On(Socket.EVENT_ERROR, OnSocketError);
Socket.On("dataUpdate", OnDataUpdate);
Socket.On("alarm", OnAlarm);
Socket.On("urgent_alarm", OnUrgentAlarm);
Socket.On("clear_alarm", OnClearAlarm);
Manager.On(Manager.EVENT_CONNECT_ERROR, (err) => Logger.InfoFormat("EVENT_CONNECT_ERROR: {0}",err));
Manager.On(Manager.EVENT_ERROR, (err) => Logger.InfoFormat("EVENT_ERROR: {0}", err));
Manager.On(Manager.EVENT_RECONNECT_ERROR, (err) => Logger.InfoFormat("EVENT_RECONNECT_ERROR: {0}", err));
Manager.On(Manager.EVENT_RECONNECT_FAILED, () => Logger.Info("EVENT_RECONNECT_FAILED"));
Manager.On(Manager.EVENT_CLOSE, (reason) => Logger.InfoFormat("EVENT_CLOSE: {0}", reason));
Task.Factory.StartNew(HeartbeatThread,TaskCreationOptions.LongRunning);
}
private void ButtonSource_ButtonStateChange(bool obj)
{
if (obj)
{ //Button pressed, STOP active alerts
ClearAlerts();
}
}
private static DateTime UnixTimeStampToDateTime(double unixTimeStamp)
{
// Unix timestamp is seconds past epoch
System.DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);
dtDateTime = dtDateTime.AddSeconds(unixTimeStamp).ToLocalTime();
return dtDateTime;
}
}
}

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="log4net" version="2.0.8" targetFramework="net40" />
<package id="Newtonsoft.Json" version="11.0.1" targetFramework="net40" />
</packages>