Branching from Cathaswatch project
https://git.nerdyssey.de/exodus/1NiM-WatingRoom-WatchController
This commit is contained in:
@@ -0,0 +1 @@
|
||||
https://github.com/typester/esp32-uuid/tree/master
|
||||
@@ -0,0 +1,7 @@
|
||||
Copyright 2019 Daisuke Murase (typester)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,84 @@
|
||||
#include "uuid.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_system.h"
|
||||
#include "esp_random.h"
|
||||
|
||||
void uuid_generate(uuid_t out)
|
||||
{
|
||||
esp_fill_random(out, sizeof(uuid_t));
|
||||
|
||||
/* uuid version */
|
||||
out[6] = 0x40 | (out[6] & 0xF);
|
||||
|
||||
/* uuid variant */
|
||||
out[8] = (0x80 | out[8]) & ~0x40;
|
||||
}
|
||||
|
||||
static uint8_t unhex_char(unsigned char s)
|
||||
{
|
||||
if (0x30 <= s && s <= 0x39) { /* 0-9 */
|
||||
return s - 0x30;
|
||||
} else if (0x41 <= s && s <= 0x46) { /* A-F */
|
||||
return s - 0x41 + 0xa;
|
||||
} else if (0x61 <= s && s <= 0x69) { /* a-f */
|
||||
return s - 0x61 + 0xa;
|
||||
} else {
|
||||
/* invalid string */
|
||||
return 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
static int unhex(unsigned char *s, size_t s_len, unsigned char *r)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s_len; i += 2) {
|
||||
uint8_t h = unhex_char(s[i]);
|
||||
uint8_t l = unhex_char(s[i + 1]);
|
||||
if (0xff == h || 0xff == l) {
|
||||
return -1;
|
||||
}
|
||||
r[i / 2] = (h << 4) | (l & 0xf);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uuid_parse(const char *in, uuid_t uu)
|
||||
{
|
||||
const char *p = in;
|
||||
uint8_t *op = (uint8_t *)uu;
|
||||
|
||||
if (0 != unhex((unsigned char *)p, 8, op)) {
|
||||
return -1;
|
||||
}
|
||||
p += 8;
|
||||
op += 4;
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if ('-' != *p++ || 0 != unhex((unsigned char *)p, 4, op)) {
|
||||
return -1;
|
||||
}
|
||||
p += 4;
|
||||
op += 2;
|
||||
}
|
||||
|
||||
if ('-' != *p++ || 0 != unhex((unsigned char *)p, 12, op)) {
|
||||
return -1;
|
||||
}
|
||||
p += 12;
|
||||
op += 6;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uuid_unparse(const uuid_t uu, char *out)
|
||||
{
|
||||
snprintf(out, UUID_STR_LEN,
|
||||
"%02x%02x%02x%02x-%02x%02x-%02x%02x-"
|
||||
"%02x%02x-%02x%02x%02x%02x%02x%02x",
|
||||
uu[0], uu[1], uu[2], uu[3], uu[4], uu[5], uu[6], uu[7], uu[8], uu[9], uu[10], uu[11],
|
||||
uu[12], uu[13], uu[14], uu[15]);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint8_t uuid_t[16];
|
||||
|
||||
#define UUID_STR_LEN 37
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void uuid_generate(uuid_t out);
|
||||
int uuid_parse(const char *in, uuid_t uu);
|
||||
void uuid_unparse(const uuid_t uu, char *out);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,650 @@
|
||||
|
||||
#include "spit_client.hpp"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "Queue.hpp"
|
||||
|
||||
namespace spit {
|
||||
static const char *TAG = "SPIT";
|
||||
static fos::Queue<vector<char>> incommingSpit(128);
|
||||
|
||||
|
||||
/**
|
||||
* Create a new SPIT_client instance, should be done once by the user app.
|
||||
* @param p_SPITClientInterface the user app should implement the SPIT_ClientInterface.
|
||||
*/
|
||||
SClient::SClient(string name, string projName, ISocket &socket, Callbacks calls)
|
||||
: ISadClient(),
|
||||
mCallbacks(calls),
|
||||
mProcessor(name, projName, getClientHostName(), createGUID(), calls,
|
||||
{
|
||||
.delay = [&](int ms){ return fos::Task::delayMs(ms); },
|
||||
.sendMessage = [&](Message::OPPart &msg){ return sendMessage(msg); },
|
||||
.parseMessage = [&](Message& msg) { parseMessage(msg); }
|
||||
}
|
||||
),
|
||||
mSocket(socket) {
|
||||
}
|
||||
|
||||
SClient::~SClient() {
|
||||
stopServerAutoDetect();
|
||||
disconnectInternal(Connection::None);
|
||||
}
|
||||
|
||||
void SClient::clientProjectClose() {
|
||||
if (mConnectionStatus == Connection::Ok) {
|
||||
ESP_LOGI(TAG, " ... tidy up ressources");
|
||||
Message::CloseProject closeSPITProject(mProcessor.getProjName());
|
||||
if (sendMessage(closeSPITProject) < 0) {
|
||||
ESP_LOGD(TAG, "Client could not send Client's ReportProject");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SClient::clientProjectLoaded() {
|
||||
// set confirmed = false for all SPITTypes and SPITObjects
|
||||
mProcessor.resetConfirmedFlags();
|
||||
if (mConnectionStatus == Connection::Ok) {
|
||||
// send ReportProject to the server
|
||||
Message::ReportProject reportSPITProject(mProcessor.getProjName(), mProcessor.getProjName());
|
||||
reportSPITProject.setFramesPerSecond(0);
|
||||
if (sendMessage(reportSPITProject) < 0) {
|
||||
ESP_LOGD(TAG, "SPIT_CLient::clientProjectLoaded could not send Client's ReportProject");
|
||||
}
|
||||
|
||||
// send RequestProjectTransfer
|
||||
Message::RequestProjectTransfer sMRequestProjectTransfer;
|
||||
if (sendMessage(sMRequestProjectTransfer) < 0) {
|
||||
ESP_LOGD(TAG, "SPIT_CLient::clientProjectLoaded could not send RequestProjectTransfer");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------- ServerAutoDetection SAD -----------
|
||||
|
||||
/** Starts the autodetection of SPITServers in the local network. If a connection is tried, the autodetection should be stopped before.
|
||||
* If a SPITServer is detected in the locale network an eventServerDetected occurs.
|
||||
*/
|
||||
void SClient::startServerAutoDetect() {
|
||||
if (mDisconnectedByServer == true) {
|
||||
// Server has previously disconnected -> do not start ServerAutodetect directly
|
||||
fos::Task::delayMs(1000); // sleep a little to give the SPITServer time to clean up its stuff
|
||||
}
|
||||
SadDetectedServer::clearDetectedServers();
|
||||
if (!sThread) {
|
||||
sThread = new fos::TaskT<SClient &>(SClient::processSocketTask, *this, "spitSock", fos::Task::DefaultStackSize * 2);
|
||||
}
|
||||
mAbortSearch = false;
|
||||
sStartSad.give();
|
||||
}
|
||||
|
||||
/** Stops the auto detection of SPIT servers. */
|
||||
void SClient::stopServerAutoDetect() {
|
||||
mAbortSearch = true;
|
||||
}
|
||||
/**
|
||||
* If a new server was found by the ServerAutoDetect mechanism, this function is called.
|
||||
* To do so the SAD_Client ServerAutoDetect has to be startet - see SAD_Client::startServerAutoDetect.
|
||||
* If a server can be reached via several IP addresses, the IP address is selected according to the following preference:
|
||||
*/
|
||||
void SClient::eventServerDetected(SadDetectedServer &server) {
|
||||
mCallbacks.onServerEvent(ServerEvent::Detect, server);
|
||||
// If a connection has been established and the connection is lost -> reconnect
|
||||
if (_doReconnect) {
|
||||
if (mServerID == server.getID()) {
|
||||
stopServerAutoDetect();
|
||||
ESP_LOGI(TAG, "reconnect to %s / %s", server.getIP().data(), server.getPort().data());
|
||||
if (connect(server.getIP(), server.getPort(), server.getClientIP(), "0")) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Stop auto detection because a suitable server was found
|
||||
stopServerAutoDetect();
|
||||
ESP_LOGI(TAG, "connect to %s / %s", server.getIP().data(), server.getPort().data());
|
||||
if (!connect(server.getIP(), server.getPort(), server.getClientIP(), "0" /*= any port*/)) {
|
||||
// connection message could not be sent -> better to restart AutoDetection
|
||||
startServerAutoDetect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If a SPITServer is not more found, this function is called.
|
||||
* To do so the SAD_Client ServerAutoDetect has to be startet - see SAD_Client::startServerAutoDetect.
|
||||
*/
|
||||
void SClient::eventServerLost(SadDetectedServer &server) {
|
||||
mCallbacks.onServerEvent(ServerEvent::Lost, server);
|
||||
}
|
||||
|
||||
void SClient::eventServerChanged(SadDetectedServer &server) {
|
||||
mCallbacks.onServerEvent(ServerEvent::Changed, server);
|
||||
// tryConnection(detectedServer);
|
||||
}
|
||||
|
||||
// void MyApp::tryConnection(const SadDetectedServer &detected) {
|
||||
// // fetch the infoemations from the detected
|
||||
// bool boolSuccess = false;
|
||||
// bool boolPossibleConnect = checkPossibleConnection(detected);
|
||||
|
||||
// if (boolPossibleConnect == false)
|
||||
// return;
|
||||
|
||||
// // --------------------------------------
|
||||
// // You have three Options to use the detected server
|
||||
// // OPTION 1 Use the first server found
|
||||
// // OPTION 2 Use the first server found that fulfills specific conditions
|
||||
// // OPTION 3 take a copy of the detected and do connection later
|
||||
// // SadDetectedServer o_DetectedServer = detected; //this will call the copy constructor
|
||||
// // ---------------------------------------
|
||||
|
||||
// // Excample use the first detected LOOPBACK server, means the server runs on the same computer as this client
|
||||
// // if (detected.getIpIsLoopback() == true) { //omit the if condition if you want to use the first server found
|
||||
|
||||
// boolSuccess = false;
|
||||
// // Stop auto detection because a suitable server was found
|
||||
// stopServerAutoDetect();
|
||||
// if (mSPIT_Client != nullptr) {
|
||||
// mServerProjectName = detected.getProjectName();
|
||||
// mServerIP = detected.getIP();
|
||||
|
||||
// #if defined(DEBUGOUTPUT)
|
||||
// ESP_LOGD(TAG, "MySPITApp: connect to %s ", detected.getIP().data());
|
||||
// #endif
|
||||
// // Use the mBoardNumber as login name
|
||||
// string stringLoginName = "";
|
||||
// string stringLoginPassword = "";
|
||||
// boolSuccess = mSPIT_Client->connect(detected.getIP(), detected.getPort(), detected.getClientIP(), stringLoginName, stringLoginPassword); // ClientPort = "0" meeans use any port
|
||||
// }
|
||||
// if (boolSuccess == false) {
|
||||
// // connection message could not be sent
|
||||
// // perhaps restart AutoDetection
|
||||
// startServerAutoDetect();
|
||||
// } else {
|
||||
// // WAITING FOR CONNECTION ANSWER
|
||||
// // If the Server is closed in the meantime no answer will return
|
||||
// //-> connect(..) has started the watchdog
|
||||
// }
|
||||
|
||||
// // } //Excample
|
||||
// }
|
||||
|
||||
int SClient::sendMessage(Message::OPPart &opPart) {
|
||||
if (mMessageNumberSend == 255)
|
||||
mMessageNumberSend = 1;
|
||||
else
|
||||
mMessageNumberSend++;
|
||||
opPart.setMessageNumber(mMessageNumberSend);
|
||||
opPart.setServerInfoInHeader(mProcessor.getID(), mServerID);
|
||||
|
||||
int opFlag = opPart.getHeader()->getType();
|
||||
try {
|
||||
auto spitMessage = Message::createMessage(opPart);
|
||||
if (spitMessage.empty()) {
|
||||
return -1;
|
||||
}
|
||||
int intResult = mSocket.sendMessage(spitMessage.data(), spitMessage.size());
|
||||
if (intResult < 0) {
|
||||
ESP_LOGD(TAG, "client could not send message OPFlage: %d", opFlag);
|
||||
}
|
||||
return intResult;
|
||||
} catch (...) {
|
||||
ESP_LOGD(TAG, "Exception client OPFlag: %d", opFlag);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool SClient::startReceiveSPITMessages() {
|
||||
if (!mSocket.getIsBound())
|
||||
return false;
|
||||
mStopReceiveMessages = false;
|
||||
mThreadReceive = new fos::TaskT<SClient &>(receiveSpitMessage, *this, "receiveTask", fos::Task::DefaultStackSize, fos::Task::MAX_PRIORITY-3);
|
||||
mThreadProcess = new fos::TaskT<SClient &>(processSpitMessage, *this, "processTask", fos::Task::DefaultStackSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SClient::stopReceiveSPITMessages() {
|
||||
mStopReceiveMessages = true;
|
||||
}
|
||||
|
||||
void SClient::receiveSpitMessage(SClient &rM) {
|
||||
char buffer[Message::MAX_MESSAGE_SIZE];
|
||||
int rdLen;
|
||||
while (rM.mStopReceiveMessages == false) {
|
||||
rdLen = rM.mSocket.receiveMessage(buffer, Message::MAX_MESSAGE_SIZE, nullptr);
|
||||
if (rdLen < 0) {
|
||||
if (rM.mStopReceiveMessages) {
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
auto vec = make_unique<vector<char>>(buffer, buffer + rdLen);
|
||||
|
||||
incommingSpit.enqueue(std::move(vec));
|
||||
}
|
||||
}
|
||||
|
||||
void SClient::processSpitMessage(SClient &rM) {
|
||||
while (true) {
|
||||
auto data = *incommingSpit.dequeue();
|
||||
auto spitMsg = make_unique<Message>(data);
|
||||
rM.checkParseMessage(move(spitMsg));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SClient::resetMessageNumberReceived() {
|
||||
mMessageNumberReceived = -1;
|
||||
mProcessor.resetMessageNumberReceived();
|
||||
}
|
||||
|
||||
void SClient::checkParseMessage(unique_ptr<Message> p_SPITMessage) {
|
||||
answerWatchdog();
|
||||
if (!p_SPITMessage || !p_SPITMessage->getHeader() || !p_SPITMessage->getOPPart()) {
|
||||
ESP_LOGD(TAG, "Client::parseMessage incomplete message");
|
||||
return;
|
||||
}
|
||||
|
||||
int msgNum = (int)(p_SPITMessage->getHeader()->getMessageNumber() & 0xFF);
|
||||
|
||||
if (msgNum == 0) {
|
||||
parseMessage(*p_SPITMessage);
|
||||
return;
|
||||
}
|
||||
if (mMessageNumberReceived < 0) {
|
||||
mMessageNumberReceived = msgNum;
|
||||
parseMessage(*p_SPITMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
int intNextMessageNumber = mMessageNumberReceived + 1;
|
||||
if (intNextMessageNumber > 255 || intNextMessageNumber < 1)
|
||||
intNextMessageNumber = 1;
|
||||
|
||||
if (msgNum == intNextMessageNumber) {
|
||||
mMessageNumberReceived = msgNum;
|
||||
parseMessage(*p_SPITMessage);
|
||||
|
||||
mMessageNumberReceived = mProcessor.sendOvertakingMessage(mMessageNumberReceived);
|
||||
} else {
|
||||
// p_SPITMessage has overtaken another message
|
||||
mProcessor.addOvertakingMessage(move(p_SPITMessage));
|
||||
}
|
||||
}
|
||||
|
||||
void SClient::parseMessage(Message& message) {
|
||||
answerWatchdog();
|
||||
if (!message.getHeader() || !message.getOPPart()) {
|
||||
ESP_LOGD(TAG, "Client::parseMessage incomplete message");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (message.getType()) {
|
||||
//----- Connection -----
|
||||
case Message::ConnectAnswer::OPFLAG:
|
||||
parseConnectAnswer(message.getAs<Message::ConnectAnswer>());
|
||||
break;
|
||||
case Message::Disconnect::OPFLAG:
|
||||
parseDisconnect(message.getAs<Message::Disconnect>());
|
||||
break;
|
||||
case Message::Watchdog::OPFLAG:
|
||||
parseWatchdog(message.getAs<Message::Watchdog>());
|
||||
break;
|
||||
case Message::WatchdogAnswer::OPFLAG:
|
||||
parseWatchdogAnswer(message.getAs<Message::WatchdogAnswer>());
|
||||
break;
|
||||
default:
|
||||
// pass object to SubProcessor
|
||||
mProcessor.parseMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
//----- Connection ------
|
||||
/**
|
||||
* Tries to connect to a SPITServer.
|
||||
* @param serverIP the ip address of the SPIT server
|
||||
* @param serverPort the port number of the SPIT server
|
||||
* @param clientIP the local IP address of the client
|
||||
* @param clientPort the port number of the Client, if "0" the OS uses any possible port number
|
||||
* @param loginName login user name (liveSHOW does not use this -> mayby ""
|
||||
* @param loginPassword login user password (liveSHOW does not use this -> mayby ""
|
||||
* @return false = an error has occured, true = the connect was sent to the server
|
||||
*/
|
||||
bool SClient::connect(string serverIP, string serverPort, string clientIP, string clientPort, string loginName, string loginPassword) {
|
||||
disconnect();
|
||||
mSocket.setConnectionParams(serverIP, serverPort, clientIP, clientPort);
|
||||
if (mSocket.bindSocket(clientIP, clientPort) < 0) {
|
||||
mStopReceiveMessages = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto connMsg = mProcessor.createConnectMessage(serverIP, serverPort, loginName, loginPassword);
|
||||
|
||||
if (startReceiveSPITMessages() == false) {
|
||||
mStopReceiveMessages = true;
|
||||
return false;
|
||||
}
|
||||
resetMessageNumberReceived();
|
||||
Connection intOldConnectionStatus = mConnectionStatus;
|
||||
|
||||
mConnectionStatus = Connection::TryConnection;
|
||||
mCallbacks.onConnectionStatus(mServerID, mServerComputerName, mServerName, mServerStepTime,
|
||||
mConnectionStatus, intOldConnectionStatus);
|
||||
|
||||
// sleep a little to make sure that the reception of the messages is started
|
||||
fos::Task::delayMs(500);
|
||||
if (sendMessage(*connMsg) < 0) {
|
||||
mConnectionStatus = Connection::None;
|
||||
mStopReceiveMessages = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminates the connection to the SPITServer.
|
||||
*/
|
||||
void SClient::disconnect() {
|
||||
disconnectInternal(Connection::None);
|
||||
}
|
||||
|
||||
void SClient::disconnectInternal(Connection cause) {
|
||||
Connection intOldConnectionStatus = mConnectionStatus;
|
||||
|
||||
mConnectionStatus = cause;
|
||||
switch (mConnectionStatus) {
|
||||
case Connection::Lost:
|
||||
break;
|
||||
case Connection::None: {
|
||||
Message::Disconnect sMDisconnect;
|
||||
sendMessage(sMDisconnect);
|
||||
} break;
|
||||
case Connection::NoneServer:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
stopReceiveSPITMessages();
|
||||
mCallbacks.onConnectionStatus(mServerID, mServerComputerName, mServerName, mServerStepTime,
|
||||
mConnectionStatus, intOldConnectionStatus);
|
||||
|
||||
if (intOldConnectionStatus != Connection::None && intOldConnectionStatus != Connection::NoneServer) {
|
||||
ESP_LOGI(TAG, "eventConnectionStatus %d ", (int)mConnectionStatus);
|
||||
switch (mConnectionStatus) {
|
||||
case SClient::Connection::None:
|
||||
ESP_LOGD(TAG, "not connected");
|
||||
break;
|
||||
case SClient::Connection::NoneServer:
|
||||
ESP_LOGD(TAG, "connection terminated by server");
|
||||
// call eventProjectClosed because this may not be sended by the server,
|
||||
// when the connection is closed
|
||||
mCallbacks.onProjectUpdate(ProjUpdate::ServerClosed, mProcessor.getServerProjectID());
|
||||
|
||||
// Server has terminated the connection -> start a new game
|
||||
// _doReconnect = false;
|
||||
startServerAutoDetect();
|
||||
break;
|
||||
case SClient::Connection::Lost:
|
||||
ESP_LOGD(TAG, "connection lost");
|
||||
// Maybe you want to reconnect, see eventServerDetected(...)
|
||||
// We have lost the connection -> set the flag to true and start the autodetection to reconnect at eventServerDetected(...)
|
||||
// _doReconnect = true;
|
||||
startServerAutoDetect();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SClient::parseConnectAnswer(Message::ConnectAnswer &message) {
|
||||
if (message.getClientID() != mProcessor.getID())
|
||||
return;
|
||||
|
||||
mServerID = message.getServerID();
|
||||
mServerComputerName = message.getServerComputerName();
|
||||
mServerName = message.getServerName();
|
||||
mServerStepTime = message.getStepTime();
|
||||
|
||||
if (message.getConnected() == false) {
|
||||
disconnectInternal(Connection::NoneServer);
|
||||
} else {
|
||||
mConnectionStatus = Connection::Ok;
|
||||
mDisconnectedByServer = false;
|
||||
ESP_LOGD(TAG, "connection established");
|
||||
stopServerAutoDetect(); // normally you should stop the autodetection before you try a connection
|
||||
// This is redundand
|
||||
mStopReceiveMessages = false;
|
||||
|
||||
// Maybe you want to reconnect, see eventServerDetected(...)
|
||||
// Here the connection is established -> don't reconnect at eventServerDetected(...)
|
||||
// _doReconnect = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The SPIT server was closed
|
||||
* @param message
|
||||
*/
|
||||
void SClient::parseDisconnect(Message::Disconnect &message) {
|
||||
Connection intCauseDisconnect = Connection::NoneServer;
|
||||
|
||||
mDisconnectedByServer = true;
|
||||
SClient::disconnectInternal(intCauseDisconnect);
|
||||
}
|
||||
|
||||
//----- Watchdog ------
|
||||
/**
|
||||
* The SPIT server sends a watchdog message at regular intervals. The client responds immediately with WatchdogAnswer
|
||||
* Conversely, the client also sends a watchdog message at regular intervals and waits for a WatchdogAnswer message.
|
||||
* This is used to check if the other side is still present.
|
||||
* @param message
|
||||
*/
|
||||
void SClient::parseWatchdog(Message::Watchdog &message) {
|
||||
Message::WatchdogAnswer o_WatchdogAnswer;
|
||||
sendMessage(o_WatchdogAnswer);
|
||||
}
|
||||
/**
|
||||
* The SPIT server sends a watchdog message at regular intervals. The client responds immediately with WatchdogAnswer
|
||||
* Conversely, the client also sends a watchdog message at regular intervals and waits for a WatchdogAnswer message.
|
||||
* This is used to check if the other side is still present.
|
||||
* @param message
|
||||
*/
|
||||
void SClient::parseWatchdogAnswer(Message::WatchdogAnswer &message) {
|
||||
answerWatchdog(); // is also done by parseMessage
|
||||
}
|
||||
void SClient::sendWatchdog() {
|
||||
int intResult;
|
||||
Message::Watchdog message;
|
||||
intResult = sendMessage(message);
|
||||
if (intResult < 0) {
|
||||
ESP_LOGW("WDT", "Failed to send Watchdog msg");
|
||||
}
|
||||
}
|
||||
void SClient::eventWatchdogLost() {
|
||||
if (mConnectionStatus == Connection::TryConnection) {
|
||||
disconnectInternal(Connection::NoneServer);
|
||||
} else {
|
||||
disconnectInternal(Connection::Lost);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SClient::sendProject(SClient *p_PtrSPIT_Client) {
|
||||
// int intResult;
|
||||
// Message::StartProjectTransfer* ptrSMStartProjectTransfer;
|
||||
// Message::EndProjectTransfer* ptrSMEndProjectTransfer;
|
||||
// int intCount;
|
||||
// SPITType* ptrSPITType;
|
||||
// SObject* ptrSPITObject;
|
||||
|
||||
// //StartProjectTransfer
|
||||
// NotifyStartProjectTransfer_Client* o_RunnableStart = new NotifyStartProjectTransfer_Client(p_PtrSPIT_Client->getSPITClientInterface());
|
||||
// SPIT_Handler::addRunnable(o_RunnableStart);
|
||||
|
||||
// ptrSMStartProjectTransfer = new Message::StartProjectTransfer();
|
||||
// intResult = p_PtrSPIT_Client->sendMessage(ptrSMStartProjectTransfer);
|
||||
// if (intResult < 0) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // mWatchdog->answerWatchdog();
|
||||
// // {
|
||||
// //send all listes SPITTypes
|
||||
// // unique_lock<recursive_mutex> o_LockType(*SPITType::getMutex());
|
||||
// intCount = SPITType::getSPITTypeCount();
|
||||
// for (int i = 0; i < intCount; i++) {
|
||||
// ptrSPITType = mSpitTypes.getEntry(i);
|
||||
// if (ptrSPITType == nullptr) continue;
|
||||
// if (ptrSPITType->getCreatedByClient() == false) continue;
|
||||
// intResult = p_PtrSPIT_Client->sendTypeInternal(ptrSPITType);
|
||||
// if (intResult < 0) return;
|
||||
// // mWatchdog->answerWatchdog();
|
||||
// this_thread::sleep_for(chrono::milliseconds(7));
|
||||
// }
|
||||
// // }
|
||||
|
||||
// // {
|
||||
// // unique_lock<recursive_mutex> o_LockObject(*SObject::getMutex());
|
||||
// intCount = mSpitObjects.getEntryCount();
|
||||
// for (int i = 0; i < intCount; i++) {
|
||||
// ptrSPITObject = mSpitObjects.getEntry(i);
|
||||
// if (ptrSPITObject == nullptr) continue;
|
||||
// if (ptrSPITObject->getCreatedByClient() == false) continue;
|
||||
// intResult = p_PtrSPIT_Client->sendObjectInternal(ptrSPITObject);
|
||||
// if (intResult < 0) return;
|
||||
// // mWatchdog->answerWatchdog();
|
||||
// this_thread::sleep_for(chrono::milliseconds(7));
|
||||
// }
|
||||
// // }
|
||||
|
||||
// //EndProjectTransfer
|
||||
// ptrSMEndProjectTransfer = new Message::EndProjectTransfer();
|
||||
// intResult = p_PtrSPIT_Client->sendMessage(ptrSMEndProjectTransfer);
|
||||
// if (intResult < 0) {
|
||||
// return;
|
||||
// }
|
||||
// // mWatchdog->answerWatchdog();
|
||||
// NotifyEndProjectTransfer_Client* o_RunnableEnd = new NotifyEndProjectTransfer_Client(p_PtrSPIT_Client->getSPITClientInterface());
|
||||
// SPIT_Handler::addRunnable(o_RunnableEnd);
|
||||
|
||||
// //Send act
|
||||
}
|
||||
|
||||
SType *SClient::createTypeFixed(string iD, string group, string name, uint64_t delay, uint64_t duration) {
|
||||
return mProcessor.createType(iD, group, name, delay, duration, true, true);
|
||||
}
|
||||
|
||||
fos::Semaphore SClient::sStartSad;
|
||||
unordered_map<string, unique_ptr<SadDetectedServer>> SadDetectedServer::sKnownServers;
|
||||
|
||||
void SClient::processSocketTask(SClient &instance) {
|
||||
ESP_LOGI(TAG, "Starting Socket managing");
|
||||
static const string SERVERAUTODETECT_PRECODE = "#SAD#";
|
||||
static const string SERVERAUTODETECT_SEPERATOR = "#,#";
|
||||
static const string SERVERAUTODETECT_TASKID = "#spit#"; // -> "#SAD##,##spit#"
|
||||
static const string SERVERAUTODETECT_PORT = "15110";
|
||||
static constexpr int WATCHDOGTIME = 3000;
|
||||
|
||||
while (true) {
|
||||
sStartSad.take();
|
||||
bool searching = true;
|
||||
instance.mSocket.setBroadcast(true);
|
||||
string stringDatagram = SERVERAUTODETECT_PRECODE + SERVERAUTODETECT_SEPERATOR + SERVERAUTODETECT_TASKID;
|
||||
int intDatagramLength = stringDatagram.length();
|
||||
while (searching) {
|
||||
if (instance.mAbortSearch)
|
||||
break;
|
||||
|
||||
SadDetectedServer::resetServerFlags();
|
||||
string stringBroadcastIP4 = instance.mSocket.mOwnIp.getBroadcastIP4();
|
||||
instance.mSocket.sendMessageTo(stringBroadcastIP4, SERVERAUTODETECT_PORT, stringDatagram.data(), intDatagramLength);
|
||||
{
|
||||
// wait for 3000 milliseconds or
|
||||
fos::Task::delayMs(3000);
|
||||
}
|
||||
|
||||
bool receiving = true;
|
||||
while (receiving) {
|
||||
if (instance.mAbortSearch) {
|
||||
break;
|
||||
}
|
||||
constexpr int SIZE = 512;
|
||||
char buffer[SIZE + 1];
|
||||
string stringServerIP;
|
||||
int intReceivedByteCount = instance.mSocket.receiveMessage(buffer, SIZE, &stringServerIP);
|
||||
if (intReceivedByteCount <= 0) {
|
||||
break;
|
||||
}
|
||||
buffer[intReceivedByteCount] = '\0';
|
||||
string stringMessage = buffer;
|
||||
// Check if precode is ok
|
||||
if (stringMessage.find(SERVERAUTODETECT_PRECODE.data(), 0) != 0) {
|
||||
break;
|
||||
}
|
||||
int intIndex = 0;
|
||||
int intOldPos = 0;
|
||||
string stringPart;
|
||||
string strParts[10];
|
||||
|
||||
while (intIndex < SadDetectedServer::PARAM_IP) {
|
||||
int intPos = stringMessage.find(SERVERAUTODETECT_SEPERATOR.data(), intOldPos);
|
||||
if (intPos == string::npos) {
|
||||
stringPart = "";
|
||||
} else {
|
||||
stringPart = stringMessage.substr(intOldPos, intPos - intOldPos);
|
||||
intOldPos = intPos + SERVERAUTODETECT_SEPERATOR.length();
|
||||
}
|
||||
strParts[intIndex] = stringPart;
|
||||
intIndex++;
|
||||
}
|
||||
|
||||
auto server = make_unique<SadDetectedServer>(
|
||||
strParts[SadDetectedServer::PARAM_NAME],
|
||||
strParts[SadDetectedServer::PARAM_ID],
|
||||
strParts[SadDetectedServer::PARAM_COMPUTERNAME],
|
||||
stringServerIP,
|
||||
strParts[SadDetectedServer::PARAM_PORT],
|
||||
strParts[SadDetectedServer::PARAM_CLIENTIP],
|
||||
strParts[SadDetectedServer::PARAM_ADDITIONS],
|
||||
strParts[SadDetectedServer::PARAM_SERVERPROJECTNAME]);
|
||||
|
||||
SadDetectedServer::addDetectedServer(move(server));
|
||||
}
|
||||
fos::Task::delayMs(100);
|
||||
SadDetectedServer::evaluateDetectedServers(instance);
|
||||
|
||||
instance.mAbortSearch = false;
|
||||
// if(instance.mConnectionStatus == Connection::Ok) break;
|
||||
fos::Task::delayMs(1000);
|
||||
}
|
||||
instance.mSocket.setBroadcast(false);
|
||||
|
||||
ESP_LOGW(TAG, "Finished searching");
|
||||
ESP_LOGW(TAG, "Start working");
|
||||
auto nextWdTrigger = fos::Timing::getMilliSec() + WATCHDOGTIME;
|
||||
// auto nextWdCheck = nextWdTrigger + WATCHDOGTIME;
|
||||
while (true) {
|
||||
// auto time = fos::Timing::getMilliSec();
|
||||
// if (time > nextWdTrigger) {
|
||||
// instance.sAnswered = false;
|
||||
// // instance.sendWatchdog();
|
||||
// nextWdTrigger = nextWdCheck + WATCHDOGTIME;
|
||||
// }
|
||||
|
||||
// if (time > nextWdCheck) {
|
||||
// if (!instance.sAnswered) {
|
||||
// ESP_LOGW(TAG, "TimeOut");
|
||||
// instance.eventWatchdogLost();
|
||||
// break;
|
||||
// }
|
||||
// nextWdCheck = nextWdTrigger + WATCHDOGTIME;
|
||||
// }
|
||||
fos::Task::delayMs(25);
|
||||
// if (instance.mStopReceiveMessages)
|
||||
// break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,202 @@
|
||||
|
||||
#include "spit_object.hpp"
|
||||
#include "spit_socket.hpp"
|
||||
#include "esp_log.h"
|
||||
|
||||
namespace spit {
|
||||
|
||||
/**
|
||||
* clears all unconfirmed SObjects not created by this SPIT_Client.
|
||||
*/
|
||||
void SObject::clearUnconfirmedObjects(SpitRepo<SObject> &repo) {
|
||||
auto &entries = repo.getRepo();
|
||||
for (auto i = entries.begin(); i != entries.end();) {
|
||||
SObject *ptrSObject = i->second.get();
|
||||
if (ptrSObject->getCreatedByClient() == false && ptrSObject->getConfirmed() == false) {
|
||||
i = entries.erase(i);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SObject::eventProjectClosed(SpitRepo<SObject> &repo, SpitRepo<SObject> &remoteRepo) {
|
||||
auto &entries = repo.getRepo();
|
||||
for (auto i = entries.begin(); i != entries.end(); ) {
|
||||
SObject *ptrSObject = i->second.get();
|
||||
if (ptrSObject->getCreatedByClient() == true) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
i = entries.erase(i);
|
||||
}
|
||||
// Remote
|
||||
remoteRepo.clearEntries();
|
||||
}
|
||||
|
||||
/**
|
||||
* sets confirmed = false for all SObjects.
|
||||
* This should be called when the client has loaded a project.
|
||||
*/
|
||||
void SObject::setConfirmedFalse(SpitRepo<SObject> &repo) {
|
||||
auto &entries = repo.getRepo();
|
||||
for (auto i = entries.begin(); i != entries.end(); i++) {
|
||||
i->second->setConfirmed(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool SObject::setMessageOP(Message::ReportObject *message) {
|
||||
bool changeAllowed;
|
||||
|
||||
if ((mCreatedByClient == true) && (Fixed.mValue == true)) {
|
||||
changeAllowed = false;
|
||||
} else {
|
||||
changeAllowed = true;
|
||||
}
|
||||
|
||||
// Confirmation should be changable
|
||||
if (mCreatedByClient) {
|
||||
// Server reports a client created SPITType -> the type is confirmed
|
||||
mConfirmed = true;
|
||||
} else {
|
||||
// type is NOT client created -> the client has to confirm the SPITtype
|
||||
mConfirmed = message->getConfirmed();
|
||||
}
|
||||
|
||||
if (changeAllowed == false) {
|
||||
return false;
|
||||
}
|
||||
bool changed = false;
|
||||
if (Delay.mValue != message->getDelay()) {
|
||||
Delay.mValue = message->getDelay();
|
||||
changed = true;
|
||||
}
|
||||
if (Duration.mValue != message->getDuration()) {
|
||||
Duration.mValue = message->getDuration();
|
||||
changed = true;
|
||||
}
|
||||
if (Fixed.mValue != message->getFixed()) {
|
||||
Fixed.mValue = message->getFixed();
|
||||
changed = true;
|
||||
}
|
||||
if (FrameFadeInLength.mValue != message->getFrameFadeInLength()) {
|
||||
FrameFadeInLength.mValue = message->getFrameFadeInLength();
|
||||
changed = true;
|
||||
}
|
||||
if (FrameFadeOutLength.mValue != message->getFrameFadeOutLength()) {
|
||||
FrameFadeOutLength.mValue = message->getFrameFadeOutLength();
|
||||
changed = true;
|
||||
}
|
||||
if (FrameLength.mValue != message->getFrameLength()) {
|
||||
FrameLength.mValue = message->getFrameLength();
|
||||
changed = true;
|
||||
}
|
||||
if (FrameStart.mValue != message->getFrameStart()) {
|
||||
FrameStart.mValue = message->getFrameStart();
|
||||
changed = true;
|
||||
}
|
||||
if (Remark.mValue != message->getRemark()) {
|
||||
Remark.mValue = message->getRemark();
|
||||
changed = true;
|
||||
}
|
||||
if (mID != message->getID()) {
|
||||
mID = message->getID();
|
||||
changed = true;
|
||||
}
|
||||
if (Name.mValue != message->getName()) {
|
||||
Name.mValue = message->getName();
|
||||
changed = true;
|
||||
}
|
||||
if (SPITTypeID.mValue != message->getSPITTypeID()) {
|
||||
SPITTypeID.mValue = message->getSPITTypeID();
|
||||
changed = true;
|
||||
}
|
||||
if (ServerParams.mValue != message->getServerParams()) {
|
||||
ServerParams.mValue = message->getServerParams();
|
||||
changed = true;
|
||||
}
|
||||
if (ServerRemote.mValue != message->getServerRemote()) {
|
||||
ServerRemote.mValue = message->getServerRemote();
|
||||
changed = true;
|
||||
}
|
||||
if (Track.mValue != message->getTrack()) {
|
||||
Track.mValue = message->getTrack();
|
||||
changed = true;
|
||||
}
|
||||
if (mValueFactor != message->getValueFactor()) {
|
||||
mValueFactor = message->getValueFactor();
|
||||
changed = true;
|
||||
}
|
||||
if (mValueUse != message->getValueUse()) {
|
||||
mValueUse = message->getValueUse();
|
||||
changed = true;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
void SObject::fillMessageOP(Message::ReportObject &message) {
|
||||
message.setActionAssigned(getActionAssigned()); // Action can be assigned to type or to this object
|
||||
message.setConfirmed(mConfirmed);
|
||||
message.setDelay(Delay.mValue);
|
||||
message.setDuration(Duration.mValue);
|
||||
message.setFixed(Fixed.mValue);
|
||||
message.setFrameFadeInLength(FrameFadeInLength.mValue);
|
||||
message.setFrameFadeOutLength(FrameFadeOutLength.mValue);
|
||||
message.setFrameLength(FrameLength.mValue);
|
||||
message.setFrameStart(FrameStart.mValue);
|
||||
message.setID(mID);
|
||||
message.setName(Name.mValue);
|
||||
message.setRemark(Remark.mValue);
|
||||
message.setSPITTypeID(SPITTypeID.mValue);
|
||||
message.setServerParams(ServerParams.mValue);
|
||||
message.setServerRemote(ServerRemote.mValue);
|
||||
message.setTrack(Track.mValue);
|
||||
message.setValueFactor(mValueFactor);
|
||||
message.setValueUse(mValueUse);
|
||||
}
|
||||
|
||||
void SObject::fillMessageOP(Message::RemoveObject &message) {
|
||||
message.setID(mID);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used by the server and client to tell if the other side has registered the changes.
|
||||
* @param p_Confirmed
|
||||
* @return if mConfirmed has changed
|
||||
*/
|
||||
bool SObject::setConfirmed(bool p_Confirmed) {
|
||||
if (mConfirmed == p_Confirmed)
|
||||
return false;
|
||||
mConfirmed = p_Confirmed;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a virtual construct - ActionAssignedType is not implemented in the SPITProtocol.
|
||||
* This is used when an action is associated with a SPITType.
|
||||
* Then this action is done for every SObject with the SPITType.
|
||||
* @return true = an action is assigned for the SPITType.
|
||||
*/
|
||||
bool SObject::setActionAssignedType(bool p_ActionAssigned) {
|
||||
if (mActionAssignedType == p_ActionAssigned)
|
||||
return false;
|
||||
bool oldActionAssigend = getActionAssigned();
|
||||
mActionAssignedType = p_ActionAssigned;
|
||||
if (getActionAssigned() == oldActionAssigend)
|
||||
return false;
|
||||
mConfirmed = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SObject::setActionAssigned(bool p_ActionAssigned) {
|
||||
if (mActionAssigned == p_ActionAssigned)
|
||||
return false;
|
||||
bool oldActionAssigend = getActionAssigned();
|
||||
mActionAssigned = p_ActionAssigned;
|
||||
if (getActionAssigned() == oldActionAssigend)
|
||||
return false;
|
||||
mConfirmed = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
|
||||
#include "spit_player.hpp"
|
||||
#include "esp_log.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace spit {
|
||||
|
||||
unordered_map<string, unique_ptr<SPlayer>> SPlayer::sSpitPlayers;
|
||||
|
||||
SPlayer &SPlayer::getSPITPlayer(string playerID) {
|
||||
auto i = sSpitPlayers.find(playerID);
|
||||
if (i == sSpitPlayers.end()) {
|
||||
sSpitPlayers[playerID] = make_unique<SPlayer>(playerID);
|
||||
}
|
||||
return *sSpitPlayers.at(playerID);
|
||||
}
|
||||
|
||||
SPlayer::SPlayer(string playerID) {
|
||||
mId = playerID;
|
||||
}
|
||||
|
||||
SPlayer::~SPlayer() {
|
||||
mPlaySPITTypes.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param p_SPITObjectID
|
||||
* @return true = new PlayObject added<br>
|
||||
* false = PlayObject exists
|
||||
*/
|
||||
SPlayer::PlayType *SPlayer::addTypePlaying(string spitTypeID) {
|
||||
PlayType *ptrPlaySPITType;
|
||||
auto i = mPlaySPITTypes.find(spitTypeID);
|
||||
if (i == mPlaySPITTypes.end()) {
|
||||
auto playSpitType = make_unique<PlayType>(this, spitTypeID);
|
||||
ptrPlaySPITType = playSpitType.get();
|
||||
mPlaySPITTypes[spitTypeID] = move(playSpitType);
|
||||
} else {
|
||||
ptrPlaySPITType = i->second.get();
|
||||
}
|
||||
ptrPlaySPITType->setIsPlayed(true);
|
||||
return ptrPlaySPITType;
|
||||
}
|
||||
|
||||
void SPlayer::startPlaySequence() {
|
||||
for (auto i = mPlaySPITTypes.begin(); i != mPlaySPITTypes.end(); i++) {
|
||||
PlayType *ptrPlaySPITType = i->second.get();
|
||||
ptrPlaySPITType->startPlaySequence();
|
||||
}
|
||||
}
|
||||
|
||||
void SPlayer::endPlaySequence() {
|
||||
for (auto i = mPlaySPITTypes.begin(); i != mPlaySPITTypes.end();) {
|
||||
if (i->second.get()->getIsPlayed() == false) {
|
||||
i = mPlaySPITTypes.erase(i);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SPlayer::PlayType::PlayType(SPlayer *p_SPITPlayer, string spitTypeID) {
|
||||
mIsNew = true;
|
||||
|
||||
mSpitTypeID = spitTypeID;
|
||||
mPlayerID = p_SPITPlayer->getID();
|
||||
mPlayerName = p_SPITPlayer->getName();
|
||||
startPlaySequence();
|
||||
mIsNew = true;
|
||||
}
|
||||
SPlayer::PlayType::PlayType(const PlayType &orig) {
|
||||
mSpitTypeID = orig.getSPITTypeID();
|
||||
mIsPlayed = orig.getIsPlayed();
|
||||
mIsNew = orig.getIsNew();
|
||||
|
||||
mPlayerID = orig.getPlayerID();
|
||||
mPlayerName = orig.getPlayerName();
|
||||
mPlayerRuns = orig.getPlayerIsRunning();
|
||||
|
||||
mValue = orig.getValue();
|
||||
}
|
||||
void SPlayer::PlayType::startPlaySequence() {
|
||||
mIsPlayed = false;
|
||||
mIsNew = false;
|
||||
|
||||
mValue = 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,745 @@
|
||||
|
||||
#include "spit_processor.hpp"
|
||||
#include "esp_log.h"
|
||||
|
||||
namespace spit {
|
||||
static const char *TAG = "SPIT";
|
||||
|
||||
/**
|
||||
* Create a new SPIT_client instance, should be done once by the user app.
|
||||
* @param p_SPITClientInterface the user app should implement the SPIT_ClientInterface.
|
||||
*/
|
||||
SProcessor::SProcessor(string name, string projName, string hostName, string guid, Callbacks calls, InternalCallbacks intern)
|
||||
: mCallbacks(calls),
|
||||
mInternCallbacks(intern),
|
||||
mName(name),
|
||||
mGuid(guid),
|
||||
mProjName(projName),
|
||||
mComputerName(hostName) {
|
||||
}
|
||||
|
||||
SProcessor::~SProcessor() {
|
||||
}
|
||||
|
||||
unique_ptr<Message::Connect> SProcessor::createConnectMessage(string serverIP, string serverPort, string loginName, string loginPassword) {
|
||||
auto msg = make_unique<Message::Connect>(getComputerName(), mName);
|
||||
msg->setLoginName(loginName);
|
||||
msg->setLoginPassword(loginPassword);
|
||||
msg->setServerIP(serverIP);
|
||||
if (serverPort.length() <= 0) {
|
||||
serverPort = "0";
|
||||
}
|
||||
int intServerPort = stoi(serverPort);
|
||||
msg->setServerPort(intServerPort);
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Virtual construct - means actionAssigned for SPITTypes is is not implemented in SPITProtocol.
|
||||
* This can used to setActionAssigned to all SPITObjects with this SPITType.
|
||||
* This may be used in the case when the same action should be done for every SPITObject with this SPITType associated.
|
||||
* This tells the server not what action is done, but it tells the server that there is an action.
|
||||
* @param typeId the id of the SPITType
|
||||
* @param actionAssigned
|
||||
*/
|
||||
void SProcessor::setActionType(string typeId, bool actionAssigned) {
|
||||
SType *spitType = mSpitTypes.getEntry(typeId);
|
||||
if (spitType == nullptr)
|
||||
return;
|
||||
if (spitType->ActionAssigned.set(actionAssigned)) {
|
||||
sendTypeInternal(*spitType);
|
||||
for (auto &[key, value] : mSpitObjects.getRepo()) {
|
||||
if (value->SPITTypeID.get() == spitType->getID()) {
|
||||
if (value->setActionAssignedType(actionAssigned) == true) {
|
||||
sendObjectInternal(value.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** This should be called if the user wants to associate/remove an action with a SPITObject. */
|
||||
void SProcessor::setActionObject(string spitObjId, bool actionAssigned) {
|
||||
SObject *spitObject = mSpitObjects.getEntry(spitObjId);
|
||||
if (spitObject == nullptr)
|
||||
return;
|
||||
if (spitObject->setActionAssigned(actionAssigned) == true) {
|
||||
sendObjectInternal(spitObject);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<char> SProcessor::createMessage(Message::OPPart &opPart) {
|
||||
if (mMessageNumberSend == 255)
|
||||
mMessageNumberSend = 1;
|
||||
else
|
||||
mMessageNumberSend++;
|
||||
opPart.setMessageNumber(mMessageNumberSend);
|
||||
opPart.setServerInfoInHeader(mGuid, mServerID);
|
||||
|
||||
int opFlag = opPart.getHeader()->getType();
|
||||
try {
|
||||
return Message::createMessage(opPart);
|
||||
} catch (...) {
|
||||
ESP_LOGD(TAG, "Exception client OPFlag: %d", opFlag);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void SProcessor::resetMessageNumberReceived() {
|
||||
mMessageNumberReceived = -1;
|
||||
mOvertakingMessages.clear();
|
||||
}
|
||||
|
||||
void SProcessor::checkParseMessageData(vector<char> data) {
|
||||
auto message = make_unique<Message>(data);
|
||||
// answerWatchdog();
|
||||
if (!message->getHeader() || !message->getOPPart()) {
|
||||
ESP_LOGD(TAG, "Client::parseMessage incomplete message");
|
||||
return;
|
||||
}
|
||||
|
||||
int msgNum = (int)(message->getHeader()->getMessageNumber() & 0xFF);
|
||||
|
||||
if (msgNum == 0) {
|
||||
parseMessage(*message);
|
||||
return;
|
||||
}
|
||||
if (mMessageNumberReceived < 0) {
|
||||
mMessageNumberReceived = msgNum;
|
||||
parseMessage(*message);
|
||||
return;
|
||||
}
|
||||
|
||||
int msgNumNext = mMessageNumberReceived + 1;
|
||||
if (msgNumNext > 255 || msgNumNext < 1)
|
||||
msgNumNext = 1;
|
||||
|
||||
if (msgNum == msgNumNext) {
|
||||
mMessageNumberReceived = msgNum;
|
||||
parseMessage(*message);
|
||||
|
||||
mMessageNumberReceived = sendOvertakingMessage(mMessageNumberReceived);
|
||||
} else {
|
||||
// message has overtaken another message
|
||||
addOvertakingMessage(move(message));
|
||||
}
|
||||
}
|
||||
|
||||
void SProcessor::addOvertakingMessage(unique_ptr<Message> message) {
|
||||
if (!message) {
|
||||
return;
|
||||
}
|
||||
if (mOvertakingMessages.size() > MAX_OVERTAKING_SPIT_MESSAGES) {
|
||||
ESP_LOGD(TAG, "Client receiveing: perhaps message lost !!!!!");
|
||||
}
|
||||
|
||||
int intMessageNumber = (int)(message->getHeader()->getMessageNumber() & 0xFF);
|
||||
for (auto it = mOvertakingMessages.begin(); it != mOvertakingMessages.end(); ++it) {
|
||||
const auto &overtaking = *it;
|
||||
int intOvertakingMessageNumber = (int)(overtaking->getHeader()->getMessageNumber() & 0xFF);
|
||||
if (intMessageNumber < intOvertakingMessageNumber) {
|
||||
mOvertakingMessages.insert(it, move(message));
|
||||
return;
|
||||
}
|
||||
}
|
||||
mOvertakingMessages.push_back(move(message));
|
||||
}
|
||||
|
||||
int SProcessor::sendOvertakingMessage(int msgNumRec) {
|
||||
// check overtaking messages
|
||||
auto it = mOvertakingMessages.begin();
|
||||
while (mOvertakingMessages.size() > 0) {
|
||||
|
||||
int msgNumNext = msgNumRec + 1;
|
||||
if (msgNumNext > 255 || msgNumNext < 1)
|
||||
msgNumNext = 1;
|
||||
|
||||
auto &overtakingMessage = *it;
|
||||
int msgNum = (int)(overtakingMessage->getHeader()->getMessageNumber() & 0xFF);
|
||||
|
||||
if (msgNum == msgNumNext) {
|
||||
msgNumRec = msgNum;
|
||||
parseMessage(*overtakingMessage);
|
||||
mOvertakingMessages.erase(it);
|
||||
it = mOvertakingMessages.begin();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return msgNumRec;
|
||||
}
|
||||
|
||||
void SProcessor::parseMessage(Message &message) {
|
||||
// answerWatchdog();
|
||||
if (!message.getHeader() || !message.getOPPart()) {
|
||||
ESP_LOGD(TAG, "Client::parseMessage incomplete message");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (message.getType()) {
|
||||
//----- Connection -----
|
||||
case Message::ReportProject::OPFLAG:
|
||||
parseReportProject(message.getAs<Message::ReportProject>());
|
||||
break;
|
||||
//----- SPIT Project -----
|
||||
case Message::StartProjectTransfer::OPFLAG:
|
||||
parseStartProjectTransfer(message.getAs<Message::StartProjectTransfer>());
|
||||
break;
|
||||
case Message::CloseProject::OPFLAG:
|
||||
parseCloseProject(message.getAs<Message::CloseProject>());
|
||||
break;
|
||||
case Message::RequestProjectTransfer::OPFLAG:
|
||||
parseRequestProjectTransfer(message.getAs<Message::RequestProjectTransfer>());
|
||||
break;
|
||||
case Message::EndProjectTransfer::OPFLAG:
|
||||
parseEndProjectTransfer(message.getAs<Message::EndProjectTransfer>());
|
||||
break;
|
||||
//----- SPIT Types ------
|
||||
case Message::ReportType::OPFLAG:
|
||||
parseReportType(message.getAs<Message::ReportType>());
|
||||
break;
|
||||
case Message::RemoveType::OPFLAG:
|
||||
parseRemoveType(message.getAs<Message::RemoveType>());
|
||||
//----- SPIT Objects ------
|
||||
break;
|
||||
case Message::ReportObject::OPFLAG:
|
||||
parseReportObject(message.getAs<Message::ReportObject>());
|
||||
break;
|
||||
case Message::RemoveObject::OPFLAG:
|
||||
parseRemoveObject(message.getAs<Message::RemoveObject>());
|
||||
break;
|
||||
//----- Play SPIT Objects ------
|
||||
case Message::PlayStart::OPFLAG:
|
||||
parsePlayStart(message.getAs<Message::PlayStart>());
|
||||
break;
|
||||
case Message::PlayObject::OPFLAG:
|
||||
parsePlayObject(message.getAs<Message::PlayObject>());
|
||||
break;
|
||||
case Message::PlayEnd::OPFLAG:
|
||||
parsePlayEnd(message.getAs<Message::PlayEnd>());
|
||||
break;
|
||||
case Message::ReportPlayStatus::OPFLAG:
|
||||
parseReportPlayStatus(message.getAs<Message::ReportPlayStatus>());
|
||||
break;
|
||||
default:
|
||||
ESP_LOGD(TAG, "Client::parseMessage: Unknown Message");
|
||||
}
|
||||
}
|
||||
|
||||
//----- SPIT Project ------
|
||||
void SProcessor::parseReportProject(Message::ReportProject &message) {
|
||||
ESP_LOGD(TAG, "parse ReportProject");
|
||||
mServerProjectID = message.getProjectID();
|
||||
mServerProjectName = message.getProjectName();
|
||||
mServerFramesPerSecond = message.getFramesPerSecond();
|
||||
mCallbacks.onProjectUpdate(ProjUpdate::ServerLoaded, mServerProjectID);
|
||||
|
||||
// Set all SPITTypes and SPITObjects confirmed = false
|
||||
SType::setConfirmedFalse(mSpitTypes);
|
||||
SObject::setConfirmedFalse(mSpitObjects);
|
||||
|
||||
// Send RequestProjectTransfer
|
||||
Message::RequestProjectTransfer sMRequestProjectTransfer;
|
||||
if (mInternCallbacks.sendMessage(sMRequestProjectTransfer) < 0) {
|
||||
ESP_LOGD(TAG, "Client::parseReportProject could not send RequestProjectTransfer");
|
||||
}
|
||||
|
||||
// todo Don't send the ReportProject here it should be sended after parseEndProjectTransfer
|
||||
// Send client's ReportProject
|
||||
Message::ReportProject reportProject(mProjName, mProjName);
|
||||
reportProject.setFramesPerSecond(0);
|
||||
if (mInternCallbacks.sendMessage(reportProject) < 0) {
|
||||
ESP_LOGD(TAG, "Client::parseReportProject could not send Client's ReportProject");
|
||||
}
|
||||
}
|
||||
|
||||
void SProcessor::parseCloseProject(Message::CloseProject &p_CloseSPITProject) {
|
||||
ESP_LOGD(TAG, "parse CloseProject");
|
||||
string stringProjectID;
|
||||
mCallbacks.onProjectUpdate(ProjUpdate::ServerClosed, p_CloseSPITProject.getProjectID());
|
||||
SPlayer::clearPlayers();
|
||||
}
|
||||
|
||||
void SProcessor::parseRequestProjectTransfer(Message::RequestProjectTransfer &message) {
|
||||
ESP_LOGD(TAG, "parse RequestProjectTransfer");
|
||||
// StartProjectTransfer
|
||||
Message::StartProjectTransfer sMStartProjectTransfer;
|
||||
if (mInternCallbacks.sendMessage(sMStartProjectTransfer) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// send all listes SPITTypes
|
||||
for (const auto &[key, value] : mSpitTypes.getRepo()) {
|
||||
sendTypeInternal(*value);
|
||||
mInternCallbacks.delay(10);
|
||||
}
|
||||
for (auto &[key, value] : mSpitObjects.getRepo()) {
|
||||
sendObjectInternal(value.get());
|
||||
mInternCallbacks.delay(10);
|
||||
}
|
||||
|
||||
// EndProjectTransfer
|
||||
Message::EndProjectTransfer sMEndProjectTransfer;
|
||||
mInternCallbacks.sendMessage(sMEndProjectTransfer);
|
||||
}
|
||||
|
||||
void SProcessor::parseStartProjectTransfer(Message::StartProjectTransfer &message) {
|
||||
mCallbacks.onProjectUpdate(ProjUpdate::StartTransfer, mServerProjectID);
|
||||
}
|
||||
|
||||
void SProcessor::parseEndProjectTransfer(Message::EndProjectTransfer &message) {
|
||||
SObject::clearUnconfirmedObjects(mSpitObjects);
|
||||
mCallbacks.onProjectUpdate(ProjUpdate::EndTransfer, mServerProjectID);
|
||||
|
||||
// Send Message::ReportProject after the mSPITClientInterface is notfied of EndProjectTransfer
|
||||
Message::ReportProject reportSPITProject(mProjName, mProjName);
|
||||
reportSPITProject.setFramesPerSecond(0);
|
||||
if (mInternCallbacks.sendMessage(reportSPITProject) < 0) {
|
||||
ESP_LOGD(TAG, "Client::parseReportProject could not send Client's ReportProject");
|
||||
}
|
||||
}
|
||||
|
||||
void SProcessor::parseReportType(Message::ReportType &message) {
|
||||
ESP_LOGD(TAG, "parse ReportType ID %s Name %s", message.getID().c_str(), message.getName().c_str());
|
||||
SType *sType;
|
||||
bool isNew = false;
|
||||
bool isChanged = false;
|
||||
if (message.getServerRemote() == false) {
|
||||
sType = mSpitTypes.getEntry(message.getID());
|
||||
if (sType == nullptr) {
|
||||
// The user's app may veto the SPITType
|
||||
bool createdByClient = false;
|
||||
if (sType) {
|
||||
createdByClient = sType->getCreatedByClient();
|
||||
}
|
||||
|
||||
if (mCallbacks.onCheckSpitTypeVeto(false, createdByClient) == false) {
|
||||
ESP_LOGW(TAG, "TypeVeto %s / %s", message.getID().c_str(), message.getName().c_str());
|
||||
return;
|
||||
}
|
||||
auto spitType = make_unique<SType>(message.getID());
|
||||
sType = spitType.get();
|
||||
mSpitTypes.addEntry(move(spitType));
|
||||
isNew = true;
|
||||
}
|
||||
} else {
|
||||
sType = mRemoteSpitTypes.getEntry(message.getID());
|
||||
if (sType == nullptr) {
|
||||
auto spitType = make_unique<SType>(message.getID());
|
||||
sType = spitType.get();
|
||||
mRemoteSpitTypes.addEntry(move(spitType));
|
||||
isNew = true;
|
||||
}
|
||||
}
|
||||
if (!sType->setMessageOP(message)) {
|
||||
// SPITType is not changable -> send back to the server
|
||||
sendTypeInternal(*sType);
|
||||
return;
|
||||
}
|
||||
|
||||
auto eventType = isNew ? ObjectEvent::Created : ObjectEvent::Changed;
|
||||
// add or change SPITType
|
||||
mCallbacks.onSpitType(eventType, sType->ServerRemote.get(), *sType);
|
||||
|
||||
if (sType != nullptr) {
|
||||
if (sType->setConfirmed(true) == true || isChanged == true) {
|
||||
sendTypeInternal(*sType);
|
||||
}
|
||||
}
|
||||
}
|
||||
void SProcessor::parseRemoveType(Message::RemoveType &message) {
|
||||
ESP_LOGD(TAG, "parse RemoveType %s", message.getID().c_str());
|
||||
bool isRemote = false;
|
||||
string sTypeID = message.getID();
|
||||
|
||||
SType *sType = mRemoteSpitTypes.getEntry(sTypeID);
|
||||
if (!sType) {
|
||||
isRemote = true;
|
||||
} else {
|
||||
sType = mSpitTypes.getEntry(sTypeID);
|
||||
if (!sType) {
|
||||
isRemote = false;
|
||||
} else {
|
||||
// No SType with sTypeID is listet
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (sType->getCreatedByClient() && sType->Fixed.get()) {
|
||||
// the SPITyte is created by client himself and it is fixed -> don't remove the SType!!
|
||||
// Send the SPITTyp back to the server
|
||||
sendTypeInternal(*sType);
|
||||
return;
|
||||
}
|
||||
|
||||
mCallbacks.onSpitType(ObjectEvent::Deleted, isRemote, *sType);
|
||||
if (isRemote == false) {
|
||||
mSpitTypes.removeEntry(*sType);
|
||||
} else {
|
||||
mRemoteSpitTypes.removeEntry(*sType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change an existing SPIT_Type.
|
||||
* @param p_SPITType the SPITType vont
|
||||
* @return true = changed, false = not changed maybe p_SPITType is NULL or the SPITType doesn't exists
|
||||
*/
|
||||
bool SProcessor::changeType(SType *sType) {
|
||||
if (sType == nullptr)
|
||||
return false;
|
||||
SType *mod = mSpitTypes.getEntry(sType->getID());
|
||||
if (mod == nullptr)
|
||||
return false;
|
||||
|
||||
mod->DefaultDelay.set(sType->DefaultDelay.get());
|
||||
mod->DefaultDuration.set(sType->DefaultDuration.get());
|
||||
mod->Fixed.set(sType->Fixed.get());
|
||||
mod->GroupName.set(sType->GroupName.get());
|
||||
mod->Name.set(sType->Name.get());
|
||||
mod->ProjectIndepended.set(sType->ProjectIndepended.get());
|
||||
|
||||
mCallbacks.onSpitType(ObjectEvent::Changed, false, *mod);
|
||||
sendTypeInternal(*mod);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* If a client has removed a SPITType where id == p_SPITTypeID, it should send it to the server.
|
||||
* The corresponding SPITType if listet in SPITType will be deleted.
|
||||
* An eventRemoveSPITType will occur.
|
||||
* If a connection to a SPITServer is established, the SPITServer may dicard the deletion an an eventSPITType will occur
|
||||
* -> the SPITType will be listet again.
|
||||
* @param p_SPITType the id of the SPITType ro remove
|
||||
* @return 0 = is sended, -1 = is not sended
|
||||
*/
|
||||
int SProcessor::removeType(string sType) {
|
||||
if (sType.length() <= 0)
|
||||
return -1;
|
||||
SType *mod = mSpitTypes.getEntry(sType);
|
||||
if (mod == nullptr)
|
||||
return -1;
|
||||
Message::RemoveType sMRemoveSPITType;
|
||||
mod->fillMessageOP(sMRemoveSPITType);
|
||||
mCallbacks.onSpitType(ObjectEvent::Deleted, false, *mod);
|
||||
mSpitTypes.removeEntry(*mod);
|
||||
return mInternCallbacks.sendMessage(sMRemoveSPITType);
|
||||
}
|
||||
|
||||
SType *SProcessor::createType(string id, string groupName, string name, uint64_t delay, uint64_t duration, bool fixed, bool projIndipendent) {
|
||||
bool boolNew;
|
||||
SType *sType = mSpitTypes.getEntry(id);
|
||||
if (sType == nullptr) {
|
||||
auto spitType = make_unique<SType>(id, groupName, name, fixed, projIndipendent, delay, duration);
|
||||
sType = spitType.get();
|
||||
mSpitTypes.addEntry(move(spitType));
|
||||
sType->setCreatedByClient(true);
|
||||
boolNew = true;
|
||||
} else {
|
||||
sType->GroupName.set(groupName);
|
||||
sType->Name.set(name);
|
||||
sType->Fixed.set(fixed);
|
||||
sType->ProjectIndepended.set(projIndipendent);
|
||||
sType->DefaultDelay.set(delay);
|
||||
sType->DefaultDuration.set(duration);
|
||||
boolNew = false;
|
||||
}
|
||||
|
||||
auto eventType = boolNew ? ObjectEvent::Created : ObjectEvent::Changed;
|
||||
mCallbacks.onSpitType(eventType, false, *sType);
|
||||
sendTypeInternal(*sType);
|
||||
return sType;
|
||||
}
|
||||
|
||||
/**
|
||||
* If a client has changed a SPITType, it should send it to the server to transmit the changes.
|
||||
* If the SPITType is a fixed one and not created by the client himself, then an eventSPITType can be sent from the server, which cancels the changes.
|
||||
* @param sType the pointer to the changed SPITType
|
||||
* @return 0 = is sended, -1 = is not sended
|
||||
*/
|
||||
int SProcessor::sendTypeInternal(SType &sType) {
|
||||
Message::ReportType reportType;
|
||||
sType.fillMessageOP(reportType);
|
||||
return mInternCallbacks.sendMessage(reportType);
|
||||
}
|
||||
|
||||
//----- SPIT Objects ------
|
||||
void SProcessor::parseReportObject(Message::ReportObject &message) {
|
||||
ESP_LOGD(TAG, "parse ReportObject %s", message.getID().c_str());
|
||||
SObject *sObject;
|
||||
SType *sType;
|
||||
bool isNew = false;
|
||||
bool isRemote = false;
|
||||
bool isChanged = false;
|
||||
{
|
||||
ESP_LOGD(TAG, "parse ReportObject %s / %s", message.getID().c_str(), message.getName().c_str());
|
||||
if (message.getServerRemote() == false) {
|
||||
isRemote = false;
|
||||
sObject = mSpitObjects.getEntry(message.getID());
|
||||
sType = mSpitTypes.getEntry(message.getSPITTypeID());
|
||||
|
||||
if (sType == nullptr) {
|
||||
// No associated SPITType found
|
||||
if (sObject != nullptr) {
|
||||
// SpitObject exists but has no corresponding SPITType -> remove the SPITObject without sending a message
|
||||
// Maybe the SPITObject has changed the SPITTypeID to a SPITTypeID of another board
|
||||
mSpitObjects.removeEntry(*sObject);
|
||||
}
|
||||
return;
|
||||
}
|
||||
bool isKnownType = sType != nullptr;
|
||||
if (sObject == nullptr) {
|
||||
// The user's app my veto the SPITType
|
||||
if (mCallbacks.onCheckSpitObjectVeto(isKnownType) == false) {
|
||||
ESP_LOGW(TAG, "ObjectVeto %s / %s", message.getID().c_str(), message.getName().c_str());
|
||||
return;
|
||||
}
|
||||
auto spitObj = make_unique<SObject>(message.getID());
|
||||
sObject = spitObj.get();
|
||||
mSpitObjects.addEntry(move(spitObj));
|
||||
isNew = true;
|
||||
} else {
|
||||
// The user's app my veto the SPITType
|
||||
if (mCallbacks.onCheckSpitObjectVeto(isKnownType) == false) {
|
||||
// SpitObject exists and User APp denied the SPITObject -> remove the SPITObject without sending a message
|
||||
mSpitObjects.removeEntry(*sObject);
|
||||
ESP_LOGW(TAG, "TypeVeto %s / %s", message.getID().c_str(), message.getName().c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
isRemote = true;
|
||||
sObject = mRemoteSpitObjects.getEntry(message.getID());
|
||||
sType = mRemoteSpitTypes.getEntry(message.getSPITTypeID());
|
||||
if (sType == nullptr) {
|
||||
// No associated SPITType found
|
||||
return;
|
||||
}
|
||||
if (sObject == nullptr) {
|
||||
auto spitObj = make_unique<SObject>(message.getID());
|
||||
sObject = spitObj.get();
|
||||
mRemoteSpitObjects.addEntry(move(spitObj));
|
||||
isNew = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sObject->setMessageOP(&message)) {
|
||||
ESP_LOGE(TAG, "Change not allowed %s", sObject->getID().c_str());
|
||||
// setMessageOP error - the SPITObject can't be changed, it may be fixed and crated by this client
|
||||
sendObjectInternal(sObject);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isRemote == false) {
|
||||
// set the virtual action assignment
|
||||
sObject->setActionAssignedType(sType->ActionAssigned.get());
|
||||
}
|
||||
|
||||
auto eventType = isNew ? ObjectEvent::Created : ObjectEvent::Changed;
|
||||
mCallbacks.onSpitObject(eventType, isRemote, *sType, *sObject);
|
||||
|
||||
if (sObject->setConfirmed(true) == true || isChanged == true) {
|
||||
// Confirmation has changed
|
||||
sendObjectInternal(sObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SProcessor::parseRemoveObject(Message::RemoveObject &message) {
|
||||
ESP_LOGD(TAG, "parse RemoveObject %s", message.getID().c_str());
|
||||
SObject *sObject = mRemoteSpitObjects.getEntry(message.getID());
|
||||
if (sObject != nullptr) {
|
||||
SType *sType = mRemoteSpitTypes.getEntry(sObject->SPITTypeID.get());
|
||||
if (sType != nullptr) {
|
||||
mCallbacks.onSpitObject(ObjectEvent::Deleted, true, *sType, *sObject);
|
||||
}
|
||||
mRemoteSpitObjects.removeEntry(*sObject);
|
||||
} else {
|
||||
// call removeObject, this will notify the server that the object is removed
|
||||
removeObject(message.getID());
|
||||
}
|
||||
}
|
||||
|
||||
int SProcessor::sendObjectInternal(SObject *sObject) {
|
||||
if (sObject == nullptr)
|
||||
return -1;
|
||||
Message::ReportObject reportObject;
|
||||
sObject->fillMessageOP(reportObject);
|
||||
return mInternCallbacks.sendMessage(reportObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* When the client changes an existing SPITObject (normally the client should not do this), then he should send the changes to the SPITServer.
|
||||
* An eventSPITObject occurs.
|
||||
* The SPITServer may discard the changes and a second eventSPITObject occurs to correct the changes.
|
||||
* @param obj the pointer to the changed SPITObject.
|
||||
* @return 0 = sended, -1 = not sended
|
||||
*/
|
||||
bool SProcessor::changeObject(SObject *sObject) {
|
||||
if (sObject == nullptr)
|
||||
return false;
|
||||
SObject *mod = mSpitObjects.getEntry(sObject->getID());
|
||||
if (mod == nullptr)
|
||||
return false;
|
||||
SType *sType = mSpitTypes.getEntry(sObject->getID());
|
||||
if (mod == nullptr)
|
||||
return false;
|
||||
|
||||
mod->setActionAssigned(sObject->getActionAssigned());
|
||||
mod->Delay.set(sObject->Delay.get());
|
||||
mod->Duration.set(sObject->Delay.get());
|
||||
mod->Fixed.set(sObject->Fixed.get());
|
||||
mod->FrameFadeInLength.set(sObject->FrameFadeInLength.get());
|
||||
mod->FrameFadeOutLength.set(sObject->FrameFadeOutLength.get());
|
||||
mod->FrameLength.set(sObject->FrameLength.get());
|
||||
mod->FrameStart.set(sObject->FrameStart.get());
|
||||
mod->Name.set(sObject->Name.get());
|
||||
mod->Remark.set(sObject->Remark.get());
|
||||
mod->SPITTypeID.set(sObject->SPITTypeID.get());
|
||||
mod->ServerParams.set(sObject->ServerParams.get());
|
||||
mod->Track.set(sObject->Track.get());
|
||||
mod->ValueFactor.set(sObject->ValueFactor.get());
|
||||
mod->ValueUse.set(sObject->ValueUse.get());
|
||||
|
||||
mCallbacks.onSpitObject(ObjectEvent::Changed, false, *sType, *mod);
|
||||
sendObjectInternal(mod);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* When the client wants to remove a SPITObject with id == p_SPITObjectID (normally the client should not do this), then he should send it to the SPITServer.
|
||||
* The corresponding SPITObject if listet in SPITObject will be deleted.
|
||||
* An eventRemoveSPITType will occur.
|
||||
* If an connection to a SPITServer is established, the SPITServer may discard the deletion and an eventSPITObject occurs
|
||||
* -> the SPITObject is listet again.
|
||||
* @param objId the id of the SPITObject to remove.
|
||||
* @return 0 = sended to the SPITServer, -1 = not sended to the SPITServer or nos SPITObject with id == objId exists.
|
||||
*/
|
||||
int SProcessor::removeObject(string objId) {
|
||||
if (objId.length() <= 0)
|
||||
return -1;
|
||||
Message::RemoveObject remObj;
|
||||
|
||||
SObject *sObject = mSpitObjects.getEntry(objId);
|
||||
if (sObject == nullptr)
|
||||
return -1;
|
||||
SType *sType = mSpitTypes.getEntry(sObject->SPITTypeID.get());
|
||||
if (sType == nullptr)
|
||||
return -1;
|
||||
|
||||
sObject->fillMessageOP(remObj);
|
||||
mCallbacks.onSpitObject(ObjectEvent::Deleted, false, *sType, *sObject);
|
||||
mSpitObjects.removeEntry(*sObject);
|
||||
return mInternCallbacks.sendMessage(remObj);
|
||||
}
|
||||
|
||||
// //----- Play SPIT Objects ------
|
||||
|
||||
void SProcessor::parsePlayStart(Message::PlayStart &message) {
|
||||
ESP_LOGD(TAG, "parse PlayStart ID: %s Name: %s Running: %d", message.getPlayerID().c_str(), message.getPlayerName().c_str(), (int)message.getPlayerIsRunning());
|
||||
SPlayer &player = SPlayer::getSPITPlayer(message.getPlayerID());
|
||||
player.setIsRunning(message.getPlayerIsRunning());
|
||||
player.setName(message.getPlayerName());
|
||||
player.setType(message.getPlayerType());
|
||||
player.setTime(message.getTime());
|
||||
player.startPlaySequence();
|
||||
|
||||
mCallbacks.onPlay(player.getID(), player.getName(), player.getType(), player.getIsRunning(), player.getTime());
|
||||
}
|
||||
|
||||
void SProcessor::parsePlayObject(Message::PlayObject &message) {
|
||||
|
||||
SPlayer &player = SPlayer::getSPITPlayer(message.getPlayerID());
|
||||
SObject *sObject = mSpitObjects.getEntry(message.getSPITObjectID());
|
||||
|
||||
if (sObject == nullptr) {
|
||||
return;
|
||||
}
|
||||
SPlayer::PlayType *playType = player.addTypePlaying(sObject->SPITTypeID.get());
|
||||
if (playType == nullptr) {
|
||||
return;
|
||||
}
|
||||
playType->setPlayerIsRunning(player.getIsRunning());
|
||||
playType->addValue(((double)message.getValue() / 1000000), ((double)sObject->ValueFactor.get() / 1000000));
|
||||
}
|
||||
|
||||
void SProcessor::parsePlayEnd(Message::PlayEnd &message) {
|
||||
SPlayer &sPlayer = SPlayer::getSPITPlayer(message.getPlayerID());
|
||||
|
||||
auto &playTypes = sPlayer.getPlaySPITTypes();
|
||||
for (auto i = playTypes.begin(); i != playTypes.end(); i++) {
|
||||
SPlayer::PlayType *playType = i->second.get();
|
||||
bool isStarted = playType->getIsNew();
|
||||
bool iStopped = (playType->getIsPlayed() == false);
|
||||
SType *ptrSPITType = mSpitTypes.getEntry(playType->getSPITTypeID());
|
||||
if (ptrSPITType == nullptr) {
|
||||
// set isplayed to false, the ptrPlayObject will be removed by sPlayer.endPlaySequence()
|
||||
playType->setIsPlayed(false);
|
||||
continue;
|
||||
}
|
||||
mCallbacks.onPlaySpitType(playType->getPlayerID(), playType->getSPITTypeID(), playType->getPlayerIsRunning(), isStarted, iStopped, playType->getValue());
|
||||
}
|
||||
sPlayer.endPlaySequence();
|
||||
}
|
||||
|
||||
void SProcessor::parseReportPlayStatus(Message::ReportPlayStatus &message) {
|
||||
SType *sType = NULL;
|
||||
|
||||
SObject *sObject = mRemoteSpitObjects.getEntry(message.getID());
|
||||
if (sObject == nullptr) {
|
||||
sObject = mSpitObjects.getEntry(message.getID());
|
||||
if (sObject == nullptr) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (sObject->ServerRemote.get() == false) {
|
||||
sType = mSpitTypes.getEntry(sObject->SPITTypeID.get());
|
||||
} else {
|
||||
sType = mRemoteSpitTypes.getEntry(sObject->SPITTypeID.get());
|
||||
}
|
||||
|
||||
if (sType == nullptr) {
|
||||
return;
|
||||
}
|
||||
mCallbacks.onPlayRemoteObjectStatus(*sType, *sObject, message.getFrameInsideObject(), message.getValue());
|
||||
}
|
||||
|
||||
// //--------- Remote ----------
|
||||
|
||||
/**
|
||||
* Send a remote control command to the SPITServer.
|
||||
* There may be different command types:
|
||||
* a) Some don't use value e.g. go to next scene, olay a jingle
|
||||
* b) Some use values e.g. the main sound fader
|
||||
* @param sObjectId the id of the remote SPITObject (this represents the remote control command)
|
||||
* @param valuePpm the value in ppm
|
||||
* in case a) the value is ignored
|
||||
* in case b) the value is used
|
||||
* @param valueFlag indicates how the server should use valuePpm:
|
||||
* VALUEFLAG_DIRECT (0x00) The server overwrites its existing value with p_Value
|
||||
* VALUEFLAG_INCREASE (0x01) The server increases his existing value by valuePpm
|
||||
* VALUEFLAG_DECREASE (0x01) The server decreases his existing value by valuePpm
|
||||
*/
|
||||
void SProcessor::sendRemoteObject(string sObjectId, int valuePpm, char valueFlag) {
|
||||
SObject *remoteObject = mRemoteSpitObjects.getEntry(sObjectId);
|
||||
if (remoteObject == nullptr)
|
||||
return;
|
||||
|
||||
Message::PlayStart playSPITStart(mGuid, mProjName, SPlayer::PTYPE_DISCRETE);
|
||||
playSPITStart.setPlayerIsRunning(false);
|
||||
playSPITStart.setTime(0);
|
||||
mInternCallbacks.sendMessage(playSPITStart);
|
||||
|
||||
Message::PlayObject playSPITObject;
|
||||
playSPITObject.setID(remoteObject->getID());
|
||||
playSPITObject.setPlayPrePosition(false);
|
||||
playSPITObject.setPlayerID(mGuid);
|
||||
playSPITObject.setValue(valuePpm);
|
||||
playSPITObject.setValueFlag(valueFlag);
|
||||
mInternCallbacks.sendMessage(playSPITObject);
|
||||
|
||||
Message::PlayEnd playSPITEnd(mGuid);
|
||||
mInternCallbacks.sendMessage(playSPITEnd);
|
||||
}
|
||||
|
||||
} // namespace spit
|
||||
@@ -0,0 +1,213 @@
|
||||
#include "spit_socket.hpp"
|
||||
#include "Task.hpp"
|
||||
#include "esp/uuid.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace spit {
|
||||
|
||||
/**
|
||||
* Creates socket. If there exist some, they will be closed.
|
||||
* @return
|
||||
*/
|
||||
bool Socket::createSocketUdp() {
|
||||
if (mUdpSocketIp4 != INVALID_SOCKET) {
|
||||
close(mUdpSocketIp4);
|
||||
mUdpSocketIp4 = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
mUdpSocketIp4 = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
int intResult = -1;
|
||||
bool success = true;
|
||||
if (mUdpSocketIp4 == INVALID_SOCKET) {
|
||||
success = false;
|
||||
} else {
|
||||
int intOptionValue4 = 1;
|
||||
intResult = setsockopt(mUdpSocketIp4, SOL_SOCKET, SO_REUSEADDR, (void *)&intOptionValue4, sizeof(intOptionValue4));
|
||||
}
|
||||
if (intResult < 0) {
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (success == true) {
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 1; // seconds
|
||||
tv.tv_usec = 0; // microseconds (0.0 sec)
|
||||
// This sets the receive timeout
|
||||
setsockopt(mUdpSocketIp4, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
||||
|
||||
// set the mUdpSocketIp4 in NON BLOCKING mode because
|
||||
// if the UDPSockets are in blocking mode the receiveMessage does not return in every constellation
|
||||
// int intFlags;
|
||||
// intFlags = fcntl(mUdpSocketIp4, F_GETFL);
|
||||
// fcntl(mUdpSocketIp4, F_SETFL, intFlags | O_NONBLOCK);
|
||||
|
||||
// int intBufferSize = 2046 * 512;
|
||||
// intResult = setsockopt(mUdpSocketIp4, SOL_SOCKET, SO_RCVBUF, (void*)&intBufferSize, sizeof(intBufferSize));
|
||||
// intResult = setsockopt(mUdpSocketIp4, SOL_SOCKET, SO_SNDBUF, (void*)&intBufferSize, sizeof(intBufferSize));
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the socket to the client's ip address and port. The socket option SO_REUSEADDR is set to true.
|
||||
* @param clientIp ip address of the client
|
||||
* @param clientPort port of the client.
|
||||
* if p_Port == "0" any free port will be used
|
||||
* @return -1 id bind fails, o if bind is ok
|
||||
*
|
||||
*/
|
||||
int Socket::bindSocket(string clientIp, string clientPort) {
|
||||
string stringClientPort;
|
||||
if (clientPort.length() <= 0)
|
||||
stringClientPort = "0";
|
||||
else
|
||||
stringClientPort = clientPort;
|
||||
addrinfo *clientAI;
|
||||
if (getaddrinfo(clientIp.data(), clientPort.data(), nullptr, &clientAI) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (mUdpSocketIp4 == INVALID_SOCKET) {
|
||||
return -1;
|
||||
}
|
||||
auto *socketAdr = (sockaddr_in *)(clientAI->ai_addr);
|
||||
int intSocklen = sizeof(*socketAdr);
|
||||
// copy the socketAdr, may be not filled correct by getaddrinfo
|
||||
sockaddr_in o_IP4SocketAddress;
|
||||
memset(&o_IP4SocketAddress, 0, intSocklen);
|
||||
o_IP4SocketAddress.sin_addr = socketAdr->sin_addr;
|
||||
o_IP4SocketAddress.sin_family = socketAdr->sin_family;
|
||||
o_IP4SocketAddress.sin_port = socketAdr->sin_port;
|
||||
int intResult = bind(mUdpSocketIp4, (const sockaddr *)(socketAdr), (socklen_t)intSocklen);
|
||||
if (intResult < 0) {
|
||||
return intResult;
|
||||
}
|
||||
if ((intResult == 0) &&
|
||||
(getsockname(mUdpSocketIp4, (sockaddr *)socketAdr, (socklen_t *)&intSocklen) != -1)) {
|
||||
setBoundClientPort(to_string((int)socketAdr->sin_port));
|
||||
}
|
||||
freeaddrinfo(clientAI);
|
||||
|
||||
setIsBound(intResult != -1);
|
||||
return intResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the socket are broadcast sockets or not. Set the sockopt SO_BROADCAST to 0 or 1
|
||||
* @param p_Broadcast true SO_BROADCAST is set
|
||||
* false = SO_BROADCAST is not set
|
||||
* @return 0 = ok, -1 = error
|
||||
*/
|
||||
int Socket::setBroadcast(bool broadcast) const {
|
||||
int intOptionValue = broadcast ? 1 : 0;
|
||||
int intResult = setsockopt(mUdpSocketIp4, SOL_SOCKET, SO_BROADCAST, &intOptionValue, sizeof(int));
|
||||
return intResult;
|
||||
}
|
||||
/**
|
||||
* @param ip the ip address of the server
|
||||
* @param port the port of the server
|
||||
* @param msg buffer containing the message
|
||||
* @param msgLength the length of the message
|
||||
* @return -1 send fails
|
||||
* 0 send is done
|
||||
*/
|
||||
int Socket::sendMessageTo(const string &ip, const string &port, const char *message, int msgLength) {
|
||||
if (mUdpSocketIp4 == INVALID_SOCKET) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// prepare AddressInfo
|
||||
int intResult = -1;
|
||||
addrinfo *ptrAddressInfo;
|
||||
if (getaddrinfo(ip.data(), port.data(), nullptr, &ptrAddressInfo) != 0) {
|
||||
return intResult;
|
||||
}
|
||||
|
||||
auto *ptrIP4SocketAddress = (sockaddr_in *)(ptrAddressInfo->ai_addr);
|
||||
int intSocklen = sizeof(*ptrIP4SocketAddress);
|
||||
if (mUdpSocketIp4 != INVALID_SOCKET) {
|
||||
// copy the ptrIP4SocketAddress, may be not filled correct by getaddrinfo
|
||||
sockaddr_in o_IP4SocketAddress;
|
||||
memset(&o_IP4SocketAddress, 0, intSocklen);
|
||||
o_IP4SocketAddress.sin_addr = ptrIP4SocketAddress->sin_addr;
|
||||
o_IP4SocketAddress.sin_family = ptrIP4SocketAddress->sin_family;
|
||||
o_IP4SocketAddress.sin_port = ptrIP4SocketAddress->sin_port;
|
||||
intResult = sendto(mUdpSocketIp4, message, msgLength, 0, (const sockaddr *)&o_IP4SocketAddress, (socklen_t)intSocklen);
|
||||
if (intResult < 0) {
|
||||
ESP_LOGE("SPIT", "sendMessageTo IP4 sendto %s FAILED %s\n", ip.data(), strerror(errno));
|
||||
ESP_LOGE("SPIT", "sendMessageTo ret=%d %s / %s / %d\n", intResult, port.data(), message, msgLength);
|
||||
}
|
||||
freeaddrinfo(ptrAddressInfo);
|
||||
}
|
||||
if (intResult < 0) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* Call this for receiving a message
|
||||
* This will block until a message is received or the underlying socket is closed
|
||||
* @param p_Buffer the buffer containing the received message later.
|
||||
* @param p_BufferLength the size of the buffer
|
||||
* @param p_ServerIP a pointer to a string, when a messages is received the string will contain the ip address of the sender/server
|
||||
* if p_ServerIP is NULL - no detection of the server will be done.
|
||||
* @return
|
||||
*/
|
||||
int Socket::receiveMessage(char *p_Buffer, int p_BufferLength, string *serverIp) const {
|
||||
int intResult = -1;
|
||||
if (mUdpSocketIp4 == INVALID_SOCKET) {
|
||||
return intResult;
|
||||
}
|
||||
sockaddr_in o_IP4SocketAddress;
|
||||
int intSocklen = sizeof(o_IP4SocketAddress);
|
||||
memset(&o_IP4SocketAddress, 0, intSocklen);
|
||||
intResult = recvfrom(mUdpSocketIp4, (void *)p_Buffer, p_BufferLength, 0, (sockaddr *)&o_IP4SocketAddress, (socklen_t *)&intSocklen);
|
||||
if (intResult >= 0 && serverIp != nullptr) {
|
||||
char bufferIP4[INET_ADDRSTRLEN];
|
||||
inet_ntop(AF_INET, &(o_IP4SocketAddress.sin_addr), bufferIP4, intSocklen);
|
||||
*serverIp = bufferIP4;
|
||||
}
|
||||
return intResult;
|
||||
}
|
||||
|
||||
ISocket::IpAddress Socket::getAdaptersInfo() {
|
||||
// iterate over active interfaces, and print out IPs
|
||||
esp_netif_t *netif = nullptr;
|
||||
if ((netif = esp_netif_next_unsafe(netif)) != nullptr) {
|
||||
ESP_LOGI("SPIT", "Connected to %s", esp_netif_get_desc(netif));
|
||||
esp_netif_ip_info_t ip;
|
||||
ESP_ERROR_CHECK(esp_netif_get_ip_info(netif, &ip));
|
||||
ESP_LOGI("SPIT", "- IPv4 address: " IPSTR ",", IP2STR(&ip.ip));
|
||||
ESP_LOGI("SPIT", "- IPv4 netmask: " IPSTR ",", IP2STR(&ip.netmask));
|
||||
char buffer[64];
|
||||
sprintf(buffer, IPSTR, IP2STR(&ip.ip));
|
||||
string stringIP4 = buffer;
|
||||
sprintf(buffer, IPSTR, IP2STR(&ip.netmask));
|
||||
string stringSubnet = buffer;
|
||||
return {stringIP4, stringSubnet};
|
||||
}
|
||||
return {"0.0.0.0", "0.0.0.0"};
|
||||
}
|
||||
|
||||
string createGUID() {
|
||||
uuid_t uu;
|
||||
uuid_generate(uu);
|
||||
char uu_str[UUID_STR_LEN];
|
||||
uuid_unparse(uu, uu_str);
|
||||
return {uu_str};
|
||||
}
|
||||
|
||||
string getClientHostName() {
|
||||
const char *hostname;
|
||||
esp_netif_get_hostname(esp_netif_get_default_netif(), &hostname);
|
||||
if (hostname != nullptr) {
|
||||
return {hostname};
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user