220 lines
8.0 KiB
C#
220 lines
8.0 KiB
C#
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;
|
|
}
|
|
|
|
}
|
|
}
|