Move lots of stuff to BleFingerprintCollection (#28)
This commit is contained in:
parent
3b384c49c7
commit
5e1f327020
|
@ -0,0 +1,68 @@
|
||||||
|
#include "BleFingerprintCollection.h"
|
||||||
|
|
||||||
|
void BleFingerprintCollection::cleanupOldFingerprints()
|
||||||
|
{
|
||||||
|
if (fingerprints.size() <= maxCapacity)
|
||||||
|
return;
|
||||||
|
|
||||||
|
long oldestTime = LONG_MAX;
|
||||||
|
BleFingerprint *oldest = nullptr;
|
||||||
|
for (auto it = fingerprints.begin(); it != fingerprints.end(); ++it)
|
||||||
|
{
|
||||||
|
long time = (*it)->getLastSeen();
|
||||||
|
if (time < oldestTime)
|
||||||
|
{
|
||||||
|
oldestTime = time;
|
||||||
|
oldest = (*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (oldest == nullptr) return;
|
||||||
|
|
||||||
|
fingerprints.remove(oldest);
|
||||||
|
delete oldest;
|
||||||
|
}
|
||||||
|
|
||||||
|
BleFingerprint *BleFingerprintCollection::getFingerprintInternal(BLEAdvertisedDevice *advertisedDevice)
|
||||||
|
{
|
||||||
|
auto mac = advertisedDevice->getAddress();
|
||||||
|
|
||||||
|
auto it = std::find_if(fingerprints.begin(), fingerprints.end(), [mac](BleFingerprint *f)
|
||||||
|
{ return f->getAddress() == mac; });
|
||||||
|
if (it != fingerprints.end())
|
||||||
|
{
|
||||||
|
return *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto created = new BleFingerprint(advertisedDevice, ONE_EURO_FCMIN, ONE_EURO_BETA, ONE_EURO_DCUTOFF);
|
||||||
|
auto it2 = std::find_if(fingerprints.begin(), fingerprints.end(), [created](BleFingerprint *f)
|
||||||
|
{ return f->getId() == created->getId(); });
|
||||||
|
if (it2 != fingerprints.end())
|
||||||
|
{
|
||||||
|
auto found = *it2;
|
||||||
|
created->setInitial(found->getRSSI(), found->getDistance());
|
||||||
|
}
|
||||||
|
|
||||||
|
fingerprints.push_front(created);
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
BleFingerprint *BleFingerprintCollection::getFingerprint(BLEAdvertisedDevice *advertisedDevice)
|
||||||
|
{
|
||||||
|
if (xSemaphoreTake(fingerprintSemaphore, 1000) != pdTRUE)
|
||||||
|
log_e("Couldn't take semaphore!");
|
||||||
|
auto f = getFingerprintInternal(advertisedDevice);
|
||||||
|
if (xSemaphoreGive(fingerprintSemaphore) != pdTRUE)
|
||||||
|
log_e("Couldn't give semaphore!");
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<BleFingerprint *> BleFingerprintCollection::getSeen()
|
||||||
|
{
|
||||||
|
if (xSemaphoreTake(fingerprintSemaphore, 1000) != pdTRUE)
|
||||||
|
log_e("Couldn't take semaphore!");
|
||||||
|
cleanupOldFingerprints();
|
||||||
|
std::list<BleFingerprint *> seen(fingerprints);
|
||||||
|
if (xSemaphoreGive(fingerprintSemaphore) != pdTRUE)
|
||||||
|
log_e("Couldn't give semaphore!");
|
||||||
|
return seen;
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
#ifndef _BLEFINGERPRINTCOLLECTION_
|
||||||
|
#define _BLEFINGERPRINTCOLLECTION_
|
||||||
|
|
||||||
|
#include "BleFingerprint.h"
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include <GUI.h>
|
||||||
|
#include <NimBLEAdvertisedDevice.h>
|
||||||
|
#include <NimBLEBeacon.h>
|
||||||
|
#include <NimBLEDevice.h>
|
||||||
|
|
||||||
|
#define ONE_EURO_FCMIN 0.01
|
||||||
|
#define ONE_EURO_BETA 0.005
|
||||||
|
#define ONE_EURO_DCUTOFF 1
|
||||||
|
|
||||||
|
class BleFingerprintCollection : public BLEAdvertisedDeviceCallbacks
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BleFingerprintCollection(int _maxCapacity) : maxCapacity{_maxCapacity}
|
||||||
|
{
|
||||||
|
fingerprintSemaphore = xSemaphoreCreateBinary();
|
||||||
|
xSemaphoreGive(fingerprintSemaphore);
|
||||||
|
}
|
||||||
|
BleFingerprint *getFingerprint(BLEAdvertisedDevice *advertisedDevice);
|
||||||
|
void cleanupOldFingerprints();
|
||||||
|
int getTotalAdverts() { return totalSeen; }
|
||||||
|
std::list<BleFingerprint *> getSeen();
|
||||||
|
void setDisable(bool val) { disable = val; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool disable = false;
|
||||||
|
int totalSeen = 0;
|
||||||
|
int maxCapacity;
|
||||||
|
SemaphoreHandle_t fingerprintSemaphore;
|
||||||
|
std::list<BleFingerprint *> fingerprints;
|
||||||
|
BleFingerprint *getFingerprintInternal(BLEAdvertisedDevice *advertisedDevice);
|
||||||
|
|
||||||
|
void onResult(BLEAdvertisedDevice *advertisedDevice)
|
||||||
|
{
|
||||||
|
totalSeen++;
|
||||||
|
if (disable) return;
|
||||||
|
|
||||||
|
Display.seenStart();
|
||||||
|
BleFingerprint *f = getFingerprint(advertisedDevice);
|
||||||
|
f->seen(advertisedDevice);
|
||||||
|
Display.seenEnd();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -2,6 +2,16 @@
|
||||||
|
|
||||||
GUI Display;
|
GUI Display;
|
||||||
|
|
||||||
|
void GUI::seenStart()
|
||||||
|
{
|
||||||
|
digitalWrite(LED_BUILTIN, LED_BUILTIN_ON);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GUI::seenEnd()
|
||||||
|
{
|
||||||
|
digitalWrite(LED_BUILTIN, !LED_BUILTIN_ON);
|
||||||
|
}
|
||||||
|
|
||||||
void GUI::connected(bool wifi = false, bool mqtt = false)
|
void GUI::connected(bool wifi = false, bool mqtt = false)
|
||||||
{
|
{
|
||||||
status("Wifi: %s Mqtt: %s", (wifi ? "no" : "yes"), (wifi ? "no" : "yes"));
|
status("Wifi: %s Mqtt: %s", (wifi ? "no" : "yes"), (wifi ? "no" : "yes"));
|
||||||
|
|
|
@ -11,9 +11,47 @@
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef M5STICK
|
||||||
|
|
||||||
|
#define LED_BUILTIN 10
|
||||||
|
#define LED_BUILTIN_ON 0
|
||||||
|
|
||||||
|
#define BUTTON 39
|
||||||
|
#define BUTTON_PRESSED 0
|
||||||
|
|
||||||
|
#else
|
||||||
|
#if M5ATOM
|
||||||
|
|
||||||
|
#define LED_BUILTIN 10
|
||||||
|
#define LED_BUILTIN_ON 0
|
||||||
|
|
||||||
|
#define BUTTON 39
|
||||||
|
#define BUTTON_PRESSED 0
|
||||||
|
|
||||||
|
#else // Huzzah32 or DevKit
|
||||||
|
|
||||||
|
#define LED_BUILTIN 13
|
||||||
|
#define LED_BUILTIN_ON 1
|
||||||
|
|
||||||
|
#define BUTTON 15
|
||||||
|
#define BUTTON_PRESSED 1
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef M5STICK
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
class GUI
|
class GUI
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
void seenStart();
|
||||||
|
void seenEnd();
|
||||||
|
void updateProgress(unsigned int percent) { digitalWrite(LED_BUILTIN, percent % 2); }
|
||||||
|
void updateEnd() { digitalWrite(LED_BUILTIN, !LED_BUILTIN_ON); }
|
||||||
void close(const char *mac, const char *id);
|
void close(const char *mac, const char *id);
|
||||||
void left(const char *mac, const char *id);
|
void left(const char *mac, const char *id);
|
||||||
void status(const char *message, ...);
|
void status(const char *message, ...);
|
||||||
|
@ -28,6 +66,6 @@ private:
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern GUI Display;
|
extern GUI Display;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -26,40 +26,6 @@
|
||||||
// Maximum distance (in meters) to report. Devices that are calculated to be further than this distance in meters will not be reported
|
// Maximum distance (in meters) to report. Devices that are calculated to be further than this distance in meters will not be reported
|
||||||
#define DEFAULT_MAX_DISTANCE 16
|
#define DEFAULT_MAX_DISTANCE 16
|
||||||
|
|
||||||
#ifdef M5STICK
|
|
||||||
|
|
||||||
#define LED_BUILTIN 10
|
|
||||||
#define LED_BUILTIN_ON 0
|
|
||||||
|
|
||||||
#define BUTTON 39
|
|
||||||
#define BUTTON_PRESSED 0
|
|
||||||
|
|
||||||
#else
|
|
||||||
#if M5ATOM
|
|
||||||
|
|
||||||
#define LED_BUILTIN 10
|
|
||||||
#define LED_BUILTIN_ON 0
|
|
||||||
|
|
||||||
#define BUTTON 39
|
|
||||||
#define BUTTON_PRESSED 0
|
|
||||||
|
|
||||||
#else // Huzzah32 or DevKit
|
|
||||||
|
|
||||||
#define LED_BUILTIN 13
|
|
||||||
#define LED_BUILTIN_ON 1
|
|
||||||
|
|
||||||
#define BUTTON 15
|
|
||||||
#define BUTTON_PRESSED 1
|
|
||||||
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef M5STICK
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//Define the base topic for room detection. Usually "espresense"
|
//Define the base topic for room detection. Usually "espresense"
|
||||||
#define CHANNEL String("espresense")
|
#define CHANNEL String("espresense")
|
||||||
|
|
||||||
|
@ -74,9 +40,3 @@
|
||||||
|
|
||||||
// Number of seconds between update checks
|
// Number of seconds between update checks
|
||||||
#define CHECK_FOR_UPDATES_INTERVAL 300
|
#define CHECK_FOR_UPDATES_INTERVAL 300
|
||||||
|
|
||||||
#define ONE_EURO_FCMIN 0.01
|
|
||||||
|
|
||||||
#define ONE_EURO_BETA 0.005
|
|
||||||
|
|
||||||
#define ONE_EURO_DCUTOFF 1
|
|
||||||
|
|
69
src/main.cpp
69
src/main.cpp
|
@ -1,37 +1,5 @@
|
||||||
#include <main.h>
|
#include <main.h>
|
||||||
|
|
||||||
BleFingerprint *getFingerprintInternal(BLEAdvertisedDevice *advertisedDevice)
|
|
||||||
{
|
|
||||||
auto mac = advertisedDevice->getAddress();
|
|
||||||
|
|
||||||
auto it = std::find_if(fingerprints.begin(), fingerprints.end(), [mac](BleFingerprint *f) { return f->getAddress() == mac; });
|
|
||||||
if (it != fingerprints.end())
|
|
||||||
{
|
|
||||||
return *it;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto created = new BleFingerprint(advertisedDevice, ONE_EURO_FCMIN, ONE_EURO_BETA, ONE_EURO_DCUTOFF);
|
|
||||||
auto it2 = std::find_if(fingerprints.begin(), fingerprints.end(), [created](BleFingerprint *f) { return f->getId() == created->getId(); });
|
|
||||||
if (it2 != fingerprints.end())
|
|
||||||
{
|
|
||||||
auto found = *it2;
|
|
||||||
created->setInitial(found->getRSSI(), found->getDistance());
|
|
||||||
}
|
|
||||||
|
|
||||||
fingerprints.push_front(created);
|
|
||||||
return created;
|
|
||||||
}
|
|
||||||
|
|
||||||
BleFingerprint *getFingerprint(BLEAdvertisedDevice *advertisedDevice)
|
|
||||||
{
|
|
||||||
if (xSemaphoreTake(fingerprintSemaphore, 1000) != pdTRUE)
|
|
||||||
log_e("Couldn't take semaphore!");
|
|
||||||
auto f = getFingerprintInternal(advertisedDevice);
|
|
||||||
if (xSemaphoreGive(fingerprintSemaphore) != pdTRUE)
|
|
||||||
log_e("Couldn't give semaphore!");
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool sendTelemetry(int totalSeen = -1, int totalReported = -1, int totalAdverts = -1)
|
bool sendTelemetry(int totalSeen = -1, int totalReported = -1, int totalAdverts = -1)
|
||||||
{
|
{
|
||||||
if (!online)
|
if (!online)
|
||||||
|
@ -204,26 +172,6 @@ void connectToMqtt()
|
||||||
mqttClient.connect();
|
mqttClient.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
int getTotalAdverts() { return totalSeen; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
int totalSeen = 0;
|
|
||||||
|
|
||||||
void onResult(BLEAdvertisedDevice *advertisedDevice)
|
|
||||||
{
|
|
||||||
if (updateInProgress)
|
|
||||||
return;
|
|
||||||
totalSeen++;
|
|
||||||
digitalWrite(LED_BUILTIN, LED_BUILTIN_ON);
|
|
||||||
BleFingerprint *f = getFingerprint(advertisedDevice);
|
|
||||||
f->seen(advertisedDevice);
|
|
||||||
digitalWrite(LED_BUILTIN, !LED_BUILTIN_ON);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
bool reportDevice(BleFingerprint *f)
|
bool reportDevice(BleFingerprint *f)
|
||||||
{
|
{
|
||||||
StaticJsonDocument<512> doc;
|
StaticJsonDocument<512> doc;
|
||||||
|
@ -257,14 +205,11 @@ bool reportDevice(BleFingerprint *f)
|
||||||
|
|
||||||
void scanForDevices(void *parameter)
|
void scanForDevices(void *parameter)
|
||||||
{
|
{
|
||||||
fingerprintSemaphore = xSemaphoreCreateBinary();
|
|
||||||
xSemaphoreGive(fingerprintSemaphore);
|
|
||||||
auto scan = MyAdvertisedDeviceCallbacks();
|
|
||||||
BLEDevice::init("");
|
BLEDevice::init("");
|
||||||
auto pBLEScan = BLEDevice::getScan();
|
auto pBLEScan = BLEDevice::getScan();
|
||||||
pBLEScan->setInterval(BLE_SCAN_INTERVAL);
|
pBLEScan->setInterval(BLE_SCAN_INTERVAL);
|
||||||
pBLEScan->setWindow(BLE_SCAN_WINDOW);
|
pBLEScan->setWindow(BLE_SCAN_WINDOW);
|
||||||
pBLEScan->setAdvertisedDeviceCallbacks(&scan, true);
|
pBLEScan->setAdvertisedDeviceCallbacks(&fingerprints, true);
|
||||||
pBLEScan->setActiveScan(BLE_ACTIVE_SCAN);
|
pBLEScan->setActiveScan(BLE_ACTIVE_SCAN);
|
||||||
pBLEScan->setMaxResults(0);
|
pBLEScan->setMaxResults(0);
|
||||||
if (!pBLEScan->start(0, nullptr, false))
|
if (!pBLEScan->start(0, nullptr, false))
|
||||||
|
@ -277,18 +222,10 @@ void scanForDevices(void *parameter)
|
||||||
if (updateInProgress || !mqttClient.connected())
|
if (updateInProgress || !mqttClient.connected())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto results = scan;
|
|
||||||
pBLEScan->setAdvertisedDeviceCallbacks(&(scan = MyAdvertisedDeviceCallbacks()), true);
|
|
||||||
|
|
||||||
int totalSeen = 0;
|
int totalSeen = 0;
|
||||||
int totalReported = 0;
|
int totalReported = 0;
|
||||||
|
|
||||||
if (xSemaphoreTake(fingerprintSemaphore, 1000) != pdTRUE)
|
auto seen = fingerprints.getSeen();
|
||||||
log_e("Couldn't take semaphore!");
|
|
||||||
cleanupOldFingerprints();
|
|
||||||
std::list<BleFingerprint *> seen(fingerprints);
|
|
||||||
if (xSemaphoreGive(fingerprintSemaphore) != pdTRUE)
|
|
||||||
log_e("Couldn't give semaphore!");
|
|
||||||
|
|
||||||
for (auto it = seen.begin(); it != seen.end(); ++it)
|
for (auto it = seen.begin(); it != seen.end(); ++it)
|
||||||
{
|
{
|
||||||
|
@ -296,7 +233,7 @@ void scanForDevices(void *parameter)
|
||||||
if (reportDevice(*it))
|
if (reportDevice(*it))
|
||||||
totalReported++;
|
totalReported++;
|
||||||
}
|
}
|
||||||
sendTelemetry(totalSeen, totalReported, results.getTotalAdverts());
|
sendTelemetry(totalSeen, totalReported, fingerprints.getTotalAdverts());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
37
src/main.h
37
src/main.h
|
@ -5,11 +5,8 @@
|
||||||
#include <AsyncTCP.h>
|
#include <AsyncTCP.h>
|
||||||
#include <HTTPClient.h>
|
#include <HTTPClient.h>
|
||||||
#include <HTTPUpdate.h>
|
#include <HTTPUpdate.h>
|
||||||
#include <NimBLEAdvertisedDevice.h>
|
|
||||||
#include <NimBLEBeacon.h>
|
#include <NimBLEBeacon.h>
|
||||||
#include <NimBLEDevice.h>
|
#include <NimBLEDevice.h>
|
||||||
#include <NimBLEEddystoneTLM.h>
|
|
||||||
#include <NimBLEEddystoneURL.h>
|
|
||||||
#include <SPIFFS.h>
|
#include <SPIFFS.h>
|
||||||
#include <WebServer.h>
|
#include <WebServer.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
|
@ -20,6 +17,7 @@
|
||||||
#include <rom/rtc.h>
|
#include <rom/rtc.h>
|
||||||
|
|
||||||
#include "BleFingerprint.h"
|
#include "BleFingerprint.h"
|
||||||
|
#include "BleFingerprintCollection.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
|
|
||||||
AsyncMqttClient mqttClient;
|
AsyncMqttClient mqttClient;
|
||||||
|
@ -45,9 +43,7 @@ bool publishTele;
|
||||||
bool publishRooms;
|
bool publishRooms;
|
||||||
bool publishDevices;
|
bool publishDevices;
|
||||||
int maxDistance;
|
int maxDistance;
|
||||||
|
BleFingerprintCollection fingerprints(MAX_MAC_ADDRESSES);
|
||||||
static SemaphoreHandle_t fingerprintSemaphore;
|
|
||||||
static std::list<BleFingerprint *> fingerprints;
|
|
||||||
|
|
||||||
String resetReason(RESET_REASON reason)
|
String resetReason(RESET_REASON reason)
|
||||||
{
|
{
|
||||||
|
@ -116,16 +112,18 @@ void configureOTA()
|
||||||
.onStart([]() {
|
.onStart([]() {
|
||||||
Serial.println("OTA Start");
|
Serial.println("OTA Start");
|
||||||
updateInProgress = true;
|
updateInProgress = true;
|
||||||
|
fingerprints.setDisable(updateInProgress);
|
||||||
})
|
})
|
||||||
.onEnd([]() {
|
.onEnd([]() {
|
||||||
updateInProgress = false;
|
updateInProgress = false;
|
||||||
digitalWrite(LED_BUILTIN, !LED_BUILTIN_ON);
|
fingerprints.setDisable(updateInProgress);
|
||||||
|
Display.updateEnd();
|
||||||
Serial.println("\n\rEnd");
|
Serial.println("\n\rEnd");
|
||||||
})
|
})
|
||||||
.onProgress([](unsigned int progress, unsigned int total) {
|
.onProgress([](unsigned int progress, unsigned int total) {
|
||||||
byte percent = (progress / (total / 100));
|
byte percent = (progress / (total / 100));
|
||||||
Serial.printf("Progress: %u\r\n", percent);
|
Serial.printf("Progress: %u\r\n", percent);
|
||||||
digitalWrite(LED_BUILTIN, percent % 2);
|
Display.updateProgress(progress);
|
||||||
})
|
})
|
||||||
.onError([](ota_error_t error) {
|
.onError([](ota_error_t error) {
|
||||||
Serial.printf("Error[%u]: ", error);
|
Serial.printf("Error[%u]: ", error);
|
||||||
|
@ -178,6 +176,7 @@ void firmwareUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
updateInProgress = true;
|
updateInProgress = true;
|
||||||
|
fingerprints.setDisable(updateInProgress);
|
||||||
httpUpdate.setLedPin(LED_BUILTIN, LED_BUILTIN_ON);
|
httpUpdate.setLedPin(LED_BUILTIN, LED_BUILTIN_ON);
|
||||||
httpUpdate.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
|
httpUpdate.setFollowRedirects(HTTPC_FORCE_FOLLOW_REDIRECTS);
|
||||||
t_httpUpdate_return ret = httpUpdate.update(client, firmwareUrl);
|
t_httpUpdate_return ret = httpUpdate.update(client, firmwareUrl);
|
||||||
|
@ -240,25 +239,3 @@ void spiffsInit()
|
||||||
|
|
||||||
SPIFFS.begin(true);
|
SPIFFS.begin(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanupOldFingerprints()
|
|
||||||
{
|
|
||||||
if (fingerprints.size() <= MAX_MAC_ADDRESSES)
|
|
||||||
return;
|
|
||||||
|
|
||||||
long oldestTime = LONG_MAX;
|
|
||||||
BleFingerprint *oldest = nullptr;
|
|
||||||
for (auto it = fingerprints.begin(); it != fingerprints.end(); ++it)
|
|
||||||
{
|
|
||||||
long time = (*it)->getLastSeen();
|
|
||||||
if (time < oldestTime)
|
|
||||||
{
|
|
||||||
oldestTime = time;
|
|
||||||
oldest = (*it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (oldest == nullptr) return;
|
|
||||||
|
|
||||||
fingerprints.remove(oldest);
|
|
||||||
delete oldest;
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue