Fix in websocket newData message sending
This commit is contained in:
		
							parent
							
								
									0f7389744d
								
							
						
					
					
						commit
						94df778a5a
					
				|  | @ -75,14 +75,13 @@ private: | ||||||
|     void saveToFileSystem() |     void saveToFileSystem() | ||||||
|     { |     { | ||||||
|         static const uint32_t arrayHeaderOffset = ChunkT::arrayHeaderOffset(); |         static const uint32_t arrayHeaderOffset = ChunkT::arrayHeaderOffset(); | ||||||
|         Serial.printf(" --------  Array header offset ---- %u\n", arrayHeaderOffset); |  | ||||||
|         const uint32_t numMeasurements = chunk->numMeasurements(); |         const uint32_t numMeasurements = chunk->numMeasurements(); | ||||||
| 
 | 
 | ||||||
|         // todo: check this! free doesn't mean that the file writing actually works ok
 |         // todo: check this! free doesn't mean that the file writing actually works ok
 | ||||||
|         // use error codes of write instead? anyway: test it!
 |         // use error codes of write instead? anyway: test it!
 | ||||||
|         Serial.printf("%ld saveToFileSystem start()\n", millis()); |         Serial.printf("%ld saveToFileSystem start\n", millis()); | ||||||
|         deleteUntilBytesFree(CONFIG_SESSION_MAX_SIZE); |         deleteUntilBytesFree(CONFIG_SESSION_MAX_SIZE); | ||||||
|         Serial.printf("%ld after deleteUntilBytesFree()\n", millis()); |         Serial.printf("   %ld after deleteUntilBytesFree()\n", millis()); | ||||||
| 
 | 
 | ||||||
|         String filename = String(CONFIG_DATA_PATH) + "/" + String(chunk->getStartTime()); |         String filename = String(CONFIG_DATA_PATH) + "/" + String(chunk->getStartTime()); | ||||||
|         if (portablefs::exists(filename.c_str())) |         if (portablefs::exists(filename.c_str())) | ||||||
|  | @ -94,12 +93,12 @@ private: | ||||||
|             Measurement_T *startPtr = chunk->getDataPointer() + existingMeasurements; |             Measurement_T *startPtr = chunk->getDataPointer() + existingMeasurements; | ||||||
|             file.write((uint8_t *)(startPtr), measurementsToWrite * sizeof(Measurement_T)); |             file.write((uint8_t *)(startPtr), measurementsToWrite * sizeof(Measurement_T)); | ||||||
|              |              | ||||||
|             Serial.printf("%ld Incr Save: before header patch\n", millis()); |             Serial.printf("   %ld Incr Save: before header patch\n", millis()); | ||||||
|             file.seek(arrayHeaderOffset); |             file.seek(arrayHeaderOffset); | ||||||
| 
 | 
 | ||||||
|             StreamingMsgPackEncoder<portablefs::File> encoder(&file); |             StreamingMsgPackEncoder<portablefs::File> encoder(&file); | ||||||
|             encoder.template sendArrayHeader<Measurement_T>(numMeasurements); |             encoder.template sendArrayHeader<Measurement_T>(numMeasurements); | ||||||
|             Serial.printf("%ld total measurements up to now %u\n", millis(), numMeasurements); |             Serial.printf("   %ld total measurements up to now %u\n", millis(), numMeasurements); | ||||||
|         } |         } | ||||||
|         else |         else | ||||||
|         { |         { | ||||||
|  | @ -108,7 +107,7 @@ private: | ||||||
|             StreamingMsgPackEncoder<portablefs::File> encoder(&file); |             StreamingMsgPackEncoder<portablefs::File> encoder(&file); | ||||||
|             chunk->serialize(encoder); |             chunk->serialize(encoder); | ||||||
|         } |         } | ||||||
|         Serial.printf("%ld saveToFileSystem done-------------\n", millis()); |         Serial.printf("   %ld saveToFileSystem done\n", millis()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void deleteUntilBytesFree(size_t requiredSpace) |     void deleteUntilBytesFree(size_t requiredSpace) | ||||||
|  |  | ||||||
|  | @ -13,38 +13,38 @@ template <typename SessionT> | ||||||
| class WebsocketServer | class WebsocketServer | ||||||
| { | { | ||||||
| public: | public: | ||||||
|     WebsocketServer(SessionManager<SessionT> &sessionManager, int port) |   WebsocketServer(SessionManager<SessionT> &sessionManager, int port) | ||||||
|         : sessionManager_(sessionManager), nextFreeClient_(0), port_(port), |       : sessionManager_(sessionManager), nextFreeClient_(0), port_(port), | ||||||
|           numSentMeasurements_(0), running_(false) |         running_(false) | ||||||
|     { |   { | ||||||
|     } |   } | ||||||
| 
 | 
 | ||||||
|     void begin() |   void begin() | ||||||
|     { |   { | ||||||
|         server_.listen(port_); |     server_.listen(port_); | ||||||
|     } |   } | ||||||
| 
 | 
 | ||||||
|     void iteration(); |   void iteration(); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     void reportSessionUpdate(); |   void reportSessionUpdate(); | ||||||
| 
 | 
 | ||||||
|     void sendMessageOnConnection(websockets::WebsocketsClient &client); |   void sendMessageOnConnection(websockets::WebsocketsClient &client); | ||||||
|     void sendSessionStartMessages(); |   void sendSessionStartMessages(); | ||||||
|     void sendSessionStopMessages(); |   void sendSessionStopMessages(); | ||||||
|     void sendNewDataMessages(); |   void sendNewDataMessages(); | ||||||
| 
 | 
 | ||||||
|     SessionManager<SessionT> &sessionManager_; |   SessionManager<SessionT> &sessionManager_; | ||||||
|     int nextFreeClient_; |   int nextFreeClient_; | ||||||
|     int port_; |   int port_; | ||||||
| 
 | 
 | ||||||
|     size_t sentMessageCount_; |   size_t sentMessageCount_; | ||||||
|     websockets::WebsocketsServer server_; |   websockets::WebsocketsServer server_; | ||||||
|     websockets::WebsocketsClient clients_[MAX_CONNECTIONS]; |   websockets::WebsocketsClient clients_[MAX_CONNECTIONS]; | ||||||
| 
 | 
 | ||||||
|     // previous session state
 |   // previous session state
 | ||||||
|     size_t numSentMeasurements_; |   size_t numSentMeasurements_[MAX_CONNECTIONS]; | ||||||
|     bool running_; |   bool running_; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| using websockets::WebsocketsClient; | using websockets::WebsocketsClient; | ||||||
|  | @ -53,75 +53,81 @@ using websockets::WebsocketsClient; | ||||||
| 
 | 
 | ||||||
| enum MessageType | enum MessageType | ||||||
| { | { | ||||||
|     INITIAL_INFO = 1, |   // from swim tracker device to frontend
 | ||||||
|     SESSION_STARTED = 2, |   INITIAL_INFO = 1, | ||||||
|     SESSION_STOPPED = 3, |   SESSION_STARTED = 2, | ||||||
|     SESSION_NEW_DATA = 4 |   SESSION_STOPPED = 3, | ||||||
|  |   SESSION_NEW_DATA = 4, | ||||||
|  | 
 | ||||||
|  |   // from frontend to device
 | ||||||
|  |   START_SESSION = 5, | ||||||
|  |   STOP_SESSION = 6, | ||||||
|  |   TARE = 7 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| #pragma pack(push, 1) | #pragma pack(push, 1) | ||||||
| class SessionStartedMessage | class SessionStartedMessage | ||||||
| { | { | ||||||
| public: | public: | ||||||
|     SessionStartedMessage(uint32_t id) : messageType_(SESSION_STARTED), sessionId_(id) {} |   SessionStartedMessage(uint32_t id) : messageType_(SESSION_STARTED), sessionId_(id) {} | ||||||
|     void send(WebsocketsClient &c) const |   void send(WebsocketsClient &c) const | ||||||
|     { |   { | ||||||
|         c.sendBinary((const char *)(this), sizeof(*this)); |     c.sendBinary((const char *)(this), sizeof(*this)); | ||||||
|     } |   } | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     uint8_t messageType_; |   uint8_t messageType_; | ||||||
|     uint32_t sessionId_; |   uint32_t sessionId_; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class SessionStoppedMessage | class SessionStoppedMessage | ||||||
| { | { | ||||||
| public: | public: | ||||||
|     SessionStoppedMessage() : messageType_(SESSION_STOPPED) {} |   SessionStoppedMessage() : messageType_(SESSION_STOPPED) {} | ||||||
|     void send(WebsocketsClient &c) const |   void send(WebsocketsClient &c) const | ||||||
|     { |   { | ||||||
|         c.sendBinary((const char *)(this), sizeof(*this)); |     c.sendBinary((const char *)(this), sizeof(*this)); | ||||||
|     } |   } | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     uint8_t messageType_; |   uint8_t messageType_; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| template <typename MeasurementT> | template <typename MeasurementT> | ||||||
| class SessionNewDataMessage | class SessionNewDataMessage | ||||||
| { | { | ||||||
| public: | public: | ||||||
|     // typically a message contains NUM_DATA_CHUNK_SIZE measurements
 |   // typically a message contains NUM_DATA_CHUNK_SIZE measurements
 | ||||||
|     // if some measurements are skipped, because loop() takes too long
 |   // if some measurements are skipped, because loop() takes too long
 | ||||||
|     // there might actually be more measurements, to be safe there is an
 |   // there might actually be more measurements, to be safe there is an
 | ||||||
|     // additional factor here
 |   // additional factor here
 | ||||||
|     static constexpr size_t MAX_MEASUREMENTS = 4 * NUM_DATA_CHUNK_SIZE; |   static constexpr size_t MAX_MEASUREMENTS = 4 * NUM_DATA_CHUNK_SIZE; | ||||||
| 
 | 
 | ||||||
|     SessionNewDataMessage(MeasurementT *ptr, size_t numMeasurements) |   SessionNewDataMessage(MeasurementT *ptr, size_t numMeasurements) | ||||||
|         : messageType_(SESSION_NEW_DATA), numMeasurements_(min(numMeasurements, MAX_MEASUREMENTS)) |       : messageType_(SESSION_NEW_DATA), numMeasurements_(min(numMeasurements, MAX_MEASUREMENTS)) | ||||||
|     { |   { | ||||||
|         memcpy(measurements_, ptr, sizeof(MeasurementT) * numMeasurements_); |     memcpy(measurements_, ptr, sizeof(MeasurementT) * numMeasurements_); | ||||||
|     } |   } | ||||||
| 
 | 
 | ||||||
|     void send(WebsocketsClient &c) const |   void send(WebsocketsClient &c) const | ||||||
|     { |   { | ||||||
|         c.sendBinary((const char *)(this), numBytes()); |     c.sendBinary((const char *)(this), numBytes()); | ||||||
|     } |   } | ||||||
| 
 | 
 | ||||||
|     size_t numMeasurements() const |   size_t numMeasurements() const | ||||||
|     { |   { | ||||||
|         return numMeasurements_; |     return numMeasurements_; | ||||||
|     } |   } | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|     size_t numBytes() const { return sizeof(uint8_t) + numMeasurements() * sizeof(MeasurementT); } |   size_t numBytes() const { return sizeof(uint8_t) + numMeasurements() * sizeof(MeasurementT); } | ||||||
| 
 | 
 | ||||||
|     // data to be sent
 |   // data to be sent
 | ||||||
|     uint8_t messageType_; |   uint8_t messageType_; | ||||||
|     MeasurementT measurements_[MAX_MEASUREMENTS]; |   MeasurementT measurements_[MAX_MEASUREMENTS]; | ||||||
| 
 | 
 | ||||||
|     // book-keeping
 |   // book-keeping
 | ||||||
|     size_t numMeasurements_; |   size_t numMeasurements_; | ||||||
| }; | }; | ||||||
| #pragma pack(pop) | #pragma pack(pop) | ||||||
| 
 | 
 | ||||||
|  | @ -130,108 +136,150 @@ private: | ||||||
| template <typename SessionT> | template <typename SessionT> | ||||||
| void WebsocketServer<SessionT>::iteration() | void WebsocketServer<SessionT>::iteration() | ||||||
| { | { | ||||||
|     if (server_.poll()) |   using namespace websockets; | ||||||
|  |   auto onMessage = [this](WebsocketsClient &client, WebsocketsMessage message) { | ||||||
|  |     if (message.isPing()) | ||||||
|  |       client.pong(); | ||||||
|  |     else if (message.isBinary()) | ||||||
|     { |     { | ||||||
|         clients_[nextFreeClient_] = server_.accept(); |       const char *data = message.c_str(); | ||||||
|         //clients_[nextFreeClient_].onMessage(onMessage); // TODO
 |       const size_t length = message.length(); | ||||||
|         Serial.println("new websocket connection"); |       if (length < 1) | ||||||
|         sendMessageOnConnection(clients_[nextFreeClient_]); |       { | ||||||
|         nextFreeClient_ = (nextFreeClient_ + 1) % MAX_CONNECTIONS; |         client.close(CloseReason_UnsupportedData); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       uint8_t opCode = uint8_t(data[0]); | ||||||
|  |       switch (opCode) | ||||||
|  |       { | ||||||
|  |       case START_SESSION: | ||||||
|  |         this->sessionManager_.startMeasurements(); | ||||||
|  |         break; | ||||||
|  |       case STOP_SESSION: | ||||||
|  |         this->sessionManager_.stopMeasurements(); | ||||||
|  |         break; | ||||||
|  |       case TARE: | ||||||
|  |         this->sessionManager_.tare(); | ||||||
|  |         break; | ||||||
|  |       default: | ||||||
|  |         client.close(CloseReason_UnsupportedData); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|  |   }; | ||||||
| 
 | 
 | ||||||
|     for (int i = 0; i < MAX_CONNECTIONS; ++i) |   if (server_.poll()) | ||||||
|         clients_[i].poll(); |   { | ||||||
|  |     clients_[nextFreeClient_] = server_.accept(); | ||||||
|  |     clients_[nextFreeClient_].onMessage(onMessage); | ||||||
|  |     Serial.println("new websocket connection"); | ||||||
|  |     sendMessageOnConnection(clients_[nextFreeClient_]); | ||||||
|  |     numSentMeasurements_[nextFreeClient_] = sessionManager_.session().numMeasurements(); | ||||||
|  |     nextFreeClient_ = (nextFreeClient_ + 1) % MAX_CONNECTIONS; | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|     reportSessionUpdate(); |   for (int i = 0; i < MAX_CONNECTIONS; ++i) | ||||||
|  |     clients_[i].poll(); | ||||||
|  | 
 | ||||||
|  |   reportSessionUpdate(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename SessionT> | template <typename SessionT> | ||||||
| void WebsocketServer<SessionT>::reportSessionUpdate() | void WebsocketServer<SessionT>::reportSessionUpdate() | ||||||
| { | { | ||||||
|     auto &session = sessionManager_.session(); |   if (!running_ && sessionManager_.isMeasuring()) | ||||||
|  |   { | ||||||
|  |     sendSessionStartMessages(); | ||||||
|  |     for (int i = 0; i < MAX_CONNECTIONS; ++i) | ||||||
|  |       numSentMeasurements_[i] = 0; | ||||||
|  |   } | ||||||
|  |   else if (running_ && !sessionManager_.isMeasuring()) | ||||||
|  |   { | ||||||
|  |     sendSessionStopMessages(); | ||||||
|  |     for (int i = 0; i < MAX_CONNECTIONS; ++i) | ||||||
|  |       numSentMeasurements_[i] = 0; | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|     // start/stop messages
 |   sendNewDataMessages(); | ||||||
|     if (!running_ && sessionManager_.isMeasuring()) |  | ||||||
|         sendSessionStartMessages(); |  | ||||||
|     else if (running_ && !sessionManager_.isMeasuring()) |  | ||||||
|         sendSessionStopMessages(); |  | ||||||
| 
 |  | ||||||
|     // new data
 |  | ||||||
|     if (session.numMeasurements() - (NUM_DATA_CHUNK_SIZE - 1) > numSentMeasurements_) |  | ||||||
|     { |  | ||||||
|         sendNewDataMessages(); |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename SessionT> | template <typename SessionT> | ||||||
| void WebsocketServer<SessionT>::sendSessionStartMessages() | void WebsocketServer<SessionT>::sendSessionStartMessages() | ||||||
| { | { | ||||||
|     SessionStartedMessage msg(sessionManager_.session().getStartTime()); |   SessionStartedMessage msg(sessionManager_.session().getStartTime()); | ||||||
|     for (auto &c : clients_) |   for (auto &c : clients_) | ||||||
|         if (c.available()) |     if (c.available()) | ||||||
|             msg.send(c); |       msg.send(c); | ||||||
|     running_ = sessionManager_.isMeasuring(); |   running_ = sessionManager_.isMeasuring(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename SessionT> | template <typename SessionT> | ||||||
| void WebsocketServer<SessionT>::sendSessionStopMessages() | void WebsocketServer<SessionT>::sendSessionStopMessages() | ||||||
| { | { | ||||||
|     SessionStoppedMessage msg; |   SessionStoppedMessage msg; | ||||||
|     for (auto &c : clients_) |   for (auto &c : clients_) | ||||||
|         if (c.available()) |     if (c.available()) | ||||||
|             msg.send(c); |       msg.send(c); | ||||||
|     running_ = sessionManager_.isMeasuring(); |   running_ = sessionManager_.isMeasuring(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename SessionT> | template <typename SessionT> | ||||||
| void WebsocketServer<SessionT>::sendNewDataMessages() | void WebsocketServer<SessionT>::sendNewDataMessages() | ||||||
| { | { | ||||||
|     using MeasurementT = typename SessionT::MeasurementType; |   using MeasurementT = typename SessionT::MeasurementType; | ||||||
|     auto &session = sessionManager_.session(); |   auto &session = sessionManager_.session(); | ||||||
|     MeasurementT *dataToSend = session.getDataPointer() + numSentMeasurements_; |  | ||||||
|     size_t numMeasurementsToSend = session.numMeasurements() - numSentMeasurements_; |  | ||||||
|     SessionNewDataMessage<MeasurementT> msg(dataToSend, numMeasurementsToSend); |  | ||||||
| 
 | 
 | ||||||
|     for (auto &c : clients_) |   for (int i = 0; i < MAX_CONNECTIONS; ++i) | ||||||
|         if (c.available()) |   { | ||||||
|             msg.send(c); |     auto &c = clients_[i]; | ||||||
| 
 |     if (c.available()) | ||||||
|     numSentMeasurements_ += msg.numMeasurements(); |     { | ||||||
|  |       MeasurementT *dataToSend = session.getDataPointer() + numSentMeasurements_[i]; | ||||||
|  |       int32_t numMeasurementsToSend = int32_t(session.numMeasurements()) - int32_t(numSentMeasurements_[i]); | ||||||
|  |       if (numMeasurementsToSend > 0) | ||||||
|  |       { | ||||||
|  |         SessionNewDataMessage<MeasurementT> msg(dataToSend, numMeasurementsToSend); | ||||||
|  |         msg.send(c); | ||||||
|  |         numSentMeasurements_[i] += msg.numMeasurements(); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| template <typename SessionT> | template <typename SessionT> | ||||||
| void WebsocketServer<SessionT>::sendMessageOnConnection(WebsocketsClient &client) | void WebsocketServer<SessionT>::sendMessageOnConnection(WebsocketsClient &client) | ||||||
| { | { | ||||||
|     using MeasurementT = typename SessionT::MeasurementType; |   using MeasurementT = typename SessionT::MeasurementType; | ||||||
| 
 | 
 | ||||||
|     // Message format:
 |   // Message format:
 | ||||||
|     //  - uint8_t messageType
 |   //  - uint8_t messageType
 | ||||||
|     //  - uint8_t running
 |   //  - uint8_t running
 | ||||||
|     //  - uint32_t sessionId
 |   //  - uint32_t sessionId
 | ||||||
|     //  - MeasurementT [] measurements (if running)
 |   //  - MeasurementT [] measurements (if running)
 | ||||||
| 
 | 
 | ||||||
|     auto &session = sessionManager_.session(); |   auto &session = sessionManager_.session(); | ||||||
|     const auto numMeasurements = session.numMeasurements(); |   const auto numMeasurements = session.numMeasurements(); | ||||||
|     const auto sessionId = session.getStartTime(); |   const auto sessionId = session.getStartTime(); | ||||||
| 
 | 
 | ||||||
|     const size_t msgSize = sizeof(uint8_t) + sizeof(uint8_t) + sizeof(sessionId) + sizeof(MeasurementT) * numMeasurements; |   const size_t msgSize = sizeof(uint8_t) + sizeof(uint8_t) + sizeof(sessionId) + sizeof(MeasurementT) * numMeasurements; | ||||||
|     char *msg = (char *)heap_caps_malloc(msgSize, MALLOC_CAP_SPIRAM); |   char *msg = (char *)heap_caps_malloc(msgSize, MALLOC_CAP_SPIRAM); | ||||||
| 
 | 
 | ||||||
|     char *writeHead = msg; |   char *writeHead = msg; | ||||||
| 
 | 
 | ||||||
|     *writeHead = INITIAL_INFO; |   *writeHead = INITIAL_INFO; | ||||||
|     writeHead += sizeof(uint8_t); |   writeHead += sizeof(uint8_t); | ||||||
| 
 | 
 | ||||||
|     *writeHead = sessionManager_.isMeasuring(); |   *writeHead = sessionManager_.isMeasuring(); | ||||||
|     writeHead += sizeof(uint8_t); |   writeHead += sizeof(uint8_t); | ||||||
| 
 | 
 | ||||||
|     *((uint32_t *)writeHead) = sessionManager_.isMeasuring() ? sessionId : 0; |   *((uint32_t *)writeHead) = sessionManager_.isMeasuring() ? sessionId : 0; | ||||||
|     writeHead += sizeof(uint32_t); |   writeHead += sizeof(uint32_t); | ||||||
| 
 | 
 | ||||||
|     assert(writeHead - msg == msgSize - sizeof(MeasurementT) * numMeasurements); |   assert(writeHead - msg == msgSize - sizeof(MeasurementT) * numMeasurements); | ||||||
| 
 | 
 | ||||||
|     memcpy(writeHead, session.getDataPointer(), sizeof(MeasurementT) * numMeasurements); |   memcpy(writeHead, session.getDataPointer(), sizeof(MeasurementT) * numMeasurements); | ||||||
|     client.sendBinary(msg, msgSize); |   client.sendBinary(msg, msgSize); | ||||||
| 
 | 
 | ||||||
|     free(msg); |   free(msg); | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue