#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) // @todo : mCallbacks(calls), mGuid(guid), mName(name), mProjName(projName), mComputerName(hostName) { } SProcessor::~SProcessor() { // @todo stopServerAutoDetect(); disconnectInternal(Connection::None); } void SProcessor::clientProjectClose() { // @todo 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 SProcessor::clientProjectLoaded() { // @todo // set confirmed = false for all SPITTypes and SPITObjects SType::setConfirmedFalse(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 SProcessor::setActionSPITType(string typeId, bool actionAssigned) { // @todo SType *spitType = 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 SProcessor::setActionSPITObject(string spitObjId, bool actionAssigned) { // @todo 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 SProcessor::startServerAutoDetect() { // @todo // 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(SProcessor::processSocketTask, *this, "spitSock", fos::Task::DefaultStackSize * 2); // @todo // } // mAbortSearch = false; // sStartSad.give(); } /** Stops the auto detection of SPIT servers. */ void SProcessor::stopServerAutoDetect() { // @todo 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 SProcessor::eventServerDetected(SadDetectedServer &server) { // @todo 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 SProcessor::eventServerLost(SadDetectedServer &server) { // @todo mCallbacks.onServerEvent(ServerEvent::Lost, server); } void SProcessor::eventServerChanged(SadDetectedServer &server) { // @todo 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 // } std::vector 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 {}; } // todo Delete SProcessor::sendSPITMessage(Message::OPPart &opPart // void SProcessor::receiveSPITMessage(SClient &rM) { // @todo // 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 = vector(buffer, buffer + rdLen); // rM.checkParseMessageData(move(spitMsg)); // // rM.checkParseMessage(move(spitMsg)); // } // } void SProcessor::resetMessageNumberReceived() { // @todo mMessageNumberReceived = -1; mOvertakingMessages.clear(); } void SProcessor::checkParseMessageData(vector data) { auto message = make_unique(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); // check overtaking messages auto it = mOvertakingMessages.begin(); while (mOvertakingMessages.size() > 0) { msgNumNext = mMessageNumberReceived + 1; if (msgNumNext > 255 || msgNumNext < 1) msgNumNext = 1; auto &overtakingMessage = *it; msgNum = (int)(overtakingMessage->getHeader()->getMessageNumber() & 0xFF); if (msgNum == msgNumNext) { mMessageNumberReceived = msgNum; parseMessage(*overtakingMessage); mOvertakingMessages.erase(it); it = mOvertakingMessages.begin(); } else { break; } } } else { // p_SPITMessage has overtaken another message addOvertakingMessage(move(message)); } } void SProcessor::addOvertakingMessage(unique_ptr message) { // @todo 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 SProcessor::parseMessage(Message& message) { // @todo 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()); break; case Message::Disconnect::OPFLAG: parseDisconnect(message.getAs()); break; case Message::ReportProject::OPFLAG: parseReportProject(message.getAs()); break; case Message::Watchdog::OPFLAG: parseWatchdog(message.getAs()); break; case Message::WatchdogAnswer::OPFLAG: parseWatchdogAnswer(message.getAs()); break; //----- SPIT Project ----- case Message::StartProjectTransfer::OPFLAG: parseStartProjectTransfer(message.getAs()); break; case Message::CloseProject::OPFLAG: parseCloseProject(message.getAs()); break; case Message::RequestProjectTransfer::OPFLAG: parseRequestProjectTransfer(message.getAs()); break; case Message::EndProjectTransfer::OPFLAG: parseEndProjectTransfer(message.getAs()); break; //----- SPIT Types ------ case Message::ReportType::OPFLAG: parseReportType(message.getAs()); break; case Message::RemoveType::OPFLAG: parseRemoveSPITType(message.getAs()); //----- SPIT Objects ------ break; case Message::ReportObject::OPFLAG: parseReportSPITObject(message.getAs()); break; case Message::RemoveObject::OPFLAG: parseRemoveSPITObject(message.getAs()); break; //----- Play SPIT Objects ------ case Message::PlayStart::OPFLAG: parsePlaySPITStart(message.getAs()); break; case Message::PlayObject::OPFLAG: parsePlaySPITObject(message.getAs()); break; case Message::PlayEnd::OPFLAG: parsePlaySPITEnd(message.getAs()); break; case Message::ReportPlayStatus::OPFLAG: parseReportPlayStatus(message.getAs()); 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 SProcessor::connect(string serverIP, string serverPort, string clientIP, string clientPort, string loginName, string loginPassword) { // @todo 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 SProcessor::disconnect() { // @todo disconnectInternal(Connection::None); } void SProcessor::disconnectInternal(Connection cause) { // @todo 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 SProcessor::Connection::None: // @todo ESP_LOGD(TAG, "not connected"); break; case SProcessor::Connection::NoneServer: // @todo 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 SProcessor::Connection::Lost: // @todo 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 SProcessor::parseConnectAnswer(Message::ConnectAnswer &message) { // @todo if (message.getClientID() != mGuid) 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 SProcessor::parseDisconnect(Message::Disconnect &message) { // @todo Connection intCauseDisconnect = Connection::NoneServer; /* function o_Callable = bind(&SProcessor::disconnectInternal, this, intCauseDisconnect); // @todo SPIT_Handler::addCallable(o_Callable); */ mDisconnectedByServer = true; SProcessor::disconnectInternal(intCauseDisconnect); // @todo } //----- 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 SProcessor::parseWatchdog(Message::Watchdog &message) { // @todo // 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 SProcessor::parseWatchdogAnswer(Message::WatchdogAnswer &message) { // @todo answerWatchdog(); // is also done by parseMessage } void SProcessor::sendWatchdog() { // @todo int intResult{}; Message::Watchdog message; // intResult = sendSPITMessage(message); if (intResult < 0) { ESP_LOGW("WDT", "Failed to send Watchdog msg"); } } void SProcessor::eventWatchdogLost() { // @todo if (mConnectionStatus == Connection::TryConnection) { disconnectInternal(Connection::NoneServer); } else { disconnectInternal(Connection::Lost); } } //----- SPIT Project ------ void SProcessor::parseReportProject(Message::ReportProject &message) { // @todo 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 (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 SProcessor::parseCloseProject(Message::CloseProject &p_CloseSPITProject) { // @todo ESP_LOGD(TAG, "parse CloseProject"); string stringProjectID; mCallbacks.onProjectUpdate(ProjUpdate::ServerClosed, p_CloseSPITProject.getProjectID()); SPlayer::clearPlayers(); } string SProcessor::getServerProjectID() { return mServerProjectID; } // @todo string SProcessor::getServerProjectName() { return mServerProjectName; } // @todo int SProcessor::getServerFramesPerSecond() { return mServerFramesPerSecond; } // @todo void SProcessor::parseRequestProjectTransfer(Message::RequestProjectTransfer &message) { // @todo ESP_LOGD(TAG, "parse RequestProjectTransfer"); // StartProjectTransfer Message::StartProjectTransfer sMStartProjectTransfer; // if (sendSPITMessage(sMStartProjectTransfer) < 0) { // return; // } // send all listes SPITTypes for (const auto& [key, value] : 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 SProcessor::parseRequestProjectTransfer(Message::RequestProjectTransfer* message) { // @todo // 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 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 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 SProcessor::sendProject(SClient *p_PtrSPIT_Client) { // @todo // 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 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 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 SProcessor::parseStartProjectTransfer(Message::StartProjectTransfer &message) { // @todo mCallbacks.onProjectUpdate(ProjUpdate::StartTransfer, mServerProjectID); } void SProcessor::parseEndProjectTransfer(Message::EndProjectTransfer &message) { // @todo 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"); // } } //----- SPIT Types ------ void SProcessor::parseReportType(Message::ReportType &message) { // @todo ESP_LOGD(TAG, "parse ReportType ID %s Name %s",message.getID().c_str(), message.getName().c_str()); SType *sType; bool boolNew = false; bool boolChanged = 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(message.getID()); sType = spitType.get(); mSpitTypes.addEntry(move(spitType)); boolNew = true; } } else { sType = mRemoteSpitTypes.getEntry(message.getID()); if (sType == nullptr) { auto spitType = make_unique(message.getID()); sType = spitType.get(); mRemoteSpitTypes.addEntry(move(spitType)); boolNew = true; } } if (!sType->setMessageOP(message)) { // SPITType is not changable -> send back to the server sendSPITTypeInternal(*sType); return; } auto eventType = boolNew ? ObjectEvent::Created : ObjectEvent::Changed; // add or change SPITType mCallbacks.onSpitType(eventType, sType->ServerRemote.get(), *sType); if (sType != nullptr) { if (sType->setConfirmed(true) == true || boolChanged == true) { sendSPITTypeInternal(*sType); } } } } void SProcessor::parseRemoveSPITType(Message::RemoveType &message) { // @todo ESP_LOGD(TAG, "parse RemoveType %s", message.getID().c_str()); bool boolRemote = false; string stringSPITTypeID = message.getID(); SType *ptrSPITType = mRemoteSpitTypes.getEntry(stringSPITTypeID); if (!ptrSPITType) { boolRemote = true; } else { ptrSPITType = 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) { mSpitTypes.removeEntry(*ptrSPITType); } else { 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 SProcessor::sendSPITTypeInternal(SType &p_SPITType) { // @todo Message::ReportType sMReportSPITType; p_SPITType.fillMessageOP(sMReportSPITType); // return sendSPITMessage(sMReportSPITType); return 1; } /** * 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::changeSPITType(SType *p_SPITType) { // @todo if (p_SPITType == nullptr) return false; SType *ptrSPITType = 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 SProcessor::removeSPITType(string p_SPITTypeID) { // @todo if (p_SPITTypeID.length() <= 0) return -1; SType *ptrSPITType; { ptrSPITType = mSpitTypes.getEntry(p_SPITTypeID); if (ptrSPITType == nullptr) return -1; Message::RemoveType sMRemoveSPITType; ptrSPITType->fillMessageOP(sMRemoveSPITType); mCallbacks.onSpitType(ObjectEvent::Deleted, false, *ptrSPITType); mSpitTypes.removeEntry(*ptrSPITType); // return sendSPITMessage(sMRemoveSPITType); return 1; } return -1; } SType *SProcessor::createTypeFixed(string iD, string p_GroupName, string p_Name, uint64_t p_DefaultDelay, uint64_t p_DefaultDuration) { // @todo return createType(iD, p_GroupName, p_Name, p_DefaultDelay, p_DefaultDuration, true, true); } SType *SProcessor::createType(string p_ID, string p_GroupName, string p_Name, uint64_t p_DefaultDelay, uint64_t p_DefaultDuration, bool p_FIxed, bool p_ProjectIndepended) { // @todo bool boolNew; SType *ptrSPITType = mSpitTypes.getEntry(p_ID); if (ptrSPITType == nullptr) { auto spitType = make_unique(p_ID, p_GroupName, p_Name, p_FIxed, p_ProjectIndepended, p_DefaultDelay, p_DefaultDuration); ptrSPITType = spitType.get(); 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 SProcessor::parseReportSPITObject(Message::ReportObject &message) { // @todo 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 = 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(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 = mRemoteSpitTypes.getEntry(message.getSPITTypeID()); if (ptrSPITType == nullptr) { // No associated SPITType found return; } if (ptrSPITObject == nullptr) { auto spitObj = make_unique(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 SProcessor::parseRemoveSPITObject(Message::RemoveObject &message) { // @todo ESP_LOGD(TAG, "parse RemoveObject %s", message.getID().c_str()); SObject *ptrSPITObject = mRemoteSpitObjects.getEntry(message.getID()); if (ptrSPITObject != nullptr) { SType *ptrSPITType = 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 SProcessor::sendSPITObjectInternal(SObject *ptrSPITObject) { // @todo if (ptrSPITObject == nullptr) return -1; Message::ReportObject sMReportSPITObject; ptrSPITObject->fillMessageOP(sMReportSPITObject); // return sendSPITMessage(sMReportSPITObject); return 1; } /** * 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 SProcessor::changeSPITObject(SObject *obj) { // @todo if (obj == nullptr) return false; SObject *ptrSPITObject; SType *ptrSPITType; { ptrSPITObject = mSpitObjects.getEntry(obj->getID()); if (ptrSPITObject == nullptr) return false; ptrSPITType = 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 SProcessor::removeSPITObject(string objId) { // @todo if (objId.length() <= 0) return -1; SType *ptrSPITType; SObject *ptrSPITObject; { Message::RemoveObject sMRemoveSPITObject; ptrSPITObject = mSpitObjects.getEntry(objId); if (ptrSPITObject == nullptr) return -1; ptrSPITType = 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; } return -1; } //----- Play SPIT Objects ------ void SProcessor::parsePlaySPITStart(Message::PlayStart &message) { // @todo 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 SProcessor::parsePlaySPITObject(Message::PlayObject &message) { // @todo 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 SProcessor::parsePlaySPITEnd(Message::PlayEnd &message) { // @todo 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 = 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 SProcessor::parseReportPlayStatus(Message::ReportPlayStatus &message) { // @todo 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 = mSpitTypes.getEntry(ptrSPITObject->SPITTypeID.get()); } else { ptrSPITType = 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 SProcessor::sendRemoteObject(string p_SPITObjectID, int valuePpm, char valueFlag) { // @todo SObject *ptrRemoteSPITObject = mRemoteSpitObjects.getEntry(p_SPITObjectID); if (ptrRemoteSPITObject == nullptr) return; Message::PlayStart playSPITStart(mGuid, mProjName, PLAYERTYPE_DISCRETE); playSPITStart.setPlayerIsRunning(false); playSPITStart.setTime(0); // sendSPITMessage(playSPITStart); Message::PlayObject playSPITObject; playSPITObject.setID(ptrRemoteSPITObject->getID()); playSPITObject.setPlayPrePosition(false); playSPITObject.setPlayerID(mGuid); playSPITObject.setValue(valuePpm); playSPITObject.setValueFlag(valueFlag); // sendSPITMessage(playSPITObject); Message::PlayEnd playSPITEnd(mGuid); // sendSPITMessage(playSPITEnd); } }