First untested version with user storage

This commit is contained in:
Martin Bauer
2021-05-16 13:45:00 +02:00
parent 06fdda1d93
commit 5984a233af
14 changed files with 174 additions and 49 deletions

View File

@@ -7,13 +7,13 @@ template <class ForwardIterator, class T>
ForwardIterator lower_bound(ForwardIterator first, ForwardIterator last, const T &val)
{
ForwardIterator it;
iterator_traits<ForwardIterator>::difference_type count, step;
count = distance(first, last);
int count, step;
count = last - first;
while (count > 0)
{
it = first;
step = count / 2;
advance(it, step);
it += step;
if (*it < val)
{
first = ++it;
@@ -30,11 +30,11 @@ static String userFileName(const String &userName)
return userDir + userName;
}
bool User::load(const String &name)
bool User::load(const String &stringId)
{
name_ = name;
assert(name_.length() > 0);
const auto fileName = userFileName(name_);
stringId_ = stringId;
assert(stringId_.length() > 0);
const auto fileName = userFileName(stringId_);
if (!portablefs::exists(fileName.c_str()))
return false;
@@ -43,7 +43,7 @@ bool User::load(const String &name)
size_t sessionsInFile;
file.read((uint8_t *)&sessionsInFile, sizeof(numSessions_));
init(name, sessionsInFile * 2);
init(stringId, sessionsInFile * 2);
size_t expectedSize = sizeof(SessionIdType) * numSessions_;
auto bytesRead = file.read((uint8_t *)sessionIds_, expectedSize);
@@ -52,14 +52,14 @@ bool User::load(const String &name)
assert(expectedSize == bytesRead);
}
void User::init(const String &name, size_t sessionAllocateSize)
void User::init(const String &stringId, size_t sessionAllocateSize)
{
if (sessionIds_ != nullptr)
{
free(sessionIds_);
sessionIds_ = nullptr;
}
name_ = name;
stringId_ = stringId;
numSessionsAllocated_ = sessionAllocateSize;
sessionIds_ = (SessionIdType *)ps_malloc(sizeof(SessionIdType) * numSessionsAllocated_);
numSessions_ = 0;
@@ -70,7 +70,7 @@ void User::save()
if (!portablefs::exists(userDir.c_str()))
portablefs::mkdir(userDir.c_str());
auto file = portablefs::open(userFileName(name_).c_str(), "w");
auto file = portablefs::open(userFileName(stringId_).c_str(), "w");
file.write((uint8_t *)&numSessions_, sizeof(numSessions_));
file.write((uint8_t *)sessionIds_, sizeof(SessionIdType) * numSessions_);
}
@@ -86,9 +86,9 @@ void User::freeResources()
void User::remove()
{
portablefs::remove(userFileName(name_).c_str());
portablefs::remove(userFileName(stringId_).c_str());
freeResources();
name_ = "";
stringId_ = "";
numSessions_ = 0;
numSessionsAllocated_ = 0;
}
@@ -134,11 +134,11 @@ bool User::removeSession(SessionIdType sessionIdToRemove)
// --------------------------------------------------------------------------------------------------------------------
User *UserStorage::getUserInfo(const String &userName)
User *UserStorage::getUserInfo(const String &stringId)
{
// index 0 is the unassigned user
for (size_t i = 1; i < numUsers_; ++i)
if (users_[i].name() == userName)
if (users_[i].stringId() == stringId)
return &users_[i];
return nullptr;
}
@@ -148,20 +148,23 @@ User *UserStorage::getUnassignedUser()
return &users_[0];
}
User *UserStorage::addNewUser(const String &userName)
User *UserStorage::addNewUser(const String &stringId)
{
if (numUsers_ >= MAX_USERS)
return nullptr;
if(stringId.length() >= USER_STRING_ID_MAX_LEN)
return nullptr;
auto userIdx = numUsers_;
numUsers_++;
assert(numUsers_ < MAX_USERS);
users_[userIdx].init(userName);
users_[userIdx].init(stringId);
}
bool UserStorage::deleteUser(const String &userName)
bool UserStorage::deleteUser(const String &stringId)
{
User *userPtr = getUserInfo(userName);
User *userPtr = getUserInfo(stringId);
if (userPtr == nullptr)
return false;

View File

@@ -5,13 +5,14 @@
using SessionIdType = uint32_t;
constexpr size_t MAX_USERS = 64;
constexpr size_t INITIAL_SESSIONS_PER_USER = 128;
constexpr size_t USER_STRING_ID_MAX_LEN = 63;
struct User
{
public:
User() : numSessions_(0), numSessionsAllocated_(0), sessionIds_(nullptr) {}
void init(const String &name, size_t sessionAllocateSize = INITIAL_SESSIONS_PER_USER);
void init(const String &stringId_, size_t sessionAllocateSize = INITIAL_SESSIONS_PER_USER);
void freeResources();
bool load(const String &name);
@@ -24,19 +25,19 @@ public:
bool hasSession(SessionIdType sessionId) const;
const String &name() const { return name_; }
const String &stringId() const { return stringId_; }
// session access
SessionIdType *sessionBegin() { return sessionIds_; }
SessionIdType *sessionEnd() { return sessionIds_ + numSessions_; }
size_t numSessions() const { return numSessions_; }
const SessionIdType *sessionBegin() const { return sessionIds_; }
const SessionIdType *sessionEnd() const { return sessionIds_ + numSessions_; }
private:
void growSessionArrayIfNecessary();
String name_;
String stringId_;
size_t numSessions_;
size_t numSessionsAllocated_;
SessionIdType *sessionIds_;
@@ -48,20 +49,29 @@ public:
UserStorage()
: numUsers_(0)
{
}
void init()
{
numUsers_ = 0;
fillFromFileSystem();
}
User *getUserInfo(const String &userName);
User *getUserInfo(const String &stringId);
User *getUnassignedUser();
User *addNewUser(const String &userName);
bool deleteUser(const String &userName);
User *addNewUser(const String &stringId);
bool deleteUser(const String &stringId);
User *begin() { return &users_[0]; }
User *beginWithoutUnassigned() { return &users_[1]; }
User *end() { return &users_[numUsers_]; }
const User *begin() const { return &users_[0]; }
const User *end() const { return &users_[numUsers_]; }
size_t numUsers() const { return numUsers_; }
private:
void fillFromFileSystem();

View File

@@ -13,8 +13,8 @@ data_dir = data
[env:esp32]
platform = espressif32
platform_packages =
framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32
#platform_packages =
# framework-arduinoespressif32@https://github.com/espressif/arduino-esp32
board = esp-wrover-kit
#platform = espressif8266
#board = esp_wroom_02

View File

@@ -1,6 +1,7 @@
#pragma once
#include "Dtypes.h"
#include "UserDB.h"
#include <ArduinoWebsockets.h>
template <typename T>
@@ -13,8 +14,8 @@ template <typename SessionT>
class WebsocketServer
{
public:
WebsocketServer(SessionManager<SessionT> &sessionManager, int port)
: sessionManager_(sessionManager), nextFreeClient_(0), port_(port),
WebsocketServer(SessionManager<SessionT> &sessionManager, UserStorage &userStorage, int port)
: sessionManager_(sessionManager), userStorage_(userStorage), nextFreeClient_(0), port_(port),
running_(false)
{
}
@@ -34,7 +35,12 @@ private:
void sendSessionStopMessages();
void sendNewDataMessages();
void sendUserList(websockets::WebsocketsClient &client);
void sendSessionList(websockets::WebsocketsClient &client, const String &userId);
SessionManager<SessionT> &sessionManager_;
UserStorage &userStorage_;
int nextFreeClient_;
int port_;
@@ -58,11 +64,15 @@ enum MessageType
SESSION_STARTED = 2,
SESSION_STOPPED = 3,
SESSION_NEW_DATA = 4,
ANSWER_USER_LIST = 5,
ANSWER_SESSION_LIST = 6,
// from frontend to device
START_SESSION = 5,
STOP_SESSION = 6,
TARE = 7
START_SESSION = 7,
STOP_SESSION = 8,
TARE = 9,
QUERY_USER_LIST = 10,
QUERY_SESSION_LIST = 11,
};
#pragma pack(push, 1)
@@ -129,6 +139,7 @@ private:
// book-keeping
size_t numMeasurements_;
};
#pragma pack(pop)
// ------------------------------------- WebsocketServer members ---------------------------
@@ -137,6 +148,7 @@ template <typename SessionT>
void WebsocketServer<SessionT>::iteration()
{
using namespace websockets;
auto onMessage = [this](WebsocketsClient &client, WebsocketsMessage message) {
if (message.isPing())
client.pong();
@@ -162,6 +174,18 @@ void WebsocketServer<SessionT>::iteration()
case TARE:
this->sessionManager_.tare();
break;
case QUERY_USER_LIST:
this->sendUserList(client);
break;
case QUERY_SESSION_LIST:
{
StaticJsonDocument<USER_STRING_ID_MAX_LEN + 16> doc;
deserializeMsgPack(doc, data, length);
String userId = doc.as<String>();
if (userId.length() > 0)
this->sendSessionList(client, userId);
}
break;
default:
client.close(CloseReason_UnsupportedData);
return;
@@ -200,10 +224,53 @@ void WebsocketServer<SessionT>::reportSessionUpdate()
for (int i = 0; i < MAX_CONNECTIONS; ++i)
numSentMeasurements_[i] = 0;
}
sendNewDataMessages();
}
template <typename SessionT>
void WebsocketServer<SessionT>::sendUserList(websockets::WebsocketsClient &client)
{
const auto numUsers = userStorage_.numUsers();
constexpr size_t constantSlack = 64;
DynamicJsonDocument result(JSON_ARRAY_SIZE(numUsers) + numUsers * (USER_STRING_ID_MAX_LEN + 2) + constantSlack);
JsonArray arr = result.to<JsonArray>();
for (auto userIt = userStorage_.beginWithoutUnassigned(); userIt != userStorage_.end(); ++userIt)
arr.add(userIt->stringId());
char buffer[MAX_USERS * (USER_STRING_ID_MAX_LEN + 1) + constantSlack];
size_t bytesWritten = serializeMsgPack(result, buffer, sizeof(buffer));
client.sendBinary(buffer, bytesWritten);
}
template <typename SessionT>
void WebsocketServer<SessionT>::sendSessionList(websockets::WebsocketsClient &client, const String &userId)
{
User *user = userStorage_.getUserInfo(userId);
if (user != nullptr)
{
DynamicJsonDocument result(JSON_ARRAY_SIZE(user->numSessions()) + user->numSessions() * (sizeof(SessionIdType) + 8));
JsonArray arr = result.to<JsonArray>();
for (SessionIdType *sIt = user->sessionBegin(); sIt != user->sessionEnd(); ++sIt)
arr.add(*sIt);
size_t bytesToWrite = measureMsgPack(result);
char *buffer = (char *)malloc(bytesToWrite);
size_t bytesWritten = serializeMsgPack(result, buffer, bytesToWrite);
assert(bytesWritten <= bytesToWrite);
client.sendBinary(buffer, bytesWritten);
free(buffer);
}
else
{
DynamicJsonDocument result(JSON_ARRAY_SIZE(1) + 8);
result.to<JsonArray>();
char buffer[32];
size_t bytesWritten = serializeMsgPack(result, buffer, sizeof(buffer));
client.sendBinary(buffer, bytesWritten);
}
}
template <typename SessionT>
void WebsocketServer<SessionT>::sendSessionStartMessages()
{

View File

@@ -7,6 +7,7 @@
#include <HTTPClient.h>
#include "esp_https_ota.h"
#include "esp_ota_ops.h"
#include "ESPmDNS.h"
// Own libs
@@ -20,12 +21,15 @@
#include "EspHttp.h"
#include "WebDAV.h"
#include "WebsocketServer.h"
#include "UserDB.h"
using Session_T = SimpleMeasurementSession<MeasurementT, CONFIG_SESSION_MAX_SIZE>;
SessionManager<Session_T> sessionManager;
UserStorage userStorage;
EspHttp espHttpServer;
WebsocketServer<Session_T> webSocketServer(sessionManager, 81);
WebsocketServer<Session_T> webSocketServer(sessionManager, userStorage, 81);
WifiManager wifiManager;
@@ -253,14 +257,14 @@ void httpSetup(SessionManager<SessionT> *sessionManager, WifiManager *wifiManage
prefs.begin("st_prefs");
json["valueRightShift"] = prefs.getInt("valueRightShift", CONFIG_VALUE_RIGHT_SHIFT);
json["tareAvgCount"] = prefs.getUInt("tareAvgCount", CONFIG_TARE_AVG_COUNT);
json["tareAvgCount"] = prefs.getUInt("tareAvgCount", CONFIG_TARE_AVG_COUNT);
json["autoStartMinThreshold"] = prefs.getUInt("aStartMinTh", CONFIG_AUTO_START_MIN_THRESHOLD);
json["autoStartMaxThreshold"] = prefs.getUInt("aStartMaxTh", CONFIG_AUTO_START_MAX_THRESHOLD);
json["autoStartMaxMeasurementsBetweenPeaks"] = prefs.getUInt("aStartCount", CONFIG_AUTO_START_MAX_MEASUREMENTS_BETWEEN_PEAKS);
json["autoStopThreshold"] = prefs.getUInt("aStopTh", CONFIG_AUTO_STOP_THRESHOLD);
json["autoStopNumMeasurements"] = prefs.getUInt("aStopCount", CONFIG_AUTO_STOP_NUM_MEASUREMENTS);
json["autoStartEnabled"] = prefs.getBool("aStartEnabled", true);
json["autoStopEnabled"] = prefs.getBool("aStopEnabled", true);
json["autoStartEnabled"] = prefs.getBool("aStartEnabled", true);
json["autoStopEnabled"] = prefs.getBool("aStopEnabled", true);
char jsonText[1024];
auto bytesWritten = serializeJson(json, jsonText);
httpd_resp_send(req, jsonText, bytesWritten);
@@ -287,25 +291,25 @@ void httpSetup(SessionManager<SessionT> *sessionManager, WifiManager *wifiManage
Preferences prefs;
prefs.begin("st_prefs");
if(json.containsKey("valueRightShift"))
if (json.containsKey("valueRightShift"))
prefs.putInt("valueRightShift", json["valueRightShift"].as<int>());
if(json.containsKey("tareAvgCount"))
if (json.containsKey("tareAvgCount"))
prefs.putUInt("tareAvgCount", json["tareAvgCount"].as<unsigned int>());
if(json.containsKey("autoStartMinThreshold"))
if (json.containsKey("autoStartMinThreshold"))
prefs.putUInt("aStartMinTh", json["autoStartMinThreshold"].as<unsigned int>());
if(json.containsKey("autoStartMaxThreshold"))
if (json.containsKey("autoStartMaxThreshold"))
prefs.putUInt("aStartMaxTh", json["autoStartMaxThreshold"].as<unsigned int>());
if(json.containsKey("autoStartMaxMeasurementsBetweenPeaks"))
if (json.containsKey("autoStartMaxMeasurementsBetweenPeaks"))
prefs.putUInt("aStartCount", json["autoStartMaxMeasurementsBetweenPeaks"].as<unsigned int>());
if(json.containsKey("autoStopThreshold"))
if (json.containsKey("autoStopThreshold"))
prefs.putUInt("aStopTh", json["autoStopThreshold"].as<unsigned int>());
if(json.containsKey("autoStopNumMeasurements"))
if (json.containsKey("autoStopNumMeasurements"))
prefs.putUInt("aStopCount", json["autoStopNumMeasurements"].as<unsigned int>());
if(json.containsKey("autoStartEnabled"))
if (json.containsKey("autoStartEnabled"))
prefs.putBool("aStartEnabled", json["autoStartEnabled"].as<bool>());
if(json.containsKey("autoStopEnabled"))
if (json.containsKey("autoStopEnabled"))
prefs.putBool("aStopEnabled", json["autoStopEnabled"].as<bool>());
sessionManagerSetup();
httpd_resp_send(req, "OK", -1);
};
@@ -399,6 +403,8 @@ void setup()
ESP_ERROR_CHECK(esp_event_loop_create_default());
userStorage.init();
// WiFi
String fullHostname = String(CONFIG_HOSTNAME) + getIdSuffix();
wifiManager.begin(fullHostname);