Skip to content

Commit

Permalink
Added PerfCounterSensor, GraphTrayIndicator; fixed minor issues
Browse files Browse the repository at this point in the history
  • Loading branch information
alex-kabin committed Apr 30, 2020
1 parent 436b179 commit 68726fc
Show file tree
Hide file tree
Showing 26 changed files with 649 additions and 200 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Tray icon has context menu with log view option to diagnose problems.
##### Currently implemented sensors and indicators:
* _Bluetooth HandsFree battery monitor_ -> BatteryTrayIndicator
* _Spring Actuator Health endpoint monitor_ -> LampTrayIndicator
* _PerformanceCounter monitor_ -> GraphTrayIndicator

#### How to use Bluetooth HandsFree battery monitor:
Bluetooth HandsFree battery sensor uses HF Indicators feature as described in [HFP V1.7](https://www.bluetooth.org/docman/handlers/downloaddoc.ashx?doc_id=292287) and Apple Specific features from [Apple Accessory Design Guidelines](https://developer.apple.com/accessories/Accessory-Design-Guidelines.pdf)
Expand All @@ -42,6 +43,14 @@ where `URL` is url of the health endpoint, and `Period` is polling period (in Ti

After successful start there will be lamp icon in the system tray with health status (GREEN - up, RED - down) or error mark. Tray icon has context menu with log view option to diagnose problems.

#### How to use PerformanceCounter monitor:
Start TrayMonitor from command line (or shortcut link) with arguments:

-c -s Sensor.PerfCounter.PerfCounterSensor,Sensor.PerfCounter "Period=1000" "Category=Processor" "Counter=% Processor Time" "Instance=_Total" -i TrayMonitor.Indicators.GraphTrayIndicator,TrayMonitor

where `Category`, `Counter`, `Instance` make up performance counter (see docs on Windows Performance Counters), and `Period` is polling period (in milliseconds)


#### How to make custom Sensor or Indicator:
* You can implement your custom sensor by subclassing `Sensor.Core.SensorBase` class
* You can implement your custom indicator by subclassing `TrayMonitor.Core.Indicators.TrayIndicator` class
Expand Down
7 changes: 7 additions & 0 deletions Sensor.Core/Disposable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ namespace Sensor.Core
{
public class Disposable : IDisposable
{
public static void Destroy<T>(ref T obj) where T : class {
if (obj is IDisposable disposable) {
disposable.Dispose();
}
obj = null;
}

private Action _dispose;

public Disposable(Action dispose) {
Expand Down
9 changes: 9 additions & 0 deletions Sensor.Core/IConfigurable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Collections.Generic;

namespace Sensor.Core
{
public interface IConfigurable
{
void Configure(IEnumerable<(string, string)> properties);
}
}
8 changes: 3 additions & 5 deletions Sensor.Core/ISensor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ namespace Sensor.Core
{
public interface ISensor
{
void Configure(IEnumerable<(string, string)> parameters);

Task Connect(CancellationToken ct);

Task Disconnect(CancellationToken ct);
Expand All @@ -21,12 +19,12 @@ public interface ISensor

event EventHandler ErrorOccured;

SensorState State { get; }
SensorStatus Status { get; }

event EventHandler<SensorStateEventArgs> StateChanged;
event EventHandler<SensorStatusEventArgs> StatusChanged;

object Value { get; }

event EventHandler<SensorValueEventArgs> ValueChanged;
event EventHandler<SensorValueEventArgs> ValueReady;
}
}
30 changes: 14 additions & 16 deletions Sensor.Core/SensorBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,32 @@ public abstract class SensorBase : ISensor
{
public abstract Task Connect(CancellationToken cancellationToken);
public abstract Task Disconnect(CancellationToken cancellationToken);
public virtual void Configure(IEnumerable<(string, string)> parameters) { }

protected SensorState _state;
public SensorState State {
get => _state;

protected SensorStatus _status = SensorStatus.Offline;
public SensorStatus Status {
get => _status;
protected set {
if (_state != value) {
_state = value;
StateChanged?.Invoke(this, new SensorStateEventArgs(_state));
if (_status != value) {
_status = value;
_value = null;
StatusChanged?.Invoke(this, new SensorStatusEventArgs(_status));
}
}
}
public event EventHandler<SensorStateEventArgs> StateChanged;
public event EventHandler<SensorStatusEventArgs> StatusChanged;

protected object _value;
public object Value {
get => _value;
protected set {
if (!Equals(_value, value)) {
_value = value;
if (_value != null) {
_error = null;
}
ValueChanged?.Invoke(this, new SensorValueEventArgs(_value));
_value = value;
if (_value != null) {
_error = null;
}
ValueReady?.Invoke(this, new SensorValueEventArgs(_value));
}
}
public event EventHandler<SensorValueEventArgs> ValueChanged;
public event EventHandler<SensorValueEventArgs> ValueReady;

protected SensorException _error;
public SensorException Error {
Expand Down
13 changes: 0 additions & 13 deletions Sensor.Core/SensorData.cs

This file was deleted.

34 changes: 18 additions & 16 deletions Sensor.Core/SensorExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,35 @@ namespace Sensor.Core
{
public static class SensorExtensions
{
public static SensorData GetData(this ISensor sensor) {
return new SensorData {
public static SensorState GetState(this ISensor sensor) {
return new SensorState {
Title = sensor.Title,
Error = sensor.Error,
State = sensor.State,
Status = sensor.Status,
Value = sensor.Value
};
}
public static IObservable<SensorData> AsObservable(this ISensor sensor) {
return Observable.Create<SensorData>(

public static IObservable<SensorState> AsObservable(this ISensor sensor) {
return Observable.Create<SensorState>(
observer => {
void OnSensorChange(object s, object a) {
observer.OnNext(GetData(sensor));
observer.OnNext(GetState(sensor));
}
sensor.ValueChanged += OnSensorChange;
sensor.StateChanged += OnSensorChange;
sensor.ValueReady += OnSensorChange;
sensor.StatusChanged += OnSensorChange;
sensor.ErrorOccured += OnSensorChange;
sensor.TitleChanged += OnSensorChange;
return new Disposable(() => {
sensor.ValueChanged -= OnSensorChange;
sensor.StateChanged -= OnSensorChange;
sensor.ErrorOccured -= OnSensorChange;
sensor.TitleChanged -= OnSensorChange;
});
return new Disposable(
() => {
sensor.ValueReady -= OnSensorChange;
sensor.StatusChanged -= OnSensorChange;
sensor.ErrorOccured -= OnSensorChange;
sensor.TitleChanged -= OnSensorChange;
}
);
}
);
}
Expand Down
30 changes: 24 additions & 6 deletions Sensor.Core/SensorState.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
namespace Sensor.Core
using System;

namespace Sensor.Core
{
public enum SensorState
public class SensorState
{
Offline,
Connecting,
Online,
Disconnecting
public string Title { get; set; }
public SensorException Error { get; set; }
public SensorStatus Status { get; set; }
public object Value { get; set; }
public DateTime Timestamp { get; } = DateTime.Now;

public static bool IsUpdated(SensorState newSensorState, SensorState oldSensorState) {
if (newSensorState == null && oldSensorState == null) {
return false;
}
if (newSensorState == null || oldSensorState == null) {
return true;
}
return newSensorState.Timestamp >= oldSensorState.Timestamp && (
!Equals(newSensorState.Value, oldSensorState.Value)
|| newSensorState.Status != oldSensorState.Status
|| !ReferenceEquals(newSensorState.Error, oldSensorState.Error)
|| !string.Equals(newSensorState.Title, oldSensorState.Title)
);
}
}
}
13 changes: 0 additions & 13 deletions Sensor.Core/SensorStateEventArgs.cs

This file was deleted.

10 changes: 10 additions & 0 deletions Sensor.Core/SensorStatus.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Sensor.Core
{
public enum SensorStatus
{
Offline,
Connecting,
Online,
Disconnecting
}
}
13 changes: 13 additions & 0 deletions Sensor.Core/SensorStatusEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;

namespace Sensor.Core
{
public class SensorStatusEventArgs : EventArgs
{
public SensorStatus Status { get; }

public SensorStatusEventArgs(SensorStatus status) {
Status = status;
}
}
}
33 changes: 15 additions & 18 deletions Sensor.HandsFreeBattery/HandsFreeBatterySensor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

namespace Sensor.HandsFreeBattery
{
public class HandsFreeBatterySensor : SensorBase, IDisposable
public class HandsFreeBatterySensor : SensorBase, IConfigurable, IDisposable
{
private static readonly ILogger Log = LoggerFactory.GetLogger(nameof(HandsFreeBatterySensor));

Expand All @@ -26,10 +26,9 @@ public class HandsFreeBatterySensor : SensorBase, IDisposable
private CancellationTokenSource _cancellation;

public HandsFreeBatterySensor() {
_state = SensorState.Offline;
}

public override void Configure(IEnumerable<(string, string)> parameters) {
public void Configure(IEnumerable<(string, string)> parameters) {
foreach ((string key, string value) in parameters) {
if (string.Equals(key, "DeviceName", StringComparison.InvariantCultureIgnoreCase)) {
Title = _deviceName = value;
Expand Down Expand Up @@ -164,26 +163,21 @@ private async Task EstablishConversation(CancellationToken cancellationToken) {
}

private void Reset() {
if (_stream != null) {
_stream.Dispose();
_stream = null;
}
if (_client != null) {
_client.Dispose();
_client = null;
}
Disposable.Destroy(ref _stream);
Disposable.Destroy(ref _client);

_device = null;
Title = _deviceName;
State = SensorState.Offline;
Status = SensorStatus.Offline;
}

public override async Task Connect(CancellationToken cancellationToken) {
if (State != SensorState.Offline) {
if (Status != SensorStatus.Offline) {
return;
}

Error = null;
State = SensorState.Connecting;
_error = null;
Status = SensorStatus.Connecting;

try {
await ConnectDevice(cancellationToken);
Expand Down Expand Up @@ -212,7 +206,7 @@ public override async Task Connect(CancellationToken cancellationToken) {
);
#pragma warning restore 4014

State = SensorState.Online;
Status = SensorStatus.Online;
}

private async void DoConversation(CancellationToken cancellationToken) {
Expand Down Expand Up @@ -301,13 +295,16 @@ private async void DoConversation(CancellationToken cancellationToken) {
}

public override async Task Disconnect(CancellationToken cancellationToken) {
if (State != SensorState.Online) {
if (Status != SensorStatus.Online) {
return;
}

Log.Debug("Disconnecting...");
State = SensorState.Disconnecting;
Status = SensorStatus.Disconnecting;

// close stream to interrupt all read awaitings
Disposable.Destroy(ref _stream);

if (_cancellation != null) {
_cancellation.Cancel();
_cancellation.Dispose();
Expand Down
Loading

0 comments on commit 68726fc

Please sign in to comment.