1331 lines
52 KiB
C++
1331 lines
52 KiB
C++
|
|
#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),
|
|
mName(name),
|
|
mProjName(projName),
|
|
mProcessor(name, projName, getClientHostName(), createGUID(), calls,
|
|
{
|
|
.sendSpitTypeInternal = [&](SType &sType) { return sendSPITTypeInternal(sType);}
|
|
}
|
|
),
|
|
mSocket(socket) {
|
|
mID = createGUID();
|
|
mComputerName = getClientHostName();
|
|
}
|
|
|
|
SClient::~SClient() {
|
|
stopServerAutoDetect();
|
|
disconnectInternal(Connection::None);
|
|
}
|
|
|
|
void SClient::clientProjectClose() {
|
|
if (mConnectionStatus == Connection::Ok) {
|
|
ESP_LOGI(TAG, " ... tidy up ressources");
|
|
Message::CloseProject closeSPITProject(mProjName);
|
|
if (sendSPITMessage(closeSPITProject) < 0) {
|
|
ESP_LOGD(TAG, "Client could not send Client's ReportProject");
|
|
}
|
|
}
|
|
}
|
|
|
|
void SClient::clientProjectLoaded() {
|
|
// set confirmed = false for all SPITTypes and SPITObjects
|
|
SType::setConfirmedFalse(mProcessor.mSpitTypes);
|
|
SObject::setConfirmedFalse(mSpitObjects);
|
|
|
|
if (mConnectionStatus == Connection::Ok) {
|
|
// send ReportProject to the server
|
|
Message::ReportProject reportSPITProject(mProjName, mProjName);
|
|
reportSPITProject.setFramesPerSecond(0);
|
|
if (sendSPITMessage(reportSPITProject) < 0) {
|
|
ESP_LOGD(TAG, "SPIT_CLient::clientProjectLoaded could not send Client's ReportProject");
|
|
}
|
|
|
|
// send RequestProjectTransfer
|
|
Message::RequestProjectTransfer sMRequestProjectTransfer;
|
|
if (sendSPITMessage(sMRequestProjectTransfer) < 0) {
|
|
ESP_LOGD(TAG, "SPIT_CLient::clientProjectLoaded could not send RequestProjectTransfer");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 SClient::setActionSPITType(string typeId, bool actionAssigned) {
|
|
SType *spitType = mProcessor.mSpitTypes.getEntry(typeId);
|
|
if (spitType == nullptr)
|
|
return;
|
|
if (spitType->ActionAssigned.set(actionAssigned)) {
|
|
sendSPITTypeInternal(*spitType);
|
|
for (auto& [key, value] : mSpitObjects.getRepo()) {
|
|
if (value->SPITTypeID.get() == spitType->getID()) {
|
|
if (value->setActionAssignedType(actionAssigned) == true) {
|
|
sendSPITObjectInternal(value.get());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** This should be called if the user wants to associate/remove an action with a SPITObject. */
|
|
void SClient::setActionSPITObject(string spitObjId, bool actionAssigned) {
|
|
SObject *spitObject = mSpitObjects.getEntry(spitObjId);
|
|
if (spitObject == nullptr)
|
|
return;
|
|
if (spitObject->setActionAssigned(actionAssigned) == true) {
|
|
sendSPITObjectInternal(spitObject);
|
|
}
|
|
}
|
|
//---------- 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::sendSPITMessage(Message::OPPart &opPart) {
|
|
if (mMessageNumberSend == 255)
|
|
mMessageNumberSend = 1;
|
|
else
|
|
mMessageNumberSend++;
|
|
opPart.setMessageNumber(mMessageNumberSend);
|
|
opPart.setServerInfoInHeader(mID, 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[MAXSPITMESSAGEBYTECOUNT + 1];
|
|
int rdLen;
|
|
while (rM.mStopReceiveMessages == false) {
|
|
rdLen = rM.mSocket.receiveMessage(buffer, MAXSPITMESSAGEBYTECOUNT, 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.parseMessage(move(spitMsg));
|
|
}
|
|
}
|
|
|
|
|
|
void SClient::resetMessageNumberReceived() {
|
|
mMessageNumberReceived = -1;
|
|
mOvertakingMessages.clear();
|
|
}
|
|
|
|
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 intActualMessageNumber = (int)(p_SPITMessage->getHeader()->getMessageNumber() & 0xFF);
|
|
|
|
if (intActualMessageNumber == 0) {
|
|
parseMessage(std::move(p_SPITMessage));
|
|
return;
|
|
}
|
|
if (mMessageNumberReceived < 0) {
|
|
mMessageNumberReceived = intActualMessageNumber;
|
|
parseMessage(std::move(p_SPITMessage));
|
|
return;
|
|
}
|
|
|
|
int intNextMessageNumber = mMessageNumberReceived + 1;
|
|
if (intNextMessageNumber > 255 || intNextMessageNumber < 1)
|
|
intNextMessageNumber = 1;
|
|
|
|
if (intActualMessageNumber == intNextMessageNumber) {
|
|
mMessageNumberReceived = intActualMessageNumber;
|
|
parseMessage(std::move(p_SPITMessage));
|
|
|
|
// check overtaking messages
|
|
auto it = mOvertakingMessages.begin();
|
|
while (mOvertakingMessages.size() > 0) {
|
|
|
|
intNextMessageNumber = mMessageNumberReceived + 1;
|
|
if (intNextMessageNumber > 255 || intNextMessageNumber < 1)
|
|
intNextMessageNumber = 1;
|
|
|
|
auto &ptrOvertakingSPIT_Message = *it;
|
|
intActualMessageNumber = (int)(ptrOvertakingSPIT_Message->getHeader()->getMessageNumber() & 0xFF);
|
|
|
|
if (intActualMessageNumber == intNextMessageNumber) {
|
|
mMessageNumberReceived = intActualMessageNumber;
|
|
parseMessage(move(ptrOvertakingSPIT_Message));
|
|
mOvertakingMessages.erase(it);
|
|
|
|
it = mOvertakingMessages.begin();
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
// p_SPITMessage has overtaken another message
|
|
addOvertakingMessage(move(p_SPITMessage));
|
|
}
|
|
}
|
|
|
|
void SClient::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));
|
|
}
|
|
|
|
void SClient::parseMessage(unique_ptr<Message> message) {
|
|
answerWatchdog();
|
|
if (!message || !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::ReportProject::OPFLAG:
|
|
parseReportProject(message->getAs<Message::ReportProject>());
|
|
break;
|
|
case Message::Watchdog::OPFLAG:
|
|
parseWatchdog(message->getAs<Message::Watchdog>());
|
|
break;
|
|
case Message::WatchdogAnswer::OPFLAG:
|
|
parseWatchdogAnswer(message->getAs<Message::WatchdogAnswer>());
|
|
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:
|
|
mProcessor.parseReportType(message->getAs<Message::ReportType>());
|
|
break;
|
|
case Message::RemoveType::OPFLAG:
|
|
parseRemoveSPITType(message->getAs<Message::RemoveType>());
|
|
//----- SPIT Objects ------
|
|
break;
|
|
case Message::ReportObject::OPFLAG:
|
|
parseReportSPITObject(message->getAs<Message::ReportObject>());
|
|
break;
|
|
case Message::RemoveObject::OPFLAG:
|
|
parseRemoveSPITObject(message->getAs<Message::RemoveObject>());
|
|
break;
|
|
//----- Play SPIT Objects ------
|
|
case Message::PlayStart::OPFLAG:
|
|
parsePlaySPITStart(message->getAs<Message::PlayStart>());
|
|
break;
|
|
case Message::PlayObject::OPFLAG:
|
|
parsePlaySPITObject(message->getAs<Message::PlayObject>());
|
|
break;
|
|
case Message::PlayEnd::OPFLAG:
|
|
parsePlaySPITEnd(message->getAs<Message::PlayEnd>());
|
|
break;
|
|
case Message::ReportPlayStatus::OPFLAG:
|
|
parseReportPlayStatus(message->getAs<Message::ReportPlayStatus>());
|
|
break;
|
|
default:
|
|
ESP_LOGD(TAG, "Client::parseMessage: Unknown 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;
|
|
}
|
|
Message::Connect sMConnect(mComputerName, mName);
|
|
sMConnect.setLoginName(loginName);
|
|
sMConnect.setLoginPassword(loginPassword);
|
|
sMConnect.setServerIP(serverIP);
|
|
if (serverPort.length() <= 0) {
|
|
serverPort = "0";
|
|
}
|
|
int intServerPort = stoi(serverPort);
|
|
sMConnect.setServerPort(intServerPort);
|
|
|
|
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 (sendSPITMessage(sMConnect) < 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;
|
|
sendSPITMessage(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, mServerProjectID);
|
|
|
|
// 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() != mID)
|
|
return;
|
|
|
|
mServerID = message.getServerID();
|
|
mServerComputerName = message.getServerComputerName();
|
|
mServerName = message.getServerName();
|
|
mServerStepTime = message.getStepTime();
|
|
|
|
if (message.getConnected() == false) {
|
|
disconnectInternal(Connection::NoneServer);
|
|
} else {
|
|
message.getStepTime();
|
|
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;
|
|
/*
|
|
function<void()> o_Callable = bind(&SClient::disconnectInternal, this, intCauseDisconnect);
|
|
SPIT_Handler::addCallable(o_Callable);
|
|
*/
|
|
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) {
|
|
// ESP_LOGD(TAG, "parse Watchdog ");
|
|
Message::WatchdogAnswer o_WatchdogAnswer;
|
|
sendSPITMessage(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 = sendSPITMessage(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);
|
|
}
|
|
}
|
|
|
|
//----- SPIT Project ------
|
|
void SClient::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(mProcessor.mSpitTypes);
|
|
SObject::setConfirmedFalse(mSpitObjects);
|
|
|
|
// Send RequestProjectTransfer
|
|
Message::RequestProjectTransfer sMRequestProjectTransfer;
|
|
if (sendSPITMessage(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 (sendSPITMessage(reportProject) < 0) {
|
|
ESP_LOGD(TAG, "Client::parseReportProject could not send Client's ReportProject");
|
|
}
|
|
}
|
|
void SClient::parseCloseProject(Message::CloseProject &p_CloseSPITProject) {
|
|
ESP_LOGD(TAG, "parse CloseProject");
|
|
string stringProjectID;
|
|
mCallbacks.onProjectUpdate(ProjUpdate::ServerClosed, p_CloseSPITProject.getProjectID());
|
|
SPlayer::clearPlayers();
|
|
}
|
|
|
|
string SClient::getServerProjectID() { return mServerProjectID; }
|
|
string SClient::getServerProjectName() { return mServerProjectName; }
|
|
int SClient::getServerFramesPerSecond() { return mServerFramesPerSecond; }
|
|
|
|
void SClient::parseRequestProjectTransfer(Message::RequestProjectTransfer &message) {
|
|
ESP_LOGD(TAG, "parse RequestProjectTransfer");
|
|
// StartProjectTransfer
|
|
Message::StartProjectTransfer sMStartProjectTransfer;
|
|
if (sendSPITMessage(sMStartProjectTransfer) < 0) {
|
|
return;
|
|
}
|
|
|
|
// send all listes SPITTypes
|
|
for (const auto& [key, value] : mProcessor.mSpitTypes.getRepo()) {
|
|
sendSPITTypeInternal(*value);
|
|
fos::Task::delayMs(10);
|
|
}
|
|
for (auto& [key, value] : mSpitObjects.getRepo()) {
|
|
sendSPITObjectInternal(value.get());
|
|
fos::Task::delayMs(10);
|
|
}
|
|
|
|
// EndProjectTransfer
|
|
Message::EndProjectTransfer sMEndProjectTransfer;
|
|
sendSPITMessage(sMEndProjectTransfer);
|
|
}
|
|
|
|
/* NEW IMPLEMENTATION
|
|
|
|
void SClient::parseRequestProjectTransfer(Message::RequestProjectTransfer* message) {
|
|
// ESP_LOGD(TAG, "parse RequestProjectTransfer");
|
|
thread* o_ThreadSend;
|
|
o_ThreadSend = new thread(&this->sendProject, this);
|
|
// int intResult;
|
|
// Message::StartProjectTransfer* ptrSMStartProjectTransfer;
|
|
// Message::EndProjectTransfer* ptrSMEndProjectTransfer;
|
|
// int intCount;
|
|
// SPITType* ptrSPITType;
|
|
// SObject* ptrSPITObject;
|
|
|
|
|
|
// //StartProjectTransfer
|
|
// NotifyStartProjectTransfer_Client* o_RunnableStart = new NotifyStartProjectTransfer_Client(mSPITClientInterface);
|
|
// SPIT_Handler::addRunnable(o_RunnableStart);
|
|
|
|
// ptrSMStartProjectTransfer = new Message::StartProjectTransfer();
|
|
// intResult = sendSPITMessage(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;
|
|
// intResult = sendSPITTypeInternal(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;
|
|
// intResult = sendSPITObjectInternal(ptrSPITObject);
|
|
// if (intResult < 0) return;
|
|
// mWatchdog->answerWatchdog();
|
|
// this_thread::sleep_for(chrono::milliseconds(7));
|
|
// }
|
|
// }
|
|
|
|
// //EndProjectTransfer
|
|
// ptrSMEndProjectTransfer = new Message::EndProjectTransfer();
|
|
// intResult = sendSPITMessage(ptrSMEndProjectTransfer);
|
|
// if (intResult < 0) {
|
|
// return;
|
|
// }
|
|
// mWatchdog->answerWatchdog();
|
|
// NotifyEndProjectTransfer_Client* o_RunnableEnd = new NotifyEndProjectTransfer_Client(mSPITClientInterface);
|
|
// SPIT_Handler::addRunnable(o_RunnableEnd);
|
|
|
|
// //Send act
|
|
}
|
|
*/
|
|
|
|
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->sendSPITMessage(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->sendSPITTypeInternal(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->sendSPITObjectInternal(ptrSPITObject);
|
|
// if (intResult < 0) return;
|
|
// // mWatchdog->answerWatchdog();
|
|
// this_thread::sleep_for(chrono::milliseconds(7));
|
|
// }
|
|
// // }
|
|
|
|
// //EndProjectTransfer
|
|
// ptrSMEndProjectTransfer = new Message::EndProjectTransfer();
|
|
// intResult = p_PtrSPIT_Client->sendSPITMessage(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
|
|
}
|
|
|
|
void SClient::parseStartProjectTransfer(Message::StartProjectTransfer &message) {
|
|
mCallbacks.onProjectUpdate(ProjUpdate::StartTransfer, mServerProjectID);
|
|
}
|
|
void SClient::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 (sendSPITMessage(reportSPITProject) < 0) {
|
|
ESP_LOGD(TAG, "Client::parseReportProject could not send Client's ReportProject");
|
|
}
|
|
}
|
|
|
|
void SClient::parseRemoveSPITType(Message::RemoveType &message) {
|
|
ESP_LOGD(TAG, "parse RemoveType %s", message.getID().c_str());
|
|
bool boolRemote = false;
|
|
string stringSPITTypeID = message.getID();
|
|
|
|
SType *ptrSPITType = mProcessor.mRemoteSpitTypes.getEntry(stringSPITTypeID);
|
|
if (!ptrSPITType) {
|
|
boolRemote = true;
|
|
} else {
|
|
ptrSPITType = mProcessor.mSpitTypes.getEntry(stringSPITTypeID);
|
|
if (!ptrSPITType) {
|
|
boolRemote = false;
|
|
} else {
|
|
// No SPITType with stringSPITTypeID is listet
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (ptrSPITType->getCreatedByClient() && ptrSPITType->Fixed.get()) {
|
|
// the SPITyte is created by client himself and it is fixed -> don't remove the SPITType!!
|
|
// Send the SPITTyp back to the server
|
|
sendSPITTypeInternal(*ptrSPITType);
|
|
return;
|
|
}
|
|
|
|
mCallbacks.onSpitType(ObjectEvent::Deleted, boolRemote, *ptrSPITType);
|
|
if (boolRemote == false) {
|
|
mProcessor.mSpitTypes.removeEntry(*ptrSPITType);
|
|
} else {
|
|
mProcessor.mRemoteSpitTypes.removeEntry(*ptrSPITType);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 p_SPITType the pointer to the changed SPITType
|
|
* @return 0 = is sended, -1 = is not sended
|
|
*/
|
|
int SClient::sendSPITTypeInternal(SType &p_SPITType) {
|
|
Message::ReportType sMReportSPITType;
|
|
p_SPITType.fillMessageOP(sMReportSPITType);
|
|
return sendSPITMessage(sMReportSPITType);
|
|
}
|
|
|
|
/**
|
|
* 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 SClient::changeSPITType(SType *p_SPITType) {
|
|
if (p_SPITType == nullptr)
|
|
return false;
|
|
SType *ptrSPITType = mProcessor.mSpitTypes.getEntry(p_SPITType->getID());
|
|
if (ptrSPITType == nullptr)
|
|
return false;
|
|
|
|
ptrSPITType->DefaultDelay.set(p_SPITType->DefaultDelay.get());
|
|
ptrSPITType->DefaultDuration.set(p_SPITType->DefaultDuration.get());
|
|
ptrSPITType->Fixed.set(p_SPITType->Fixed.get());
|
|
ptrSPITType->GroupName.set(p_SPITType->GroupName.get());
|
|
ptrSPITType->Name.set(p_SPITType->Name.get());
|
|
ptrSPITType->ProjectIndepended.set(p_SPITType->ProjectIndepended.get());
|
|
|
|
mCallbacks.onSpitType(ObjectEvent::Changed, false, *ptrSPITType);
|
|
sendSPITTypeInternal(*ptrSPITType);
|
|
|
|
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 SClient::removeSPITType(string p_SPITTypeID) {
|
|
if (p_SPITTypeID.length() <= 0)
|
|
return -1;
|
|
SType *ptrSPITType;
|
|
{
|
|
ptrSPITType = mProcessor.mSpitTypes.getEntry(p_SPITTypeID);
|
|
if (ptrSPITType == nullptr)
|
|
return -1;
|
|
Message::RemoveType sMRemoveSPITType;
|
|
ptrSPITType->fillMessageOP(sMRemoveSPITType);
|
|
mCallbacks.onSpitType(ObjectEvent::Deleted, false, *ptrSPITType);
|
|
mProcessor.mSpitTypes.removeEntry(*ptrSPITType);
|
|
return sendSPITMessage(sMRemoveSPITType);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
SType *SClient::createTypeFixed(string iD, string p_GroupName, string p_Name, uint64_t p_DefaultDelay, uint64_t p_DefaultDuration) {
|
|
return createType(iD, p_GroupName, p_Name, p_DefaultDelay, p_DefaultDuration, true, true);
|
|
}
|
|
|
|
SType *SClient::createType(string p_ID, string p_GroupName, string p_Name, uint64_t p_DefaultDelay, uint64_t p_DefaultDuration, bool p_FIxed, bool p_ProjectIndepended) {
|
|
bool boolNew;
|
|
SType *ptrSPITType = mProcessor.mSpitTypes.getEntry(p_ID);
|
|
if (ptrSPITType == nullptr) {
|
|
auto spitType = make_unique<SType>(p_ID, p_GroupName, p_Name, p_FIxed, p_ProjectIndepended, p_DefaultDelay, p_DefaultDuration);
|
|
ptrSPITType = spitType.get();
|
|
mProcessor.mSpitTypes.addEntry(move(spitType));
|
|
ptrSPITType->setCreatedByClient(true);
|
|
boolNew = true;
|
|
} else {
|
|
ptrSPITType->GroupName.set(p_GroupName);
|
|
ptrSPITType->Name.set(p_Name);
|
|
ptrSPITType->Fixed.set(p_FIxed);
|
|
ptrSPITType->ProjectIndepended.set(p_ProjectIndepended);
|
|
ptrSPITType->DefaultDelay.set(p_DefaultDelay);
|
|
ptrSPITType->DefaultDuration.set(p_DefaultDuration);
|
|
boolNew = false;
|
|
}
|
|
|
|
auto eventType = boolNew ? ObjectEvent::Created : ObjectEvent::Changed;
|
|
mCallbacks.onSpitType(eventType, false, *ptrSPITType);
|
|
sendSPITTypeInternal(*ptrSPITType);
|
|
return ptrSPITType;
|
|
}
|
|
|
|
//----- SPIT Objects ------
|
|
void SClient::parseReportSPITObject(Message::ReportObject &message) {
|
|
ESP_LOGD(TAG, "parse ReportObject %s", message.getID().c_str());
|
|
SObject *ptrSPITObject;
|
|
SType *ptrSPITType;
|
|
bool boolNew = false;
|
|
bool boolRemote = false;
|
|
bool boolChanged = false;
|
|
{
|
|
ESP_LOGD(TAG, "parse ReportObject %s / %s", message.getID().c_str(), message.getName().c_str());
|
|
if (message.getServerRemote() == false) {
|
|
boolRemote = false;
|
|
ptrSPITObject = mSpitObjects.getEntry(message.getID());
|
|
ptrSPITType = mProcessor.mSpitTypes.getEntry(message.getSPITTypeID());
|
|
|
|
if (ptrSPITType == nullptr) {
|
|
// No associated SPITType found
|
|
if (ptrSPITObject != 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(*ptrSPITObject);
|
|
}
|
|
return;
|
|
}
|
|
bool isKnownType = ptrSPITType != nullptr;
|
|
if (ptrSPITObject == 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());
|
|
ptrSPITObject = spitObj.get();
|
|
mSpitObjects.addEntry(move(spitObj));
|
|
boolNew = 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(*ptrSPITObject);
|
|
ESP_LOGW(TAG, "TypeVeto %s / %s", message.getID().c_str(), message.getName().c_str());
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
boolRemote = true;
|
|
ptrSPITObject = mRemoteSpitObjects.getEntry(message.getID());
|
|
ptrSPITType = mProcessor.mRemoteSpitTypes.getEntry(message.getSPITTypeID());
|
|
if (ptrSPITType == nullptr) {
|
|
// No associated SPITType found
|
|
return;
|
|
}
|
|
if (ptrSPITObject == nullptr) {
|
|
auto spitObj = make_unique<SObject>(message.getID());
|
|
ptrSPITObject = spitObj.get();
|
|
mRemoteSpitObjects.addEntry(move(spitObj));
|
|
boolNew = true;
|
|
}
|
|
}
|
|
|
|
if (!ptrSPITObject->setMessageOP(&message)) {
|
|
ESP_LOGE(TAG, "Change not allowed");
|
|
// setMessageOP error - the SPITObject can't be changed, it may be fixed and crated by this client
|
|
sendSPITObjectInternal(ptrSPITObject);
|
|
return;
|
|
}
|
|
|
|
if (boolRemote == false) {
|
|
// set the virtual action assignment
|
|
ptrSPITObject->setActionAssignedType(ptrSPITType->ActionAssigned.get());
|
|
}
|
|
|
|
auto eventType = boolNew ? ObjectEvent::Created : ObjectEvent::Changed;
|
|
mCallbacks.onSpitObject(eventType, boolRemote, *ptrSPITType, *ptrSPITObject);
|
|
|
|
if (ptrSPITObject->setConfirmed(true) == true || boolChanged == true) {
|
|
// Confirmation has changed
|
|
sendSPITObjectInternal(ptrSPITObject);
|
|
}
|
|
}
|
|
}
|
|
void SClient::parseRemoveSPITObject(Message::RemoveObject &message) {
|
|
ESP_LOGD(TAG, "parse RemoveObject %s", message.getID().c_str());
|
|
SObject *ptrSPITObject = mRemoteSpitObjects.getEntry(message.getID());
|
|
if (ptrSPITObject != nullptr) {
|
|
SType *ptrSPITType = mProcessor.mRemoteSpitTypes.getEntry(ptrSPITObject->SPITTypeID.get());
|
|
if (ptrSPITType != nullptr) {
|
|
mCallbacks.onSpitObject(ObjectEvent::Deleted, true, *ptrSPITType, *ptrSPITObject);
|
|
}
|
|
mRemoteSpitObjects.removeEntry(*ptrSPITObject);
|
|
} else {
|
|
// call removeSPITObject, this will notify the server that the object is removed
|
|
removeSPITObject(message.getID());
|
|
}
|
|
}
|
|
|
|
int SClient::sendSPITObjectInternal(SObject *ptrSPITObject) {
|
|
if (ptrSPITObject == nullptr)
|
|
return -1;
|
|
Message::ReportObject sMReportSPITObject;
|
|
ptrSPITObject->fillMessageOP(sMReportSPITObject);
|
|
|
|
return sendSPITMessage(sMReportSPITObject);
|
|
}
|
|
/**
|
|
* 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.
|
|
* CAUTION: The SPITObject must already exist.
|
|
* @return true = SPITObject is changed, false = not changed, maybe obj is NULL or the SPITObject doesn't exists
|
|
*/
|
|
bool SClient::changeSPITObject(SObject *obj) {
|
|
if (obj == nullptr)
|
|
return false;
|
|
SObject *ptrSPITObject;
|
|
SType *ptrSPITType;
|
|
{
|
|
ptrSPITObject = mSpitObjects.getEntry(obj->getID());
|
|
if (ptrSPITObject == nullptr)
|
|
return false;
|
|
ptrSPITType = mProcessor.mSpitTypes.getEntry(obj->getID());
|
|
if (ptrSPITObject == nullptr)
|
|
return false;
|
|
|
|
ptrSPITObject->setActionAssigned(obj->getActionAssigned());
|
|
;
|
|
ptrSPITObject->Delay.set(obj->Delay.get());
|
|
ptrSPITObject->Duration.set(obj->Delay.get());
|
|
ptrSPITObject->Fixed.set(obj->Fixed.get());
|
|
ptrSPITObject->FrameFadeInLength.set(obj->FrameFadeInLength.get());
|
|
ptrSPITObject->FrameFadeOutLength.set(obj->FrameFadeOutLength.get());
|
|
ptrSPITObject->FrameLength.set(obj->FrameLength.get());
|
|
ptrSPITObject->FrameStart.set(obj->FrameStart.get());
|
|
ptrSPITObject->Name.set(obj->Name.get());
|
|
ptrSPITObject->Remark.set(obj->Remark.get());
|
|
ptrSPITObject->SPITTypeID.set(obj->SPITTypeID.get());
|
|
ptrSPITObject->ServerParams.set(obj->ServerParams.get());
|
|
ptrSPITObject->Track.set(obj->Track.get());
|
|
ptrSPITObject->ValueFactor.set(obj->ValueFactor.get());
|
|
ptrSPITObject->ValueUse.set(obj->ValueUse.get());
|
|
|
|
mCallbacks.onSpitObject(ObjectEvent::Changed, false, *ptrSPITType, *ptrSPITObject);
|
|
sendSPITObjectInternal(ptrSPITObject);
|
|
}
|
|
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 SClient::removeSPITObject(string objId) {
|
|
if (objId.length() <= 0)
|
|
return -1;
|
|
SType *ptrSPITType;
|
|
SObject *ptrSPITObject;
|
|
{
|
|
Message::RemoveObject sMRemoveSPITObject;
|
|
|
|
ptrSPITObject = mSpitObjects.getEntry(objId);
|
|
if (ptrSPITObject == nullptr)
|
|
return -1;
|
|
ptrSPITType = mProcessor.mSpitTypes.getEntry(ptrSPITObject->SPITTypeID.get());
|
|
if (ptrSPITType == nullptr)
|
|
return -1;
|
|
ptrSPITObject->fillMessageOP(sMRemoveSPITObject);
|
|
mCallbacks.onSpitObject(ObjectEvent::Deleted, false, *ptrSPITType, *ptrSPITObject);
|
|
mSpitObjects.removeEntry(*ptrSPITObject);
|
|
return sendSPITMessage(sMRemoveSPITObject);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
//----- Play SPIT Objects ------
|
|
|
|
void SClient::parsePlaySPITStart(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 &spitPlayer = SPlayer::getSPITPlayer(message.getPlayerID());
|
|
spitPlayer.setIsRunning(message.getPlayerIsRunning());
|
|
spitPlayer.setName(message.getPlayerName());
|
|
spitPlayer.setType(message.getPlayerType());
|
|
spitPlayer.setTime(message.getTime());
|
|
spitPlayer.startPlaySequence();
|
|
|
|
mCallbacks.onPlay(spitPlayer.getID(), spitPlayer.getName(), spitPlayer.getType(), spitPlayer.getIsRunning(), spitPlayer.getTime());
|
|
}
|
|
void SClient::parsePlaySPITObject(Message::PlayObject &message) {
|
|
|
|
SPlayer &ptrSPITPlayer = SPlayer::getSPITPlayer(message.getPlayerID());
|
|
SObject *ptrSPITObject = mSpitObjects.getEntry(message.getSPITObjectID());
|
|
|
|
if (ptrSPITObject == nullptr) {
|
|
return;
|
|
}
|
|
SPlayer::PlayType *ptrPlaySPITType = ptrSPITPlayer.addTypePlaying(ptrSPITObject->SPITTypeID.get());
|
|
if (ptrPlaySPITType == nullptr) {
|
|
return;
|
|
}
|
|
ptrPlaySPITType->setPlayerIsRunning(ptrSPITPlayer.getIsRunning());
|
|
ptrPlaySPITType->addValue(((double)message.getValue() / 1000000), ((double)ptrSPITObject->ValueFactor.get() / 1000000));
|
|
}
|
|
|
|
void SClient::parsePlaySPITEnd(Message::PlayEnd &message) {
|
|
SPlayer &ptrSPITPlayer = SPlayer::getSPITPlayer(message.getPlayerID());
|
|
|
|
auto &ptrPlaySPITTypes = ptrSPITPlayer.getPlaySPITTypes();
|
|
for (auto i = ptrPlaySPITTypes.begin(); i != ptrPlaySPITTypes.end(); i++) {
|
|
SPlayer::PlayType *ptrPlaySPITType = i->second.get();
|
|
bool boolStarted = ptrPlaySPITType->getIsNew();
|
|
bool boolStopped = (ptrPlaySPITType->getIsPlayed() == false);
|
|
SType *ptrSPITType = mProcessor.mSpitTypes.getEntry(ptrPlaySPITType->getSPITTypeID());
|
|
if (ptrSPITType == nullptr) {
|
|
// set isplayed to false, the ptrPlayObject will be removed by ptrSPITPlayer.endPlaySequence()
|
|
ptrPlaySPITType->setIsPlayed(false);
|
|
continue;
|
|
}
|
|
mCallbacks.onPlaySpitType(ptrPlaySPITType->getPlayerID(), ptrPlaySPITType->getSPITTypeID(), ptrPlaySPITType->getPlayerIsRunning(), boolStarted, boolStopped, ptrPlaySPITType->getValue());
|
|
}
|
|
ptrSPITPlayer.endPlaySequence();
|
|
}
|
|
|
|
void SClient::parseReportPlayStatus(Message::ReportPlayStatus &message) {
|
|
SType *ptrSPITType = NULL;
|
|
|
|
SObject *ptrSPITObject = mRemoteSpitObjects.getEntry(message.getID());
|
|
if (ptrSPITObject == nullptr) {
|
|
ptrSPITObject = mSpitObjects.getEntry(message.getID());
|
|
if (ptrSPITObject == nullptr) {
|
|
return;
|
|
}
|
|
}
|
|
if (ptrSPITObject->ServerRemote.get() == false) {
|
|
ptrSPITType = mProcessor.mSpitTypes.getEntry(ptrSPITObject->SPITTypeID.get());
|
|
} else {
|
|
ptrSPITType = mProcessor.mRemoteSpitTypes.getEntry(ptrSPITObject->SPITTypeID.get());
|
|
}
|
|
|
|
if (ptrSPITType == nullptr) {
|
|
return;
|
|
}
|
|
mCallbacks.onPlayRemoteObjectStatus(*ptrSPITType, *ptrSPITObject, 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 p_SPITObjectID 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 SClient::sendRemoteObject(string p_SPITObjectID, int valuePpm, char valueFlag) {
|
|
SObject *ptrRemoteSPITObject = mRemoteSpitObjects.getEntry(p_SPITObjectID);
|
|
if (ptrRemoteSPITObject == nullptr) {
|
|
ESP_LOGD(TAG, "Unknown ID %s", p_SPITObjectID.c_str());
|
|
return;
|
|
}
|
|
|
|
Message::PlayStart playSPITStart(mID, mProjName, PLAYERTYPE_DISCRETE);
|
|
playSPITStart.setPlayerIsRunning(false);
|
|
playSPITStart.setTime(0);
|
|
sendSPITMessage(playSPITStart);
|
|
|
|
Message::PlayObject playSPITObject;
|
|
playSPITObject.setID(ptrRemoteSPITObject->getID());
|
|
playSPITObject.setPlayPrePosition(false);
|
|
playSPITObject.setPlayerID(mID);
|
|
playSPITObject.setValue(valuePpm);
|
|
playSPITObject.setValueFlag(valueFlag);
|
|
sendSPITMessage(playSPITObject);
|
|
|
|
Message::PlayEnd playSPITEnd(mID);
|
|
sendSPITMessage(playSPITEnd);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
} |