Skip to content

Commit 30f35f0

Browse files
authored
Purge scan results (#72)
1 parent 61d0562 commit 30f35f0

File tree

3 files changed

+181
-111
lines changed

3 files changed

+181
-111
lines changed

nanoFramework.Device.Bluetooth/Advertisement/BluetoothLEAdvertisementFilter.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,11 @@ public BluetoothLEAdvertisementFilter()
3131
///// a received Bluetooth LE advertisement.
3232
///// </summary>
3333
//public IList<BluetoothLEAdvertisementBytePattern> BytePatterns { get; }
34+
35+
internal bool Filter(BluetoothLEAdvertisementReceivedEventArgs args)
36+
{
37+
return true;
38+
}
39+
3440
}
3541
}

nanoFramework.Device.Bluetooth/BluetoothLEAdvertisementWatcher.cs

Lines changed: 15 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System;
77
using System.Collections;
88
using System.Runtime.CompilerServices;
9+
using System.Threading;
910
using nanoFramework.Device.Bluetooth.Advertisement;
1011

1112
namespace nanoFramework.Device.Bluetooth
@@ -15,19 +16,10 @@ namespace nanoFramework.Device.Bluetooth
1516
/// </summary>
1617
public class BluetoothLEAdvertisementWatcher
1718
{
18-
BluetoothLEAdvertisementWatcherStatus _status;
19-
BluetoothLEScanningMode _scanningMode;
20-
BluetoothLEAdvertisementFilter _advertisementFilter;
21-
BluetoothSignalStrengthFilter _signalStrengthFilter;
22-
23-
Hashtable _scanResults = new();
24-
25-
private class ScanItem
26-
{
27-
public short rssi;
28-
public bool inRange;
29-
public DateTime outRangeTime;
30-
}
19+
private BluetoothLEAdvertisementWatcherStatus _status;
20+
private BluetoothLEScanningMode _scanningMode;
21+
private BluetoothLEAdvertisementFilter _advertisementFilter;
22+
private BluetoothSignalStrengthFilter _signalStrengthFilter;
3123

3224
/// <summary>
3325
/// Delegate for new Bluetooth LE advertisement events received.
@@ -43,7 +35,6 @@ private class ScanItem
4335
/// <param name="args">Event arguments</param>
4436
public delegate void BluetoothLEAdvertisementStoppedEvenHandler(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementWatcherStoppedEventArgs args);
4537

46-
4738
/// <summary>
4839
/// Creates a new BluetoothLEAdvertisementWatcher object.
4940
/// </summary>
@@ -60,7 +51,7 @@ public BluetoothLEAdvertisementWatcher(BluetoothLEAdvertisementFilter advertisem
6051
{
6152
_status = BluetoothLEAdvertisementWatcherStatus.Created;
6253
_advertisementFilter = advertisementFilter;
63-
SignalStrengthFilter = new BluetoothSignalStrengthFilter();
54+
SignalStrengthFilter = new();
6455
}
6556

6657
/// <summary>
@@ -72,7 +63,6 @@ public void Start()
7263
BluetoothNanoDevice.CheckMode(BluetoothNanoDevice.Mode.Client);
7364

7465
_status = BluetoothLEAdvertisementWatcherStatus.Started;
75-
_scanResults = new Hashtable();
7666

7767
NativeStartAdvertisementWatcher((int)_scanningMode);
7868
BluetoothLEServer._bluetoothEventManager.Watcher = this;
@@ -119,109 +109,30 @@ public void Stop()
119109

120110
internal void OnReceived(BluetoothLEAdvertisementReceivedEventArgs args)
121111
{
122-
// Check Scan in RSSI range
123-
if (ScanRssiFilter(args))
124-
{
125-
Received?.Invoke(this, args);
126-
}
127-
}
128-
129-
/// <summary>
130-
/// Check Event against RSSI filter
131-
/// </summary>
132-
/// <param name="args">BluetoothLEAdvertisementReceivedEventArgs</param>
133-
/// <returns>Returns False to ignore event</returns>
134-
internal bool ScanRssiFilter(BluetoothLEAdvertisementReceivedEventArgs args)
135-
{
136-
ScanItem scan = FindScanEntry(args.BluetoothAddress);
137-
bool inRange = (args.RawSignalStrengthInDBm >= SignalStrengthFilter.InRangeThresholdInDBm);
138-
if (scan == null && inRange)
139-
{
140-
// New entry and in range then add it to list
141-
scan = AddOrReplaceScanEntry(args.BluetoothAddress, args.RawSignalStrengthInDBm, inRange);
142-
}
143-
144-
if (scan != null)
145-
{
146-
if (!inRange)
147-
{
148-
if (args.RawSignalStrengthInDBm < SignalStrengthFilter.OutOfRangeThresholdInDBm)
149-
{
150-
// Completely out of range, ignore
151-
DeleteScanEntry(args.BluetoothAddress);
152-
return false;
153-
}
154-
155-
// In between in and out of range thresholds, check time out of range
156-
if (scan.inRange)
157-
{
158-
// If previously in range and not out
159-
// then set date time for time out
160-
AddOrReplaceScanEntry(args.BluetoothAddress, args.RawSignalStrengthInDBm, inRange);
161-
}
162-
else
163-
{
164-
// If previously moved out of range then check timer
165-
if ((scan.outRangeTime + SignalStrengthFilter.OutOfRangeTimeout) < DateTime.UtcNow )
166-
{
167-
// Moved out of range for time out period
168-
// Remove scan
169-
DeleteScanEntry(args.BluetoothAddress);
170-
return false;
171-
}
172-
}
173-
}
174-
175-
return true;
176-
}
177-
return false;
178-
}
179-
180-
private ScanItem FindScanEntry(UInt64 address)
181-
{
182-
if (_scanResults.Contains(address))
112+
// Check filters
113+
// Signal strength filter
114+
if (!_signalStrengthFilter.Filter(args))
183115
{
184-
return (ScanItem)_scanResults[address];
116+
return;
185117
}
186-
return null;
187-
}
188118

189-
private ScanItem AddOrReplaceScanEntry(UInt64 address, short Rssi, bool InRange)
190-
{
191-
ScanItem item = new()
119+
// Advertisement section Filter (TODO)
120+
if (_advertisementFilter != null && !_advertisementFilter.Filter(args))
192121
{
193-
rssi = Rssi,
194-
inRange = InRange,
195-
};
196-
197-
if (!InRange)
198-
{
199-
// It out of range now, save time for time out checking
200-
item.outRangeTime = DateTime.UtcNow;
122+
return;
201123
}
202124

203-
DeleteScanEntry(address);
204-
205-
_scanResults.Add(address, item);
206-
207-
return item;
125+
Received?.Invoke(this, args);
208126
}
209127

210-
private void DeleteScanEntry(UInt64 address)
211-
{
212-
if (_scanResults.Contains(address))
213-
{
214-
_scanResults.Remove(address);
215-
}
216-
}
217128
/// <summary>
218129
/// Notification for new Bluetooth LE advertisement events received.
219130
/// </summary>
220131
public event BluetoothLEAdvertisementReceivedHandler Received;
221132

222133
/// <summary>
223134
/// Notification to the application that the Bluetooth LE scanning for advertisements has
224-
/// been cancelled or aborted either by the application or due to an error.
135+
/// been canceled or aborted either by the application or due to an error.
225136
/// </summary>
226137
public event BluetoothLEAdvertisementStoppedEvenHandler Stopped;
227138

nanoFramework.Device.Bluetooth/BluetoothSignalStrengthFilter.cs

Lines changed: 160 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
// Copyright (c) .NET Foundation and Contributors
33
// See LICENSE file in the project root for full license information.
44
//
5+
using nanoFramework.Device.Bluetooth.Advertisement;
56
using System;
7+
using System.Collections;
8+
using System.Threading;
69

710
namespace nanoFramework.Device.Bluetooth
811
{
@@ -16,38 +19,188 @@ public class BluetoothSignalStrengthFilter
1619
private short _outOfRangeThresholdInDBm;
1720
private TimeSpan _outOfRangeTimeout;
1821

22+
private Timer _scanCheck;
23+
private Hashtable _scanResults = new();
24+
private Object _scanResultsLock = new Object();
25+
26+
private const int DefaultDBM = -127;
27+
private const int DefaultOorTimeout = 60;
28+
29+
private class ScanItem
30+
{
31+
public short Rssi; // RSSI of last scan
32+
public bool InRange; // True if device was in range.
33+
public DateTime OutRangeTime; // Time device went out of range
34+
public bool Active; // Scan item Active, used for purging entries
35+
}
36+
1937
/// <summary>
2038
/// Create a new BluetoothSignalStrengthFilter object.
2139
/// </summary>
2240
public BluetoothSignalStrengthFilter()
2341
{
24-
InRangeThresholdInDBm = -128;
25-
OutOfRangeThresholdInDBm = -128;
26-
OutOfRangeTimeout = new TimeSpan(0, 0, 10); // seconds
42+
InRangeThresholdInDBm = DefaultDBM;
43+
OutOfRangeThresholdInDBm = DefaultDBM;
44+
OutOfRangeTimeout = new TimeSpan(0, 0, DefaultOorTimeout); // seconds
45+
46+
_scanResults = new Hashtable();
47+
48+
// Remove inactive entries from scan results every 1 minute
49+
TimeSpan time = new TimeSpan(0, 1, 0);
50+
_scanCheck = new Timer(ScanCheckCallback, 0, time, time);
2751
}
2852

2953
///// <summary>
30-
///// The interval at which received signal strength indicator (RSSI) events are sampled.
54+
///// The interval at which received signal strength indicator (RSSI) events are sampled. (TODO)
3155
///// </summary>
3256
//public TimeSpan? SamplingInterval { get; set; }
3357

3458
/// <summary>
3559
/// The time out for a received signal strength indicator (RSSI) event to be considered
36-
/// out of range.
60+
/// out of range. Value between 1 and 60 seconds.
3761
/// </summary>
3862
public TimeSpan OutOfRangeTimeout { get => _outOfRangeTimeout; set => _outOfRangeTimeout = value; }
3963

4064
/// <summary>
4165
/// The minimum received signal strength indicator (RSSI) value in dBm on which RSSI
42-
/// events will be considered out of range.
66+
/// events will be considered out of range. Value between +20 and -127. Default vale is -127.
4367
/// </summary>
4468
public short OutOfRangeThresholdInDBm { get => _outOfRangeThresholdInDBm; set => _outOfRangeThresholdInDBm = value; }
4569

4670
/// <summary>
4771
/// The minimum received signal strength indicator (RSSI) value in dBm on which RSSI
4872
/// events will be propagated or considered in range if the previous events were
49-
/// considered out of range.
73+
/// considered out of range. Value between +20 and -127. Default vale is -127.
5074
/// </summary>
5175
public short InRangeThresholdInDBm { get => _inRangeThresholdInDBm; set => _inRangeThresholdInDBm = value; }
76+
77+
/// <summary>
78+
/// Signal strength filter (RSSI).
79+
/// </summary>
80+
/// <param name="args">BluetoothLEAdvertisementReceivedEventArgs</param>
81+
/// <returns>Returns False to ignore event</returns>
82+
internal bool Filter(BluetoothLEAdvertisementReceivedEventArgs args)
83+
{
84+
// If default setting then just accept all events
85+
if (InRangeThresholdInDBm == DefaultDBM)
86+
{
87+
return true;
88+
}
89+
90+
ScanItem scan = FindScanEntry(args.BluetoothAddress);
91+
bool inRange = (args.RawSignalStrengthInDBm >= InRangeThresholdInDBm);
92+
if (scan == null && inRange)
93+
{
94+
// New entry and in range then add it to list
95+
scan = AddOrReplaceScanEntry(args.BluetoothAddress, args.RawSignalStrengthInDBm, inRange);
96+
}
97+
98+
if (scan != null)
99+
{
100+
scan.Active = true; // flag entry as active so not purged
101+
102+
if (!inRange)
103+
{
104+
if (args.RawSignalStrengthInDBm < OutOfRangeThresholdInDBm)
105+
{
106+
// Completely out of range, ignore
107+
DeleteScanEntry(args.BluetoothAddress);
108+
return false;
109+
}
110+
111+
// In between in and out of range thresholds, check time out of range
112+
if (scan.InRange)
113+
{
114+
// If previously in range and now out
115+
// then set date time for time out of range
116+
AddOrReplaceScanEntry(args.BluetoothAddress, args.RawSignalStrengthInDBm, inRange);
117+
}
118+
else
119+
{
120+
// If previously moved out of range then check timer and still out of range.
121+
if ((scan.OutRangeTime + OutOfRangeTimeout) < DateTime.UtcNow)
122+
{
123+
// Moved out of range for time out period
124+
// Remove scan
125+
DeleteScanEntry(args.BluetoothAddress);
126+
return false;
127+
}
128+
}
129+
}
130+
131+
return true;
132+
}
133+
return false;
134+
}
135+
136+
/// <summary>
137+
/// Called every 5 minutes to purge scan hash.
138+
/// </summary>
139+
/// <param name="state"></param>
140+
private void ScanCheckCallback(object state)
141+
{
142+
lock (_scanResultsLock)
143+
{
144+
ArrayList removeList = new ArrayList();
145+
146+
foreach (DictionaryEntry item in _scanResults)
147+
{
148+
if (!((ScanItem)item.Value).Active)
149+
{
150+
removeList.Add(item.Key);
151+
}
152+
else
153+
{
154+
((ScanItem)item.Value).Active = false;
155+
}
156+
}
157+
158+
// Now delete expired items
159+
foreach (ulong addressKey in removeList)
160+
{
161+
DeleteScanEntry(addressKey);
162+
}
163+
}
164+
}
165+
166+
private ScanItem FindScanEntry(UInt64 address)
167+
{
168+
if (_scanResults.Contains(address))
169+
{
170+
return (ScanItem)_scanResults[address];
171+
}
172+
173+
return null;
174+
}
175+
176+
private ScanItem AddOrReplaceScanEntry(UInt64 address, short rssi, bool inRange)
177+
{
178+
ScanItem item = new()
179+
{
180+
Rssi = rssi,
181+
InRange = inRange,
182+
Active = true
183+
};
184+
185+
if (!inRange)
186+
{
187+
// It out of range now, save time for time out checking
188+
item.OutRangeTime = DateTime.UtcNow;
189+
}
190+
191+
DeleteScanEntry(address);
192+
193+
_scanResults.Add(address, item);
194+
195+
return item;
196+
}
197+
198+
private void DeleteScanEntry(UInt64 address)
199+
{
200+
if (_scanResults.Contains(address))
201+
{
202+
_scanResults.Remove(address);
203+
}
204+
}
52205
}
53206
}

0 commit comments

Comments
 (0)