Branching from Cathaswatch project

https://git.nerdyssey.de/exodus/1NiM-WatingRoom-WatchController
This commit is contained in:
2024-11-16 11:53:28 +01:00
parent 1a13428c9d
commit 8c49e2f11c
116 changed files with 14510 additions and 590 deletions
+5
View File
@@ -0,0 +1,5 @@
idf_component_register(
SRC_DIRS "src" "src/esp"
INCLUDE_DIRS "include"
REQUIRES esp_system esp_netif freeRtosApi sockets
)
+11
View File
@@ -0,0 +1,11 @@
# SPIT
Original implementation from Hag seem to be not suited for Embedded devices.
- Supports IPv4 only
- Reduced Threads to a minimum
- Then reduced usage of Mutexes to a minimum
- Reduced Sockets to a minimum
- Introduced containers and removed plain new and delete
- Removed UUID-Generation because SPIT-Types never change on embedded devices
- Callbacks instead of interfaces where suitable
Binary file not shown.
@@ -0,0 +1,155 @@
#pragma once
#include <list>
#include <memory>
#include <string>
#include <unordered_map>
using namespace std;
namespace spit {
class SadDetectedServer;
class ISadClient {
public:
virtual void eventServerDetected(SadDetectedServer &server) = 0;
virtual void eventServerChanged(SadDetectedServer &server) = 0;
virtual void eventServerLost(SadDetectedServer &server) = 0;
};
class SadDetectedServer {
public:
SadDetectedServer(string name, string iD, string computerName,
string iP, string port, string clientIP,
string additions, string projName)
: mName(name),
mID(iD),
mComputerName(computerName),
mIP(iP),
mPort(port),
mClientIP(clientIP),
mAdditions(additions),
mServerProjectName(projName) {
}
private:
string mName{};
string mID{};
string mComputerName{};
string mIP{};
string mPort{};
string mClientIP{};
string mAdditions{};
string mServerProjectName{};
bool mNew{true};
bool mChanged{};
bool mFoundAgain{};
public:
string getID() const { return mID; }
string getName() const { return mName; }
string getComputerName() const { return mComputerName; }
string getIP() const { return mIP; }
string getPort() const { return mPort; }
string getClientIP() const { return mClientIP; }
string getAdditions() const { return mAdditions; }
string getProjectName() const { return mServerProjectName; }
bool getNew() const { return mNew; }
bool getChanged() const { return mChanged; }
bool getFoundAgain() const { return mFoundAgain; }
void setChanged() { mChanged = true; }
void setKnown() { mNew = false; }
void setFoundAgain() { mFoundAgain = true; }
void updateProjectName(string newProjectName) {
mServerProjectName = newProjectName;
if (mServerProjectName != newProjectName) {
mChanged = true;
}
}
// SAD_Client mThreadSend will send to Multicast addresses
void resetFlags() {
mChanged = false;
mFoundAgain = false;
mNew = false;
}
private:
static unordered_map<string, unique_ptr<SadDetectedServer>> sKnownServers;
public:
enum ParamIndex {
PARAM_PRECODE = 0,
PARAM_SPITTASKID = 1,
PARAM_NAME = 2,
PARAM_ID = 3,
PARAM_COMPUTERNAME = 4,
PARAM_PORT = 5,
PARAM_CLIENTIP = 6,
PARAM_ADDITIONS = 7,
PARAM_SERVERPROJECTNAME = 8,
PARAM_IP = 9,
};
/**
* Used by SAD_Client to add a found SPITServer - don't use this manually
* @param id the id of the server, every instance of a SPITServer has an own id
* @param p_ServerParams the server's parameter
*/
static void addDetectedServer(unique_ptr<SadDetectedServer> detected) {
if (auto i = sKnownServers.find(detected->getID()); i != sKnownServers.end()) {
auto &known = *(i->second.get());
known.setFoundAgain();
if (detected->getProjectName() != known.getProjectName()) {
known.updateProjectName(detected->getProjectName());
}
return;
}
sKnownServers[detected->getID()] = move(detected);
}
static void removeDetectedServer(SadDetectedServer &server) {
auto i = sKnownServers.find(server.getID());
if (i != sKnownServers.end()) {
sKnownServers.erase(i);
}
}
static void clearDetectedServers() {
sKnownServers.clear();
}
static void resetServerFlags() {
for (auto &srv : sKnownServers) {
srv.second->resetFlags();
}
}
/**
* Used by SAD_Client internally -don't use this manually
*/
static void evaluateDetectedServers(ISadClient &sadClient) {
for (auto &srv : sKnownServers) {
SadDetectedServer &known = *srv.second;
if (known.getNew()) {
known.setKnown();
sadClient.eventServerDetected(known);
} else if (known.getChanged()) {
sadClient.eventServerChanged(known);
} else if (known.getFoundAgain() == false) {
sadClient.eventServerLost(known);
removeDetectedServer(known);
}
}
}
/**
* Returns a reference of the DetectedServer with the id p_ServerID.<br>
* If no DetectedServer is found an eyception is thrown.
* @param id the id of the SPITServer
* @return the reference of the SadDetectedServer instance with getID() == p_ServerID
*/
static const SadDetectedServer &getRefDetectedServer(const string &id) {
return *sKnownServers.at(id);
}
};
}
+203
View File
@@ -0,0 +1,203 @@
#pragma once
#include "sad_DetectedServer.hpp"
#include "spit_message.hpp"
#include "spit_object.hpp"
#include "spit_player.hpp"
#include "spit_processor.hpp"
#include "spit_repo.hpp"
#include "spit_socket.hpp"
#include "spit_type.hpp"
#include <functional>
#include <memory>
#include <vector>
using namespace std;
namespace spit {
class SClient : ISadClient {
public:
using Callbacks = SProcessor::Callbacks;
using ProjUpdate = SProcessor::ProjUpdate;
using ServerEvent = SProcessor::ServerEvent;
using ObjectEvent = SProcessor::ObjectEvent;
using Connection = SProcessor::Connection;
using PlayerType = SPlayer::PTypes;
using ValueFlags = SPlayer::VFlags;
SClient(string name, string projName, ISocket &socket, Callbacks calls);
virtual ~SClient();
private:
Callbacks mCallbacks;
fos::TaskT<SClient &> *mThreadReceive{};
fos::TaskT<SClient &> *mThreadProcess{};
bool mStopReceiveMessages{};
Connection mConnectionStatus{Connection::None};
bool mDisconnectedByServer{}; // flag true = when server disconnects
SProcessor mProcessor;
string mClientProjectName;
uint8_t mMessageNumberSend{1};
int mMessageNumberReceived{-1}; // set to -1 because the first received message will set the number (1..255)
// Server's info filled by parseConnectAnswer
string mServerID{};
string mServerName{};
string mServerComputerName{};
int mServerStepTime{-1};
public:
/** The ID of the client.<br>Every client instance should set an unique id*/
string getID() { return mProcessor.getID(); }
/** The name of the client, should be set by the user app*/
string getName() { return mProcessor.getName(); }
/** The name of the client's host computer*/
string getComputerName() { return mProcessor.getComputerName(); }
void setClientProjectName(string clientProjectName) { mClientProjectName = clientProjectName; }
string getClientProjectName() { return mClientProjectName; }
// connected Server Infos
/** The ID of the connected SPITServer*/
string getServerID() { return mServerID; }
/** The host computername of the connected SPITServer*/
string getServerComputerName() { return mServerComputerName; }
/** The name of the connected SPITServer*/
string getServerName() { return mServerName; }
/** Expected Refreah Rate of the connectd SPITServer.<br>
* When the server plays objects, it will do so at discrete time steps.<br>
* Play events will take place in these time steps..
*/
int getServerStepTime() { return mServerStepTime; }
/** Should be called when the client wants to close his project*/
void clientProjectClose();
/**
* Should be called whenever the client has a project loaded
*/
void clientProjectLoaded();
void setActionType(string spitypeID, bool actionAssigned) {
mProcessor.setActionType(spitypeID, actionAssigned);
}
void setActionObject(string objectID, bool actionAssigned) {
mProcessor.setActionObject(objectID, actionAssigned);
}
//--------------- SPITMessage ---------------
private:
bool startReceiveSPITMessages();
void stopReceiveSPITMessages();
static void receiveSpitMessage(SClient &rM);
static void processSpitMessage(SClient &rM);
// Reconnect to the server when connection is lost see eventConnectionStatus and eventServerDetected
bool _doReconnect{}; // true = on eventServerDetected connect to the server identified by mServerID
public:
int sendMessage(Message::OPPart &spitMessageOPPart);
//--------------- ServerAutoDetection SAD ---------------
public:
/** Starts the autodetection of SPITServers in the local network. If a connection is tried, the autodetection should be stopped before.*/
void startServerAutoDetect();
/** Stops the autodetection of SPITServers in the local network*/
void stopServerAutoDetect();
/** Called every time a new SPITServer is detetced in the local network */
void eventServerDetected(SadDetectedServer &server);
/** Called every time a prior detected SPITServer is is no longer found.*/
void eventServerLost(SadDetectedServer &server);
void eventServerChanged(SadDetectedServer &server);
//--------------- Connection ---------------
public:
/**
* Tries a connection to the SPITServer
* @param p_ServerIP ip address of the SPITServer
* @param p_ServerPort port of the SPITServer
* @param p_ClientIP IP of the client
* @param p_ClientPort if "0" any free port will be used
* @param p_LoginName maybe some SPITServer need a login - liveSHOWsoftware will not.
* @param p_LoginPassword maybe some SPITServer need a login - liveSHOWsoftware will not
* @return true = the connection try is ok, SPITServer will answer with COnnectAnswer and an eventConnectionStatus will occur.<br>
* false = something is going wrong. (client could not bind)
*/
bool connect(string serverIP, string serverPort, string clientIP, string clientPort, string loginName = "", string loginPassword = "");
/**
* Terminates the connection to the SPITServer.
*/
void disconnect();
private:
void disconnectInternal(Connection cause);
void resetMessageNumberReceived();
void parseMessage(Message& message);
void checkParseMessage(unique_ptr<Message> message);
void addOvertakingMessage(unique_ptr<Message> message);
void parseConnectAnswer(Message::ConnectAnswer &message);
void parseDisconnect(Message::Disconnect &message);
void parseWatchdog(Message::Watchdog &message);
void parseWatchdogAnswer(Message::WatchdogAnswer &message);
public:
/** Used internally*/
void sendWatchdog();
/** Used internally. When the connection to the SPITServer is lost an eventConnectionStatus will occur*/
void eventWatchdogLost();
//--------------- SPIT Project ---------------
/** Returns the SPITServer's project ID - may be the same as the project's name*/
string getServerProjectID() { return mProcessor.getServerProjectID(); }
/** Returns the SPITServer's project name*/
string getServerProjectName() { return mProcessor.getServerProjectName(); }
/** Returns frame rate per second. This is the time resolution in which the server transmits time data. The unit of time data is always frames.*/
int getServerFramesPerSecond() { return mProcessor.getServerFramesPerSecond(); }
public:
static void sendProject(SClient *p_PtrSClient);
// --------------- SPIT Types ---------------
public:
/**
* Creates a fixed SPITType for application actions that are always present and project independent.<br>
* Fixed SPIT types cannot be changed or deleted by the server.<br>
* These SPITTypes will be automatically added to the SPITType-List and sended to the server.
* @param p_ID an unique of the SPITType, these are always the same per client approach. Different client approaches should use different ids.
* @param p_GroupName a name which groups the actions/SPITTypes
* @param p_Name the name of the Action/SPITType
* @param p_DefaultDelay maybe there is an default delay until the action starts, this is only used for graphical representations, 0 is allowd
* @param p_DefaultDuration maybe the action has a default duration, this is only used for graphical representations, 0 is allowd
* @return the pointer to the new SPITType.
*/
SType *createTypeFixed(string iD, string groupName, string name, uint64_t defaultDelay, uint64_t defaultDuration);
public:
bool changeType(SType *spitType) { return mProcessor.changeType(spitType); }
int removeType(string spitypeID) { return mProcessor.removeType(spitypeID); }
public:
public:
bool changeObject(SObject *sObject) { return mProcessor.changeObject(sObject); }
void sendRemoteObject(string objectID, int valuePpm = 0, char valueFlag = 0x00) {
mProcessor.sendRemoteObject(objectID, valuePpm, valueFlag);
}
private:
static void processSocketTask(SClient &instance);
fos::TaskT<SClient &> *sThread{};
bool mAbortSearch{};
bool sAnswered{};
static fos::Semaphore sStartSad;
ISocket &mSocket;
void answerWatchdog() {
sAnswered = true;
}
};
} // namespace spit
+720
View File
@@ -0,0 +1,720 @@
#pragma once
#include <cstdlib>
#include <memory>
#include <optional>
#include <string>
#include <vector>
using namespace std;
static const string SPITID = "#spit#";
namespace spit {
class Message {
//----------- Header -----------
public:
/** Maximal length of a SPITMessage*/
static const int MAX_MESSAGE_SIZE = 512;
class Header {
public:
static constexpr int BYTESLENGTH = 99;
Header(const Header &orig);
Header();
private:
string mID;
int mVersion{SPITVERSION};
unsigned char mMessageNumber{};
int mBytesCount{BYTESLENGTH};
string mServerId{};
string mClientId{};
int mOpFlag{-1};
int mParseOffset{};
public:
string getSPITID() const { return mID; }
int getSPITVersion() const { return mVersion; }
unsigned char getMessageNumber() const { return mMessageNumber; }
int getBytesCount() const { return mBytesCount; }
string getServerID() const { return mServerId; }
string getClientID() const { return mClientId; }
int getType() const { return mOpFlag; }
// Receive
bool parse(const std::vector<char> &message);
// Send
Header(int p_SPITVersion, int pOpFlag);
void setMessageNumber(unsigned char messageNumber) { mMessageNumber = messageNumber; }
void setServerID(string iD) { mServerId = iD; }
void setClientID(string iD) { mClientId = iD; }
void setOPFlag(int pOpFlag) { mOpFlag = pOpFlag; }
bool fillBytes(std::vector<char> &message);
};
//----------- OPPart -----------
public:
class OPPart {
// Members
protected:
unique_ptr<Header> mHeader{};
int mParseOffset{};
int mBytesCount;
public:
virtual ~OPPart();
Header *getHeader() const { return mHeader.get(); }
int getBytesCount() const { return mBytesCount; }
void setServerInfoInHeader(string clientId, string serverId) {
if (!mHeader) {
return;
}
mHeader->setClientID(clientId);
mHeader->setServerID(serverId);
}
void setMessageNumber(uint8_t no) {
if (!mHeader) {
return;
}
mHeader->setMessageNumber(no);
}
const string getServerID() const {
if (!mHeader) {
return "";
}
return mHeader->getServerID();
}
const string getClientID() const {
if (!mHeader) {
return "";
}
return mHeader->getClientID();
}
int getType() const {
if (!mHeader) {
return -1;
}
return mHeader->getType();
}
// Receive
OPPart(unique_ptr<Header> header, int pOpFlag, int size);
void setHeader(int opFlag);
virtual bool parse(const std::vector<char> &message) = 0;
// Send
OPPart(int size);
virtual bool fillBytes(std::vector<char> &message) = 0;
};
//----------- Message -----------
public:
static constexpr int SPITVERSION = 1;
// SPIT_Object value usage
static constexpr char VALUEUSE_DISCARD = 0x00; // value of SPITObejct is not used
static constexpr char VALUEUSE_INRANGE = 0x01; // value of SPITOBject in range 0 - 1000000ppm
static constexpr char VALUEUSE_OVERRIDE = 0x02; // value can be overridden
// SPIT Player type
static constexpr char PLAYERTYPE_MAIN = 0x02;
static constexpr char PLAYERTYPE_CONTINIOUS = 0x01;
static constexpr char PLAYERTYPE_DISCRETE = 0x00;
// PlayObject value usage
static constexpr char VALUEFLAG_DIRECT = 0x00; // use the vlue direct
static constexpr char VALUEFLAG_INCREASE = 0x01; // increase the value
static constexpr char VALUEFLAG_DECREASE = 0x02; // decrease the value
Message() = default;
// Members
private:
unique_ptr<OPPart> oPPart{};
// Receive
public:
Message(std::vector<char> message);
const Header *getHeader() const { return oPPart->getHeader(); }
int getType() const {
if (!oPPart || !oPPart->getHeader()) {
return -1;
}
return oPPart->getHeader()->getType();
}
const OPPart *getOPPart() const { return oPPart.get(); }
template<typename T>
T &getAs() const { return *reinterpret_cast<T *>(oPPart.get()); }
private:
bool parse(std::vector<char> message);
// Send
public:
static std::vector<char> createMessage(OPPart & p_OPPart);
//------------------------------ Connection ----------------------------------
//----------- Connect -----------
public:
class Connect : public OPPart {
public:
static constexpr int OPFLAG = 1;
static constexpr int BYTESLENGTH = 304;
private:
string mClientComputerName{};
string mClientName{};
string mLoginName{};
string mLoginPassword{};
string mServerIp{};
int mServerPort{};
public:
// Receive
Connect(unique_ptr<Header> header);
bool parse(const std::vector<char> &message);
string getClientComputerName() const { return mClientComputerName; }
string getClientName() const { return mClientName; }
string getLoginName() const { return mLoginName; }
string getLoginPassword() const { return mLoginPassword; }
string getServerIP() const { return mServerIp; }
int getServerPort() const { return mServerPort; }
// Send
Connect(string p_ComputerName, string p_ClientName);
void setLoginName(string p_LoginName) { mLoginName = p_LoginName; }
void setLoginPassword(string p_LoginPassword) { mLoginPassword = p_LoginPassword; }
void setServerIP(string p_ServerIP) { mServerIp = p_ServerIP; }
void setServerPort(int p_ServerPort);
bool fillBytes(std::vector<char> &message);
};
//----------- ConnectAnswer -----------
class ConnectAnswer : public OPPart {
public:
static constexpr int OPFLAG = 2;
static constexpr int BYTESLENGTH = 137;
private:
string mServerComputerName{};
string mServerName{};
bool mConnected{};
int mStepTime{40}; // ms step size the Server will send play sequences
public:
ConnectAnswer(const ConnectAnswer &orig);
// Receive
ConnectAnswer(unique_ptr<Header> header);
bool parse(const std::vector<char> &message);
const string getServerComputerName() const { return mServerComputerName; }
const string getServerName() const { return mServerName; }
bool getConnected() const { return mConnected; }
int getStepTime() const { return mStepTime; }
// Send
ConnectAnswer();
void setServerComputerName(string p_ServerComputerName) { mServerComputerName = p_ServerComputerName; }
void setServerName(string p_ServerName) { mServerName = p_ServerName; }
void setConnected(bool p_Connected) { mConnected = p_Connected; }
void setStepTime(int p_StepTime_ms);
bool fillBytes(std::vector<char> &message);
};
//----------- Disconnect -----------
class Disconnect : public OPPart {
public:
static constexpr int OPFLAG = 3;
static constexpr int BYTESLENGTH = 4;
Disconnect(const Disconnect &orig);
// Receive
Disconnect(unique_ptr<Header> header);
bool parse(const std::vector<char> &message);
// Send
Disconnect();
bool fillBytes(std::vector<char> &message);
};
//----------- Watchdog -----------
class Watchdog : public OPPart {
public:
static constexpr int OPFLAG = 8;
static constexpr int BYTESLENGTH = 4;
Watchdog(const Watchdog &orig);
// Receive
Watchdog(unique_ptr<Header> header);
bool parse(const std::vector<char> &message);
// Send
Watchdog();
bool fillBytes(std::vector<char> &message);
};
//----------- WatchdogAnswer -----------
class WatchdogAnswer : public OPPart {
public:
static constexpr int OPFLAG = 9;
static constexpr int BYTESLENGTH = 4;
// Receive
WatchdogAnswer(const WatchdogAnswer &orig);
WatchdogAnswer(unique_ptr<Header> header);
bool parse(const std::vector<char> &message);
// Send
WatchdogAnswer();
bool fillBytes(std::vector<char> &message);
};
//------------------------------ SPITProject ----------------------------------
//----------- ReportProject -----------
class ReportProject : public OPPart {
public:
static constexpr int OPFLAG = 11;
static constexpr int BYTESLENGTH = 113;
private:
string mProjectID{};
string mProjectName{};
int mFramesPerSecond{1000}; // Frames per second
// Receive
public:
ReportProject(const ReportProject &orig);
ReportProject(unique_ptr<Header> header);
bool parse(const std::vector<char> &message);
const string getProjectID() const { return mProjectID; }
const string getProjectName() const { return mProjectName; }
int getFramesPerSecond() const { return mFramesPerSecond; }
// Send
ReportProject(string p_ProjectID, string p_ProjectName);
void setFramesPerSecond(const int p_FramesPerSecond) { mFramesPerSecond = p_FramesPerSecond; }
bool fillBytes(std::vector<char> &message);
};
//----------- CloseProject -----------
class CloseProject : public OPPart {
public:
static constexpr int OPFLAG = 13;
static constexpr int BYTESLENGTH = 45;
private:
string mProjectID{};
public:
CloseProject(const CloseProject &orig);
// Receive
CloseProject(unique_ptr<Header> header);
bool parse(const std::vector<char> &message);
const string getProjectID() const {
return mProjectID;
}
// Send
CloseProject(string projectID);
bool fillBytes(std::vector<char> &message);
};
//----------- StartProjectTransfer -----------
class RequestProjectTransfer : public OPPart {
public:
static constexpr int OPFLAG = 15;
static constexpr int BYTESLENGTH = 4;
RequestProjectTransfer(const RequestProjectTransfer &orig);
// Receive
RequestProjectTransfer(unique_ptr<Header> header);
bool parse(const std::vector<char> &message);
// Send
RequestProjectTransfer();
bool fillBytes(std::vector<char> &message);
};
//----------- StartProjectTransfer -----------
class StartProjectTransfer : public OPPart {
public:
static constexpr int OPFLAG = 16;
static constexpr int BYTESLENGTH = 4;
StartProjectTransfer(const StartProjectTransfer &orig);
// Receive
StartProjectTransfer(unique_ptr<Header> header);
bool parse(const std::vector<char> &message);
// Send
StartProjectTransfer();
bool fillBytes(std::vector<char> &message);
};
//----------- EndProjectTransfer -----------
class EndProjectTransfer : public OPPart {
public:
static constexpr int OPFLAG = 17;
static constexpr int BYTESLENGTH = 4;
EndProjectTransfer(const EndProjectTransfer &orig);
// Receive
EndProjectTransfer(unique_ptr<Header> header);
bool parse(const std::vector<char> &message);
// Send
EndProjectTransfer();
bool fillBytes(std::vector<char> &message);
};
//---------------------------- SPIT_Types ----------------------------------
//----------- ReportType -----------
class ReportType : public OPPart {
public:
static constexpr int OPFLAG = 21;
static constexpr int BYTESLENGTH = 168;
private:
bool mConfirmed{};
string mTypeID{};
bool mFixed{};
bool mServerRemote{};
bool mServerProjectIndepended{};
string mTypeGroupName{};
string mSPITTypeName{};
uint64_t mDelay{};
uint64_t mDuration{};
public:
ReportType(const ReportType &orig);
// Receive
ReportType(unique_ptr<Header> header);
bool parse(const std::vector<char> &message);
bool getConfirmed() const { return mConfirmed; }
const string getID() const { return mTypeID; }
bool getFixed() const { return mFixed; }
bool getServerRemote() const { return mServerRemote; }
bool getServerProjectIndepended() const { return mServerProjectIndepended; }
const string getGroupName() const { return mTypeGroupName; }
const string getName() const { return mSPITTypeName; }
uint64_t getDelay() const { return mDelay; }
uint64_t getDuration() const { return mDuration; }
// Send
ReportType();
void setConfirmed(bool p_Confirmed) { mConfirmed = p_Confirmed; }
void setID(string iD) { mTypeID = iD; }
void setFixed(bool p_Fixed) { mFixed = p_Fixed; }
void setServerRemote(bool p_IsServerRemote) { mServerRemote = p_IsServerRemote; }
void setServerProjectIndepended(bool p_ProjectIndpended) { mServerProjectIndepended = p_ProjectIndpended; }
void setGroupName(string p_GroupName) { mTypeGroupName = p_GroupName; }
void setName(string p_Name) { mSPITTypeName = p_Name; }
void setDelay(uint64_t p_Delay) { mDelay = p_Delay; }
void setDuration(uint64_t p_Duration) { mDuration = p_Duration; }
bool fillBytes(std::vector<char> &message);
};
//----------- RemoveType -----------
class RemoveType : public OPPart {
public:
static constexpr int OPFLAG = 23;
static constexpr int BYTESLENGTH = 45;
private:
string mTypeID{};
public:
RemoveType(const RemoveType &orig);
// Receive
RemoveType(unique_ptr<Header> header);
bool parse(const std::vector<char> &message);
const string getID() const { return mTypeID; }
// Send
RemoveType();
void setID(string iD) { mTypeID = iD; }
bool fillBytes(std::vector<char> &message);
};
//---------------------------- SPITObjects ----------------------------------
//----------- ReportObject -----------
class ReportObject : public OPPart {
public:
static constexpr int OPFLAG = 31;
static constexpr int BYTESLENGTH = 339;
private:
bool mConfirmed{};
string mSPITObjectID{};
bool mFixed{};
bool mServerRemote{};
string mSPITObjectName{};
string mTypeID{};
string mServerParams{};
uint64_t mFrameStart{};
uint64_t mFrameLength{};
uint64_t mFrameFadeInLength{};
uint64_t mFrameFadeOutLength{};
int mValueFactor{};
char mValueUse{VALUEUSE_DISCARD};
int mTrack{};
string mRemark{};
uint64_t mDelay{};
uint64_t mDuration{};
bool mActionAssigned{};
public:
ReportObject(const ReportObject &orig);
// Receive
ReportObject(unique_ptr<Header> header);
bool parse(const std::vector<char> &message);
bool getConfirmed() const { return mConfirmed; }
const string getID() const { return mSPITObjectID; }
bool getFixed() const { return mFixed; }
bool getServerRemote() const { return mServerRemote; }
const string getName() const { return mSPITObjectName; }
const string getSPITTypeID() const { return mTypeID; }
const string getServerParams() const { return mServerParams; }
uint64_t getFrameStart() const { return mFrameStart; }
uint64_t getFrameLength() const { return mFrameLength; }
uint64_t getFrameFadeInLength() const { return mFrameFadeInLength; }
uint64_t getFrameFadeOutLength() const { return mFrameFadeOutLength; }
int getValueFactor() const { return mValueFactor; }
char getValueUse() const { return mValueUse; }
int getTrack() const { return mTrack; }
const string getRemark() const { return mRemark; }
uint64_t getDelay() const { return mDelay; }
uint64_t getDuration() const { return mDuration; }
bool getActionAssigned() const { return mActionAssigned; }
// Send
public:
ReportObject();
void setConfirmed(bool p_Confirmed) { mConfirmed = p_Confirmed; };
void setID(string iD) { mSPITObjectID = iD; };
void setFixed(bool p_Fixed) { mFixed = p_Fixed; };
void setServerRemote(bool p_IsRemote) { mServerRemote = p_IsRemote; };
void setName(string p_Name) { mSPITObjectName = p_Name; };
void setSPITTypeID(string iD) { mTypeID = iD; };
void setServerParams(string p_Parameters) { mServerParams = p_Parameters; };
void setFrameStart(uint64_t p_StartTime) { mFrameStart = p_StartTime; };
void setFrameLength(uint64_t p_Length) { mFrameLength = p_Length; };
void setFrameFadeInLength(uint64_t p_FadeInTime) { mFrameFadeInLength = p_FadeInTime; };
void setFrameFadeOutLength(uint64_t p_FadeOutTime) { mFrameFadeOutLength = p_FadeOutTime; };
void setValueFactor(int p_Volume) { mValueFactor = p_Volume; };
void setValueUse(char p_ValueUse) { mValueUse = p_ValueUse; };
void setTrack(int p_Track) { mTrack = p_Track; };
void setRemark(string p_Description) { mRemark = p_Description; };
void setDelay(uint64_t p_Delay);
void setDuration(uint64_t p_Duration);
void setActionAssigned(bool p_ActionAssigned) { mActionAssigned = p_ActionAssigned; }
bool fillBytes(std::vector<char> &message);
};
//----------- RemoveObject -----------
class RemoveObject : public OPPart {
public:
static constexpr int OPFLAG = 33;
static constexpr int BYTESLENGTH = 45;
private:
string mSPITObjectID{};
public:
RemoveObject(const RemoveObject &orig);
// Receive
RemoveObject(unique_ptr<Header> header);
bool parse(const std::vector<char> &message);
const string getID() const { return mSPITObjectID; }
// Send
public:
RemoveObject();
void setID(string iD) { mSPITObjectID = iD; }
bool fillBytes(std::vector<char> &message);
};
//---------------------------- SPITPlayer ----------------------------------
//----------- PlayStart -----------
class PlayStart : public OPPart {
public:
static constexpr int OPFLAG = 37;
static constexpr int BYTESLENGTH = 119;
private:
string mPlayerID{};
string mPlayerName{};
char mPlayerType{};
bool mPlayerIsRunning{};
uint64_t mTime; // Frames
public:
PlayStart(const PlayStart &orig);
// Receive
PlayStart(unique_ptr<Header> header);
bool parse(const std::vector<char> &message);
const string getPlayerID() const { return mPlayerID; }
const string getPlayerName() const { return mPlayerName; }
char getPlayerType() const { return mPlayerType; }
bool getPlayerIsRunning() const { return mPlayerIsRunning; }
uint64_t getTime() const { return mTime; }
// Send
PlayStart(string p_PlayerID, string p_PlayerName, char p_PlayerType);
void setPlayerIsRunning(bool p_IsPlaying) { mPlayerIsRunning = p_IsPlaying; }
void setTime(uint64_t p_Time) { mTime = p_Time; }
bool fillBytes(std::vector<char> &message);
};
//----------- PlayObject -----------
class PlayObject : public OPPart {
public:
static constexpr int OPFLAG = 38;
static constexpr int BYTESLENGTH = 100;
private:
string mPlayerID{};
string mSPITObjectID{};
uint64_t mFrameInsideObject{}; // Frames
bool mPlayPrePosition{};
int mValue{};
char mValueFlag{VALUEFLAG_DIRECT};
public:
PlayObject(const PlayObject &orig);
// Receive
PlayObject(unique_ptr<Header> header);
bool parse(const std::vector<char> &message);
const string getPlayerID() const { return mPlayerID; }
const string getSPITObjectID() const { return mSPITObjectID; }
uint64_t getFrameInsideObject() const { return mFrameInsideObject; }
bool getPlayPrePosition() const { return mPlayPrePosition; }
int getValue() const { return mValue; }
char getValueFlag() const { return mValueFlag; }
// Send
PlayObject();
void setPlayerID(string p_PlayerID) { mPlayerID = p_PlayerID; }
void setID(string iD) { mSPITObjectID = iD; }
void setFrameInsideObject(uint64_t p_TimeInside) { mFrameInsideObject = p_TimeInside; }
void setPlayPrePosition(bool p_PlayOldPosition) { mPlayPrePosition = p_PlayOldPosition; }
void setValue(int p_Value) { mValue = p_Value; }
void setValueFlag(char p_ValueFlag) { mValueFlag = p_ValueFlag; }
bool fillBytes(std::vector<char> &message);
};
//----------- PlayEnd -----------
class PlayEnd : public OPPart {
public:
static constexpr int OPFLAG = 39;
static constexpr int BYTESLENGTH = 45;
private:
string mPlayerID{};
public:
PlayEnd(const PlayEnd &orig);
// Receive
PlayEnd(unique_ptr<Header> header);
bool parse(const std::vector<char> &message);
const string getPlayerID() const { return mPlayerID; }
// Send
PlayEnd(string p_PlayerID);
bool fillBytes(std::vector<char> &message);
};
//----------- ReportPlayStatus -----------
class ReportPlayStatus : public OPPart {
public:
static constexpr int OPFLAG = 40;
static constexpr int BYTESLENGTH = 57;
private:
string mSPITObjectID{};
uint64_t mFrameInsideObject{};
int mValue{};
public:
ReportPlayStatus(const ReportPlayStatus &orig);
// Receive
ReportPlayStatus(unique_ptr<Header> header);
bool parse(const std::vector<char> &message);
const string getID() const { return mSPITObjectID; }
uint64_t getFrameInsideObject() const { return mFrameInsideObject; }
int getValue() const { return mValue; }
// Send
ReportPlayStatus();
void setID(string iD);
void setFrameInsideObject(uint64_t p_TimeInside);
void setValue(int p_Value);
bool fillBytes(std::vector<char> &message);
};
//----------------------- static helper functions ----------------------------
public:
static bool isSystemLittleEndian();
static optional<int> getInt(const std::vector<char> &message, int &offset);
static optional<uint64_t> getLong(const std::vector<char> &message, int &offset);
static optional<bool> getBoolean(const std::vector<char> &message, int &offset);
static optional<char> getByte(const std::vector<char> &message, int &offset);
static int setInt(int value, std::vector<char> &message, int offset);
static int setLong(uint64_t value, std::vector<char> &message, int offset);
static int setBoolean(bool value, std::vector<char> &message, int offset);
static int setByte(int value, std::vector<char> &message, int offset);
/**
* Returns the string char* of the part of the byte sequence (0x00 null terminated).
* @param p_Bytes the byte array
* @param offset the startposition of strings bytes
* @param p_Length the length of the string included the null terminate byte (0x00).
* The length of the string will be p_Length -1.
*/
static optional<string> getString(const std::vector<char> &message, int &offset, int maxLength);
/**
* Fill the p_String in the byte array.
* If p_String length is greater than p_Length -1, the p_String will be truncated.
* CAUTION the return value should be freed after use.
* @param p_String the string
* @param p_Length the maximal length of the string included the null character.
* @throws Exception if p_String == nullptr, p_Bytes == nullptr
* p_Offset less than 0
* p_ByteCount less than p_Offset + p_Length
* @return p_Length
*/
static int setString(string p_String, std::vector<char> &message, int p_Offset, int p_Length);
};
}
+122
View File
@@ -0,0 +1,122 @@
#pragma once
#include "spit_type.hpp"
#include "spit_message.hpp"
#include "spit_repo.hpp"
using namespace std;
namespace spit {
class SObjectListener;
class SObject {
public:
template <typename T>
struct Property {
friend SObject;
Property(bool &confirmed)
: rConfirmed(confirmed) {
}
bool set(const T& val) {
if (mValue == val)
return false;
mValue = val;
rConfirmed = false;
return true;
}
T get () const {
return mValue;
}
private:
bool & rConfirmed;
T mValue{};
};
Property<string> Name{mConfirmed};
Property<string> Remark{mConfirmed};
Property<string> ServerParams{mConfirmed};
Property<string> SPITTypeID{mConfirmed};
// Returns if the SPITType triggers an action on the Server or on the client.
// @return false = the action is triggered on the client The client waits for the server to play objects of this SPITType
// true = the action is triggered on the server. The server waits for the client to play objects of this SPITType.
Property<bool> ServerRemote{mConfirmed};
Property<bool> Fixed{mConfirmed};
Property<uint64_t> FrameStart{mConfirmed};
Property<uint64_t> FrameLength{mConfirmed};
Property<uint64_t> FrameFadeInLength{mConfirmed};
Property<uint64_t> FrameFadeOutLength{mConfirmed};
Property<char> ValueUse{mConfirmed};
Property<int> ValueFactor{mConfirmed};
Property<int> Track{mConfirmed};
Property<uint64_t> Delay{mConfirmed};
Property<uint64_t> Duration{mConfirmed};
static void clearUnconfirmedObjects(SpitRepo<SObject> &repo);
static void setConfirmedFalse(SpitRepo<SObject> &repo);
static void eventProjectClosed(SpitRepo<SObject> &repo, SpitRepo<SObject> &remoteRepo);
SObject(string iD) {
mID = iD;
}
private:
string mID;
bool mConfirmed{};
bool mCreatedByClient{};
bool mActionAssigned{};
bool mActionAssignedType{}; // Virtual construct
char mValueUse{0x01};
int mValueFactor{1000000}; // Values are given in ppm -> 1000000 = 1/1;
bool mPlayPrePosition;
uint64_t mPlayTimeInsideFrames;
int mPlayValue;
char mPlayValueFlag;
public:
string getID() const { return mID; }
/**
* This is used by the server and client to tell if the other side has registered the changes.
* @return true = other side has registered the changes.
*/
bool getConfirmed() const { return mConfirmed; }
bool getCreatedByClient() const { return mCreatedByClient; }
/**
* This is a virtual construct - ActionAssignedType is not implemented in the SPITProtocol.
* This is used when an action is associated with a SPITType.
* Then this action is done for every SObject with the SPITType.
* @return true = an action is assigned for the SPITType.
*/
bool getActionAssignedType() const { return mActionAssignedType; }
/** Returns if this SObject has an associated action or not.
* @return true = the user has asscociated an action with this SObject or
* the user has asscociated an action with the SPITType of this SObject
*/
bool getActionAssigned() const { return mActionAssigned || mActionAssignedType; }
bool getProjectIndepended(SpitRepo<SType> &repo, SpitRepo<SType> &remoteRepo) const {
SType *ptrSPITType;
if (ServerRemote.get() == false) {
ptrSPITType = repo.getEntry(SPITTypeID.get());
if (ptrSPITType == nullptr)
return true;
return ptrSPITType->ProjectIndepended.get();
} else {
ptrSPITType = remoteRepo.getEntry(SPITTypeID.get());
if (ptrSPITType == nullptr)
return true;
return ptrSPITType->ProjectIndepended.get();
}
};
bool setConfirmed(bool confirmed);
void setCreatedByClient(bool createdByClient) { mCreatedByClient = createdByClient; }
bool setActionAssignedType(bool actionAssigned);
bool setActionAssigned(bool actionAssigned);
bool setMessageOP(Message::ReportObject *message);
void fillMessageOP(Message::ReportObject &message);
void fillMessageOP(Message::RemoveObject &message);
};
}
+98
View File
@@ -0,0 +1,98 @@
#pragma once
#include "spit_object.hpp"
#include <unordered_map>
namespace spit {
class SPlayer {
//----- PlayObject used in PlaySequences: PlaySPITStart, PlaySPITObject,..., PlaySPITEnd ------
public:
enum PTypes {
// diescrete player: This player is used to transmit single discrete values.
PTYPE_DISCRETE = 0x00,
// continous player: This player plays SPIT objects sometime during the project.
PTYPE_CONTINOUS = 0x01,
// continous main player: time value can be used like a timecode.<br>The time value is the position in server's project.<br>A server should use only one main player.
PTYPE_MAIN = 0x02,
};
enum VFlags {
// Set the given value directly
VFLAG_DIRECT = 0x00,
// Increase the existing value by the given value.<br>This is usefull e.g. when instead of a fader a endless wheel is used*/
VFLAG_INCREASE = 0x01,
// Decrease the existing value by the given value.<br>This is usefull e.g. when instead of a fader a endless wheel is used*/
VFLAG_DECREASE = 0x02,
};
class PlayType {
public:
PlayType(SPlayer *spitPlayer, string spitObjectID);
PlayType(const PlayType &orig);
string getSPITTypeID() const { return mSpitTypeID; }
void startPlaySequence();
bool getIsNew() const { return mIsNew; }
void setIsNew(bool isNew) { mIsNew = isNew; }
bool getIsPlayed() const { return mIsPlayed; }
void setIsPlayed(bool p_IsFound) { mIsPlayed = p_IsFound; }
string getPlayerID() const { return mPlayerID; }
string getPlayerName() const { return mPlayerName; }
bool getPlayerIsRunning() const { return mPlayerRuns; }
void setPlayerIsRunning(bool playerRuns) { mPlayerRuns = playerRuns; }
void setValue(double value) { mValue = value; }
void addValue(double value, double valueFactor) { mValue += (value * valueFactor); }
double getValue() const { return mValue; }
private:
string mPlayerID;
string mPlayerName;
bool mPlayerRuns;
string mSpitTypeID;
bool mIsPlayed;
bool mIsNew;
double mValue;
};
//----- SPlayer ------
public:
// list the SPITPlayers
static unordered_map<string, unique_ptr<SPlayer>> sSpitPlayers; // all actual played objects
static SPlayer &getSPITPlayer(string pPlayerID); // if a player does not exist creates a new one
static void clearPlayers() { sSpitPlayers.clear(); }
SPlayer(string pPlayerID);
virtual ~SPlayer();
void startPlaySequence(); // called by SPIT_Client::parsePlaySPITStart
PlayType *addTypePlaying(string spitTypeID); // called by SPIT_Client::parsePlaySPITStart
void endPlaySequence(); // called by SPIT_Client::parsePlaySPITEnd
string getID() {
return mId;
}
unordered_map<string, unique_ptr<PlayType>> &getPlaySPITTypes() { return mPlaySPITTypes; }
void setIsRunning(bool isRunning) { mIsRunning = isRunning; }
void setName(string name) { mName = name; }
void setType(char type) { mType = type; }
void setTime(uint64_t frames) { mTimeFrames = frames; }
bool getIsRunning() const { return mIsRunning; }
string getName() const { return mName; }
char getType() const { return mType; }
uint64_t getTime() const { return mTimeFrames; }
private:
string mId;
string mName;
char mType;
unordered_map<string, unique_ptr<PlayType>> mPlaySPITTypes;
bool mIsRunning;
uint64_t mTimeFrames;
};
} // namespace spit
+169
View File
@@ -0,0 +1,169 @@
#pragma once
#include "sad_DetectedServer.hpp"
#include "spit_message.hpp"
#include "spit_object.hpp"
#include "spit_player.hpp"
#include "spit_repo.hpp"
#include "spit_type.hpp"
#include <functional>
#include <memory>
#include <vector>
using namespace std;
namespace spit {
class SProcessor {
public:
enum class ProjUpdate {
ServerLoaded,
StartTransfer,
EndTransfer,
ServerClosed,
};
enum class ServerEvent {
Detect,
Lost,
Changed,
};
enum class ObjectEvent {
Created,
Changed,
Deleted
};
enum class Connection {
None = 0, // No Connection established.<br>Either no connect tried or disconnected
NoneServer = 1, // Server has disconnected
Lost = 2, // The connection is lost<br>Either because of a network crash or because the server no longer responds.
Ok = 3, // Connection to the serv er is established.
TryConnection = 4, // A connection to the server is tried.
};
struct Callbacks {
function<void(ServerEvent event, const SadDetectedServer &detectedServer)> onServerEvent;
function<void(string serverID, string serverComputerName, string serverName, int serverStepTime, Connection connectionStatus, Connection oldConnectionStatus)> onConnectionStatus;
function<void(ProjUpdate state, string projectId)> onProjectUpdate;
function<void(ObjectEvent event, bool isRemote, const SType &sType)> onSpitType;
function<void(ObjectEvent event, bool isRemote, const SType &sType, const SObject &sObject)> onSpitObject;
function<void(string playerID, string playerName, char playerType, bool playerIsRunning, uint64_t timeFrames)> onPlay;
function<void(string playerID, string spitTypeID, bool playerIsRunning, bool started, bool ended, double value)> onPlaySpitType;
function<void(const SType &sType, const SObject &sObject, uint64_t p_FrameInsideObject, int value)> onPlayRemoteObjectStatus;
function<bool(bool knownType, bool createdByClient)> onCheckSpitTypeVeto;
function<bool(bool knownType)> onCheckSpitObjectVeto;
};
struct InternalCallbacks {
function<void(int ms)> delay;
function<int(Message::OPPart &msg)> sendMessage;
function<void(Message &msg)> parseMessage;
};
static constexpr int MAX_OVERTAKING_SPIT_MESSAGES = 10;
//----- SProcessor -----
public:
SProcessor(string name, string projName, string hostName, string guid,
Callbacks calls, InternalCallbacks intern);
virtual ~SProcessor();
private:
Callbacks mCallbacks;
InternalCallbacks mInternCallbacks;
string mName;
string mGuid;
string mProjName;
string mComputerName;
uint8_t mMessageNumberSend{1};
int mMessageNumberReceived{-1}; // set to -1 because the first received message will set the number (1..255)
vector<unique_ptr<Message>> mOvertakingMessages;
string mServerID{};
string mServerProjectID;
string mServerProjectName;
int mServerFramesPerSecond;
public:
string getID() { return mGuid; }
string getName() { return mName; }
string getProjName() { return mProjName; }
string getComputerName() { return mComputerName; }
void setActionType(string spitypeID, bool actionAssigned);
void setActionObject(string objectID, bool actionAssigned);
void checkParseMessageData(vector<char> data);
unique_ptr<Message::Connect> createConnectMessage(string serverIP, string serverPort, string loginName, string loginPassword);
std::vector<char> createMessage(Message::OPPart &spitMessageOPPart);
void parseMessage(Message &message);
void addOvertakingMessage(unique_ptr<Message> message);
int sendOvertakingMessage(int msgNumRec);
void resetMessageNumberReceived();
string getServerProjectID() { return mServerProjectID; }
string getServerProjectName() { return mServerProjectName; }
int getServerFramesPerSecond() { return mServerFramesPerSecond; }
// /**
// * Creates a SPITType for application actions.<br>
// * These SPITTypes will be automatically added to the SPITType-List and sended to the server.
// * @param p_ID an unique of the SPITType, these are always the same per client approach. Different client approaches should use different ids.
// * @param p_GroupName a name which groups the actions/SPITTypes
// * @param p_Name the name of the Action/SPITType
// * @param p_DefaultDelay maybe there is an default delay until the action starts, this is only used for graphical representations, 0 is allowd
// * @param p_DefaultDuration maybe the action has a default duration, this is only used for graphical representations, 0 is allowd
// * @param p_Fixed true = the server is not allowed to change this SPITType, false = the server may change or delete this SPITType
// * @param p_ProjectIndepended true = the SPITType belongs to a specific project, false = the SpitType always exists for every project
// * @return the pointer to the new SPITType.
// */
SType *createType(string iD, string groupName, string name, uint64_t defaultDelay, uint64_t defaultDuration, bool fixed, bool projectIndepended);
int sendTypeInternal(SType &sType);
/**
* If a client wants to change a SPITType, he should send it to the server.<br>
* If the SPITTye is a fixed one and not created by the client himself, then an eventSPITType can be sent from the server, which cancels the change.<br>
* @param spitypeID the id of the SPITType to remove.
* @return 0 = is sended, -1 = is not sended
*/
bool changeType(SType *spitType);
/**
* If a client wants to remove a SPITType, he should send it to the server.<br>
* If the SPITTye is a fixed one and not created by the client himself, then an eventSPITType can be sent from the server, which cancels the deletion.<br>
* @param spitypeID the id of the SPITType to remove.
* @return 0 = is sended, -1 = is not sended
*/
int removeType(string spitypeID);
int removeObject(string objectID);
bool changeObject(SObject *sObject);
void sendRemoteObject(string objectID, int valuePpm = 0, char valueFlag = 0x00);
void resetConfirmedFlags() {
SType::setConfirmedFalse(mSpitTypes);
SObject::setConfirmedFalse(mSpitObjects);
}
private:
void parseReportProject(Message::ReportProject &message);
void parseCloseProject(Message::CloseProject &message);
void parseRequestProjectTransfer(Message::RequestProjectTransfer &message);
void parseStartProjectTransfer(Message::StartProjectTransfer &message);
void parseEndProjectTransfer(Message::EndProjectTransfer &message);
void parseReportType(Message::ReportType &message);
void parseRemoveType(Message::RemoveType &message);
void parseReportObject(Message::ReportObject &message);
void parseRemoveObject(Message::RemoveObject &message);
void parsePlayStart(Message::PlayStart &message);
void parsePlayObject(Message::PlayObject &message);
void parsePlayEnd(Message::PlayEnd &message);
void parseReportPlayStatus(Message::ReportPlayStatus &message);
int sendObjectInternal(SObject *sObject);
SpitRepo<SObject> mSpitObjects;
SpitRepo<SType> mSpitTypes;
SpitRepo<SObject> mRemoteSpitObjects;
SpitRepo<SType> mRemoteSpitTypes;
};
} // namespace spit
+59
View File
@@ -0,0 +1,59 @@
#pragma once
#include <memory>
#include <string>
#include <unordered_map>
using namespace std;
namespace spit {
template <typename T>
class SpitRepo {
public:
bool hasEntry(const string &id) {
return mEntries.contains(id);
}
T *getEntry(const string &id) {
auto i = mEntries.find(id);
if (i == mEntries.end()) {
return nullptr;
}
return i->second.get();
}
const T &getEntryRef(const string &id) {
return *mEntries.at(id);
}
void addEntry(unique_ptr<T> entry) {
mEntries[entry->getID()] = move(entry);
}
bool removeEntry(const T &entry) {
return removeEntry(entry.getID());
}
bool removeEntry(const string &id) {
auto i = mEntries.find(id);
if (i == mEntries.end()) {
return false;
}
mEntries.erase(i);
return true;
}
int getSize() {
return mEntries.size();
}
unordered_map<string, unique_ptr<T>> &getRepo() {
return mEntries;
}
void clearEntries() {
mEntries.clear();
}
private:
unordered_map<string, unique_ptr<T>> mEntries;
};
}
+149
View File
@@ -0,0 +1,149 @@
#pragma once
#include "Task.hpp"
#include "esp_log.h"
#include "esp_netif.h"
#include <arpa/inet.h>
#include <fcntl.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <string>
#include <sys/socket.h>
#include <unistd.h>
using namespace std;
namespace spit {
/** Returns an guid */
string createGUID();
/** Returns the name of the client's host computer*/
string getClientHostName();
class ISocket {
public:
/**
* IpAddress class is used to fetch all available IP addresses of the computer
* This is for internal usage
*/
struct IpAddress {
public:
IpAddress(const string &iPAddress, const string &subnetMask)
: mIpAddress(iPAddress), mSubnetMask(subnetMask) {}
const string mIpAddress;
const string mSubnetMask;
/**
* Calculates the broadcast address from a ip4 address
* @param iPAddress the ip4 address
* @return returns the broadcast address
*/
string getBroadcastIP4() const {
struct in_addr broadcast;
unsigned long ipAddress;
unsigned long subnetMask;
ipAddress = inet_addr(mIpAddress.data());
subnetMask = inet_addr(mSubnetMask.data());
broadcast.s_addr = ipAddress | ~subnetMask;
return inet_ntoa(broadcast);
}
};
const IpAddress mOwnIp;
ISocket(IpAddress ip)
: mOwnIp(ip) {}
virtual int sendMessage(const char *message, int length) = 0;
virtual int receiveMessage(char *bu1ffer, int bufferLength, string *serverIp) const =0;
virtual void setConnectionParams(const string &serverIp, const string &serverPort, const string &clientIp, const string &clientPort) =0;
virtual int bindSocket(string clientIp, string clientPort) =0;
virtual int setBroadcast(bool broadcast) const =0;
virtual int sendMessageTo(const string &ip, const string &port, const char *message, int msgLength) =0;
bool getIsBound() const { return mIsBound; }
protected:
bool mIsBound{};
};
class Socket : public ISocket {
public:
static const int FAMILY_IP4 = AF_INET;
static IpAddress getAdaptersInfo();
bool createSocketUdp();
static const int INVALID_SOCKET = -1;
private:
int mUdpSocketIp4;
public:
//-------- class --------------
Socket(IpAddress ip) : ISocket(ip) {
mUdpSocketIp4 = INVALID_SOCKET;
createSocketUdp();
}
virtual ~Socket() {
ESP_LOGD("UDP", "Shutting down ...");
if (mUdpSocketIp4 != INVALID_SOCKET) {
shutdown(mUdpSocketIp4, 2);
close(mUdpSocketIp4);
mUdpSocketIp4 = INVALID_SOCKET;
}
fos::Task::delayMs(100);
mIsBound = false;
}
private:
string mServerIP{};
string mServerPort{};
string mClientIp{};
string mClientPort{}; // port given by the user maybe "0"
string mBoundClientPort{}; // port set after bind
public:
// Sets the ip address and port for server and client.
void setConnectionParams(const string &serverIp, const string &serverPort, const string &clientIp, const string &clientPort) override {
mServerIP = serverIp;
mServerPort = serverPort;
mClientIp = clientIp;
mClientPort = clientPort;
}
/**
* Send a message to the server as set by setConnectionParams
* @param message byte buffer containing the message
* @param length length of the message
* @return
*/
int sendMessage(const char *message, int length) override {
if (mServerPort.length() <= 0) {
return -1;
}
if (mIsBound == false) {
return -1;
}
int intResult = sendMessageTo(mServerIP, mServerPort, message, length);
return intResult;
}
int receiveMessage(char *bu1ffer, int bufferLength, string *serverIp) const override;
int setBroadcast(bool broadcast) const override;
int bindSocket(string clientIp, string clientPort) override;
int sendMessageTo(const string &ip, const string &port, const char *message, int msgLength) override;
string getServerIP() const { return mServerIP; }
string getServerPort() const { return mServerPort; }
string getClientIP() const { return mClientIp; }
string getClientPort() const { return mClientPort; }
string getBoundClientPort() const { return mBoundClientPort; }
void setBoundClientPort(const string &port) { mBoundClientPort = port; }
void setIsBound(bool isBound) { mIsBound = isBound; }
private:
};
}
+189
View File
@@ -0,0 +1,189 @@
#pragma once
#include "spit_message.hpp"
#include "spit_repo.hpp"
using namespace std;
namespace spit {
class STypeListener;
class SType {
public:
template <typename T>
struct Property {
friend SType;
Property(bool &confirmed)
: rConfirmed(confirmed) {
}
bool set(const T& val) {
if (mValue == val)
return false;
mValue = val;
rConfirmed = false;
return true;
}
T get () const {
return mValue;
}
private:
bool & rConfirmed;
T mValue{};
};
Property<string> GroupName{mConfirmed};
Property<string> Name{mConfirmed};
Property<bool> Fixed{mConfirmed};
Property<bool> ProjectIndepended{mConfirmed};
// Returns if the SType triggers an action on the Server or on the client.
// @return false = the action is triggered on the client The client waits for the server to play objects of this SType
// true = the action is triggered on the server. The server waits for the client to play objects of this SType.
Property<bool> ServerRemote{mConfirmed};
Property<uint64_t> DefaultDelay{mConfirmed};
Property<uint64_t> DefaultDuration{mConfirmed};
// Virtual construct - means ActionAssigned for STypes is is not implemented in SPITProtocol.
// This can used to setActionAssigned to all SPITObjects with this SType.
// This may be used in the case when the same action should be done for every SPITObject with this SType associated.
// @return true = the user has associated an action with this SType.
Property<bool> ActionAssigned{mConfirmed};
// Sets confirmed = false for all STypes.
// This should be called when the client has loaded a project.
static void setConfirmedFalse(SpitRepo<SType> &repo) {
auto &entries = repo.getRepo();
for (auto i = entries.begin(); i != entries.end(); i++) {
i->second->setConfirmed(false);
}
}
static void eventProjectClosed(SpitRepo<SType> &repo, SpitRepo<SType> &remoteRepo) {
auto &entries = repo.getRepo();
for (auto i = entries.begin(); i != entries.end();) {
SType *ptrSType = i->second.get();
if (ptrSType->getCreatedByClient() == true) {
i++;
continue;
}
i = entries.erase(i);
}
// Remote
remoteRepo.clearEntries();
}
SType(string iD) {
mID = iD;
}
SType(string iD, string grpName, string name, bool fixed, bool projectIndependent, uint64_t delay, uint64_t duration) {
mConfirmed = false;
mID = iD;
ActionAssigned.set(false);
GroupName.set(grpName);
Name.set(name);
Fixed.set(fixed);
ProjectIndepended.set(projectIndependent);
DefaultDelay.set(delay);
DefaultDuration.set(duration);
}
private:
bool mConfirmed{};
string mID;
bool mCreatedByClient{}; // flag true = the SPITTyype is created by the client
// false = the SType is received from the server
public:
string getID() const { return mID; }
// This is used by the server and client to tell if the other side has registered the changes.
// @return true = other side has registered the changes.
bool getConfirmed() const { return mConfirmed; }
bool getCreatedByClient() const { return mCreatedByClient; }
void setCreatedByClient(bool createdByClient) { mCreatedByClient = createdByClient; }
// Set the content of the SType as received from the server
// @param p_ReportSType
// @return true = the SType is changed
// false = the SType is NOT changed, because it is not allowed to change.
bool setMessageOP(Message::ReportType &message) {
bool changeAllowed;
if ((mCreatedByClient == true) && (Fixed.get() == true)) {
changeAllowed = false;
} else {
changeAllowed = true;
}
// Confirmation should be changeable
if (mCreatedByClient) {
// Server reports a client created SType -> the type is confirmed
mConfirmed = true;
} else {
// type is NOT client created -> the client has to confirm the SPITtype
mConfirmed = message.getConfirmed();
}
if (changeAllowed == false) {
return false;
}
bool changed = false;
if (DefaultDelay.mValue != message.getDelay()) {
DefaultDelay.mValue = message.getDelay();
changed = true;
}
if (DefaultDuration.mValue != message.getDuration()) {
DefaultDuration.mValue = message.getDuration();
changed = true;
}
if (Fixed.mValue != message.getFixed()) {
Fixed.mValue = message.getFixed();
changed = true;
}
if (GroupName.mValue != message.getGroupName()) {
GroupName.mValue = message.getGroupName();
changed = true;
}
if (mID != message.getID()) {
mID = message.getID();
changed = true;
}
if (Name.mValue != message.getName()) {
Name.mValue = message.getName();
changed = true;
}
if (ProjectIndepended.mValue != message.getServerProjectIndepended()) {
ProjectIndepended.mValue = message.getServerProjectIndepended();
changed = true;
}
if (ServerRemote.mValue != message.getServerRemote()) {
ServerRemote.mValue = message.getServerRemote();
changed = true;
}
return changed;
}
void fillMessageOP(Message::ReportType &message) const {
message.setConfirmed(mConfirmed);
message.setDelay(DefaultDelay.mValue);
message.setDuration(DefaultDuration.mValue);
message.setFixed(Fixed.mValue);
message.setGroupName(GroupName.mValue);
message.setID(mID);
message.setName(Name.mValue);
message.setServerProjectIndepended(ProjectIndepended.mValue);
message.setServerRemote(ServerRemote.mValue);
}
void fillMessageOP(Message::RemoveType &message) const {
message.setID(mID);
}
// This is used by the server and client to tell if the other side has registered the changes.
// @param confirmed
// @return if mConfirmed has changed
bool setConfirmed(bool confirmed) {
if (mConfirmed == confirmed)
return false;
mConfirmed = confirmed;
return true;
}
};
}
+1
View File
@@ -0,0 +1 @@
https://github.com/typester/esp32-uuid/tree/master
+7
View File
@@ -0,0 +1,7 @@
Copyright 2019 Daisuke Murase (typester)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+84
View File
@@ -0,0 +1,84 @@
#include "uuid.h"
#include <string.h>
#include "esp_system.h"
#include "esp_random.h"
void uuid_generate(uuid_t out)
{
esp_fill_random(out, sizeof(uuid_t));
/* uuid version */
out[6] = 0x40 | (out[6] & 0xF);
/* uuid variant */
out[8] = (0x80 | out[8]) & ~0x40;
}
static uint8_t unhex_char(unsigned char s)
{
if (0x30 <= s && s <= 0x39) { /* 0-9 */
return s - 0x30;
} else if (0x41 <= s && s <= 0x46) { /* A-F */
return s - 0x41 + 0xa;
} else if (0x61 <= s && s <= 0x69) { /* a-f */
return s - 0x61 + 0xa;
} else {
/* invalid string */
return 0xff;
}
}
static int unhex(unsigned char *s, size_t s_len, unsigned char *r)
{
int i;
for (i = 0; i < s_len; i += 2) {
uint8_t h = unhex_char(s[i]);
uint8_t l = unhex_char(s[i + 1]);
if (0xff == h || 0xff == l) {
return -1;
}
r[i / 2] = (h << 4) | (l & 0xf);
}
return 0;
}
int uuid_parse(const char *in, uuid_t uu)
{
const char *p = in;
uint8_t *op = (uint8_t *)uu;
if (0 != unhex((unsigned char *)p, 8, op)) {
return -1;
}
p += 8;
op += 4;
for (int i = 0; i < 3; i++) {
if ('-' != *p++ || 0 != unhex((unsigned char *)p, 4, op)) {
return -1;
}
p += 4;
op += 2;
}
if ('-' != *p++ || 0 != unhex((unsigned char *)p, 12, op)) {
return -1;
}
p += 12;
op += 6;
return 0;
}
void uuid_unparse(const uuid_t uu, char *out)
{
snprintf(out, UUID_STR_LEN,
"%02x%02x%02x%02x-%02x%02x-%02x%02x-"
"%02x%02x-%02x%02x%02x%02x%02x%02x",
uu[0], uu[1], uu[2], uu[3], uu[4], uu[5], uu[6], uu[7], uu[8], uu[9], uu[10], uu[11],
uu[12], uu[13], uu[14], uu[15]);
}
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include <stdint.h>
typedef uint8_t uuid_t[16];
#define UUID_STR_LEN 37
#ifdef __cplusplus
extern "C" {
#endif
void uuid_generate(uuid_t out);
int uuid_parse(const char *in, uuid_t uu);
void uuid_unparse(const uuid_t uu, char *out);
#ifdef __cplusplus
}
#endif
+650
View File
@@ -0,0 +1,650 @@
#include "spit_client.hpp"
#include "esp_log.h"
#include "Queue.hpp"
namespace spit {
static const char *TAG = "SPIT";
static fos::Queue<vector<char>> incommingSpit(128);
/**
* Create a new SPIT_client instance, should be done once by the user app.
* @param p_SPITClientInterface the user app should implement the SPIT_ClientInterface.
*/
SClient::SClient(string name, string projName, ISocket &socket, Callbacks calls)
: ISadClient(),
mCallbacks(calls),
mProcessor(name, projName, getClientHostName(), createGUID(), calls,
{
.delay = [&](int ms){ return fos::Task::delayMs(ms); },
.sendMessage = [&](Message::OPPart &msg){ return sendMessage(msg); },
.parseMessage = [&](Message& msg) { parseMessage(msg); }
}
),
mSocket(socket) {
}
SClient::~SClient() {
stopServerAutoDetect();
disconnectInternal(Connection::None);
}
void SClient::clientProjectClose() {
if (mConnectionStatus == Connection::Ok) {
ESP_LOGI(TAG, " ... tidy up ressources");
Message::CloseProject closeSPITProject(mProcessor.getProjName());
if (sendMessage(closeSPITProject) < 0) {
ESP_LOGD(TAG, "Client could not send Client's ReportProject");
}
}
}
void SClient::clientProjectLoaded() {
// set confirmed = false for all SPITTypes and SPITObjects
mProcessor.resetConfirmedFlags();
if (mConnectionStatus == Connection::Ok) {
// send ReportProject to the server
Message::ReportProject reportSPITProject(mProcessor.getProjName(), mProcessor.getProjName());
reportSPITProject.setFramesPerSecond(0);
if (sendMessage(reportSPITProject) < 0) {
ESP_LOGD(TAG, "SPIT_CLient::clientProjectLoaded could not send Client's ReportProject");
}
// send RequestProjectTransfer
Message::RequestProjectTransfer sMRequestProjectTransfer;
if (sendMessage(sMRequestProjectTransfer) < 0) {
ESP_LOGD(TAG, "SPIT_CLient::clientProjectLoaded could not send RequestProjectTransfer");
}
}
}
//---------- ServerAutoDetection SAD -----------
/** Starts the autodetection of SPITServers in the local network. If a connection is tried, the autodetection should be stopped before.
* If a SPITServer is detected in the locale network an eventServerDetected occurs.
*/
void SClient::startServerAutoDetect() {
if (mDisconnectedByServer == true) {
// Server has previously disconnected -> do not start ServerAutodetect directly
fos::Task::delayMs(1000); // sleep a little to give the SPITServer time to clean up its stuff
}
SadDetectedServer::clearDetectedServers();
if (!sThread) {
sThread = new fos::TaskT<SClient &>(SClient::processSocketTask, *this, "spitSock", fos::Task::DefaultStackSize * 2);
}
mAbortSearch = false;
sStartSad.give();
}
/** Stops the auto detection of SPIT servers. */
void SClient::stopServerAutoDetect() {
mAbortSearch = true;
}
/**
* If a new server was found by the ServerAutoDetect mechanism, this function is called.
* To do so the SAD_Client ServerAutoDetect has to be startet - see SAD_Client::startServerAutoDetect.
* If a server can be reached via several IP addresses, the IP address is selected according to the following preference:
*/
void SClient::eventServerDetected(SadDetectedServer &server) {
mCallbacks.onServerEvent(ServerEvent::Detect, server);
// If a connection has been established and the connection is lost -> reconnect
if (_doReconnect) {
if (mServerID == server.getID()) {
stopServerAutoDetect();
ESP_LOGI(TAG, "reconnect to %s / %s", server.getIP().data(), server.getPort().data());
if (connect(server.getIP(), server.getPort(), server.getClientIP(), "0")) {
return;
}
}
}
// Stop auto detection because a suitable server was found
stopServerAutoDetect();
ESP_LOGI(TAG, "connect to %s / %s", server.getIP().data(), server.getPort().data());
if (!connect(server.getIP(), server.getPort(), server.getClientIP(), "0" /*= any port*/)) {
// connection message could not be sent -> better to restart AutoDetection
startServerAutoDetect();
}
}
/**
* If a SPITServer is not more found, this function is called.
* To do so the SAD_Client ServerAutoDetect has to be startet - see SAD_Client::startServerAutoDetect.
*/
void SClient::eventServerLost(SadDetectedServer &server) {
mCallbacks.onServerEvent(ServerEvent::Lost, server);
}
void SClient::eventServerChanged(SadDetectedServer &server) {
mCallbacks.onServerEvent(ServerEvent::Changed, server);
// tryConnection(detectedServer);
}
// void MyApp::tryConnection(const SadDetectedServer &detected) {
// // fetch the infoemations from the detected
// bool boolSuccess = false;
// bool boolPossibleConnect = checkPossibleConnection(detected);
// if (boolPossibleConnect == false)
// return;
// // --------------------------------------
// // You have three Options to use the detected server
// // OPTION 1 Use the first server found
// // OPTION 2 Use the first server found that fulfills specific conditions
// // OPTION 3 take a copy of the detected and do connection later
// // SadDetectedServer o_DetectedServer = detected; //this will call the copy constructor
// // ---------------------------------------
// // Excample use the first detected LOOPBACK server, means the server runs on the same computer as this client
// // if (detected.getIpIsLoopback() == true) { //omit the if condition if you want to use the first server found
// boolSuccess = false;
// // Stop auto detection because a suitable server was found
// stopServerAutoDetect();
// if (mSPIT_Client != nullptr) {
// mServerProjectName = detected.getProjectName();
// mServerIP = detected.getIP();
// #if defined(DEBUGOUTPUT)
// ESP_LOGD(TAG, "MySPITApp: connect to %s ", detected.getIP().data());
// #endif
// // Use the mBoardNumber as login name
// string stringLoginName = "";
// string stringLoginPassword = "";
// boolSuccess = mSPIT_Client->connect(detected.getIP(), detected.getPort(), detected.getClientIP(), stringLoginName, stringLoginPassword); // ClientPort = "0" meeans use any port
// }
// if (boolSuccess == false) {
// // connection message could not be sent
// // perhaps restart AutoDetection
// startServerAutoDetect();
// } else {
// // WAITING FOR CONNECTION ANSWER
// // If the Server is closed in the meantime no answer will return
// //-> connect(..) has started the watchdog
// }
// // } //Excample
// }
int SClient::sendMessage(Message::OPPart &opPart) {
if (mMessageNumberSend == 255)
mMessageNumberSend = 1;
else
mMessageNumberSend++;
opPart.setMessageNumber(mMessageNumberSend);
opPart.setServerInfoInHeader(mProcessor.getID(), mServerID);
int opFlag = opPart.getHeader()->getType();
try {
auto spitMessage = Message::createMessage(opPart);
if (spitMessage.empty()) {
return -1;
}
int intResult = mSocket.sendMessage(spitMessage.data(), spitMessage.size());
if (intResult < 0) {
ESP_LOGD(TAG, "client could not send message OPFlage: %d", opFlag);
}
return intResult;
} catch (...) {
ESP_LOGD(TAG, "Exception client OPFlag: %d", opFlag);
}
return -1;
}
bool SClient::startReceiveSPITMessages() {
if (!mSocket.getIsBound())
return false;
mStopReceiveMessages = false;
mThreadReceive = new fos::TaskT<SClient &>(receiveSpitMessage, *this, "receiveTask", fos::Task::DefaultStackSize, fos::Task::MAX_PRIORITY-3);
mThreadProcess = new fos::TaskT<SClient &>(processSpitMessage, *this, "processTask", fos::Task::DefaultStackSize);
return true;
}
void SClient::stopReceiveSPITMessages() {
mStopReceiveMessages = true;
}
void SClient::receiveSpitMessage(SClient &rM) {
char buffer[Message::MAX_MESSAGE_SIZE];
int rdLen;
while (rM.mStopReceiveMessages == false) {
rdLen = rM.mSocket.receiveMessage(buffer, Message::MAX_MESSAGE_SIZE, nullptr);
if (rdLen < 0) {
if (rM.mStopReceiveMessages) {
return;
}
continue;
}
auto vec = make_unique<vector<char>>(buffer, buffer + rdLen);
incommingSpit.enqueue(std::move(vec));
}
}
void SClient::processSpitMessage(SClient &rM) {
while (true) {
auto data = *incommingSpit.dequeue();
auto spitMsg = make_unique<Message>(data);
rM.checkParseMessage(move(spitMsg));
}
}
void SClient::resetMessageNumberReceived() {
mMessageNumberReceived = -1;
mProcessor.resetMessageNumberReceived();
}
void SClient::checkParseMessage(unique_ptr<Message> p_SPITMessage) {
answerWatchdog();
if (!p_SPITMessage || !p_SPITMessage->getHeader() || !p_SPITMessage->getOPPart()) {
ESP_LOGD(TAG, "Client::parseMessage incomplete message");
return;
}
int msgNum = (int)(p_SPITMessage->getHeader()->getMessageNumber() & 0xFF);
if (msgNum == 0) {
parseMessage(*p_SPITMessage);
return;
}
if (mMessageNumberReceived < 0) {
mMessageNumberReceived = msgNum;
parseMessage(*p_SPITMessage);
return;
}
int intNextMessageNumber = mMessageNumberReceived + 1;
if (intNextMessageNumber > 255 || intNextMessageNumber < 1)
intNextMessageNumber = 1;
if (msgNum == intNextMessageNumber) {
mMessageNumberReceived = msgNum;
parseMessage(*p_SPITMessage);
mMessageNumberReceived = mProcessor.sendOvertakingMessage(mMessageNumberReceived);
} else {
// p_SPITMessage has overtaken another message
mProcessor.addOvertakingMessage(move(p_SPITMessage));
}
}
void SClient::parseMessage(Message& message) {
answerWatchdog();
if (!message.getHeader() || !message.getOPPart()) {
ESP_LOGD(TAG, "Client::parseMessage incomplete message");
return;
}
switch (message.getType()) {
//----- Connection -----
case Message::ConnectAnswer::OPFLAG:
parseConnectAnswer(message.getAs<Message::ConnectAnswer>());
break;
case Message::Disconnect::OPFLAG:
parseDisconnect(message.getAs<Message::Disconnect>());
break;
case Message::Watchdog::OPFLAG:
parseWatchdog(message.getAs<Message::Watchdog>());
break;
case Message::WatchdogAnswer::OPFLAG:
parseWatchdogAnswer(message.getAs<Message::WatchdogAnswer>());
break;
default:
// pass object to SubProcessor
mProcessor.parseMessage(message);
}
}
//----- Connection ------
/**
* Tries to connect to a SPITServer.
* @param serverIP the ip address of the SPIT server
* @param serverPort the port number of the SPIT server
* @param clientIP the local IP address of the client
* @param clientPort the port number of the Client, if "0" the OS uses any possible port number
* @param loginName login user name (liveSHOW does not use this -> mayby ""
* @param loginPassword login user password (liveSHOW does not use this -> mayby ""
* @return false = an error has occured, true = the connect was sent to the server
*/
bool SClient::connect(string serverIP, string serverPort, string clientIP, string clientPort, string loginName, string loginPassword) {
disconnect();
mSocket.setConnectionParams(serverIP, serverPort, clientIP, clientPort);
if (mSocket.bindSocket(clientIP, clientPort) < 0) {
mStopReceiveMessages = true;
return false;
}
auto connMsg = mProcessor.createConnectMessage(serverIP, serverPort, loginName, loginPassword);
if (startReceiveSPITMessages() == false) {
mStopReceiveMessages = true;
return false;
}
resetMessageNumberReceived();
Connection intOldConnectionStatus = mConnectionStatus;
mConnectionStatus = Connection::TryConnection;
mCallbacks.onConnectionStatus(mServerID, mServerComputerName, mServerName, mServerStepTime,
mConnectionStatus, intOldConnectionStatus);
// sleep a little to make sure that the reception of the messages is started
fos::Task::delayMs(500);
if (sendMessage(*connMsg) < 0) {
mConnectionStatus = Connection::None;
mStopReceiveMessages = true;
return false;
}
return true;
}
/**
* Terminates the connection to the SPITServer.
*/
void SClient::disconnect() {
disconnectInternal(Connection::None);
}
void SClient::disconnectInternal(Connection cause) {
Connection intOldConnectionStatus = mConnectionStatus;
mConnectionStatus = cause;
switch (mConnectionStatus) {
case Connection::Lost:
break;
case Connection::None: {
Message::Disconnect sMDisconnect;
sendMessage(sMDisconnect);
} break;
case Connection::NoneServer:
break;
default:
break;
}
stopReceiveSPITMessages();
mCallbacks.onConnectionStatus(mServerID, mServerComputerName, mServerName, mServerStepTime,
mConnectionStatus, intOldConnectionStatus);
if (intOldConnectionStatus != Connection::None && intOldConnectionStatus != Connection::NoneServer) {
ESP_LOGI(TAG, "eventConnectionStatus %d ", (int)mConnectionStatus);
switch (mConnectionStatus) {
case SClient::Connection::None:
ESP_LOGD(TAG, "not connected");
break;
case SClient::Connection::NoneServer:
ESP_LOGD(TAG, "connection terminated by server");
// call eventProjectClosed because this may not be sended by the server,
// when the connection is closed
mCallbacks.onProjectUpdate(ProjUpdate::ServerClosed, mProcessor.getServerProjectID());
// Server has terminated the connection -> start a new game
// _doReconnect = false;
startServerAutoDetect();
break;
case SClient::Connection::Lost:
ESP_LOGD(TAG, "connection lost");
// Maybe you want to reconnect, see eventServerDetected(...)
// We have lost the connection -> set the flag to true and start the autodetection to reconnect at eventServerDetected(...)
// _doReconnect = true;
startServerAutoDetect();
break;
default:
break;
}
}
}
void SClient::parseConnectAnswer(Message::ConnectAnswer &message) {
if (message.getClientID() != mProcessor.getID())
return;
mServerID = message.getServerID();
mServerComputerName = message.getServerComputerName();
mServerName = message.getServerName();
mServerStepTime = message.getStepTime();
if (message.getConnected() == false) {
disconnectInternal(Connection::NoneServer);
} else {
mConnectionStatus = Connection::Ok;
mDisconnectedByServer = false;
ESP_LOGD(TAG, "connection established");
stopServerAutoDetect(); // normally you should stop the autodetection before you try a connection
// This is redundand
mStopReceiveMessages = false;
// Maybe you want to reconnect, see eventServerDetected(...)
// Here the connection is established -> don't reconnect at eventServerDetected(...)
// _doReconnect = false;
}
}
/**
* The SPIT server was closed
* @param message
*/
void SClient::parseDisconnect(Message::Disconnect &message) {
Connection intCauseDisconnect = Connection::NoneServer;
mDisconnectedByServer = true;
SClient::disconnectInternal(intCauseDisconnect);
}
//----- Watchdog ------
/**
* The SPIT server sends a watchdog message at regular intervals. The client responds immediately with WatchdogAnswer
* Conversely, the client also sends a watchdog message at regular intervals and waits for a WatchdogAnswer message.
* This is used to check if the other side is still present.
* @param message
*/
void SClient::parseWatchdog(Message::Watchdog &message) {
Message::WatchdogAnswer o_WatchdogAnswer;
sendMessage(o_WatchdogAnswer);
}
/**
* The SPIT server sends a watchdog message at regular intervals. The client responds immediately with WatchdogAnswer
* Conversely, the client also sends a watchdog message at regular intervals and waits for a WatchdogAnswer message.
* This is used to check if the other side is still present.
* @param message
*/
void SClient::parseWatchdogAnswer(Message::WatchdogAnswer &message) {
answerWatchdog(); // is also done by parseMessage
}
void SClient::sendWatchdog() {
int intResult;
Message::Watchdog message;
intResult = sendMessage(message);
if (intResult < 0) {
ESP_LOGW("WDT", "Failed to send Watchdog msg");
}
}
void SClient::eventWatchdogLost() {
if (mConnectionStatus == Connection::TryConnection) {
disconnectInternal(Connection::NoneServer);
} else {
disconnectInternal(Connection::Lost);
}
}
void SClient::sendProject(SClient *p_PtrSPIT_Client) {
// int intResult;
// Message::StartProjectTransfer* ptrSMStartProjectTransfer;
// Message::EndProjectTransfer* ptrSMEndProjectTransfer;
// int intCount;
// SPITType* ptrSPITType;
// SObject* ptrSPITObject;
// //StartProjectTransfer
// NotifyStartProjectTransfer_Client* o_RunnableStart = new NotifyStartProjectTransfer_Client(p_PtrSPIT_Client->getSPITClientInterface());
// SPIT_Handler::addRunnable(o_RunnableStart);
// ptrSMStartProjectTransfer = new Message::StartProjectTransfer();
// intResult = p_PtrSPIT_Client->sendMessage(ptrSMStartProjectTransfer);
// if (intResult < 0) {
// return;
// }
// // mWatchdog->answerWatchdog();
// // {
// //send all listes SPITTypes
// // unique_lock<recursive_mutex> o_LockType(*SPITType::getMutex());
// intCount = SPITType::getSPITTypeCount();
// for (int i = 0; i < intCount; i++) {
// ptrSPITType = mSpitTypes.getEntry(i);
// if (ptrSPITType == nullptr) continue;
// if (ptrSPITType->getCreatedByClient() == false) continue;
// intResult = p_PtrSPIT_Client->sendTypeInternal(ptrSPITType);
// if (intResult < 0) return;
// // mWatchdog->answerWatchdog();
// this_thread::sleep_for(chrono::milliseconds(7));
// }
// // }
// // {
// // unique_lock<recursive_mutex> o_LockObject(*SObject::getMutex());
// intCount = mSpitObjects.getEntryCount();
// for (int i = 0; i < intCount; i++) {
// ptrSPITObject = mSpitObjects.getEntry(i);
// if (ptrSPITObject == nullptr) continue;
// if (ptrSPITObject->getCreatedByClient() == false) continue;
// intResult = p_PtrSPIT_Client->sendObjectInternal(ptrSPITObject);
// if (intResult < 0) return;
// // mWatchdog->answerWatchdog();
// this_thread::sleep_for(chrono::milliseconds(7));
// }
// // }
// //EndProjectTransfer
// ptrSMEndProjectTransfer = new Message::EndProjectTransfer();
// intResult = p_PtrSPIT_Client->sendMessage(ptrSMEndProjectTransfer);
// if (intResult < 0) {
// return;
// }
// // mWatchdog->answerWatchdog();
// NotifyEndProjectTransfer_Client* o_RunnableEnd = new NotifyEndProjectTransfer_Client(p_PtrSPIT_Client->getSPITClientInterface());
// SPIT_Handler::addRunnable(o_RunnableEnd);
// //Send act
}
SType *SClient::createTypeFixed(string iD, string group, string name, uint64_t delay, uint64_t duration) {
return mProcessor.createType(iD, group, name, delay, duration, true, true);
}
fos::Semaphore SClient::sStartSad;
unordered_map<string, unique_ptr<SadDetectedServer>> SadDetectedServer::sKnownServers;
void SClient::processSocketTask(SClient &instance) {
ESP_LOGI(TAG, "Starting Socket managing");
static const string SERVERAUTODETECT_PRECODE = "#SAD#";
static const string SERVERAUTODETECT_SEPERATOR = "#,#";
static const string SERVERAUTODETECT_TASKID = "#spit#"; // -> "#SAD##,##spit#"
static const string SERVERAUTODETECT_PORT = "15110";
static constexpr int WATCHDOGTIME = 3000;
while (true) {
sStartSad.take();
bool searching = true;
instance.mSocket.setBroadcast(true);
string stringDatagram = SERVERAUTODETECT_PRECODE + SERVERAUTODETECT_SEPERATOR + SERVERAUTODETECT_TASKID;
int intDatagramLength = stringDatagram.length();
while (searching) {
if (instance.mAbortSearch)
break;
SadDetectedServer::resetServerFlags();
string stringBroadcastIP4 = instance.mSocket.mOwnIp.getBroadcastIP4();
instance.mSocket.sendMessageTo(stringBroadcastIP4, SERVERAUTODETECT_PORT, stringDatagram.data(), intDatagramLength);
{
// wait for 3000 milliseconds or
fos::Task::delayMs(3000);
}
bool receiving = true;
while (receiving) {
if (instance.mAbortSearch) {
break;
}
constexpr int SIZE = 512;
char buffer[SIZE + 1];
string stringServerIP;
int intReceivedByteCount = instance.mSocket.receiveMessage(buffer, SIZE, &stringServerIP);
if (intReceivedByteCount <= 0) {
break;
}
buffer[intReceivedByteCount] = '\0';
string stringMessage = buffer;
// Check if precode is ok
if (stringMessage.find(SERVERAUTODETECT_PRECODE.data(), 0) != 0) {
break;
}
int intIndex = 0;
int intOldPos = 0;
string stringPart;
string strParts[10];
while (intIndex < SadDetectedServer::PARAM_IP) {
int intPos = stringMessage.find(SERVERAUTODETECT_SEPERATOR.data(), intOldPos);
if (intPos == string::npos) {
stringPart = "";
} else {
stringPart = stringMessage.substr(intOldPos, intPos - intOldPos);
intOldPos = intPos + SERVERAUTODETECT_SEPERATOR.length();
}
strParts[intIndex] = stringPart;
intIndex++;
}
auto server = make_unique<SadDetectedServer>(
strParts[SadDetectedServer::PARAM_NAME],
strParts[SadDetectedServer::PARAM_ID],
strParts[SadDetectedServer::PARAM_COMPUTERNAME],
stringServerIP,
strParts[SadDetectedServer::PARAM_PORT],
strParts[SadDetectedServer::PARAM_CLIENTIP],
strParts[SadDetectedServer::PARAM_ADDITIONS],
strParts[SadDetectedServer::PARAM_SERVERPROJECTNAME]);
SadDetectedServer::addDetectedServer(move(server));
}
fos::Task::delayMs(100);
SadDetectedServer::evaluateDetectedServers(instance);
instance.mAbortSearch = false;
// if(instance.mConnectionStatus == Connection::Ok) break;
fos::Task::delayMs(1000);
}
instance.mSocket.setBroadcast(false);
ESP_LOGW(TAG, "Finished searching");
ESP_LOGW(TAG, "Start working");
auto nextWdTrigger = fos::Timing::getMilliSec() + WATCHDOGTIME;
// auto nextWdCheck = nextWdTrigger + WATCHDOGTIME;
while (true) {
// auto time = fos::Timing::getMilliSec();
// if (time > nextWdTrigger) {
// instance.sAnswered = false;
// // instance.sendWatchdog();
// nextWdTrigger = nextWdCheck + WATCHDOGTIME;
// }
// if (time > nextWdCheck) {
// if (!instance.sAnswered) {
// ESP_LOGW(TAG, "TimeOut");
// instance.eventWatchdogLost();
// break;
// }
// nextWdCheck = nextWdTrigger + WATCHDOGTIME;
// }
fos::Task::delayMs(25);
// if (instance.mStopReceiveMessages)
// break;
}
}
}
}
File diff suppressed because it is too large Load Diff
+202
View File
@@ -0,0 +1,202 @@
#include "spit_object.hpp"
#include "spit_socket.hpp"
#include "esp_log.h"
namespace spit {
/**
* clears all unconfirmed SObjects not created by this SPIT_Client.
*/
void SObject::clearUnconfirmedObjects(SpitRepo<SObject> &repo) {
auto &entries = repo.getRepo();
for (auto i = entries.begin(); i != entries.end();) {
SObject *ptrSObject = i->second.get();
if (ptrSObject->getCreatedByClient() == false && ptrSObject->getConfirmed() == false) {
i = entries.erase(i);
} else {
i++;
}
}
}
void SObject::eventProjectClosed(SpitRepo<SObject> &repo, SpitRepo<SObject> &remoteRepo) {
auto &entries = repo.getRepo();
for (auto i = entries.begin(); i != entries.end(); ) {
SObject *ptrSObject = i->second.get();
if (ptrSObject->getCreatedByClient() == true) {
i++;
continue;
}
i = entries.erase(i);
}
// Remote
remoteRepo.clearEntries();
}
/**
* sets confirmed = false for all SObjects.
* This should be called when the client has loaded a project.
*/
void SObject::setConfirmedFalse(SpitRepo<SObject> &repo) {
auto &entries = repo.getRepo();
for (auto i = entries.begin(); i != entries.end(); i++) {
i->second->setConfirmed(false);
}
}
bool SObject::setMessageOP(Message::ReportObject *message) {
bool changeAllowed;
if ((mCreatedByClient == true) && (Fixed.mValue == true)) {
changeAllowed = false;
} else {
changeAllowed = true;
}
// Confirmation should be changable
if (mCreatedByClient) {
// Server reports a client created SPITType -> the type is confirmed
mConfirmed = true;
} else {
// type is NOT client created -> the client has to confirm the SPITtype
mConfirmed = message->getConfirmed();
}
if (changeAllowed == false) {
return false;
}
bool changed = false;
if (Delay.mValue != message->getDelay()) {
Delay.mValue = message->getDelay();
changed = true;
}
if (Duration.mValue != message->getDuration()) {
Duration.mValue = message->getDuration();
changed = true;
}
if (Fixed.mValue != message->getFixed()) {
Fixed.mValue = message->getFixed();
changed = true;
}
if (FrameFadeInLength.mValue != message->getFrameFadeInLength()) {
FrameFadeInLength.mValue = message->getFrameFadeInLength();
changed = true;
}
if (FrameFadeOutLength.mValue != message->getFrameFadeOutLength()) {
FrameFadeOutLength.mValue = message->getFrameFadeOutLength();
changed = true;
}
if (FrameLength.mValue != message->getFrameLength()) {
FrameLength.mValue = message->getFrameLength();
changed = true;
}
if (FrameStart.mValue != message->getFrameStart()) {
FrameStart.mValue = message->getFrameStart();
changed = true;
}
if (Remark.mValue != message->getRemark()) {
Remark.mValue = message->getRemark();
changed = true;
}
if (mID != message->getID()) {
mID = message->getID();
changed = true;
}
if (Name.mValue != message->getName()) {
Name.mValue = message->getName();
changed = true;
}
if (SPITTypeID.mValue != message->getSPITTypeID()) {
SPITTypeID.mValue = message->getSPITTypeID();
changed = true;
}
if (ServerParams.mValue != message->getServerParams()) {
ServerParams.mValue = message->getServerParams();
changed = true;
}
if (ServerRemote.mValue != message->getServerRemote()) {
ServerRemote.mValue = message->getServerRemote();
changed = true;
}
if (Track.mValue != message->getTrack()) {
Track.mValue = message->getTrack();
changed = true;
}
if (mValueFactor != message->getValueFactor()) {
mValueFactor = message->getValueFactor();
changed = true;
}
if (mValueUse != message->getValueUse()) {
mValueUse = message->getValueUse();
changed = true;
}
return changed;
}
void SObject::fillMessageOP(Message::ReportObject &message) {
message.setActionAssigned(getActionAssigned()); // Action can be assigned to type or to this object
message.setConfirmed(mConfirmed);
message.setDelay(Delay.mValue);
message.setDuration(Duration.mValue);
message.setFixed(Fixed.mValue);
message.setFrameFadeInLength(FrameFadeInLength.mValue);
message.setFrameFadeOutLength(FrameFadeOutLength.mValue);
message.setFrameLength(FrameLength.mValue);
message.setFrameStart(FrameStart.mValue);
message.setID(mID);
message.setName(Name.mValue);
message.setRemark(Remark.mValue);
message.setSPITTypeID(SPITTypeID.mValue);
message.setServerParams(ServerParams.mValue);
message.setServerRemote(ServerRemote.mValue);
message.setTrack(Track.mValue);
message.setValueFactor(mValueFactor);
message.setValueUse(mValueUse);
}
void SObject::fillMessageOP(Message::RemoveObject &message) {
message.setID(mID);
}
/**
* This is used by the server and client to tell if the other side has registered the changes.
* @param p_Confirmed
* @return if mConfirmed has changed
*/
bool SObject::setConfirmed(bool p_Confirmed) {
if (mConfirmed == p_Confirmed)
return false;
mConfirmed = p_Confirmed;
return true;
}
/**
* This is a virtual construct - ActionAssignedType is not implemented in the SPITProtocol.
* This is used when an action is associated with a SPITType.
* Then this action is done for every SObject with the SPITType.
* @return true = an action is assigned for the SPITType.
*/
bool SObject::setActionAssignedType(bool p_ActionAssigned) {
if (mActionAssignedType == p_ActionAssigned)
return false;
bool oldActionAssigend = getActionAssigned();
mActionAssignedType = p_ActionAssigned;
if (getActionAssigned() == oldActionAssigend)
return false;
mConfirmed = false;
return true;
}
bool SObject::setActionAssigned(bool p_ActionAssigned) {
if (mActionAssigned == p_ActionAssigned)
return false;
bool oldActionAssigend = getActionAssigned();
mActionAssigned = p_ActionAssigned;
if (getActionAssigned() == oldActionAssigend)
return false;
mConfirmed = false;
return true;
}
}
+93
View File
@@ -0,0 +1,93 @@
#include "spit_player.hpp"
#include "esp_log.h"
#include <memory>
#include <vector>
using namespace std;
namespace spit {
unordered_map<string, unique_ptr<SPlayer>> SPlayer::sSpitPlayers;
SPlayer &SPlayer::getSPITPlayer(string playerID) {
auto i = sSpitPlayers.find(playerID);
if (i == sSpitPlayers.end()) {
sSpitPlayers[playerID] = make_unique<SPlayer>(playerID);
}
return *sSpitPlayers.at(playerID);
}
SPlayer::SPlayer(string playerID) {
mId = playerID;
}
SPlayer::~SPlayer() {
mPlaySPITTypes.clear();
}
/**
*
* @param p_SPITObjectID
* @return true = new PlayObject added<br>
* false = PlayObject exists
*/
SPlayer::PlayType *SPlayer::addTypePlaying(string spitTypeID) {
PlayType *ptrPlaySPITType;
auto i = mPlaySPITTypes.find(spitTypeID);
if (i == mPlaySPITTypes.end()) {
auto playSpitType = make_unique<PlayType>(this, spitTypeID);
ptrPlaySPITType = playSpitType.get();
mPlaySPITTypes[spitTypeID] = move(playSpitType);
} else {
ptrPlaySPITType = i->second.get();
}
ptrPlaySPITType->setIsPlayed(true);
return ptrPlaySPITType;
}
void SPlayer::startPlaySequence() {
for (auto i = mPlaySPITTypes.begin(); i != mPlaySPITTypes.end(); i++) {
PlayType *ptrPlaySPITType = i->second.get();
ptrPlaySPITType->startPlaySequence();
}
}
void SPlayer::endPlaySequence() {
for (auto i = mPlaySPITTypes.begin(); i != mPlaySPITTypes.end();) {
if (i->second.get()->getIsPlayed() == false) {
i = mPlaySPITTypes.erase(i);
} else {
++i;
}
}
}
SPlayer::PlayType::PlayType(SPlayer *p_SPITPlayer, string spitTypeID) {
mIsNew = true;
mSpitTypeID = spitTypeID;
mPlayerID = p_SPITPlayer->getID();
mPlayerName = p_SPITPlayer->getName();
startPlaySequence();
mIsNew = true;
}
SPlayer::PlayType::PlayType(const PlayType &orig) {
mSpitTypeID = orig.getSPITTypeID();
mIsPlayed = orig.getIsPlayed();
mIsNew = orig.getIsNew();
mPlayerID = orig.getPlayerID();
mPlayerName = orig.getPlayerName();
mPlayerRuns = orig.getPlayerIsRunning();
mValue = orig.getValue();
}
void SPlayer::PlayType::startPlaySequence() {
mIsPlayed = false;
mIsNew = false;
mValue = 0;
}
}
+745
View File
@@ -0,0 +1,745 @@
#include "spit_processor.hpp"
#include "esp_log.h"
namespace spit {
static const char *TAG = "SPIT";
/**
* Create a new SPIT_client instance, should be done once by the user app.
* @param p_SPITClientInterface the user app should implement the SPIT_ClientInterface.
*/
SProcessor::SProcessor(string name, string projName, string hostName, string guid, Callbacks calls, InternalCallbacks intern)
: mCallbacks(calls),
mInternCallbacks(intern),
mName(name),
mGuid(guid),
mProjName(projName),
mComputerName(hostName) {
}
SProcessor::~SProcessor() {
}
unique_ptr<Message::Connect> SProcessor::createConnectMessage(string serverIP, string serverPort, string loginName, string loginPassword) {
auto msg = make_unique<Message::Connect>(getComputerName(), mName);
msg->setLoginName(loginName);
msg->setLoginPassword(loginPassword);
msg->setServerIP(serverIP);
if (serverPort.length() <= 0) {
serverPort = "0";
}
int intServerPort = stoi(serverPort);
msg->setServerPort(intServerPort);
return msg;
}
/**
* Virtual construct - means actionAssigned for SPITTypes is is not implemented in SPITProtocol.
* This can used to setActionAssigned to all SPITObjects with this SPITType.
* This may be used in the case when the same action should be done for every SPITObject with this SPITType associated.
* This tells the server not what action is done, but it tells the server that there is an action.
* @param typeId the id of the SPITType
* @param actionAssigned
*/
void SProcessor::setActionType(string typeId, bool actionAssigned) {
SType *spitType = mSpitTypes.getEntry(typeId);
if (spitType == nullptr)
return;
if (spitType->ActionAssigned.set(actionAssigned)) {
sendTypeInternal(*spitType);
for (auto &[key, value] : mSpitObjects.getRepo()) {
if (value->SPITTypeID.get() == spitType->getID()) {
if (value->setActionAssignedType(actionAssigned) == true) {
sendObjectInternal(value.get());
}
}
}
}
}
/** This should be called if the user wants to associate/remove an action with a SPITObject. */
void SProcessor::setActionObject(string spitObjId, bool actionAssigned) {
SObject *spitObject = mSpitObjects.getEntry(spitObjId);
if (spitObject == nullptr)
return;
if (spitObject->setActionAssigned(actionAssigned) == true) {
sendObjectInternal(spitObject);
}
}
std::vector<char> SProcessor::createMessage(Message::OPPart &opPart) {
if (mMessageNumberSend == 255)
mMessageNumberSend = 1;
else
mMessageNumberSend++;
opPart.setMessageNumber(mMessageNumberSend);
opPart.setServerInfoInHeader(mGuid, mServerID);
int opFlag = opPart.getHeader()->getType();
try {
return Message::createMessage(opPart);
} catch (...) {
ESP_LOGD(TAG, "Exception client OPFlag: %d", opFlag);
}
return {};
}
void SProcessor::resetMessageNumberReceived() {
mMessageNumberReceived = -1;
mOvertakingMessages.clear();
}
void SProcessor::checkParseMessageData(vector<char> data) {
auto message = make_unique<Message>(data);
// answerWatchdog();
if (!message->getHeader() || !message->getOPPart()) {
ESP_LOGD(TAG, "Client::parseMessage incomplete message");
return;
}
int msgNum = (int)(message->getHeader()->getMessageNumber() & 0xFF);
if (msgNum == 0) {
parseMessage(*message);
return;
}
if (mMessageNumberReceived < 0) {
mMessageNumberReceived = msgNum;
parseMessage(*message);
return;
}
int msgNumNext = mMessageNumberReceived + 1;
if (msgNumNext > 255 || msgNumNext < 1)
msgNumNext = 1;
if (msgNum == msgNumNext) {
mMessageNumberReceived = msgNum;
parseMessage(*message);
mMessageNumberReceived = sendOvertakingMessage(mMessageNumberReceived);
} else {
// message has overtaken another message
addOvertakingMessage(move(message));
}
}
void SProcessor::addOvertakingMessage(unique_ptr<Message> message) {
if (!message) {
return;
}
if (mOvertakingMessages.size() > MAX_OVERTAKING_SPIT_MESSAGES) {
ESP_LOGD(TAG, "Client receiveing: perhaps message lost !!!!!");
}
int intMessageNumber = (int)(message->getHeader()->getMessageNumber() & 0xFF);
for (auto it = mOvertakingMessages.begin(); it != mOvertakingMessages.end(); ++it) {
const auto &overtaking = *it;
int intOvertakingMessageNumber = (int)(overtaking->getHeader()->getMessageNumber() & 0xFF);
if (intMessageNumber < intOvertakingMessageNumber) {
mOvertakingMessages.insert(it, move(message));
return;
}
}
mOvertakingMessages.push_back(move(message));
}
int SProcessor::sendOvertakingMessage(int msgNumRec) {
// check overtaking messages
auto it = mOvertakingMessages.begin();
while (mOvertakingMessages.size() > 0) {
int msgNumNext = msgNumRec + 1;
if (msgNumNext > 255 || msgNumNext < 1)
msgNumNext = 1;
auto &overtakingMessage = *it;
int msgNum = (int)(overtakingMessage->getHeader()->getMessageNumber() & 0xFF);
if (msgNum == msgNumNext) {
msgNumRec = msgNum;
parseMessage(*overtakingMessage);
mOvertakingMessages.erase(it);
it = mOvertakingMessages.begin();
} else {
break;
}
}
return msgNumRec;
}
void SProcessor::parseMessage(Message &message) {
// answerWatchdog();
if (!message.getHeader() || !message.getOPPart()) {
ESP_LOGD(TAG, "Client::parseMessage incomplete message");
return;
}
switch (message.getType()) {
//----- Connection -----
case Message::ReportProject::OPFLAG:
parseReportProject(message.getAs<Message::ReportProject>());
break;
//----- SPIT Project -----
case Message::StartProjectTransfer::OPFLAG:
parseStartProjectTransfer(message.getAs<Message::StartProjectTransfer>());
break;
case Message::CloseProject::OPFLAG:
parseCloseProject(message.getAs<Message::CloseProject>());
break;
case Message::RequestProjectTransfer::OPFLAG:
parseRequestProjectTransfer(message.getAs<Message::RequestProjectTransfer>());
break;
case Message::EndProjectTransfer::OPFLAG:
parseEndProjectTransfer(message.getAs<Message::EndProjectTransfer>());
break;
//----- SPIT Types ------
case Message::ReportType::OPFLAG:
parseReportType(message.getAs<Message::ReportType>());
break;
case Message::RemoveType::OPFLAG:
parseRemoveType(message.getAs<Message::RemoveType>());
//----- SPIT Objects ------
break;
case Message::ReportObject::OPFLAG:
parseReportObject(message.getAs<Message::ReportObject>());
break;
case Message::RemoveObject::OPFLAG:
parseRemoveObject(message.getAs<Message::RemoveObject>());
break;
//----- Play SPIT Objects ------
case Message::PlayStart::OPFLAG:
parsePlayStart(message.getAs<Message::PlayStart>());
break;
case Message::PlayObject::OPFLAG:
parsePlayObject(message.getAs<Message::PlayObject>());
break;
case Message::PlayEnd::OPFLAG:
parsePlayEnd(message.getAs<Message::PlayEnd>());
break;
case Message::ReportPlayStatus::OPFLAG:
parseReportPlayStatus(message.getAs<Message::ReportPlayStatus>());
break;
default:
ESP_LOGD(TAG, "Client::parseMessage: Unknown Message");
}
}
//----- SPIT Project ------
void SProcessor::parseReportProject(Message::ReportProject &message) {
ESP_LOGD(TAG, "parse ReportProject");
mServerProjectID = message.getProjectID();
mServerProjectName = message.getProjectName();
mServerFramesPerSecond = message.getFramesPerSecond();
mCallbacks.onProjectUpdate(ProjUpdate::ServerLoaded, mServerProjectID);
// Set all SPITTypes and SPITObjects confirmed = false
SType::setConfirmedFalse(mSpitTypes);
SObject::setConfirmedFalse(mSpitObjects);
// Send RequestProjectTransfer
Message::RequestProjectTransfer sMRequestProjectTransfer;
if (mInternCallbacks.sendMessage(sMRequestProjectTransfer) < 0) {
ESP_LOGD(TAG, "Client::parseReportProject could not send RequestProjectTransfer");
}
// todo Don't send the ReportProject here it should be sended after parseEndProjectTransfer
// Send client's ReportProject
Message::ReportProject reportProject(mProjName, mProjName);
reportProject.setFramesPerSecond(0);
if (mInternCallbacks.sendMessage(reportProject) < 0) {
ESP_LOGD(TAG, "Client::parseReportProject could not send Client's ReportProject");
}
}
void SProcessor::parseCloseProject(Message::CloseProject &p_CloseSPITProject) {
ESP_LOGD(TAG, "parse CloseProject");
string stringProjectID;
mCallbacks.onProjectUpdate(ProjUpdate::ServerClosed, p_CloseSPITProject.getProjectID());
SPlayer::clearPlayers();
}
void SProcessor::parseRequestProjectTransfer(Message::RequestProjectTransfer &message) {
ESP_LOGD(TAG, "parse RequestProjectTransfer");
// StartProjectTransfer
Message::StartProjectTransfer sMStartProjectTransfer;
if (mInternCallbacks.sendMessage(sMStartProjectTransfer) < 0) {
return;
}
// send all listes SPITTypes
for (const auto &[key, value] : mSpitTypes.getRepo()) {
sendTypeInternal(*value);
mInternCallbacks.delay(10);
}
for (auto &[key, value] : mSpitObjects.getRepo()) {
sendObjectInternal(value.get());
mInternCallbacks.delay(10);
}
// EndProjectTransfer
Message::EndProjectTransfer sMEndProjectTransfer;
mInternCallbacks.sendMessage(sMEndProjectTransfer);
}
void SProcessor::parseStartProjectTransfer(Message::StartProjectTransfer &message) {
mCallbacks.onProjectUpdate(ProjUpdate::StartTransfer, mServerProjectID);
}
void SProcessor::parseEndProjectTransfer(Message::EndProjectTransfer &message) {
SObject::clearUnconfirmedObjects(mSpitObjects);
mCallbacks.onProjectUpdate(ProjUpdate::EndTransfer, mServerProjectID);
// Send Message::ReportProject after the mSPITClientInterface is notfied of EndProjectTransfer
Message::ReportProject reportSPITProject(mProjName, mProjName);
reportSPITProject.setFramesPerSecond(0);
if (mInternCallbacks.sendMessage(reportSPITProject) < 0) {
ESP_LOGD(TAG, "Client::parseReportProject could not send Client's ReportProject");
}
}
void SProcessor::parseReportType(Message::ReportType &message) {
ESP_LOGD(TAG, "parse ReportType ID %s Name %s", message.getID().c_str(), message.getName().c_str());
SType *sType;
bool isNew = false;
bool isChanged = false;
if (message.getServerRemote() == false) {
sType = mSpitTypes.getEntry(message.getID());
if (sType == nullptr) {
// The user's app may veto the SPITType
bool createdByClient = false;
if (sType) {
createdByClient = sType->getCreatedByClient();
}
if (mCallbacks.onCheckSpitTypeVeto(false, createdByClient) == false) {
ESP_LOGW(TAG, "TypeVeto %s / %s", message.getID().c_str(), message.getName().c_str());
return;
}
auto spitType = make_unique<SType>(message.getID());
sType = spitType.get();
mSpitTypes.addEntry(move(spitType));
isNew = true;
}
} else {
sType = mRemoteSpitTypes.getEntry(message.getID());
if (sType == nullptr) {
auto spitType = make_unique<SType>(message.getID());
sType = spitType.get();
mRemoteSpitTypes.addEntry(move(spitType));
isNew = true;
}
}
if (!sType->setMessageOP(message)) {
// SPITType is not changable -> send back to the server
sendTypeInternal(*sType);
return;
}
auto eventType = isNew ? ObjectEvent::Created : ObjectEvent::Changed;
// add or change SPITType
mCallbacks.onSpitType(eventType, sType->ServerRemote.get(), *sType);
if (sType != nullptr) {
if (sType->setConfirmed(true) == true || isChanged == true) {
sendTypeInternal(*sType);
}
}
}
void SProcessor::parseRemoveType(Message::RemoveType &message) {
ESP_LOGD(TAG, "parse RemoveType %s", message.getID().c_str());
bool isRemote = false;
string sTypeID = message.getID();
SType *sType = mRemoteSpitTypes.getEntry(sTypeID);
if (!sType) {
isRemote = true;
} else {
sType = mSpitTypes.getEntry(sTypeID);
if (!sType) {
isRemote = false;
} else {
// No SType with sTypeID is listet
return;
}
}
if (sType->getCreatedByClient() && sType->Fixed.get()) {
// the SPITyte is created by client himself and it is fixed -> don't remove the SType!!
// Send the SPITTyp back to the server
sendTypeInternal(*sType);
return;
}
mCallbacks.onSpitType(ObjectEvent::Deleted, isRemote, *sType);
if (isRemote == false) {
mSpitTypes.removeEntry(*sType);
} else {
mRemoteSpitTypes.removeEntry(*sType);
}
}
/**
* Change an existing SPIT_Type.
* @param p_SPITType the SPITType vont
* @return true = changed, false = not changed maybe p_SPITType is NULL or the SPITType doesn't exists
*/
bool SProcessor::changeType(SType *sType) {
if (sType == nullptr)
return false;
SType *mod = mSpitTypes.getEntry(sType->getID());
if (mod == nullptr)
return false;
mod->DefaultDelay.set(sType->DefaultDelay.get());
mod->DefaultDuration.set(sType->DefaultDuration.get());
mod->Fixed.set(sType->Fixed.get());
mod->GroupName.set(sType->GroupName.get());
mod->Name.set(sType->Name.get());
mod->ProjectIndepended.set(sType->ProjectIndepended.get());
mCallbacks.onSpitType(ObjectEvent::Changed, false, *mod);
sendTypeInternal(*mod);
return true;
}
/**
* If a client has removed a SPITType where id == p_SPITTypeID, it should send it to the server.
* The corresponding SPITType if listet in SPITType will be deleted.
* An eventRemoveSPITType will occur.
* If a connection to a SPITServer is established, the SPITServer may dicard the deletion an an eventSPITType will occur
* -> the SPITType will be listet again.
* @param p_SPITType the id of the SPITType ro remove
* @return 0 = is sended, -1 = is not sended
*/
int SProcessor::removeType(string sType) {
if (sType.length() <= 0)
return -1;
SType *mod = mSpitTypes.getEntry(sType);
if (mod == nullptr)
return -1;
Message::RemoveType sMRemoveSPITType;
mod->fillMessageOP(sMRemoveSPITType);
mCallbacks.onSpitType(ObjectEvent::Deleted, false, *mod);
mSpitTypes.removeEntry(*mod);
return mInternCallbacks.sendMessage(sMRemoveSPITType);
}
SType *SProcessor::createType(string id, string groupName, string name, uint64_t delay, uint64_t duration, bool fixed, bool projIndipendent) {
bool boolNew;
SType *sType = mSpitTypes.getEntry(id);
if (sType == nullptr) {
auto spitType = make_unique<SType>(id, groupName, name, fixed, projIndipendent, delay, duration);
sType = spitType.get();
mSpitTypes.addEntry(move(spitType));
sType->setCreatedByClient(true);
boolNew = true;
} else {
sType->GroupName.set(groupName);
sType->Name.set(name);
sType->Fixed.set(fixed);
sType->ProjectIndepended.set(projIndipendent);
sType->DefaultDelay.set(delay);
sType->DefaultDuration.set(duration);
boolNew = false;
}
auto eventType = boolNew ? ObjectEvent::Created : ObjectEvent::Changed;
mCallbacks.onSpitType(eventType, false, *sType);
sendTypeInternal(*sType);
return sType;
}
/**
* If a client has changed a SPITType, it should send it to the server to transmit the changes.
* If the SPITType is a fixed one and not created by the client himself, then an eventSPITType can be sent from the server, which cancels the changes.
* @param sType the pointer to the changed SPITType
* @return 0 = is sended, -1 = is not sended
*/
int SProcessor::sendTypeInternal(SType &sType) {
Message::ReportType reportType;
sType.fillMessageOP(reportType);
return mInternCallbacks.sendMessage(reportType);
}
//----- SPIT Objects ------
void SProcessor::parseReportObject(Message::ReportObject &message) {
ESP_LOGD(TAG, "parse ReportObject %s", message.getID().c_str());
SObject *sObject;
SType *sType;
bool isNew = false;
bool isRemote = false;
bool isChanged = false;
{
ESP_LOGD(TAG, "parse ReportObject %s / %s", message.getID().c_str(), message.getName().c_str());
if (message.getServerRemote() == false) {
isRemote = false;
sObject = mSpitObjects.getEntry(message.getID());
sType = mSpitTypes.getEntry(message.getSPITTypeID());
if (sType == nullptr) {
// No associated SPITType found
if (sObject != nullptr) {
// SpitObject exists but has no corresponding SPITType -> remove the SPITObject without sending a message
// Maybe the SPITObject has changed the SPITTypeID to a SPITTypeID of another board
mSpitObjects.removeEntry(*sObject);
}
return;
}
bool isKnownType = sType != nullptr;
if (sObject == nullptr) {
// The user's app my veto the SPITType
if (mCallbacks.onCheckSpitObjectVeto(isKnownType) == false) {
ESP_LOGW(TAG, "ObjectVeto %s / %s", message.getID().c_str(), message.getName().c_str());
return;
}
auto spitObj = make_unique<SObject>(message.getID());
sObject = spitObj.get();
mSpitObjects.addEntry(move(spitObj));
isNew = true;
} else {
// The user's app my veto the SPITType
if (mCallbacks.onCheckSpitObjectVeto(isKnownType) == false) {
// SpitObject exists and User APp denied the SPITObject -> remove the SPITObject without sending a message
mSpitObjects.removeEntry(*sObject);
ESP_LOGW(TAG, "TypeVeto %s / %s", message.getID().c_str(), message.getName().c_str());
return;
}
}
} else {
isRemote = true;
sObject = mRemoteSpitObjects.getEntry(message.getID());
sType = mRemoteSpitTypes.getEntry(message.getSPITTypeID());
if (sType == nullptr) {
// No associated SPITType found
return;
}
if (sObject == nullptr) {
auto spitObj = make_unique<SObject>(message.getID());
sObject = spitObj.get();
mRemoteSpitObjects.addEntry(move(spitObj));
isNew = true;
}
}
if (!sObject->setMessageOP(&message)) {
ESP_LOGE(TAG, "Change not allowed %s", sObject->getID().c_str());
// setMessageOP error - the SPITObject can't be changed, it may be fixed and crated by this client
sendObjectInternal(sObject);
return;
}
if (isRemote == false) {
// set the virtual action assignment
sObject->setActionAssignedType(sType->ActionAssigned.get());
}
auto eventType = isNew ? ObjectEvent::Created : ObjectEvent::Changed;
mCallbacks.onSpitObject(eventType, isRemote, *sType, *sObject);
if (sObject->setConfirmed(true) == true || isChanged == true) {
// Confirmation has changed
sendObjectInternal(sObject);
}
}
}
void SProcessor::parseRemoveObject(Message::RemoveObject &message) {
ESP_LOGD(TAG, "parse RemoveObject %s", message.getID().c_str());
SObject *sObject = mRemoteSpitObjects.getEntry(message.getID());
if (sObject != nullptr) {
SType *sType = mRemoteSpitTypes.getEntry(sObject->SPITTypeID.get());
if (sType != nullptr) {
mCallbacks.onSpitObject(ObjectEvent::Deleted, true, *sType, *sObject);
}
mRemoteSpitObjects.removeEntry(*sObject);
} else {
// call removeObject, this will notify the server that the object is removed
removeObject(message.getID());
}
}
int SProcessor::sendObjectInternal(SObject *sObject) {
if (sObject == nullptr)
return -1;
Message::ReportObject reportObject;
sObject->fillMessageOP(reportObject);
return mInternCallbacks.sendMessage(reportObject);
}
/**
* When the client changes an existing SPITObject (normally the client should not do this), then he should send the changes to the SPITServer.
* An eventSPITObject occurs.
* The SPITServer may discard the changes and a second eventSPITObject occurs to correct the changes.
* @param obj the pointer to the changed SPITObject.
* @return 0 = sended, -1 = not sended
*/
bool SProcessor::changeObject(SObject *sObject) {
if (sObject == nullptr)
return false;
SObject *mod = mSpitObjects.getEntry(sObject->getID());
if (mod == nullptr)
return false;
SType *sType = mSpitTypes.getEntry(sObject->getID());
if (mod == nullptr)
return false;
mod->setActionAssigned(sObject->getActionAssigned());
mod->Delay.set(sObject->Delay.get());
mod->Duration.set(sObject->Delay.get());
mod->Fixed.set(sObject->Fixed.get());
mod->FrameFadeInLength.set(sObject->FrameFadeInLength.get());
mod->FrameFadeOutLength.set(sObject->FrameFadeOutLength.get());
mod->FrameLength.set(sObject->FrameLength.get());
mod->FrameStart.set(sObject->FrameStart.get());
mod->Name.set(sObject->Name.get());
mod->Remark.set(sObject->Remark.get());
mod->SPITTypeID.set(sObject->SPITTypeID.get());
mod->ServerParams.set(sObject->ServerParams.get());
mod->Track.set(sObject->Track.get());
mod->ValueFactor.set(sObject->ValueFactor.get());
mod->ValueUse.set(sObject->ValueUse.get());
mCallbacks.onSpitObject(ObjectEvent::Changed, false, *sType, *mod);
sendObjectInternal(mod);
return true;
}
/**
* When the client wants to remove a SPITObject with id == p_SPITObjectID (normally the client should not do this), then he should send it to the SPITServer.
* The corresponding SPITObject if listet in SPITObject will be deleted.
* An eventRemoveSPITType will occur.
* If an connection to a SPITServer is established, the SPITServer may discard the deletion and an eventSPITObject occurs
* -> the SPITObject is listet again.
* @param objId the id of the SPITObject to remove.
* @return 0 = sended to the SPITServer, -1 = not sended to the SPITServer or nos SPITObject with id == objId exists.
*/
int SProcessor::removeObject(string objId) {
if (objId.length() <= 0)
return -1;
Message::RemoveObject remObj;
SObject *sObject = mSpitObjects.getEntry(objId);
if (sObject == nullptr)
return -1;
SType *sType = mSpitTypes.getEntry(sObject->SPITTypeID.get());
if (sType == nullptr)
return -1;
sObject->fillMessageOP(remObj);
mCallbacks.onSpitObject(ObjectEvent::Deleted, false, *sType, *sObject);
mSpitObjects.removeEntry(*sObject);
return mInternCallbacks.sendMessage(remObj);
}
// //----- Play SPIT Objects ------
void SProcessor::parsePlayStart(Message::PlayStart &message) {
ESP_LOGD(TAG, "parse PlayStart ID: %s Name: %s Running: %d", message.getPlayerID().c_str(), message.getPlayerName().c_str(), (int)message.getPlayerIsRunning());
SPlayer &player = SPlayer::getSPITPlayer(message.getPlayerID());
player.setIsRunning(message.getPlayerIsRunning());
player.setName(message.getPlayerName());
player.setType(message.getPlayerType());
player.setTime(message.getTime());
player.startPlaySequence();
mCallbacks.onPlay(player.getID(), player.getName(), player.getType(), player.getIsRunning(), player.getTime());
}
void SProcessor::parsePlayObject(Message::PlayObject &message) {
SPlayer &player = SPlayer::getSPITPlayer(message.getPlayerID());
SObject *sObject = mSpitObjects.getEntry(message.getSPITObjectID());
if (sObject == nullptr) {
return;
}
SPlayer::PlayType *playType = player.addTypePlaying(sObject->SPITTypeID.get());
if (playType == nullptr) {
return;
}
playType->setPlayerIsRunning(player.getIsRunning());
playType->addValue(((double)message.getValue() / 1000000), ((double)sObject->ValueFactor.get() / 1000000));
}
void SProcessor::parsePlayEnd(Message::PlayEnd &message) {
SPlayer &sPlayer = SPlayer::getSPITPlayer(message.getPlayerID());
auto &playTypes = sPlayer.getPlaySPITTypes();
for (auto i = playTypes.begin(); i != playTypes.end(); i++) {
SPlayer::PlayType *playType = i->second.get();
bool isStarted = playType->getIsNew();
bool iStopped = (playType->getIsPlayed() == false);
SType *ptrSPITType = mSpitTypes.getEntry(playType->getSPITTypeID());
if (ptrSPITType == nullptr) {
// set isplayed to false, the ptrPlayObject will be removed by sPlayer.endPlaySequence()
playType->setIsPlayed(false);
continue;
}
mCallbacks.onPlaySpitType(playType->getPlayerID(), playType->getSPITTypeID(), playType->getPlayerIsRunning(), isStarted, iStopped, playType->getValue());
}
sPlayer.endPlaySequence();
}
void SProcessor::parseReportPlayStatus(Message::ReportPlayStatus &message) {
SType *sType = NULL;
SObject *sObject = mRemoteSpitObjects.getEntry(message.getID());
if (sObject == nullptr) {
sObject = mSpitObjects.getEntry(message.getID());
if (sObject == nullptr) {
return;
}
}
if (sObject->ServerRemote.get() == false) {
sType = mSpitTypes.getEntry(sObject->SPITTypeID.get());
} else {
sType = mRemoteSpitTypes.getEntry(sObject->SPITTypeID.get());
}
if (sType == nullptr) {
return;
}
mCallbacks.onPlayRemoteObjectStatus(*sType, *sObject, message.getFrameInsideObject(), message.getValue());
}
// //--------- Remote ----------
/**
* Send a remote control command to the SPITServer.
* There may be different command types:
* a) Some don't use value e.g. go to next scene, olay a jingle
* b) Some use values e.g. the main sound fader
* @param sObjectId the id of the remote SPITObject (this represents the remote control command)
* @param valuePpm the value in ppm
* in case a) the value is ignored
* in case b) the value is used
* @param valueFlag indicates how the server should use valuePpm:
* VALUEFLAG_DIRECT (0x00) The server overwrites its existing value with p_Value
* VALUEFLAG_INCREASE (0x01) The server increases his existing value by valuePpm
* VALUEFLAG_DECREASE (0x01) The server decreases his existing value by valuePpm
*/
void SProcessor::sendRemoteObject(string sObjectId, int valuePpm, char valueFlag) {
SObject *remoteObject = mRemoteSpitObjects.getEntry(sObjectId);
if (remoteObject == nullptr)
return;
Message::PlayStart playSPITStart(mGuid, mProjName, SPlayer::PTYPE_DISCRETE);
playSPITStart.setPlayerIsRunning(false);
playSPITStart.setTime(0);
mInternCallbacks.sendMessage(playSPITStart);
Message::PlayObject playSPITObject;
playSPITObject.setID(remoteObject->getID());
playSPITObject.setPlayPrePosition(false);
playSPITObject.setPlayerID(mGuid);
playSPITObject.setValue(valuePpm);
playSPITObject.setValueFlag(valueFlag);
mInternCallbacks.sendMessage(playSPITObject);
Message::PlayEnd playSPITEnd(mGuid);
mInternCallbacks.sendMessage(playSPITEnd);
}
} // namespace spit
+213
View File
@@ -0,0 +1,213 @@
#include "spit_socket.hpp"
#include "Task.hpp"
#include "esp/uuid.h"
#include "esp_log.h"
#include "esp_netif.h"
#include <string>
#include <unistd.h>
using namespace std;
namespace spit {
/**
* Creates socket. If there exist some, they will be closed.
* @return
*/
bool Socket::createSocketUdp() {
if (mUdpSocketIp4 != INVALID_SOCKET) {
close(mUdpSocketIp4);
mUdpSocketIp4 = INVALID_SOCKET;
}
mUdpSocketIp4 = socket(AF_INET, SOCK_DGRAM, 0);
int intResult = -1;
bool success = true;
if (mUdpSocketIp4 == INVALID_SOCKET) {
success = false;
} else {
int intOptionValue4 = 1;
intResult = setsockopt(mUdpSocketIp4, SOL_SOCKET, SO_REUSEADDR, (void *)&intOptionValue4, sizeof(intOptionValue4));
}
if (intResult < 0) {
success = false;
}
if (success == true) {
struct timeval tv;
tv.tv_sec = 1; // seconds
tv.tv_usec = 0; // microseconds (0.0 sec)
// This sets the receive timeout
setsockopt(mUdpSocketIp4, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
// set the mUdpSocketIp4 in NON BLOCKING mode because
// if the UDPSockets are in blocking mode the receiveMessage does not return in every constellation
// int intFlags;
// intFlags = fcntl(mUdpSocketIp4, F_GETFL);
// fcntl(mUdpSocketIp4, F_SETFL, intFlags | O_NONBLOCK);
// int intBufferSize = 2046 * 512;
// intResult = setsockopt(mUdpSocketIp4, SOL_SOCKET, SO_RCVBUF, (void*)&intBufferSize, sizeof(intBufferSize));
// intResult = setsockopt(mUdpSocketIp4, SOL_SOCKET, SO_SNDBUF, (void*)&intBufferSize, sizeof(intBufferSize));
}
return success;
}
/**
* Binds the socket to the client's ip address and port. The socket option SO_REUSEADDR is set to true.
* @param clientIp ip address of the client
* @param clientPort port of the client.
* if p_Port == "0" any free port will be used
* @return -1 id bind fails, o if bind is ok
*
*/
int Socket::bindSocket(string clientIp, string clientPort) {
string stringClientPort;
if (clientPort.length() <= 0)
stringClientPort = "0";
else
stringClientPort = clientPort;
addrinfo *clientAI;
if (getaddrinfo(clientIp.data(), clientPort.data(), nullptr, &clientAI) != 0) {
return -1;
}
if (mUdpSocketIp4 == INVALID_SOCKET) {
return -1;
}
auto *socketAdr = (sockaddr_in *)(clientAI->ai_addr);
int intSocklen = sizeof(*socketAdr);
// copy the socketAdr, may be not filled correct by getaddrinfo
sockaddr_in o_IP4SocketAddress;
memset(&o_IP4SocketAddress, 0, intSocklen);
o_IP4SocketAddress.sin_addr = socketAdr->sin_addr;
o_IP4SocketAddress.sin_family = socketAdr->sin_family;
o_IP4SocketAddress.sin_port = socketAdr->sin_port;
int intResult = bind(mUdpSocketIp4, (const sockaddr *)(socketAdr), (socklen_t)intSocklen);
if (intResult < 0) {
return intResult;
}
if ((intResult == 0) &&
(getsockname(mUdpSocketIp4, (sockaddr *)socketAdr, (socklen_t *)&intSocklen) != -1)) {
setBoundClientPort(to_string((int)socketAdr->sin_port));
}
freeaddrinfo(clientAI);
setIsBound(intResult != -1);
return intResult;
}
/**
* Set whether the socket are broadcast sockets or not. Set the sockopt SO_BROADCAST to 0 or 1
* @param p_Broadcast true SO_BROADCAST is set
* false = SO_BROADCAST is not set
* @return 0 = ok, -1 = error
*/
int Socket::setBroadcast(bool broadcast) const {
int intOptionValue = broadcast ? 1 : 0;
int intResult = setsockopt(mUdpSocketIp4, SOL_SOCKET, SO_BROADCAST, &intOptionValue, sizeof(int));
return intResult;
}
/**
* @param ip the ip address of the server
* @param port the port of the server
* @param msg buffer containing the message
* @param msgLength the length of the message
* @return -1 send fails
* 0 send is done
*/
int Socket::sendMessageTo(const string &ip, const string &port, const char *message, int msgLength) {
if (mUdpSocketIp4 == INVALID_SOCKET) {
return -1;
}
// prepare AddressInfo
int intResult = -1;
addrinfo *ptrAddressInfo;
if (getaddrinfo(ip.data(), port.data(), nullptr, &ptrAddressInfo) != 0) {
return intResult;
}
auto *ptrIP4SocketAddress = (sockaddr_in *)(ptrAddressInfo->ai_addr);
int intSocklen = sizeof(*ptrIP4SocketAddress);
if (mUdpSocketIp4 != INVALID_SOCKET) {
// copy the ptrIP4SocketAddress, may be not filled correct by getaddrinfo
sockaddr_in o_IP4SocketAddress;
memset(&o_IP4SocketAddress, 0, intSocklen);
o_IP4SocketAddress.sin_addr = ptrIP4SocketAddress->sin_addr;
o_IP4SocketAddress.sin_family = ptrIP4SocketAddress->sin_family;
o_IP4SocketAddress.sin_port = ptrIP4SocketAddress->sin_port;
intResult = sendto(mUdpSocketIp4, message, msgLength, 0, (const sockaddr *)&o_IP4SocketAddress, (socklen_t)intSocklen);
if (intResult < 0) {
ESP_LOGE("SPIT", "sendMessageTo IP4 sendto %s FAILED %s\n", ip.data(), strerror(errno));
ESP_LOGE("SPIT", "sendMessageTo ret=%d %s / %s / %d\n", intResult, port.data(), message, msgLength);
}
freeaddrinfo(ptrAddressInfo);
}
if (intResult < 0) {
return -1;
}
return 0;
}
/**
* Call this for receiving a message
* This will block until a message is received or the underlying socket is closed
* @param p_Buffer the buffer containing the received message later.
* @param p_BufferLength the size of the buffer
* @param p_ServerIP a pointer to a string, when a messages is received the string will contain the ip address of the sender/server
* if p_ServerIP is NULL - no detection of the server will be done.
* @return
*/
int Socket::receiveMessage(char *p_Buffer, int p_BufferLength, string *serverIp) const {
int intResult = -1;
if (mUdpSocketIp4 == INVALID_SOCKET) {
return intResult;
}
sockaddr_in o_IP4SocketAddress;
int intSocklen = sizeof(o_IP4SocketAddress);
memset(&o_IP4SocketAddress, 0, intSocklen);
intResult = recvfrom(mUdpSocketIp4, (void *)p_Buffer, p_BufferLength, 0, (sockaddr *)&o_IP4SocketAddress, (socklen_t *)&intSocklen);
if (intResult >= 0 && serverIp != nullptr) {
char bufferIP4[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(o_IP4SocketAddress.sin_addr), bufferIP4, intSocklen);
*serverIp = bufferIP4;
}
return intResult;
}
ISocket::IpAddress Socket::getAdaptersInfo() {
// iterate over active interfaces, and print out IPs
esp_netif_t *netif = nullptr;
if ((netif = esp_netif_next_unsafe(netif)) != nullptr) {
ESP_LOGI("SPIT", "Connected to %s", esp_netif_get_desc(netif));
esp_netif_ip_info_t ip;
ESP_ERROR_CHECK(esp_netif_get_ip_info(netif, &ip));
ESP_LOGI("SPIT", "- IPv4 address: " IPSTR ",", IP2STR(&ip.ip));
ESP_LOGI("SPIT", "- IPv4 netmask: " IPSTR ",", IP2STR(&ip.netmask));
char buffer[64];
sprintf(buffer, IPSTR, IP2STR(&ip.ip));
string stringIP4 = buffer;
sprintf(buffer, IPSTR, IP2STR(&ip.netmask));
string stringSubnet = buffer;
return {stringIP4, stringSubnet};
}
return {"0.0.0.0", "0.0.0.0"};
}
string createGUID() {
uuid_t uu;
uuid_generate(uu);
char uu_str[UUID_STR_LEN];
uuid_unparse(uu, uu_str);
return {uu_str};
}
string getClientHostName() {
const char *hostname;
esp_netif_get_hostname(esp_netif_get_default_netif(), &hostname);
if (hostname != nullptr) {
return {hostname};
}
return "";
}
}
+5
View File
@@ -0,0 +1,5 @@
idf_component_register(
SRC_DIRS "."
INCLUDE_DIRS "."
REQUIRES cmock spit
)
+123
View File
@@ -0,0 +1,123 @@
/**
* @file test_mean.cpp
* @author Gustice
* @brief Hardware bound unit-Test example device
*/
#include "spit_client.hpp"
#include "unity.h"
#include <limits.h>
#include <queue>
#include
using namespace spit;
using namespace std;
queue<string> callbackEvents;
class ClientMock {
public:
void onServerEvent(SClient::ServerEvent event, const SadDetectedServer &detectedServer) {
}
void onConnectionStatus(string serverID, string serverComputerName, string serverName, int serverStepTime, SClient::Connection connectionStatus, SClient::Connection oldConnectionStatus) {
}
void onProjectUpdate(SClient::ProjUpdate state, string projectId) {
}
void onSpitType(SClient::ObjectEvent event, bool isRemote, const SType &spitType) {
}
void onSpitObject(SClient::ObjectEvent event, bool isRemote, const SType &spitType, const SObject &spitObject) {
}
void onPlay(string playerID, string playerName, char playerType, bool playerIsRunning, uint64_t timeFrames) {
}
void onPlaySpitType(string playerID, string spitTypeID, bool playerIsRunning, bool started, bool ended, double value) {
}
void onPlayRemoteObjectStatus(const SType &spitType, const SObject &spitObject, uint64_t p_FrameInsideObject, int value) {
}
bool onCheckSpitTypeVeto(bool knownType, bool createdByClient) {
return true;
}
bool onCheckSpitObjectVeto(bool knownType) {
return true;
}
};
class SocketMock : public ISocket {
public:
SocketMock(IpAddress ip) : ISocket(ip) {}
int sendMessage(const char *message, int length) override {
return 0;
}
int receiveMessage(char *bu1ffer, int bufferLength, string *serverIp) const override {
return 0;
}
void setConnectionParams(const string &serverIp, const string &serverPort, const string &clientIp, const string &clientPort) override {
}
int bindSocket(string clientIp, string clientPort) override {
return 0;
}
int setBroadcast(bool broadcast) const override {
return 0;
}
int sendMessageTo(const string &ip, const string &port, const char *message, int msgLength) override {
return 0;
}
};
// class SpitClientFix {
// protected:
// static ClientMock client;
// ISocket::IpAddress ip{"10.10.0.1", "255.255.255.0"};
// SocketMock socket{ip};
// SClient::Callbacks callbacks{
// .onServerEvent = [&](SClient::ServerEvent event, const SadDetectedServer &detectedServer){client.onServerEvent(event, detectedServer);} ,
// .onConnectionStatus = [&](string serverID, string serverComputerName, string serverName, int serverStepTime, SClient::Connection connectionStatus, SClient::Connection oldConnectionStatus){client.onConnectionStatus(serverID, serverComputerName, serverName, serverStepTime, connectionStatus, oldConnectionStatus);} ,
// .onProjectUpdate = [&](SClient::ProjUpdate state, string projectId){client.onProjectUpdate(state, projectId);} ,
// .onSpitType = [&](SClient::ObjectEvent event, bool isRemote, const SType &spitType){client.onSpitType(event, isRemote, spitType);} ,
// .onSpitObject = [&](SClient::ObjectEvent event, bool isRemote, const SType &spitType, const SObject &spitObject){client.onSpitObject(event, isRemote, spitType, spitObject);} ,
// .onPlay = [&](string playerID, string playerName, char playerType, bool playerIsRunning, uint64_t timeFrames){client.onPlay(playerID, playerName, playerType, playerIsRunning, timeFrames);} ,
// .onPlaySpitType = [&](string playerID, string spitTypeID, bool playerIsRunning, bool started, bool ended, double value){client.onPlaySpitType(playerID, spitTypeID, playerIsRunning, started, ended, value);} ,
// .onPlayRemoteObjectStatus = [&](const SType &spitType, const SObject &spitObject, uint64_t p_FrameInsideObject, int value){client.onPlayRemoteObjectStatus( spitType, spitObject, p_FrameInsideObject, value);} ,
// .onCheckSpitTypeVeto = [&](bool knownType, bool createdByClient){return client.onCheckSpitTypeVeto(knownType, createdByClient);} ,
// .onCheckSpitObjectVeto = [&](bool knownType){return client.onCheckSpitObjectVeto(knownType);}
// };
// SClient sut{"sut", "sutProj", socket, callbacks};
// };
TEST_CASE("Full Connect precedure", "[spit][init]") {
static ClientMock client;
ISocket::IpAddress ip{"10.10.0.1", "255.255.255.0"};
SocketMock socket{ip};
SClient::Callbacks callbacks{
.onServerEvent = [&](SClient::ServerEvent event, const SadDetectedServer &detectedServer) { client.onServerEvent(event, detectedServer); },
.onConnectionStatus = [&](string serverID, string serverComputerName, string serverName, int serverStepTime, SClient::Connection connectionStatus, SClient::Connection oldConnectionStatus) { client.onConnectionStatus(serverID, serverComputerName, serverName, serverStepTime, connectionStatus, oldConnectionStatus); },
.onProjectUpdate = [&](SClient::ProjUpdate state, string projectId) { client.onProjectUpdate(state, projectId); },
.onSpitType = [&](SClient::ObjectEvent event, bool isRemote, const SType &spitType) { client.onSpitType(event, isRemote, spitType); },
.onSpitObject = [&](SClient::ObjectEvent event, bool isRemote, const SType &spitType, const SObject &spitObject) { client.onSpitObject(event, isRemote, spitType, spitObject); },
.onPlay = [&](string playerID, string playerName, char playerType, bool playerIsRunning, uint64_t timeFrames) { client.onPlay(playerID, playerName, playerType, playerIsRunning, timeFrames); },
.onPlaySpitType = [&](string playerID, string spitTypeID, bool playerIsRunning, bool started, bool ended, double value) { client.onPlaySpitType(playerID, spitTypeID, playerIsRunning, started, ended, value); },
.onPlayRemoteObjectStatus = [&](const SType &spitType, const SObject &spitObject, uint64_t p_FrameInsideObject, int value) { client.onPlayRemoteObjectStatus(spitType, spitObject, p_FrameInsideObject, value); },
.onCheckSpitTypeVeto = [&](bool knownType, bool createdByClient) { return client.onCheckSpitTypeVeto(knownType, createdByClient); },
.onCheckSpitObjectVeto = [&](bool knownType) { return client.onCheckSpitObjectVeto(knownType); }};
SClient sut{"sut", "sutProj", socket, callbacks};
// tood connect - Response - Request project
// Transfer items liveSHOW -> Device
{
Message::ReportType t1;
t1.setConfirmed(false);
t1.setID("type1Id#");
t1.setFixed(true);
t1.setServerRemote("serverId");
t1.setServerProjectIndepended(true);
t1.setGroupName("sGroup");
t1.setName("type1Name");
t1.setDelay(100);
t1.setDuration(200);
socket.
}
}
+361
View File
@@ -0,0 +1,361 @@
-- COPY to: /usr/lib/x86_64-linux-gnu/wireshark/plugins/spit.lua
-- LiveShowSoftware SPIT-Protocol
spit_proto = Proto("spit", "LiveShowSoftware SPIT")
spit_sad_proto = Proto("spit_sad", "LiveShowSoftware SPIT SAD")
-- Define fields (add more as needed)
local f_hVer = ProtoField.uint32("spit.version", "Version")
local f_hNum = ProtoField.uint8("spit.msgCount", "MsgCount")
local f_hLen = ProtoField.uint32("spit.len", "Length")
local f_hSerId = ProtoField.string("spit.serverId", "ServerID")
local f_hCliId = ProtoField.string("spit.clientId", "ClientID")
local f_hType = ProtoField.uint8("spit.msgType", "MsgType")
local f_pLen = ProtoField.uint32("spit.paylen", "Payload Length")
local f_pSession = ProtoField.string("spit.key", "Spit Session")
local f_pKey = ProtoField.string("spit.key", "Spit Key")
local f_pSadKey = ProtoField.string("spit.sadKey", "Spit SAD Key")
local f_pServerDevName = ProtoField.string("spit.serverDevName", "Server Device Name")
local f_pClientDevName = ProtoField.string("spit.clientDevName", "Client Device Name")
local f_pClientName = ProtoField.string("spit.clientName", "Client Name")
local f_pLoginName = ProtoField.string("spit.loginName", "Login Name")
local f_pLoginPassword = ProtoField.string("spit.loginPassword", "Login Password")
local f_pServerName = ProtoField.string("spit.serverName", "Server Name")
local f_pServerIp = ProtoField.string("spit.serverIp", "Server IP")
local f_pServerPort = ProtoField.uint16("spit.serverIp", "Server Port")
local f_pSrvParam = ProtoField.string("spit.serverParam", "Server Paramter")
local f_pStepTime = ProtoField.string("spit.stepTime", "Step Time")
local f_pConnected = ProtoField.string("spit.connected", "Connected")
local f_pProjectId = ProtoField.string("spit.projectId", "Project ID")
local f_pProjectName = ProtoField.string("spit.projectName", "Project Name")
local f_pObjectId = ProtoField.string("spit.ObjectId", "Object ID")
local f_pObjectName = ProtoField.string("spit.ObjectName", "Object Name")
local f_pTypeId = ProtoField.string("spit.TypeId", "Type ID")
local f_pTypeName = ProtoField.string("spit.TypeName", "Type Name")
local f_pTypeGroupName = ProtoField.string("spit.TypeGroupName", "Type Group Name")
local f_pObjectsFrames = ProtoField.uint64("spit.ObjectFrames", "Object Frames")
local f_pValue = ProtoField.uint64("spit.Value", "Value")
local f_pValueFlag = ProtoField.uint8("spit.ValueFlag", "ValueFlag")
local f_pConfirm = ProtoField.uint8("spit.confirmed", "Confirmation")
local f_pFixed = ProtoField.uint8("spit.fixed", "Fixed")
local f_pAssigned = ProtoField.uint8("spit.assigned", "Action assigned")
local f_pSrvRemote = ProtoField.uint8("spit.serverRemote", "Server Remote")
local f_pProjIndep = ProtoField.uint8("spit.projectIndipendent", "Project Indipendent")
local f_pValueUse = ProtoField.uint8("spit.valueUse", "Value Use")
local f_pFrameStart = ProtoField.uint64("spit.frameStart", "Frame Start")
local f_pFrameLength = ProtoField.uint64("spit.frameLength", "Frame Length")
local f_pFrameFadeIn = ProtoField.uint64("spit.fadeInLength", "Fade In Length")
local f_pFrameFadeOut = ProtoField.uint64("spit.fadeOutLength", "Fade Out Length")
local f_pValueFactor = ProtoField.uint32("spit.valueFactor", "Value Factor")
local f_pFramesPerSec = ProtoField.uint32("spit.framesPerSec", "Frames per second")
local f_pTrack = ProtoField.uint32("spit.track", "Track")
local f_pRemark = ProtoField.string("spit.remark", "Remack")
local f_pDelay = ProtoField.uint64("spit.delay", "Delay")
local f_pDuration = ProtoField.uint64("spit.duration", "Duration")
local f_pPlayerId = ProtoField.string("spit.PlayerId", "Player ID")
local f_pPlayerName = ProtoField.string("spit.PlayerName", "Player Name")
local f_pPlayerType = ProtoField.uint8("spit.PlayerType", "Player Type")
local f_pPlayerRunning = ProtoField.uint8("spit.PlayerRunning", "Player Running")
local f_pPlayerFrames = ProtoField.uint64("spit.PlayerFrame", "Player Frames")
local f_pPlayerPrePos = ProtoField.uint8("spit.PlayerPrePos", "Player Preposition")
local sessionInfos = {}
spit_proto.fields = {
f_pSession, f_pKey, f_pSadKey, f_pServerDevName,
f_hVer, f_hNum, f_hLen, f_hSerId, f_hCliId, f_hType, f_pLen,
f_pClientDevName, f_pClientName, f_pLoginName, f_pLoginPassword, f_pServerName, f_pServerIp, f_pServerPort, f_pSrvParam, f_pStepTime, f_pConnected, f_pProjectId, f_pProjectName,
f_pObjectId, f_pObjectName, f_pTypeId, f_pTypeName, f_pTypeGroupName, f_pObjectsFrames, f_pValue, f_pValueFlag,
f_pConfirm, f_pFixed, f_pAssigned, f_pSrvRemote, f_pProjIndep, f_pValueUse,
f_pFrameStart, f_pFrameLength, f_pFrameFadeIn, f_pFrameFadeOut, f_pValueFactor, f_pFramesPerSec, f_pTrack, f_pRemark, f_pDelay, f_pDuration,
f_pPlayerId, f_pPlayerName, f_pPlayerType, f_pPlayerRunning, f_pPlayerFrames, f_pPlayerPrePos
}
function extractSession(buffer, pinfo)
local serverHash = buffer(16,41):string()
local clientHash = buffer(57,41):string()
return string.format("%s - %s", sessionInfos[serverHash], sessionInfos[clientHash])
end
function extractVersionString(buffer)
local vBytes = buffer(7,4):bytes()
local vStr = ""
for i = 0, 3 do
local byte = vBytes:get_index(i)
local high_nibble = bit.rshift(byte, 4)
local low_nibble = bit.band(byte, 0x0F)
vStr = vStr .. tostring(high_nibble) .. tostring(low_nibble)
end
return vStr
end
function addBigEndianValue(tree, proto, buffer)
local val = -1;
-- local val = buffer(99,4):le_uint()
if proto.type == ftypes.UINT32 then
val = buffer:le_uint()
elseif proto.type == ftypes.UINT64 then
val = buffer:le_uint64()
end
tree:add(proto, buffer):set_text(proto.name .. ": " .. val)
end
-- Heuristic dissector function
function spit_proto.dissector(buffer, pinfo, tree)
if buffer:len() < 6 then return false end
if buffer(0,6):string() ~= "#spit#" then return false end
pinfo.cols.protocol = "SPIT"
local subtree = tree:add(spit_proto, buffer(), "SPIT Protocol Data")
local hTree = subtree:add("Header")
hTree:add(f_hVer, buffer(7,4)):set_text("Version: " .. extractVersionString(buffer))
hTree:add(f_hNum, buffer(11,1))
local hLen = buffer(12,4):le_uint()
subtree:add(f_hLen, buffer(12,4)):set_text("Length: " .. hLen)
hTree:add(f_hSerId, buffer(16,41))
hTree:add(f_hCliId, buffer(57,41))
local type = buffer(98,1):uint()
hTree:add(f_hType, buffer(98,1))
local pTree = subtree:add("Payload")
-- local pLen = buffer(99,4):le_uint()
-- pTree:add(f_pLen, buffer(99,4)):set_text("Pyload Length: " .. pLen)
addBigEndianValue(pTree, f_pLen, buffer(99,4))
local info_text = "Type: " .. type
if type == 1 then -- Connect
pTree:add(f_pClientDevName, buffer(103,64))
pTree:add(f_pClientName, buffer(167,64))
pTree:add(f_pLoginName, buffer(231,64))
pTree:add(f_pLoginPassword, buffer(295,64))
pTree:add(f_pServerIp, buffer(359,40))
pTree:add(f_pServerPort, buffer(399,2))
local clientName = buffer(167,64):string()
local clientHash = buffer(57,41):string()
sessionInfos[clientName] = clientHash -- e.g. 70866c9b-af1c-4765-a17e-8b45c93f3aca
sessionInfos[clientHash] = clientName --
sessionInfos[tostring(pinfo.src)] = clientName
info_text = string.format("Connect Req | Client: %s (%s) | ADDR: %s:%s",
clientName, clientHash, buffer(359,40):string(), buffer(399,2):uint())
elseif type == 2 then -- Connect Response
pTree:add(f_pServerDevName, buffer(103,64))
pTree:add(f_pServerName, buffer(167,64))
pTree:add(f_pStepTime, buffer(231,4))
pTree:add(f_pConnected, buffer(235,1))
local serverName = buffer(167,64):string()
local serverHash = buffer(16,41):string()
sessionInfos[serverName] = serverHash -- e.g. 405c7100-64e8-435d-8a7b-00c5629f9ccf
sessionInfos[serverHash] = serverName
sessionInfos[tostring(pinfo.src)] = serverName
info_text = string.format("Connect Res | Server: %s (%s) ",
serverName, serverHash)
elseif type == 3 then
pTree:add(f_pSession, extractSession(buffer, pinfo))
info_text = string.format("Disconnect")
elseif type == 8 then
info_text = string.format("Watchdog")
elseif type == 9 then
info_text = string.format("Watchdog Ans")
elseif type == 11 then -- Report Porject
pTree:add(f_pSession, extractSession(buffer, pinfo))
pTree:add(f_pProjectId, buffer(103,41))
pTree:add(f_pProjectName, buffer(144,64))
addBigEndianValue(pTree, f_pFramesPerSec, buffer(208,4))
info_text = string.format("%s -> %s | Report Project | Project: %s / %s",
sessionInfos[tostring(pinfo.src)], sessionInfos[tostring(pinfo.dst)], buffer(103,41):string(), buffer(144,64):string())
elseif type == 13 then -- Close Porject
pTree:add(f_pSession, extractSession(buffer, pinfo))
pTree:add(f_pProjectId, buffer(103,41))
info_text = string.format("Close Project | Project: %s",
buffer(103,64):string())
elseif type == 15 then
pTree:add(f_pSession, extractSession(buffer, pinfo))
info_text = string.format("Request Project Transfer")
elseif type == 16 then
pTree:add(f_pSession, extractSession(buffer, pinfo))
info_text = string.format("Start Project Transfer")
elseif type == 17 then
pTree:add(f_pSession, extractSession(buffer, pinfo))
info_text = string.format("End Project Transfer")
elseif type == 21 then -- Report Type
pTree:add(f_pSession, extractSession(buffer, pinfo))
pTree:add(f_pConfirm, buffer(103,1))
pTree:add(f_pTypeId, buffer(104,41))
pTree:add(f_pFixed, buffer(145,1))
pTree:add(f_pSrvRemote, buffer(146,1))
pTree:add(f_pProjIndep, buffer(147,1))
pTree:add(f_pTypeGroupName, buffer(148,39))
pTree:add(f_pTypeName, buffer(187,64))
addBigEndianValue(pTree, f_pDelay, buffer(251,8))
addBigEndianValue(pTree, f_pDuration, buffer(259,8))
local group = buffer(148,39):string()
local name = buffer(187,64):string()
local id = buffer(104,41):string()
info_text = string.format("%s -> %s | Report SPIT Type: %s.%s (%s)", sessionInfos[tostring(pinfo.src)], sessionInfos[tostring(pinfo.dst)], group, name, id)
elseif type == 23 then
pTree:add(f_pSession, extractSession(buffer, pinfo))
pTree:add(f_pTypeId, buffer(103,41))
info_text = string.format("Remove SPIT Type")
elseif type == 31 then -- Report Object
pTree:add(f_pSession, extractSession(buffer, pinfo))
pTree:add(f_pConfirm, buffer(103,1))
pTree:add(f_pObjectId, buffer(104,41))
pTree:add(f_pFixed, buffer(145,1))
pTree:add(f_pSrvRemote, buffer(146,1))
pTree:add(f_pObjectName, buffer(147,64))
pTree:add(f_pTypeId, buffer(211,41))
pTree:add(f_pSrvParam, buffer(252,64))
addBigEndianValue(pTree, f_pFrameStart, buffer(316,8))
addBigEndianValue(pTree, f_pFrameLength, buffer(324,8))
addBigEndianValue(pTree, f_pFrameFadeIn, buffer(332,8))
addBigEndianValue(pTree, f_pFrameFadeOut, buffer(340,8))
addBigEndianValue(pTree, f_pValueFactor, buffer(348,4))
pTree:add(f_pValueUse, buffer(349,1))
addBigEndianValue(pTree, f_pTrack, buffer(350,4))
pTree:add(f_pRemark, buffer(354,64))
addBigEndianValue(pTree, f_pDelay, buffer(418,8))
addBigEndianValue(pTree, f_pDuration, buffer(426,8))
pTree:add(f_pAssigned, buffer(435,1))
local name = buffer(147,64):string()
local tId = buffer(211,41):string()
local oId = buffer(104,41):string()
info_text = string.format("%s -> %s | Report SPIT Object: %s (%s / %s)", sessionInfos[tostring(pinfo.src)], sessionInfos[tostring(pinfo.dst)], name, oId, tId)
elseif type == 33 then
pTree:add(f_pSession, extractSession(buffer, pinfo))
pTree:add(f_pObjectId, buffer(103,41))
info_text = string.format("Remove SPIT Object")
elseif type == 37 then
pTree:add(f_pSession, extractSession(buffer, pinfo))
pTree:add(f_pPlayerId, buffer(103,41))
pTree:add(f_pPlayerName, buffer(144,64))
pTree:add(f_pPlayerType, buffer(208,1))
pTree:add(f_pPlayerRunning, buffer(209,1))
addBigEndianValue(pTree, f_pPlayerFrames, buffer(210,8))
info_text = string.format("Play SPIT Start")
elseif type == 38 then -- Play Spit object
pTree:add(f_pSession, extractSession(buffer, pinfo))
pTree:add(f_pPlayerId, buffer(103,41))
pTree:add(f_pObjectId, buffer(144,41))
addBigEndianValue(pTree, f_pObjectsFrames, buffer(185,8))
pTree:add(f_pPlayerPrePos, buffer(193,1))
addBigEndianValue(pTree, f_pValue, buffer(194,4))
pTree:add(f_pValueFlag, buffer(198,1))
local oId = buffer(144,41):string()
info_text = string.format("%s -> %s | Play SPIT Object: %s", sessionInfos[tostring(pinfo.src)], sessionInfos[tostring(pinfo.dst)], oId)
elseif type == 39 then
pTree:add(f_pSession, extractSession(buffer, pinfo))
pTree:add(f_pPlayerId, buffer(103,41))
info_text = string.format("Play SPIT End")
elseif type == 40 then
pTree:add(f_pSession, extractSession(buffer, pinfo))
pTree:add(f_pObjectId, buffer(103,41))
addBigEndianValue(pTree, f_pObjectsFrames, buffer(144,8))
addBigEndianValue(pTree, f_pValue, buffer(152,4))
info_text = string.format("Report Play Status")
else
info_text = string.format("ERROR Unknown")
end
pinfo.cols['info'] = info_text
return true -- We handled this packet
end
function split(str, delim)
local result = {}
local delim_len = delim
local s = 1
while true do
local e, next_e = string.find(str, delim, s)
if not e then
table.insert(result, string.sub(str, s))
break
end
table.insert(result, string.sub(str, s, e - 1))
s = next_e + 1
end
return result
end
-- Heuristic dissector function
function spit_sad_proto.dissector(buffer, pinfo, tree)
if buffer:len() < 14 then return false end
if buffer(0,14):string() ~= "#SAD##,##spit#" then return false end
pinfo.cols.protocol = "SPIT-SAD"
local info_text = "SAD"
if buffer:len() > 16 then
local payload = buffer(0,buffer:len()):string()
local parts = split(payload, "#,#")
local subtree = tree:add(spit_sad_proto, buffer(), "Discovery Data")
subtree:add(f_pSadKey, parts[1]) -- 1 #SAD#
subtree:add(f_pKey, parts[2]) -- 2 #spit#
subtree:add(f_pServerName, parts[3]) -- 3 liveShowSPIT
subtree:add(f_hSerId , parts[4]) -- 4 405c7100-64e8-435d-8a7b-00c5629f9ccf
subtree:add(f_pServerDevName, parts[5]) -- 5 P17gen1
subtree:add(f_pServerPort, parts[6]) -- 6 15120
subtree:add(f_pServerIp, parts[7]) -- 7 172.16.17.56
subtree:add(f_pProjectName, parts[9]) -- 9 CatasWatchProject
info_text = string.format("SPIT-SAD Discover Data | Client: %s Porject: %s ",
parts[4], parts[9])
else
info_text = string.format("SPIT-SAD Discover")
end
pinfo.cols['info'] = info_text
return true -- We handled this packet
end
-- Register as a heuristic UDP dissector
spit_proto:register_heuristic("udp", spit_proto.dissector)
spit_sad_proto:register_heuristic("udp", spit_sad_proto.dissector)
+5
View File
@@ -0,0 +1,5 @@
UnitTestsFrame()
target_link_libraries(${moduleName} PUBLIC
fmt
hwMock
)
+320
View File
@@ -0,0 +1,320 @@
/**
* @file TestableUTest.cpp
* @author Gustice
* @brief
*/
#include "doctest/doctest.h"
#include <fmt/core.h>
#include <iostream>
#include "spit_message.hpp"
using namespace spit;
class SpitMessageFix {
protected:
};
void checkData(std::vector<char> a, std::vector<char> b) {
CHECK(a.size() == b.size());
for (size_t i = 0; i < b.size(); ++i) {
CHECK_MESSAGE(a[i] == b[i],
fmt::format("Mismatch at index, {} : a = 0x{:02x} , b = 0x{:02x}",
i, (int)a[i], (int)b[i]));
}
}
TEST_CASE_FIXTURE(SpitMessageFix, "Connect Request") {
std::vector<uint8_t> msg = {
/*0000*/ 0x23, 0x73, 0x70, 0x69, 0x74, 0x23, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x63, 0x00, 0x00, 0x00, // #spit#......c...
/*0010*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0020*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0030*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x62, 0x33, 0x63, 0x66, 0x32, 0x34, // .........db3cf24
/*0040*/ 0x34, 0x2d, 0x36, 0x30, 0x35, 0x39, 0x2d, 0x34, 0x30, 0x65, 0x36, 0x2d, 0x61, 0x33, 0x35, 0x35, // 4-6059-40e6-a355
/*0050*/ 0x2d, 0x32, 0x62, 0x66, 0x34, 0x34, 0x36, 0x37, 0x33, 0x32, 0x62, 0x30, 0x36, 0x00, 0x00, 0x00, // -2bf446732b06...
/*0060*/ 0x00, 0x00, 0x01, 0x30, 0x01, 0x00, 0x00, 0x64, 0x65, 0x76, 0x70, 0x69, 0x00, 0x00, 0x00, 0x00, // ...0...devpi....
/*0070*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0080*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0090*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00a0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x71, 0x74, 0x74, 0x53, 0x70, 0x69, 0x74, 0x42, // .......MqttSpitB
/*00b0*/ 0x72, 0x69, 0x64, 0x67, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ridge...........
/*00c0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00d0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00e0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00f0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0100*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0110*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0120*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0130*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0140*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0150*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0160*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x37, 0x32, 0x2e, 0x31, 0x36, 0x2e, 0x31, 0x36, // .......172.16.16
/*0170*/ 0x2e, 0x31, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .10.............
/*0180*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, // ................
/*0190*/ 0x3b, 0x00, 0x00 // ;..
};
std::vector<char> msgC(msg.begin(), msg.end());
{
Message sut = Message(msgC);
CHECK_EQ(sut.getType(), Message::Connect::OPFLAG);
auto &pM = sut.getAs<Message::Connect>();
CHECK_EQ(pM.getClientComputerName(), "devpi");
CHECK_EQ(pM.getClientName(), "MqttSpitBridge");
CHECK_EQ(pM.getLoginName(), "");
CHECK_EQ(pM.getLoginPassword(), "");
CHECK_EQ(pM.getServerIP(), "172.16.16.10");
CHECK_EQ(pM.getServerPort(), 15120);
}
{
Message::Connect sut("devpi", "MqttSpitBridge");
sut.setLoginName("");
sut.setLoginPassword("");
sut.setServerIP("172.16.16.10");
sut.setServerPort(15120);
sut.setMessageNumber(2);
sut.setServerInfoInHeader("db3cf244-6059-40e6-a355-2bf446732b06", "");
auto data = Message::createMessage(sut);
CHECK(data.size() == msg.size());
for (size_t i = 0; i < msg.size(); ++i) {
CHECK_MESSAGE(data[i] == msgC[i],
fmt::format("Mismatch at index, {} : a = 0x{:02x} , b = 0x{:02x}",
i, (int)data[i], (int)msg[i]));
}
}
}
TEST_CASE_FIXTURE(SpitMessageFix, "Connect Response") {
std::vector<uint8_t> msg = {
/*0000*/ 0x23, 0x73, 0x70, 0x69, 0x74, 0x23, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x63, 0x00, 0x00, 0x00, // #spit#......c...
/*0010*/ 0x34, 0x30, 0x35, 0x63, 0x37, 0x31, 0x30, 0x30, 0x2d, 0x36, 0x34, 0x65, 0x38, 0x2d, 0x34, 0x33, // 405c7100-64e8-43
/*0020*/ 0x35, 0x64, 0x2d, 0x38, 0x61, 0x37, 0x62, 0x2d, 0x30, 0x30, 0x63, 0x35, 0x36, 0x32, 0x39, 0x66, // 5d-8a7b-00c5629f
/*0030*/ 0x39, 0x63, 0x63, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x62, 0x33, 0x63, 0x66, 0x32, 0x34, // 9ccf.....db3cf24
/*0040*/ 0x34, 0x2d, 0x36, 0x30, 0x35, 0x39, 0x2d, 0x34, 0x30, 0x65, 0x36, 0x2d, 0x61, 0x33, 0x35, 0x35, // 4-6059-40e6-a355
/*0050*/ 0x2d, 0x32, 0x62, 0x66, 0x34, 0x34, 0x36, 0x37, 0x33, 0x32, 0x62, 0x30, 0x36, 0x00, 0x00, 0x00, // -2bf446732b06...
/*0060*/ 0x00, 0x00, 0x02, 0x89, 0x00, 0x00, 0x00, 0x50, 0x31, 0x37, 0x67, 0x65, 0x6e, 0x31, 0x00, 0x00, // .......P17gen1..
/*0070*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0080*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0090*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00a0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x69, 0x76, 0x65, 0x53, 0x48, 0x4f, 0x57, 0x00, // .......liveSHOW.
/*00b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00c0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00d0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00e0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01 // .......(....
};
std::vector<char> msgC(msg.begin(), msg.end());
{
Message sut = Message(msgC);
CHECK_EQ(sut.getType(), Message::ConnectAnswer::OPFLAG);
auto &pM = sut.getAs<Message::ConnectAnswer>();
CHECK_EQ(pM.getServerComputerName(), "P17gen1");
CHECK_EQ(pM.getServerName(), "liveSHOW");
CHECK_EQ(pM.getConnected(), true);
CHECK_EQ(pM.getStepTime(), 40);
}
{
Message::ConnectAnswer sut;
sut.setServerComputerName("P17gen1");
sut.setServerName("liveSHOW");
sut.setConnected(true);
sut.setStepTime(40);
sut.setMessageNumber(1);
sut.setServerInfoInHeader("db3cf244-6059-40e6-a355-2bf446732b06", "405c7100-64e8-435d-8a7b-00c5629f9ccf");
auto data = Message::createMessage(sut);
checkData(data, msgC);
}
}
TEST_CASE_FIXTURE(SpitMessageFix, "Report Spit Type") {
std::vector<uint8_t> msg = {
/*0000*/ 0x23, 0x73, 0x70, 0x69, 0x74, 0x23, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x63, 0x00, 0x00, 0x00, // #spit#......c...
/*0010*/ 0x34, 0x30, 0x35, 0x63, 0x37, 0x31, 0x30, 0x30, 0x2d, 0x36, 0x34, 0x65, 0x38, 0x2d, 0x34, 0x33, // 405c7100-64e8-43
/*0020*/ 0x35, 0x64, 0x2d, 0x38, 0x61, 0x37, 0x62, 0x2d, 0x30, 0x30, 0x63, 0x35, 0x36, 0x32, 0x39, 0x66, // 5d-8a7b-00c5629f
/*0030*/ 0x39, 0x63, 0x63, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x62, 0x33, 0x63, 0x66, 0x32, 0x34, // 9ccf.....db3cf24
/*0040*/ 0x34, 0x2d, 0x36, 0x30, 0x35, 0x39, 0x2d, 0x34, 0x30, 0x65, 0x36, 0x2d, 0x61, 0x33, 0x35, 0x35, // 4-6059-40e6-a355
/*0050*/ 0x2d, 0x32, 0x62, 0x66, 0x34, 0x34, 0x36, 0x37, 0x33, 0x32, 0x62, 0x30, 0x36, 0x00, 0x00, 0x00, // -2bf446732b06...
/*0060*/ 0x00, 0x00, 0x15, 0xa8, 0x00, 0x00, 0x00, 0x01, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, // ........ActionMo
/*0070*/ 0x76, 0x65, 0x54, 0x6f, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // veTo1...........
/*0080*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0090*/ 0x00, 0x01, 0x00, 0x01, 0x43, 0x61, 0x74, 0x61, 0x73, 0x57, 0x61, 0x74, 0x63, 0x68, 0x4d, 0x6f, // ....CatasWatchMo
/*00a0*/ 0x76, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // vement..........
/*00b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x63, 0x74, 0x69, 0x6f, // ...........Actio
/*00c0*/ 0x6e, 0x4d, 0x6f, 0x76, 0x65, 0x54, 0x6f, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // nMoveTo1........
/*00d0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00e0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00f0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0100*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // ...........
};
std::vector<char> msgC(msg.begin(), msg.end());
{
Message sut = Message(msgC);
CHECK_EQ(sut.getType(), Message::ReportType::OPFLAG);
auto &pM = sut.getAs<Message::ReportType>();
CHECK_EQ(pM.getConfirmed(), true);
CHECK_EQ(pM.getID(), "ActionMoveTo1");
CHECK_EQ(pM.getFixed(), true);
CHECK_EQ(pM.getServerRemote(), false);
CHECK_EQ(pM.getServerProjectIndepended(), true);
CHECK_EQ(pM.getGroupName(), "CatasWatchMovement");
CHECK_EQ(pM.getName(), "ActionMoveTo1");
CHECK_EQ(pM.getDelay(), 0);
CHECK_EQ(pM.getDuration(), 0);
}
{
Message::ReportType sut;
sut.setConfirmed(true);
sut.setID("ActionMoveTo1");
sut.setFixed(true);
sut.setServerRemote(false);
sut.setServerProjectIndepended(true);
sut.setGroupName("CatasWatchMovement");
sut.setName("ActionMoveTo1");
sut.setDelay(0);
sut.setDuration(0);
sut.setMessageNumber(6);
sut.setServerInfoInHeader("db3cf244-6059-40e6-a355-2bf446732b06", "405c7100-64e8-435d-8a7b-00c5629f9ccf");
auto data = Message::createMessage(sut);
checkData(data, msgC);
}
}
TEST_CASE_FIXTURE(SpitMessageFix, "Report Spit Object") {
std::vector<uint8_t> msg = {
/*0000*/ 0x23, 0x73, 0x70, 0x69, 0x74, 0x23, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x63, 0x00, 0x00, 0x00, // #spit#......c...
/*0010*/ 0x34, 0x30, 0x35, 0x63, 0x37, 0x31, 0x30, 0x30, 0x2d, 0x36, 0x34, 0x65, 0x38, 0x2d, 0x34, 0x33, // 405c7100-64e8-43
/*0020*/ 0x35, 0x64, 0x2d, 0x38, 0x61, 0x37, 0x62, 0x2d, 0x30, 0x30, 0x63, 0x35, 0x36, 0x32, 0x39, 0x66, // 5d-8a7b-00c5629f
/*0030*/ 0x39, 0x63, 0x63, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x62, 0x33, 0x63, 0x66, 0x32, 0x34, // 9ccf.....db3cf24
/*0040*/ 0x34, 0x2d, 0x36, 0x30, 0x35, 0x39, 0x2d, 0x34, 0x30, 0x65, 0x36, 0x2d, 0x61, 0x33, 0x35, 0x35, // 4-6059-40e6-a355
/*0050*/ 0x2d, 0x32, 0x62, 0x66, 0x34, 0x34, 0x36, 0x37, 0x33, 0x32, 0x62, 0x30, 0x36, 0x00, 0x00, 0x00, // -2bf446732b06...
/*0060*/ 0x00, 0x00, 0x1f, 0x53, 0x01, 0x00, 0x00, 0x01, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x68, 0x63, // ...S....00000mhc
/*0070*/ 0x67, 0x65, 0x64, 0x30, 0x69, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // ged0i00000000000
/*0080*/ 0x30, 0x30, 0x30, 0x30, 0x30, 0x7a, 0x34, 0x30, 0x74, 0x63, 0x79, 0x6b, 0x63, 0x33, 0x00, 0x00, // 00000z40tcykc3..
/*0090*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00a0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00c0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00d0*/ 0x00, 0x00, 0x00, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x76, 0x65, 0x54, 0x6f, 0x31, // ...ActionMoveTo1
/*00e0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00f0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x69, 0x70, 0x00, // ............vip.
/*0100*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0110*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0120*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0130*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6d, 0xfc, 0x12, 0x00, // ............m...
/*0140*/ 0x00, 0x00, 0x00, 0x00, 0x4b, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ....K...........
/*0150*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x42, 0x0f, 0x00, // ............@B..
/*0160*/ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0170*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0180*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0190*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*01a0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*01b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // ......
};
std::vector<char> msgC(msg.begin(), msg.end());
{
Message sut = Message(msgC);
CHECK_EQ(sut.getType(), Message::ReportObject::OPFLAG);
auto &pM = sut.getAs<Message::ReportObject>();
CHECK_EQ(pM.getConfirmed(), true);
CHECK_EQ(pM.getID(), "00000mhcged0i0000000000000000z40tcykc3");
CHECK_EQ(pM.getFixed(), false);
CHECK_EQ(pM.getServerRemote(), false);
CHECK_EQ(pM.getName(), "");
CHECK_EQ(pM.getSPITTypeID(), "ActionMoveTo1");
CHECK_EQ(pM.getServerParams(), "vip");
CHECK_EQ(pM.getFrameStart(), 1244269);
CHECK_EQ(pM.getFrameLength(), 53323);
CHECK_EQ(pM.getFrameFadeInLength(), 0);
CHECK_EQ(pM.getFrameFadeOutLength(), 0);
CHECK_EQ(pM.getValueFactor(), 1000000);
CHECK_EQ(pM.getValueUse(), 0);
CHECK_EQ(pM.getTrack(), 1);
CHECK_EQ(pM.getRemark(), "");
CHECK_EQ(pM.getDelay(), 0);
CHECK_EQ(pM.getDuration(), 0);
CHECK_EQ(pM.getActionAssigned(), false);
}
{
Message::ReportObject sut;
sut.setConfirmed(true);
sut.setID("00000mhcged0i0000000000000000z40tcykc3");
sut.setFixed(false);
sut.setServerRemote(false);
sut.setName("");
sut.setSPITTypeID("ActionMoveTo1");
sut.setServerParams("vip");
sut.setFrameStart(1244269);
sut.setFrameLength(53323);
sut.setFrameFadeInLength(0);
sut.setFrameFadeOutLength(0);
sut.setValueFactor(1000000);
sut.setValueUse(0);
sut.setTrack(1);
sut.setRemark("");
sut.setDelay(0);
sut.setDuration(0);
sut.setActionAssigned(false);
sut.setMessageNumber(21);
sut.setServerInfoInHeader("db3cf244-6059-40e6-a355-2bf446732b06", "405c7100-64e8-435d-8a7b-00c5629f9ccf");
auto data = Message::createMessage(sut);
checkData(data, msgC);
}
}
TEST_CASE_FIXTURE(SpitMessageFix, "Report Play Object") {
std::vector<uint8_t> msg = {
/*0000*/ 0x23, 0x73, 0x70, 0x69, 0x74, 0x23, 0x00, 0x01, 0x00, 0x00, 0x00, 0x83, 0x63, 0x00, 0x00, 0x00, // #spit#......c...
/*0010*/ 0x34, 0x30, 0x35, 0x63, 0x37, 0x31, 0x30, 0x30, 0x2d, 0x36, 0x34, 0x65, 0x38, 0x2d, 0x34, 0x33, // 405c7100-64e8-43
/*0020*/ 0x35, 0x64, 0x2d, 0x38, 0x61, 0x37, 0x62, 0x2d, 0x30, 0x30, 0x63, 0x35, 0x36, 0x32, 0x39, 0x66, // 5d-8a7b-00c5629f
/*0030*/ 0x39, 0x63, 0x63, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x62, 0x33, 0x63, 0x66, 0x32, 0x34, // 9ccf.....db3cf24
/*0040*/ 0x34, 0x2d, 0x36, 0x30, 0x35, 0x39, 0x2d, 0x34, 0x30, 0x65, 0x36, 0x2d, 0x61, 0x33, 0x35, 0x35, // 4-6059-40e6-a355
/*0050*/ 0x2d, 0x32, 0x62, 0x66, 0x34, 0x34, 0x36, 0x37, 0x33, 0x32, 0x62, 0x30, 0x36, 0x00, 0x00, 0x00, // -2bf446732b06...
/*0060*/ 0x00, 0x00, 0x28, 0x39, 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x38, 0x75, 0x65, // ..(9...00000m8ue
/*0070*/ 0x64, 0x79, 0x79, 0x74, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // dyyt000000000000
/*0080*/ 0x30, 0x30, 0x30, 0x31, 0x66, 0x6a, 0x30, 0x76, 0x61, 0x38, 0x62, 0x6f, 0x6d, 0x00, 0x00, 0x00, // 0001fj0va8bom...
/*0090*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // ............
};
std::vector<char> msgC(msg.begin(), msg.end());
{
Message sut = Message(msgC);
CHECK_EQ(sut.getType(), Message::ReportPlayStatus::OPFLAG);
auto &pM = sut.getAs<Message::ReportPlayStatus>();
CHECK_EQ(pM.getID(), "00000m8uedyyt0000000000000001fj0va8bom");
CHECK_EQ(pM.getFrameInsideObject(), 0);
CHECK_EQ(pM.getValue(), 0);
}
{
Message::ReportPlayStatus sut;
sut.setID("00000m8uedyyt0000000000000001fj0va8bom");
sut.setFrameInsideObject(0);
sut.setValue(0);
sut.setMessageNumber(131);
sut.setServerInfoInHeader("db3cf244-6059-40e6-a355-2bf446732b06", "405c7100-64e8-435d-8a7b-00c5629f9ccf");
auto data = Message::createMessage(sut);
checkData(data, msgC);
}
}
+92
View File
@@ -0,0 +1,92 @@
/**
* @file spitRepoTest.cpp
* @author Gustice
* @brief
*/
#include "doctest/doctest.h"
#include <fmt/core.h>
#include <iostream>
#include "spit_object.hpp"
using namespace spit;
using namespace std;
class SpitObjectFix {
protected:
};
TEST_CASE_FIXTURE(SpitObjectFix, "Typical use case") {
{
SObject sut("#Id#123");
CHECK(sut.getID() == "#Id#123");
CHECK(sut.getConfirmed() == false);
sut.setConfirmed(true);
CHECK(sut.getConfirmed() == true);
CHECK(sut.Name.set("G123") == true);
CHECK(sut.Name.get() == "G123");
CHECK(sut.getConfirmed() == false);
sut.setConfirmed(true);
CHECK(sut.getConfirmed() == true);
CHECK(sut.Name.set("G123") == false);
CHECK(sut.getConfirmed() == true);
CHECK(sut.Name.set("G321") == true);
CHECK(sut.Name.get() == "G321");
CHECK(sut.getConfirmed() == false);
}
{
SObject sut("#Id#123");
CHECK(sut.getID() == "#Id#123");
CHECK(sut.getConfirmed() == false);
sut.setConfirmed(true);
CHECK(sut.getConfirmed() == true);
CHECK(sut.Fixed.set(true) == true);
CHECK(sut.Fixed.get() == true);
CHECK(sut.getConfirmed() == false);
sut.setConfirmed(true);
CHECK(sut.getConfirmed() == true);
CHECK(sut.Fixed.set(true) == false);
CHECK(sut.getConfirmed() == true);
CHECK(sut.Fixed.set(false) == true);
CHECK(sut.Fixed.get() == false);
CHECK(sut.getConfirmed() == false);
}
{
SObject sut("#Id#123");
CHECK(sut.getID() == "#Id#123");
CHECK(sut.getConfirmed() == false);
sut.setConfirmed(true);
CHECK(sut.getConfirmed() == true);
CHECK(sut.FrameLength.set(123) == true);
CHECK(sut.FrameLength.get() == 123);
CHECK(sut.getConfirmed() == false);
sut.setConfirmed(true);
CHECK(sut.getConfirmed() == true);
CHECK(sut.FrameLength.set(123) == false);
CHECK(sut.getConfirmed() == true);
CHECK(sut.FrameLength.set(321) == true);
CHECK(sut.FrameLength.get() == 321);
CHECK(sut.getConfirmed() == false);
}
// ...
// {
// SObject sut("#Id#123", "grp123", "name123", true, false, 1234, 4321);
// CHECK(sut.getID() == "#Id#123");
// CHECK(sut.GroupName.get() == "grp123");
// CHECK(sut.Name.get() == "name123");
// CHECK(sut.Fixed.get() == true);
// CHECK(sut.ProjectIndepended.get() == false);
// CHECK(sut.DefaultDelay.get() == 1234);
// CHECK(sut.DefaultDuration.get() == 4321);
// }
}
+350
View File
@@ -0,0 +1,350 @@
/**
* @file spitRepoTest.cpp
* @author Gustice
* @brief
*/
#include "doctest/doctest.h"
#include <fmt/core.h>
#include <iostream>
#include <queue>
#include "spit_processor.hpp"
using namespace spit;
using namespace std;
static queue<string> cbEvents;
class ClientMock {
public:
void onServerEvent(SProcessor::ServerEvent event, const SadDetectedServer &detectedServer) {
}
void onConnectionStatus(string serverID, string serverComputerName, string serverName, int serverStepTime, SProcessor::Connection connectionStatus, SProcessor::Connection oldConnectionStatus) {
}
void onProjectUpdate(SProcessor::ProjUpdate state, string projectId) {
}
void onSpitType(SProcessor::ObjectEvent event, bool isRemote, const SType &sType) {
cbEvents.push(fmt::format("{}: {} {} {} {}", (int)event, isRemote, sType.getID(), sType.Name.get(), sType.GroupName.get()));
}
void onSpitObject(SProcessor::ObjectEvent event, bool isRemote, const SType &sType, const SObject &sObject) {
cbEvents.push(fmt::format("{}: {} {} {} {} {}", (int)event, isRemote, sType.getID(), sType.Name.get(), sObject.Name.get(), sObject.getID()));
}
void onPlay(string playerID, string playerName, char playerType, bool playerIsRunning, uint64_t timeFrames) {
}
void onPlaySpitType(string playerID, string spitTypeID, bool playerIsRunning, bool started, bool ended, double value) {
}
void onPlayRemoteObjectStatus(const SType &sType, const SObject &sObject, uint64_t p_FrameInsideObject, int value) {
}
bool onCheckSpitTypeVeto(bool knownType, bool createdByClient) {
cbEvents.push(fmt::format("veto check {}/{}", knownType, createdByClient));
return true;
}
bool onCheckSpitObjectVeto(bool knownType) {
return true;
}
};
class SpitClientFix {
protected:
std::vector<string> messages;
ClientMock client;
SProcessor::Callbacks callbacks{
.onServerEvent = [&](SProcessor::ServerEvent event, const SadDetectedServer &detectedServer) { client.onServerEvent(event, detectedServer); },
.onConnectionStatus = [&](string serverID, string serverComputerName, string serverName, int serverStepTime, SProcessor::Connection connectionStatus, SProcessor::Connection oldConnectionStatus) { client.onConnectionStatus(serverID, serverComputerName, serverName, serverStepTime, connectionStatus, oldConnectionStatus); },
.onProjectUpdate = [&](SProcessor::ProjUpdate state, string projectId) { client.onProjectUpdate(state, projectId); },
.onSpitType = [&](SProcessor::ObjectEvent event, bool isRemote, const SType &t) { client.onSpitType(event, isRemote, t); },
.onSpitObject = [&](SProcessor::ObjectEvent event, bool isRemote, const SType &t, const SObject &o) { client.onSpitObject(event, isRemote, t, o); },
.onPlay = [&](string playerID, string playerName, char playerType, bool playerIsRunning, uint64_t timeFrames) { client.onPlay(playerID, playerName, playerType, playerIsRunning, timeFrames); },
.onPlaySpitType = [&](string playerID, string spitTypeID, bool playerIsRunning, bool started, bool ended, double value) { client.onPlaySpitType(playerID, spitTypeID, playerIsRunning, started, ended, value); },
.onPlayRemoteObjectStatus = [&](const SType &t, const SObject &o, uint64_t p_FrameInsideObject, int value) { client.onPlayRemoteObjectStatus(t, o, p_FrameInsideObject, value); },
.onCheckSpitTypeVeto = [&](bool knownType, bool createdByClient) { return client.onCheckSpitTypeVeto(knownType, createdByClient); },
.onCheckSpitObjectVeto = [&](bool knownType) { return client.onCheckSpitObjectVeto(knownType); }};
SProcessor::InternalCallbacks intCallbacks{
.delay = [&](int ms) { return; },
.sendMessage = [&](Message::OPPart &msg) { return 0; },
.parseMessage = [&](Message &msg) {
// auto o = fmt::format("msg-{}", msg.getHeader()->getMessageNumber());
// messages.push_back(o);
},
};
SProcessor sut{"sut", "sutProj", "dev", "##GUID##", callbacks, intCallbacks};
};
TEST_CASE_FIXTURE(SpitClientFix, "Spit types") {
{
Message::ReportType t;
t.setConfirmed(false);
t.setID("type1Id#Rem");
t.setFixed(true);
t.setServerRemote(true);
t.setServerProjectIndepended(true);
t.setGroupName("sGroup");
t.setName("type1Name");
t.setDelay(100);
t.setDuration(200);
sut.checkParseMessageData(sut.createMessage(t));
}
CHECK_MESSAGE(cbEvents.front() == "0: true type1Id#Rem type1Name sGroup", cbEvents.front());
cbEvents.pop();
{
Message::ReportType t;
t.setConfirmed(false);
t.setID("type2Id#Rem");
t.setFixed(true);
t.setServerRemote(true);
t.setServerProjectIndepended(true);
t.setGroupName("sGroup");
t.setName("type2Name");
t.setDelay(100);
t.setDuration(200);
sut.checkParseMessageData(sut.createMessage(t));
}
CHECK_MESSAGE(cbEvents.front() == "0: true type2Id#Rem type2Name sGroup", cbEvents.front());
cbEvents.pop();
{
Message::ReportType t;
t.setConfirmed(false);
t.setID("type1Id#Rem");
t.setFixed(true);
t.setServerRemote(true);
t.setServerProjectIndepended(true);
t.setGroupName("sGroup");
t.setName("type1Name");
t.setDelay(200);
t.setDuration(300);
sut.checkParseMessageData(sut.createMessage(t));
}
CHECK_MESSAGE(cbEvents.front() == "1: true type1Id#Rem type1Name sGroup", cbEvents.front());
cbEvents.pop();
CHECK(cbEvents.empty());
// Local Objects
{
Message::ReportType t;
t.setConfirmed(false);
t.setID("type1Id#Loc");
t.setFixed(true);
t.setServerRemote(false);
t.setServerProjectIndepended(true);
t.setGroupName("sGroup");
t.setName("type1Name");
t.setDelay(100);
t.setDuration(200);
sut.checkParseMessageData(sut.createMessage(t));
}
CHECK_MESSAGE(cbEvents.front() == "veto check false/false", cbEvents.front());
cbEvents.pop();
CHECK_MESSAGE(cbEvents.front() == "0: false type1Id#Loc type1Name sGroup", cbEvents.front());
cbEvents.pop();
{
Message::ReportType t;
t.setConfirmed(false);
t.setID("type2Id#Loc");
t.setFixed(true);
t.setServerRemote(false);
t.setServerProjectIndepended(true);
t.setGroupName("sGroup");
t.setName("type2Name");
t.setDelay(100);
t.setDuration(200);
sut.checkParseMessageData(sut.createMessage(t));
}
CHECK_MESSAGE(cbEvents.front() == "veto check false/false", cbEvents.front());
cbEvents.pop();
CHECK_MESSAGE(cbEvents.front() == "0: false type2Id#Loc type2Name sGroup", cbEvents.front());
cbEvents.pop();
{
Message::ReportType t;
t.setConfirmed(false);
t.setID("type1Id#Loc");
t.setFixed(true);
t.setServerRemote(false);
t.setServerProjectIndepended(true);
t.setGroupName("sGroup");
t.setName("type1Name");
t.setDelay(200);
t.setDuration(300);
sut.checkParseMessageData(sut.createMessage(t));
}
CHECK_MESSAGE(cbEvents.front() == "1: false type1Id#Loc type1Name sGroup", cbEvents.front());
cbEvents.pop();
CHECK(cbEvents.empty());
}
TEST_CASE_FIXTURE(SpitClientFix, "Message Counter") {
Message::ReportType t1;
t1.setID("type#1");
t1.setServerRemote(true);
auto m1 = sut.createMessage(t1);
Message::ReportType t2;
t2.setID("type#2");
t2.setServerRemote(true);
auto m2 = sut.createMessage(t2);
Message::ReportType t3;
t3.setID("type#3");
t3.setServerRemote(true);
auto m3 = sut.createMessage(t3);
Message::ReportType t4;
t4.setID("type#4");
t4.setServerRemote(true);
auto m4 = sut.createMessage(t4);
Message::ReportType t5;
t5.setID("type#5");
t5.setServerRemote(true);
auto m5 = sut.createMessage(t5);
Message::ReportType t6;
t6.setID("type#6");
t6.setServerRemote(true);
auto m6 = sut.createMessage(t6);
CHECK(cbEvents.empty());
SUBCASE("Normal order") {
REQUIRE(cbEvents.size() == 0);
sut.checkParseMessageData(m1);
CHECK_MESSAGE(cbEvents.front() == "0: true type#1 ", cbEvents.front());
cbEvents.pop();
CHECK(cbEvents.empty());
sut.checkParseMessageData(m2);
CHECK_MESSAGE(cbEvents.front() == "0: true type#2 ", cbEvents.front());
cbEvents.pop();
CHECK(cbEvents.empty());
sut.checkParseMessageData(m3);
CHECK_MESSAGE(cbEvents.front() == "0: true type#3 ", cbEvents.front());
cbEvents.pop();
CHECK(cbEvents.empty());
sut.checkParseMessageData(m4);
CHECK_MESSAGE(cbEvents.front() == "0: true type#4 ", cbEvents.front());
cbEvents.pop();
CHECK(cbEvents.empty());
}
CHECK(cbEvents.empty());
SUBCASE("overtakes one") {
REQUIRE(cbEvents.size() == 0);
sut.checkParseMessageData(m1);
CHECK_MESSAGE(cbEvents.front() == "0: true type#1 ", cbEvents.front());
cbEvents.pop();
CHECK(cbEvents.empty());
sut.checkParseMessageData(m3);
CHECK(cbEvents.empty());
sut.checkParseMessageData(m2);
CHECK_MESSAGE(cbEvents.front() == "0: true type#2 ", cbEvents.front());
cbEvents.pop();
CHECK_MESSAGE(cbEvents.front() == "0: true type#3 ", cbEvents.front());
cbEvents.pop();
CHECK(cbEvents.empty());
sut.checkParseMessageData(m4);
CHECK_MESSAGE(cbEvents.front() == "0: true type#4 ", cbEvents.front());
cbEvents.pop();
CHECK(cbEvents.empty());
}
CHECK(cbEvents.empty());
SUBCASE("overtakes two") {
REQUIRE(cbEvents.size() == 0);
sut.checkParseMessageData(m1);
CHECK_MESSAGE(cbEvents.front() == "0: true type#1 ", cbEvents.front());
cbEvents.pop();
CHECK(cbEvents.empty());
sut.checkParseMessageData(m4);
CHECK(cbEvents.empty());
sut.checkParseMessageData(m2);
CHECK_MESSAGE(cbEvents.front() == "0: true type#2 ", cbEvents.front());
cbEvents.pop();
CHECK(cbEvents.empty());
sut.checkParseMessageData(m3);
CHECK_MESSAGE(cbEvents.front() == "0: true type#3 ", cbEvents.front());
cbEvents.pop();
CHECK_MESSAGE(cbEvents.front() == "0: true type#4 ", cbEvents.front());
cbEvents.pop();
CHECK(cbEvents.empty());
sut.checkParseMessageData(m5);
CHECK_MESSAGE(cbEvents.front() == "0: true type#5 ", cbEvents.front());
cbEvents.pop();
CHECK(cbEvents.empty());
}
CHECK(cbEvents.empty());
SUBCASE("many overtakes") {
REQUIRE(cbEvents.size() == 0);
sut.checkParseMessageData(m1);
CHECK_MESSAGE(cbEvents.front() == "0: true type#1 ", cbEvents.front());
cbEvents.pop();
CHECK(cbEvents.empty());
sut.checkParseMessageData(m5);
CHECK(cbEvents.empty());
sut.checkParseMessageData(m6);
CHECK(cbEvents.empty());
sut.checkParseMessageData(m4);
CHECK(cbEvents.empty());
sut.checkParseMessageData(m3);
CHECK(cbEvents.empty());
sut.checkParseMessageData(m2);
CHECK_MESSAGE(cbEvents.front() == "0: true type#2 ", cbEvents.front());
cbEvents.pop();
CHECK_MESSAGE(cbEvents.front() == "0: true type#3 ", cbEvents.front());
cbEvents.pop();
CHECK_MESSAGE(cbEvents.front() == "0: true type#4 ", cbEvents.front());
cbEvents.pop();
CHECK_MESSAGE(cbEvents.front() == "0: true type#5 ", cbEvents.front());
cbEvents.pop();
CHECK_MESSAGE(cbEvents.front() == "0: true type#6 ", cbEvents.front());
cbEvents.pop();
CHECK(cbEvents.empty());
}
CHECK(cbEvents.empty());
SUBCASE("reverse order") {
REQUIRE(cbEvents.size() == 0);
sut.checkParseMessageData(m1);
CHECK_MESSAGE(cbEvents.front() == "0: true type#1 ", cbEvents.front());
cbEvents.pop();
CHECK(cbEvents.empty());
sut.checkParseMessageData(m6);
CHECK(cbEvents.empty());
sut.checkParseMessageData(m5);
CHECK(cbEvents.empty());
sut.checkParseMessageData(m4);
CHECK(cbEvents.empty());
sut.checkParseMessageData(m3);
CHECK(cbEvents.empty());
sut.checkParseMessageData(m2);
CHECK_MESSAGE(cbEvents.front() == "0: true type#2 ", cbEvents.front());
cbEvents.pop();
CHECK_MESSAGE(cbEvents.front() == "0: true type#3 ", cbEvents.front());
cbEvents.pop();
CHECK_MESSAGE(cbEvents.front() == "0: true type#4 ", cbEvents.front());
cbEvents.pop();
CHECK_MESSAGE(cbEvents.front() == "0: true type#5 ", cbEvents.front());
cbEvents.pop();
CHECK_MESSAGE(cbEvents.front() == "0: true type#6 ", cbEvents.front());
cbEvents.pop();
CHECK(cbEvents.empty());
}
}
@@ -0,0 +1,516 @@
/**
* @file spitRepoTest.cpp
* @author Gustice
* @brief
*/
#include "doctest/doctest.h"
#include <fmt/core.h>
#include <iostream>
#include <queue>
#include "spit_processor.hpp"
using namespace spit;
using namespace std;
static queue<string> cbEvents;
class ClientMockCmplx {
public:
void onServerEvent(SProcessor::ServerEvent event, const SadDetectedServer &detectedServer) {
}
void onConnectionStatus(string serverID, string serverComputerName, string serverName, int serverStepTime, SProcessor::Connection connectionStatus, SProcessor::Connection oldConnectionStatus) {
}
void onProjectUpdate(SProcessor::ProjUpdate state, string projectId) {
}
void onSpitType(SProcessor::ObjectEvent event, bool isRemote, const SType &sType) {
cbEvents.push(fmt::format("{}: {} {} {} {}", (int)event, isRemote, sType.getID(), sType.Name.get(), sType.GroupName.get()));
}
void onSpitObject(SProcessor::ObjectEvent event, bool isRemote, const SType &sType, const SObject &sObject) {
cbEvents.push(fmt::format("{}: {} {} {} {} {}", (int)event, isRemote, sType.getID(), sType.Name.get(), sObject.Name.get(), sObject.getID()));
}
void onPlay(string playerID, string playerName, char playerType, bool playerIsRunning, uint64_t timeFrames) {
}
void onPlaySpitType(string playerID, string spitTypeID, bool playerIsRunning, bool started, bool ended, double value) {
}
void onPlayRemoteObjectStatus(const SType &sType, const SObject &sObject, uint64_t p_FrameInsideObject, int value) {
}
bool onCheckSpitTypeVeto(bool knownType, bool createdByClient) {
cbEvents.push(fmt::format("veto check {}/{}", knownType, createdByClient));
return true;
}
bool onCheckSpitObjectVeto(bool knownType) {
return true;
}
};
class SpitClientCmplsFix {
protected:
ClientMockCmplx client;
SProcessor::Callbacks callbacks{
.onServerEvent = [&](SProcessor::ServerEvent event, const SadDetectedServer &detectedServer) { client.onServerEvent(event, detectedServer); },
.onConnectionStatus = [&](string serverID, string serverComputerName, string serverName, int serverStepTime, SProcessor::Connection connectionStatus, SProcessor::Connection oldConnectionStatus) { client.onConnectionStatus(serverID, serverComputerName, serverName, serverStepTime, connectionStatus, oldConnectionStatus); },
.onProjectUpdate = [&](SProcessor::ProjUpdate state, string projectId) { client.onProjectUpdate(state, projectId); },
.onSpitType = [&](SProcessor::ObjectEvent event, bool isRemote, const SType &t) { client.onSpitType(event, isRemote, t); },
.onSpitObject = [&](SProcessor::ObjectEvent event, bool isRemote, const SType &t, const SObject &o) { client.onSpitObject(event, isRemote, t, o); },
.onPlay = [&](string playerID, string playerName, char playerType, bool playerIsRunning, uint64_t timeFrames) { client.onPlay(playerID, playerName, playerType, playerIsRunning, timeFrames); },
.onPlaySpitType = [&](string playerID, string spitTypeID, bool playerIsRunning, bool started, bool ended, double value) { client.onPlaySpitType(playerID, spitTypeID, playerIsRunning, started, ended, value); },
.onPlayRemoteObjectStatus = [&](const SType &t, const SObject &o, uint64_t p_FrameInsideObject, int value) { client.onPlayRemoteObjectStatus(t, o, p_FrameInsideObject, value); },
.onCheckSpitTypeVeto = [&](bool knownType, bool createdByClient) { return client.onCheckSpitTypeVeto(knownType, createdByClient); },
.onCheckSpitObjectVeto = [&](bool knownType) { return client.onCheckSpitObjectVeto(knownType); }};
SProcessor::InternalCallbacks intCallbacks {
.delay = [&](int ms){ return ; },
.sendMessage = [&](Message::OPPart &msg){ return 0; },
.parseMessage = [&](Message &msg) { },
};
SProcessor sut{"sut", "sutProj", "dev", "##GUID##", callbacks, intCallbacks};
};
static void spoofMsgCounter(std::vector<uint8_t> &msg, uint8_t cnt) {
msg.at(11) = cnt;
}
TEST_CASE_FIXTURE(SpitClientCmplsFix, "Spit types Raw") {
int cnt = 1;
{
std::vector<uint8_t> msg = {
/*0000*/ 0x23, 0x73, 0x70, 0x69, 0x74, 0x23, 0x00, 0x01, 0x00, 0x00, 0x00, 0x41, 0x63, 0x00, 0x00, 0x00, // #spit#.....Ac...
/*0010*/ 0x34, 0x30, 0x35, 0x63, 0x37, 0x31, 0x30, 0x30, 0x2d, 0x36, 0x34, 0x65, 0x38, 0x2d, 0x34, 0x33, // 405c7100-64e8-43
/*0020*/ 0x35, 0x64, 0x2d, 0x38, 0x61, 0x37, 0x62, 0x2d, 0x30, 0x30, 0x63, 0x35, 0x36, 0x32, 0x39, 0x66, // 5d-8a7b-00c5629f
/*0030*/ 0x39, 0x63, 0x63, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x30, 0x38, 0x36, 0x36, 0x63, 0x39, // 9ccf.....70866c9
/*0040*/ 0x62, 0x2d, 0x61, 0x66, 0x31, 0x63, 0x2d, 0x34, 0x37, 0x36, 0x35, 0x2d, 0x61, 0x31, 0x37, 0x65, // b-af1c-4765-a17e
/*0050*/ 0x2d, 0x38, 0x62, 0x34, 0x35, 0x63, 0x39, 0x33, 0x66, 0x33, 0x61, 0x63, 0x61, 0x00, 0x00, 0x00, // -8b45c93f3aca...
/*0060*/ 0x00, 0x00, 0x15, 0xa8, 0x00, 0x00, 0x00, 0x01, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x38, 0x75, // ........00000m8u
/*0070*/ 0x65, 0x64, 0x79, 0x79, 0x74, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // edyyt00000000000
/*0080*/ 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x37, 0x30, 0x76, 0x61, 0x38, 0x62, 0x6f, 0x6d, 0x00, 0x00, // 00000m70va8bom..
/*0090*/ 0x00, 0x01, 0x01, 0x00, 0x4d, 0x61, 0x69, 0x6e, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x00, 0x6c, 0x00, // ....MainScene.l.
/*00a0*/ 0x00, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .ement..........
/*00b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x50, 0x6f, 0x73, // ...........InPos
/*00c0*/ 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x00, 0x63, 0x65, 0x6e, 0x65, // ition1Scene.cene
/*00d0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00e0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00f0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0100*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // ...........
};
spoofMsgCounter(msg, cnt++);
sut.checkParseMessageData(std::vector<char>(msg.begin(), msg.end()));
CHECK_MESSAGE(cbEvents.front() == "0: true 00000m8uedyyt0000000000000000m70va8bom InPosition1Scene MainScene", cbEvents.front());
cbEvents.pop();
}
{
std::vector<uint8_t> msg = {
/*0000*/ 0x23, 0x73, 0x70, 0x69, 0x74, 0x23, 0x00, 0x01, 0x00, 0x00, 0x00, 0x42, 0x63, 0x00, 0x00, 0x00, // #spit#.....Bc...
/*0010*/ 0x34, 0x30, 0x35, 0x63, 0x37, 0x31, 0x30, 0x30, 0x2d, 0x36, 0x34, 0x65, 0x38, 0x2d, 0x34, 0x33, // 405c7100-64e8-43
/*0020*/ 0x35, 0x64, 0x2d, 0x38, 0x61, 0x37, 0x62, 0x2d, 0x30, 0x30, 0x63, 0x35, 0x36, 0x32, 0x39, 0x66, // 5d-8a7b-00c5629f
/*0030*/ 0x39, 0x63, 0x63, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x30, 0x38, 0x36, 0x36, 0x63, 0x39, // 9ccf.....70866c9
/*0040*/ 0x62, 0x2d, 0x61, 0x66, 0x31, 0x63, 0x2d, 0x34, 0x37, 0x36, 0x35, 0x2d, 0x61, 0x31, 0x37, 0x65, // b-af1c-4765-a17e
/*0050*/ 0x2d, 0x38, 0x62, 0x34, 0x35, 0x63, 0x39, 0x33, 0x66, 0x33, 0x61, 0x63, 0x61, 0x00, 0x00, 0x00, // -8b45c93f3aca...
/*0060*/ 0x00, 0x00, 0x15, 0xa8, 0x00, 0x00, 0x00, 0x01, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x38, 0x75, // ........00000m8u
/*0070*/ 0x65, 0x64, 0x79, 0x79, 0x74, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // edyyt00000000000
/*0080*/ 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x68, 0x30, 0x76, 0x61, 0x38, 0x62, 0x6f, 0x6d, 0x00, 0x00, // 00000mh0va8bom..
/*0090*/ 0x00, 0x01, 0x01, 0x00, 0x4d, 0x61, 0x69, 0x6e, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x00, 0x6c, 0x00, // ....MainScene.l.
/*00a0*/ 0x00, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .ement..........
/*00b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x50, 0x6f, 0x73, // ...........InPos
/*00c0*/ 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x00, 0x63, 0x65, 0x6e, 0x65, // ition2Scene.cene
/*00d0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00e0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00f0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0100*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // ...........
};
spoofMsgCounter(msg, cnt++);
sut.checkParseMessageData(std::vector<char>(msg.begin(), msg.end()));
CHECK_MESSAGE(cbEvents.front() == "0: true 00000m8uedyyt0000000000000000mh0va8bom InPosition2Scene MainScene", cbEvents.front());
cbEvents.pop();
}
{
std::vector<uint8_t> msg = {
/*0000*/ 0x23, 0x73, 0x70, 0x69, 0x74, 0x23, 0x00, 0x01, 0x00, 0x00, 0x00, 0x43, 0x63, 0x00, 0x00, 0x00, // #spit#.....Cc...
/*0010*/ 0x34, 0x30, 0x35, 0x63, 0x37, 0x31, 0x30, 0x30, 0x2d, 0x36, 0x34, 0x65, 0x38, 0x2d, 0x34, 0x33, // 405c7100-64e8-43
/*0020*/ 0x35, 0x64, 0x2d, 0x38, 0x61, 0x37, 0x62, 0x2d, 0x30, 0x30, 0x63, 0x35, 0x36, 0x32, 0x39, 0x66, // 5d-8a7b-00c5629f
/*0030*/ 0x39, 0x63, 0x63, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x30, 0x38, 0x36, 0x36, 0x63, 0x39, // 9ccf.....70866c9
/*0040*/ 0x62, 0x2d, 0x61, 0x66, 0x31, 0x63, 0x2d, 0x34, 0x37, 0x36, 0x35, 0x2d, 0x61, 0x31, 0x37, 0x65, // b-af1c-4765-a17e
/*0050*/ 0x2d, 0x38, 0x62, 0x34, 0x35, 0x63, 0x39, 0x33, 0x66, 0x33, 0x61, 0x63, 0x61, 0x00, 0x00, 0x00, // -8b45c93f3aca...
/*0060*/ 0x00, 0x00, 0x15, 0xa8, 0x00, 0x00, 0x00, 0x01, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x38, 0x75, // ........00000m8u
/*0070*/ 0x65, 0x64, 0x79, 0x79, 0x74, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // edyyt00000000000
/*0080*/ 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x72, 0x30, 0x76, 0x61, 0x38, 0x62, 0x6f, 0x6d, 0x00, 0x00, // 00000mr0va8bom..
/*0090*/ 0x00, 0x01, 0x01, 0x00, 0x4d, 0x61, 0x69, 0x6e, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x00, 0x6c, 0x00, // ....MainScene.l.
/*00a0*/ 0x00, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .ement..........
/*00b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x50, 0x6f, 0x73, // ...........InPos
/*00c0*/ 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x33, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x00, 0x63, 0x65, 0x6e, 0x65, // ition3Scene.cene
/*00d0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00e0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00f0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0100*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // ...........
};
spoofMsgCounter(msg, cnt++);
sut.checkParseMessageData(std::vector<char>(msg.begin(), msg.end()));
CHECK_MESSAGE(cbEvents.front() == "0: true 00000m8uedyyt0000000000000000mr0va8bom InPosition3Scene MainScene", cbEvents.front());
cbEvents.pop();
}
{
std::vector<uint8_t> msg = {
/*0000*/ 0x23, 0x73, 0x70, 0x69, 0x74, 0x23, 0x00, 0x01, 0x00, 0x00, 0x00, 0x44, 0x63, 0x00, 0x00, 0x00, // #spit#.....Dc...
/*0010*/ 0x34, 0x30, 0x35, 0x63, 0x37, 0x31, 0x30, 0x30, 0x2d, 0x36, 0x34, 0x65, 0x38, 0x2d, 0x34, 0x33, // 405c7100-64e8-43
/*0020*/ 0x35, 0x64, 0x2d, 0x38, 0x61, 0x37, 0x62, 0x2d, 0x30, 0x30, 0x63, 0x35, 0x36, 0x32, 0x39, 0x66, // 5d-8a7b-00c5629f
/*0030*/ 0x39, 0x63, 0x63, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x30, 0x38, 0x36, 0x36, 0x63, 0x39, // 9ccf.....70866c9
/*0040*/ 0x62, 0x2d, 0x61, 0x66, 0x31, 0x63, 0x2d, 0x34, 0x37, 0x36, 0x35, 0x2d, 0x61, 0x31, 0x37, 0x65, // b-af1c-4765-a17e
/*0050*/ 0x2d, 0x38, 0x62, 0x34, 0x35, 0x63, 0x39, 0x33, 0x66, 0x33, 0x61, 0x63, 0x61, 0x00, 0x00, 0x00, // -8b45c93f3aca...
/*0060*/ 0x00, 0x00, 0x15, 0xa8, 0x00, 0x00, 0x00, 0x01, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x38, 0x75, // ........00000m8u
/*0070*/ 0x65, 0x64, 0x79, 0x79, 0x74, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // edyyt00000000000
/*0080*/ 0x30, 0x30, 0x30, 0x30, 0x30, 0x6e, 0x6b, 0x30, 0x76, 0x61, 0x38, 0x62, 0x6f, 0x6d, 0x00, 0x00, // 00000nk0va8bom..
/*0090*/ 0x00, 0x01, 0x01, 0x00, 0x4d, 0x61, 0x69, 0x6e, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x00, 0x6c, 0x00, // ....MainScene.l.
/*00a0*/ 0x00, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .ement..........
/*00b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x50, 0x6f, 0x73, // ...........InPos
/*00c0*/ 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x34, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x00, 0x63, 0x65, 0x6e, 0x65, // ition4Scene.cene
/*00d0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00e0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00f0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0100*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // ...........
};
spoofMsgCounter(msg, cnt++);
sut.checkParseMessageData(std::vector<char>(msg.begin(), msg.end()));
CHECK_MESSAGE(cbEvents.front() == "0: true 00000m8uedyyt0000000000000000nk0va8bom InPosition4Scene MainScene", cbEvents.front());
cbEvents.pop();
}
{
std::vector<uint8_t> msg = {
/*0000*/ 0x23, 0x73, 0x70, 0x69, 0x74, 0x23, 0x00, 0x01, 0x00, 0x00, 0x00, 0x45, 0x63, 0x00, 0x00, 0x00, // #spit#.....Ec...
/*0010*/ 0x34, 0x30, 0x35, 0x63, 0x37, 0x31, 0x30, 0x30, 0x2d, 0x36, 0x34, 0x65, 0x38, 0x2d, 0x34, 0x33, // 405c7100-64e8-43
/*0020*/ 0x35, 0x64, 0x2d, 0x38, 0x61, 0x37, 0x62, 0x2d, 0x30, 0x30, 0x63, 0x35, 0x36, 0x32, 0x39, 0x66, // 5d-8a7b-00c5629f
/*0030*/ 0x39, 0x63, 0x63, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x30, 0x38, 0x36, 0x36, 0x63, 0x39, // 9ccf.....70866c9
/*0040*/ 0x62, 0x2d, 0x61, 0x66, 0x31, 0x63, 0x2d, 0x34, 0x37, 0x36, 0x35, 0x2d, 0x61, 0x31, 0x37, 0x65, // b-af1c-4765-a17e
/*0050*/ 0x2d, 0x38, 0x62, 0x34, 0x35, 0x63, 0x39, 0x33, 0x66, 0x33, 0x61, 0x63, 0x61, 0x00, 0x00, 0x00, // -8b45c93f3aca...
/*0060*/ 0x00, 0x00, 0x15, 0xa8, 0x00, 0x00, 0x00, 0x01, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x38, 0x75, // ........00000m8u
/*0070*/ 0x65, 0x64, 0x79, 0x79, 0x74, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // edyyt00000000000
/*0080*/ 0x30, 0x30, 0x30, 0x30, 0x30, 0x6e, 0x75, 0x30, 0x76, 0x61, 0x38, 0x62, 0x6f, 0x6d, 0x00, 0x00, // 00000nu0va8bom..
/*0090*/ 0x00, 0x01, 0x01, 0x00, 0x4d, 0x61, 0x69, 0x6e, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x00, 0x6c, 0x00, // ....MainScene.l.
/*00a0*/ 0x00, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .ement..........
/*00b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x50, 0x6f, 0x73, // ...........InPos
/*00c0*/ 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x35, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x00, 0x63, 0x65, 0x6e, 0x65, // ition5Scene.cene
/*00d0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00e0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00f0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0100*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // ...........
};
spoofMsgCounter(msg, cnt++);
sut.checkParseMessageData(std::vector<char>(msg.begin(), msg.end()));
CHECK_MESSAGE(cbEvents.front() == "0: true 00000m8uedyyt0000000000000000nu0va8bom InPosition5Scene MainScene", cbEvents.front());
cbEvents.pop();
}
{
std::vector<uint8_t> msg = {
/*0000*/ 0x23, 0x73, 0x70, 0x69, 0x74, 0x23, 0x00, 0x01, 0x00, 0x00, 0x00, 0x46, 0x63, 0x00, 0x00, 0x00, // #spit#.....Fc...
/*0010*/ 0x34, 0x30, 0x35, 0x63, 0x37, 0x31, 0x30, 0x30, 0x2d, 0x36, 0x34, 0x65, 0x38, 0x2d, 0x34, 0x33, // 405c7100-64e8-43
/*0020*/ 0x35, 0x64, 0x2d, 0x38, 0x61, 0x37, 0x62, 0x2d, 0x30, 0x30, 0x63, 0x35, 0x36, 0x32, 0x39, 0x66, // 5d-8a7b-00c5629f
/*0030*/ 0x39, 0x63, 0x63, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x30, 0x38, 0x36, 0x36, 0x63, 0x39, // 9ccf.....70866c9
/*0040*/ 0x62, 0x2d, 0x61, 0x66, 0x31, 0x63, 0x2d, 0x34, 0x37, 0x36, 0x35, 0x2d, 0x61, 0x31, 0x37, 0x65, // b-af1c-4765-a17e
/*0050*/ 0x2d, 0x38, 0x62, 0x34, 0x35, 0x63, 0x39, 0x33, 0x66, 0x33, 0x61, 0x63, 0x61, 0x00, 0x00, 0x00, // -8b45c93f3aca...
/*0060*/ 0x00, 0x00, 0x15, 0xa8, 0x00, 0x00, 0x00, 0x01, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x38, 0x75, // ........00000m8u
/*0070*/ 0x65, 0x64, 0x79, 0x79, 0x74, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // edyyt00000000000
/*0080*/ 0x30, 0x30, 0x30, 0x30, 0x30, 0x6f, 0x34, 0x30, 0x76, 0x61, 0x38, 0x62, 0x6f, 0x6d, 0x00, 0x00, // 00000o40va8bom..
/*0090*/ 0x00, 0x01, 0x01, 0x00, 0x4d, 0x61, 0x69, 0x6e, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x00, 0x6c, 0x00, // ....MainScene.l.
/*00a0*/ 0x00, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .ement..........
/*00b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x50, 0x6f, 0x73, // ...........InPos
/*00c0*/ 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x36, 0x53, 0x63, 0x65, 0x6e, 0x65, 0x00, 0x63, 0x65, 0x6e, 0x65, // ition6Scene.cene
/*00d0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00e0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00f0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0100*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // ...........
};
spoofMsgCounter(msg, cnt++);
sut.checkParseMessageData(std::vector<char>(msg.begin(), msg.end()));
CHECK_MESSAGE(cbEvents.front() == "0: true 00000m8uedyyt0000000000000000o40va8bom InPosition6Scene MainScene", cbEvents.front());
cbEvents.pop();
}
CHECK(cbEvents.empty());
{ // In InPosition1Scene
std::vector<uint8_t> msg = {
/*0000*/ 0x23, 0x73, 0x70, 0x69, 0x74, 0x23, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7b, 0x63, 0x00, 0x00, 0x00, // #spit#.....{c...
/*0010*/ 0x34, 0x30, 0x35, 0x63, 0x37, 0x31, 0x30, 0x30, 0x2d, 0x36, 0x34, 0x65, 0x38, 0x2d, 0x34, 0x33, // 405c7100-64e8-43
/*0020*/ 0x35, 0x64, 0x2d, 0x38, 0x61, 0x37, 0x62, 0x2d, 0x30, 0x30, 0x63, 0x35, 0x36, 0x32, 0x39, 0x66, // 5d-8a7b-00c5629f
/*0030*/ 0x39, 0x63, 0x63, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x30, 0x38, 0x36, 0x36, 0x63, 0x39, // 9ccf.....70866c9
/*0040*/ 0x62, 0x2d, 0x61, 0x66, 0x31, 0x63, 0x2d, 0x34, 0x37, 0x36, 0x35, 0x2d, 0x61, 0x31, 0x37, 0x65, // b-af1c-4765-a17e
/*0050*/ 0x2d, 0x38, 0x62, 0x34, 0x35, 0x63, 0x39, 0x33, 0x66, 0x33, 0x61, 0x63, 0x61, 0x00, 0x00, 0x00, // -8b45c93f3aca...
/*0060*/ 0x00, 0x00, 0x1f, 0x53, 0x01, 0x00, 0x00, 0x01, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x38, 0x75, // ...S....00000m8u
/*0070*/ 0x65, 0x64, 0x79, 0x79, 0x74, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // edyyt00000000000
/*0080*/ 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x37, 0x30, 0x76, 0x61, 0x38, 0x62, 0x6f, 0x6d, 0x00, 0x00, // 00000m70va8bom..
/*0090*/ 0x00, 0x01, 0x01, 0x70, 0x6c, 0x61, 0x79, 0x00, 0x2f, 0x20, 0x46, 0x61, 0x64, 0x65, 0x20, 0x4f, // ...play./ Fade O
/*00a0*/ 0x75, 0x74, 0x00, 0x63, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ut.cy...........
/*00b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00c0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00d0*/ 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x38, 0x75, 0x65, 0x64, 0x79, 0x79, 0x74, // ...00000m8uedyyt
/*00e0*/ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // 0000000000000000
/*00f0*/ 0x6d, 0x37, 0x30, 0x76, 0x61, 0x38, 0x62, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x69, 0x70, 0x00, // m70va8bom....ip.
/*0100*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0110*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0120*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0130*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0xb9, 0x04, 0x00, // ................
/*0140*/ 0x00, 0x00, 0x00, 0x00, 0xed, 0x35, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .....5..........
/*0150*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x42, 0x0f, 0x00, // ............@B..
/*0160*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0170*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0180*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0190*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*01a0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*01b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // ......
};
spoofMsgCounter(msg, cnt++);
sut.checkParseMessageData(std::vector<char>(msg.begin(), msg.end()));
CHECK_MESSAGE(cbEvents.front() == "0: true 00000m8uedyyt0000000000000000m70va8bom InPosition1Scene play 00000m8uedyyt0000000000000000m70va8bom", cbEvents.front());
cbEvents.pop();
}
{ // In InPosition2Scene
std::vector<uint8_t> msg = {
/*0000*/ 0x23, 0x73, 0x70, 0x69, 0x74, 0x23, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7d, 0x63, 0x00, 0x00, 0x00, // #spit#.....}c...
/*0010*/ 0x34, 0x30, 0x35, 0x63, 0x37, 0x31, 0x30, 0x30, 0x2d, 0x36, 0x34, 0x65, 0x38, 0x2d, 0x34, 0x33, // 405c7100-64e8-43
/*0020*/ 0x35, 0x64, 0x2d, 0x38, 0x61, 0x37, 0x62, 0x2d, 0x30, 0x30, 0x63, 0x35, 0x36, 0x32, 0x39, 0x66, // 5d-8a7b-00c5629f
/*0030*/ 0x39, 0x63, 0x63, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x30, 0x38, 0x36, 0x36, 0x63, 0x39, // 9ccf.....70866c9
/*0040*/ 0x62, 0x2d, 0x61, 0x66, 0x31, 0x63, 0x2d, 0x34, 0x37, 0x36, 0x35, 0x2d, 0x61, 0x31, 0x37, 0x65, // b-af1c-4765-a17e
/*0050*/ 0x2d, 0x38, 0x62, 0x34, 0x35, 0x63, 0x39, 0x33, 0x66, 0x33, 0x61, 0x63, 0x61, 0x00, 0x00, 0x00, // -8b45c93f3aca...
/*0060*/ 0x00, 0x00, 0x1f, 0x53, 0x01, 0x00, 0x00, 0x01, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x38, 0x75, // ...S....00000m8u
/*0070*/ 0x65, 0x64, 0x79, 0x79, 0x74, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // edyyt00000000000
/*0080*/ 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x68, 0x30, 0x76, 0x61, 0x38, 0x62, 0x6f, 0x6d, 0x00, 0x00, // 00000mh0va8bom..
/*0090*/ 0x00, 0x01, 0x01, 0x70, 0x6c, 0x61, 0x79, 0x00, 0x2f, 0x20, 0x46, 0x61, 0x64, 0x65, 0x20, 0x4f, // ...play./ Fade O
/*00a0*/ 0x75, 0x74, 0x00, 0x63, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ut.cy...........
/*00b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00c0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00d0*/ 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x38, 0x75, 0x65, 0x64, 0x79, 0x79, 0x74, // ...00000m8uedyyt
/*00e0*/ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // 0000000000000000
/*00f0*/ 0x6d, 0x68, 0x30, 0x76, 0x61, 0x38, 0x62, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x69, 0x70, 0x00, // mh0va8bom....ip.
/*0100*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0110*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0120*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0130*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0xef, 0x06, 0x00, // ............r...
/*0140*/ 0x00, 0x00, 0x00, 0x00, 0x26, 0x2d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ....&-..........
/*0150*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x42, 0x0f, 0x00, // ............@B..
/*0160*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0170*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0180*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0190*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*01a0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*01b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // ......
};
spoofMsgCounter(msg, cnt++);
sut.checkParseMessageData(std::vector<char>(msg.begin(), msg.end()));
CHECK_MESSAGE(cbEvents.front() == "0: true 00000m8uedyyt0000000000000000mh0va8bom InPosition2Scene play 00000m8uedyyt0000000000000000mh0va8bom", cbEvents.front());
cbEvents.pop();
}
{ // In InPosition3Scene
std::vector<uint8_t> msg = {
/*0000*/ 0x23, 0x73, 0x70, 0x69, 0x74, 0x23, 0x00, 0x01, 0x00, 0x00, 0x00, 0x7f, 0x63, 0x00, 0x00, 0x00, // #spit#......c...
/*0010*/ 0x34, 0x30, 0x35, 0x63, 0x37, 0x31, 0x30, 0x30, 0x2d, 0x36, 0x34, 0x65, 0x38, 0x2d, 0x34, 0x33, // 405c7100-64e8-43
/*0020*/ 0x35, 0x64, 0x2d, 0x38, 0x61, 0x37, 0x62, 0x2d, 0x30, 0x30, 0x63, 0x35, 0x36, 0x32, 0x39, 0x66, // 5d-8a7b-00c5629f
/*0030*/ 0x39, 0x63, 0x63, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x30, 0x38, 0x36, 0x36, 0x63, 0x39, // 9ccf.....70866c9
/*0040*/ 0x62, 0x2d, 0x61, 0x66, 0x31, 0x63, 0x2d, 0x34, 0x37, 0x36, 0x35, 0x2d, 0x61, 0x31, 0x37, 0x65, // b-af1c-4765-a17e
/*0050*/ 0x2d, 0x38, 0x62, 0x34, 0x35, 0x63, 0x39, 0x33, 0x66, 0x33, 0x61, 0x63, 0x61, 0x00, 0x00, 0x00, // -8b45c93f3aca...
/*0060*/ 0x00, 0x00, 0x1f, 0x53, 0x01, 0x00, 0x00, 0x01, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x38, 0x75, // ...S....00000m8u
/*0070*/ 0x65, 0x64, 0x79, 0x79, 0x74, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // edyyt00000000000
/*0080*/ 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x72, 0x30, 0x76, 0x61, 0x38, 0x62, 0x6f, 0x6d, 0x00, 0x00, // 00000mr0va8bom..
/*0090*/ 0x00, 0x01, 0x01, 0x70, 0x6c, 0x61, 0x79, 0x00, 0x2f, 0x20, 0x46, 0x61, 0x64, 0x65, 0x20, 0x4f, // ...play./ Fade O
/*00a0*/ 0x75, 0x74, 0x00, 0x63, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ut.cy...........
/*00b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00c0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00d0*/ 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x38, 0x75, 0x65, 0x64, 0x79, 0x79, 0x74, // ...00000m8uedyyt
/*00e0*/ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // 0000000000000000
/*00f0*/ 0x6d, 0x72, 0x30, 0x76, 0x61, 0x38, 0x62, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x69, 0x70, 0x00, // mr0va8bom....ip.
/*0100*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0110*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0120*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0130*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xef, 0xfc, 0x08, 0x00, // ................
/*0140*/ 0x00, 0x00, 0x00, 0x00, 0x52, 0x2e, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ....R...........
/*0150*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x42, 0x0f, 0x00, // ............@B..
/*0160*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0170*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0180*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0190*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*01a0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*01b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // ......
};
spoofMsgCounter(msg, cnt++);
sut.checkParseMessageData(std::vector<char>(msg.begin(), msg.end()));
CHECK_MESSAGE(cbEvents.front() == "0: true 00000m8uedyyt0000000000000000mr0va8bom InPosition3Scene play 00000m8uedyyt0000000000000000mr0va8bom", cbEvents.front());
cbEvents.pop();
}
{ // In InPosition4Scene
std::vector<uint8_t> msg = {
/*0000*/ 0x23, 0x73, 0x70, 0x69, 0x74, 0x23, 0x00, 0x01, 0x00, 0x00, 0x00, 0x81, 0x63, 0x00, 0x00, 0x00, // #spit#......c...
/*0010*/ 0x34, 0x30, 0x35, 0x63, 0x37, 0x31, 0x30, 0x30, 0x2d, 0x36, 0x34, 0x65, 0x38, 0x2d, 0x34, 0x33, // 405c7100-64e8-43
/*0020*/ 0x35, 0x64, 0x2d, 0x38, 0x61, 0x37, 0x62, 0x2d, 0x30, 0x30, 0x63, 0x35, 0x36, 0x32, 0x39, 0x66, // 5d-8a7b-00c5629f
/*0030*/ 0x39, 0x63, 0x63, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x30, 0x38, 0x36, 0x36, 0x63, 0x39, // 9ccf.....70866c9
/*0040*/ 0x62, 0x2d, 0x61, 0x66, 0x31, 0x63, 0x2d, 0x34, 0x37, 0x36, 0x35, 0x2d, 0x61, 0x31, 0x37, 0x65, // b-af1c-4765-a17e
/*0050*/ 0x2d, 0x38, 0x62, 0x34, 0x35, 0x63, 0x39, 0x33, 0x66, 0x33, 0x61, 0x63, 0x61, 0x00, 0x00, 0x00, // -8b45c93f3aca...
/*0060*/ 0x00, 0x00, 0x1f, 0x53, 0x01, 0x00, 0x00, 0x01, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x38, 0x75, // ...S....00000m8u
/*0070*/ 0x65, 0x64, 0x79, 0x79, 0x74, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // edyyt00000000000
/*0080*/ 0x30, 0x30, 0x30, 0x30, 0x30, 0x6e, 0x6b, 0x30, 0x76, 0x61, 0x38, 0x62, 0x6f, 0x6d, 0x00, 0x00, // 00000nk0va8bom..
/*0090*/ 0x00, 0x01, 0x01, 0x70, 0x6c, 0x61, 0x79, 0x00, 0x2f, 0x20, 0x46, 0x61, 0x64, 0x65, 0x20, 0x4f, // ...play./ Fade O
/*00a0*/ 0x75, 0x74, 0x00, 0x63, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ut.cy...........
/*00b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00c0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00d0*/ 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x38, 0x75, 0x65, 0x64, 0x79, 0x79, 0x74, // ...00000m8uedyyt
/*00e0*/ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // 0000000000000000
/*00f0*/ 0x6e, 0x6b, 0x30, 0x76, 0x61, 0x38, 0x62, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x69, 0x70, 0x00, // nk0va8bom....ip.
/*0100*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0110*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0120*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0130*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xff, 0x0a, 0x00, // ................
/*0140*/ 0x00, 0x00, 0x00, 0x00, 0x36, 0x33, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ....63..........
/*0150*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x42, 0x0f, 0x00, // ............@B..
/*0160*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0170*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0180*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0190*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*01a0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*01b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // ......
};
spoofMsgCounter(msg, cnt++);
sut.checkParseMessageData(std::vector<char>(msg.begin(), msg.end()));
CHECK_MESSAGE(cbEvents.front() == "0: true 00000m8uedyyt0000000000000000nk0va8bom InPosition4Scene play 00000m8uedyyt0000000000000000nk0va8bom", cbEvents.front());
cbEvents.pop();
}
{ // In InPosition5Scene
std::vector<uint8_t> msg = {
/*0000*/ 0x23, 0x73, 0x70, 0x69, 0x74, 0x23, 0x00, 0x01, 0x00, 0x00, 0x00, 0x83, 0x63, 0x00, 0x00, 0x00, // #spit#......c...
/*0010*/ 0x34, 0x30, 0x35, 0x63, 0x37, 0x31, 0x30, 0x30, 0x2d, 0x36, 0x34, 0x65, 0x38, 0x2d, 0x34, 0x33, // 405c7100-64e8-43
/*0020*/ 0x35, 0x64, 0x2d, 0x38, 0x61, 0x37, 0x62, 0x2d, 0x30, 0x30, 0x63, 0x35, 0x36, 0x32, 0x39, 0x66, // 5d-8a7b-00c5629f
/*0030*/ 0x39, 0x63, 0x63, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x30, 0x38, 0x36, 0x36, 0x63, 0x39, // 9ccf.....70866c9
/*0040*/ 0x62, 0x2d, 0x61, 0x66, 0x31, 0x63, 0x2d, 0x34, 0x37, 0x36, 0x35, 0x2d, 0x61, 0x31, 0x37, 0x65, // b-af1c-4765-a17e
/*0050*/ 0x2d, 0x38, 0x62, 0x34, 0x35, 0x63, 0x39, 0x33, 0x66, 0x33, 0x61, 0x63, 0x61, 0x00, 0x00, 0x00, // -8b45c93f3aca...
/*0060*/ 0x00, 0x00, 0x1f, 0x53, 0x01, 0x00, 0x00, 0x01, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x38, 0x75, // ...S....00000m8u
/*0070*/ 0x65, 0x64, 0x79, 0x79, 0x74, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // edyyt00000000000
/*0080*/ 0x30, 0x30, 0x30, 0x30, 0x30, 0x6e, 0x75, 0x30, 0x76, 0x61, 0x38, 0x62, 0x6f, 0x6d, 0x00, 0x00, // 00000nu0va8bom..
/*0090*/ 0x00, 0x01, 0x01, 0x70, 0x6c, 0x61, 0x79, 0x00, 0x2f, 0x20, 0x46, 0x61, 0x64, 0x65, 0x20, 0x4f, // ...play./ Fade O
/*00a0*/ 0x75, 0x74, 0x00, 0x63, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ut.cy...........
/*00b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00c0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00d0*/ 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x38, 0x75, 0x65, 0x64, 0x79, 0x79, 0x74, // ...00000m8uedyyt
/*00e0*/ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // 0000000000000000
/*00f0*/ 0x6e, 0x75, 0x30, 0x76, 0x61, 0x38, 0x62, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x69, 0x70, 0x00, // nu0va8bom....ip.
/*0100*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0110*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0120*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0130*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0x01, 0x0d, 0x00, // ................
/*0140*/ 0x00, 0x00, 0x00, 0x00, 0x75, 0x29, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ....u)..........
/*0150*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x42, 0x0f, 0x00, // ............@B..
/*0160*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0170*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0180*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0190*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*01a0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*01b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // ......
};
spoofMsgCounter(msg, cnt++);
sut.checkParseMessageData(std::vector<char>(msg.begin(), msg.end()));
CHECK_MESSAGE(cbEvents.front() == "0: true 00000m8uedyyt0000000000000000nu0va8bom InPosition5Scene play 00000m8uedyyt0000000000000000nu0va8bom", cbEvents.front());
cbEvents.pop();
}
{ // In InPosition6Scene
std::vector<uint8_t> msg = {
/*0000*/ 0x23, 0x73, 0x70, 0x69, 0x74, 0x23, 0x00, 0x01, 0x00, 0x00, 0x00, 0x85, 0x63, 0x00, 0x00, 0x00, // #spit#......c...
/*0010*/ 0x34, 0x30, 0x35, 0x63, 0x37, 0x31, 0x30, 0x30, 0x2d, 0x36, 0x34, 0x65, 0x38, 0x2d, 0x34, 0x33, // 405c7100-64e8-43
/*0020*/ 0x35, 0x64, 0x2d, 0x38, 0x61, 0x37, 0x62, 0x2d, 0x30, 0x30, 0x63, 0x35, 0x36, 0x32, 0x39, 0x66, // 5d-8a7b-00c5629f
/*0030*/ 0x39, 0x63, 0x63, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x30, 0x38, 0x36, 0x36, 0x63, 0x39, // 9ccf.....70866c9
/*0040*/ 0x62, 0x2d, 0x61, 0x66, 0x31, 0x63, 0x2d, 0x34, 0x37, 0x36, 0x35, 0x2d, 0x61, 0x31, 0x37, 0x65, // b-af1c-4765-a17e
/*0050*/ 0x2d, 0x38, 0x62, 0x34, 0x35, 0x63, 0x39, 0x33, 0x66, 0x33, 0x61, 0x63, 0x61, 0x00, 0x00, 0x00, // -8b45c93f3aca...
/*0060*/ 0x00, 0x00, 0x1f, 0x53, 0x01, 0x00, 0x00, 0x01, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x38, 0x75, // ...S....00000m8u
/*0070*/ 0x65, 0x64, 0x79, 0x79, 0x74, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // edyyt00000000000
/*0080*/ 0x30, 0x30, 0x30, 0x30, 0x30, 0x6f, 0x34, 0x30, 0x76, 0x61, 0x38, 0x62, 0x6f, 0x6d, 0x00, 0x00, // 00000o40va8bom..
/*0090*/ 0x00, 0x01, 0x01, 0x70, 0x6c, 0x61, 0x79, 0x00, 0x2f, 0x20, 0x46, 0x61, 0x64, 0x65, 0x20, 0x4f, // ...play./ Fade O
/*00a0*/ 0x75, 0x74, 0x00, 0x63, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ut.cy...........
/*00b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00c0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00d0*/ 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x38, 0x75, 0x65, 0x64, 0x79, 0x79, 0x74, // ...00000m8uedyyt
/*00e0*/ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // 0000000000000000
/*00f0*/ 0x6f, 0x34, 0x30, 0x76, 0x61, 0x38, 0x62, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x69, 0x70, 0x00, // o40va8bom....ip.
/*0100*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0110*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0120*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0130*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x05, 0x0f, 0x00, // ................
/*0140*/ 0x00, 0x00, 0x00, 0x00, 0x13, 0x36, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // .....6..........
/*0150*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x42, 0x0f, 0x00, // ............@B..
/*0160*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0170*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0180*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0190*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*01a0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*01b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // ......
};
spoofMsgCounter(msg, cnt++);
sut.checkParseMessageData(std::vector<char>(msg.begin(), msg.end()));
CHECK_MESSAGE(cbEvents.front() == "0: true 00000m8uedyyt0000000000000000o40va8bom InPosition6Scene play 00000m8uedyyt0000000000000000o40va8bom", cbEvents.front());
cbEvents.pop();
}
{ // unknown
std::vector<uint8_t> msg = {
/*0000*/ 0x23, 0x73, 0x70, 0x69, 0x74, 0x23, 0x00, 0x01, 0x00, 0x00, 0x00, 0x87, 0x63, 0x00, 0x00, 0x00, // #spit#......c...
/*0010*/ 0x34, 0x30, 0x35, 0x63, 0x37, 0x31, 0x30, 0x30, 0x2d, 0x36, 0x34, 0x65, 0x38, 0x2d, 0x34, 0x33, // 405c7100-64e8-43
/*0020*/ 0x35, 0x64, 0x2d, 0x38, 0x61, 0x37, 0x62, 0x2d, 0x30, 0x30, 0x63, 0x35, 0x36, 0x32, 0x39, 0x66, // 5d-8a7b-00c5629f
/*0030*/ 0x39, 0x63, 0x63, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x30, 0x38, 0x36, 0x36, 0x63, 0x39, // 9ccf.....70866c9
/*0040*/ 0x62, 0x2d, 0x61, 0x66, 0x31, 0x63, 0x2d, 0x34, 0x37, 0x36, 0x35, 0x2d, 0x61, 0x31, 0x37, 0x65, // b-af1c-4765-a17e
/*0050*/ 0x2d, 0x38, 0x62, 0x34, 0x35, 0x63, 0x39, 0x33, 0x66, 0x33, 0x61, 0x63, 0x61, 0x00, 0x00, 0x00, // -8b45c93f3aca...
/*0060*/ 0x00, 0x00, 0x1f, 0x53, 0x01, 0x00, 0x00, 0x01, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x38, 0x75, // ...S....00000m8u
/*0070*/ 0x65, 0x64, 0x79, 0x79, 0x74, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // edyyt00000000000
/*0080*/ 0x30, 0x30, 0x30, 0x30, 0x31, 0x61, 0x75, 0x30, 0x76, 0x61, 0x38, 0x62, 0x6f, 0x6d, 0x00, 0x00, // 00001au0va8bom..
/*0090*/ 0x00, 0x01, 0x01, 0x70, 0x6c, 0x61, 0x79, 0x00, 0x2f, 0x20, 0x46, 0x61, 0x64, 0x65, 0x20, 0x4f, // ...play./ Fade O
/*00a0*/ 0x75, 0x74, 0x00, 0x63, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ut.cy...........
/*00b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00c0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00d0*/ 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x38, 0x75, 0x65, 0x64, 0x79, 0x79, 0x74, // ...00000m8uedyyt
/*00e0*/ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, // 0000000000000001
/*00f0*/ 0x61, 0x75, 0x30, 0x76, 0x61, 0x38, 0x62, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x69, 0x70, 0x00, // au0va8bom....ip.
/*0100*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0110*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0120*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0130*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x06, 0x11, 0x00, // ............2...
/*0140*/ 0x00, 0x00, 0x00, 0x00, 0x33, 0x53, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ....3S..........
/*0150*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x42, 0x0f, 0x00, // ............@B..
/*0160*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0170*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0180*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0190*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*01a0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*01b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // ......
};
spoofMsgCounter(msg, cnt++);
sut.checkParseMessageData(std::vector<char>(msg.begin(), msg.end()));
CHECK(cbEvents.empty());
}
{ // unknown
std::vector<uint8_t> msg = {
/*0000*/ 0x23, 0x73, 0x70, 0x69, 0x74, 0x23, 0x00, 0x01, 0x00, 0x00, 0x00, 0x89, 0x63, 0x00, 0x00, 0x00, // #spit#......c...
/*0010*/ 0x34, 0x30, 0x35, 0x63, 0x37, 0x31, 0x30, 0x30, 0x2d, 0x36, 0x34, 0x65, 0x38, 0x2d, 0x34, 0x33, // 405c7100-64e8-43
/*0020*/ 0x35, 0x64, 0x2d, 0x38, 0x61, 0x37, 0x62, 0x2d, 0x30, 0x30, 0x63, 0x35, 0x36, 0x32, 0x39, 0x66, // 5d-8a7b-00c5629f
/*0030*/ 0x39, 0x63, 0x63, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x30, 0x38, 0x36, 0x36, 0x63, 0x39, // 9ccf.....70866c9
/*0040*/ 0x62, 0x2d, 0x61, 0x66, 0x31, 0x63, 0x2d, 0x34, 0x37, 0x36, 0x35, 0x2d, 0x61, 0x31, 0x37, 0x65, // b-af1c-4765-a17e
/*0050*/ 0x2d, 0x38, 0x62, 0x34, 0x35, 0x63, 0x39, 0x33, 0x66, 0x33, 0x61, 0x63, 0x61, 0x00, 0x00, 0x00, // -8b45c93f3aca...
/*0060*/ 0x00, 0x00, 0x1f, 0x53, 0x01, 0x00, 0x00, 0x01, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x38, 0x75, // ...S....00000m8u
/*0070*/ 0x65, 0x64, 0x79, 0x79, 0x74, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, // edyyt00000000000
/*0080*/ 0x30, 0x30, 0x30, 0x30, 0x31, 0x66, 0x6a, 0x30, 0x76, 0x61, 0x38, 0x62, 0x6f, 0x6d, 0x00, 0x00, // 00001fj0va8bom..
/*0090*/ 0x00, 0x01, 0x01, 0x70, 0x6c, 0x61, 0x79, 0x00, 0x2f, 0x20, 0x46, 0x61, 0x64, 0x65, 0x20, 0x4f, // ...play./ Fade O
/*00a0*/ 0x75, 0x74, 0x00, 0x63, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ut.cy...........
/*00b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00c0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*00d0*/ 0x00, 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x6d, 0x38, 0x75, 0x65, 0x64, 0x79, 0x79, 0x74, // ...00000m8uedyyt
/*00e0*/ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, // 0000000000000001
/*00f0*/ 0x66, 0x6a, 0x30, 0x76, 0x61, 0x38, 0x62, 0x6f, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x69, 0x70, 0x00, // fj0va8bom....ip.
/*0100*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0110*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0120*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0130*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xad, 0x3a, 0x14, 0x00, // .............:..
/*0140*/ 0x00, 0x00, 0x00, 0x00, 0xb4, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0150*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x42, 0x0f, 0x00, // ............@B..
/*0160*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0170*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0180*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*0190*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*01a0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ................
/*01b0*/ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // ......
};
spoofMsgCounter(msg, cnt++);
sut.checkParseMessageData(std::vector<char>(msg.begin(), msg.end()));
CHECK(cbEvents.empty());
}
}
+128
View File
@@ -0,0 +1,128 @@
/**
* @file spitRepoTest.cpp
* @author Gustice
* @brief
*/
#include "doctest/doctest.h"
#include <fmt/core.h>
#include <iostream>
#include "spit_repo.hpp"
using namespace spit;
using namespace std;
struct SomeType {
string id;
int val{};
int tst{};
string getID() { return id; }
};
class SpitRepoFix {
protected:
SpitRepo<SomeType> sut;
};
TEST_CASE_FIXTURE(SpitRepoFix, "Typical use case") {
CHECK_EQ(sut.getSize(), 0);
CHECK_EQ(sut.hasEntry("obj1"), false);
CHECK_EQ(sut.getEntry("obj1"), nullptr);
CHECK_THROWS(sut.getEntryRef("obj1"));
CHECK_EQ(sut.removeEntry("obj1"), false);
// CHECK_EQ(sut.removeEntry(1), false);
auto obj1 = make_unique<SomeType>("obj1", 1, 0);
sut.addEntry(move(obj1));
{
CHECK_EQ(sut.getSize(), 1);
CHECK_EQ(sut.hasEntry("obj1"), true);
CHECK_EQ(sut.getEntry("obj1")->val, 1);
CHECK_EQ(sut.getEntryRef("obj1").val, 1);
}
auto obj2 = make_unique<SomeType>("obj2", 2, 0);
sut.addEntry(move(obj2));
{
CHECK_EQ(sut.getSize(), 2);
CHECK_EQ(sut.hasEntry("obj2"), true);
CHECK_EQ(sut.getEntry("obj2")->val, 2);
CHECK_EQ(sut.getEntryRef("obj2").val, 2);
}
auto obj3 = make_unique<SomeType>("obj3", 3, 0);
sut.addEntry(move(obj3));
{
CHECK_EQ(sut.getSize(), 3);
CHECK_EQ(sut.hasEntry("obj3"), true);
CHECK_EQ(sut.getEntry("obj3")->val, 3);
CHECK_EQ(sut.getEntryRef("obj3").val, 3);
}
auto obj4 = make_unique<SomeType>("obj4", 4, 0);
sut.addEntry(move(obj4));
{
CHECK_EQ(sut.getSize(), 4);
CHECK_EQ(sut.hasEntry("obj4"), true);
CHECK_EQ(sut.getEntry("obj4")->val, 4);
CHECK_EQ(sut.getEntryRef("obj4").val, 4);
}
{
auto *p = const_cast<SomeType *>(sut.getEntry("obj3"));
auto &r = sut.getEntryRef("obj3");
CHECK_EQ(p->val, 3);
CHECK_EQ(r.val, 3);
CHECK_EQ(r.tst, 0);
p->tst = 20;
CHECK_EQ(r.tst, 20);
}
{
const auto & repo = sut.getRepo();
CHECK_EQ(repo.size(), 4);
CHECK_EQ(repo.at("obj1")->val, 1);
CHECK_EQ(repo.at("obj2")->val, 2);
CHECK_EQ(repo.at("obj3")->val, 3);
CHECK_EQ(repo.at("obj4")->val, 4);
}
{
CHECK_EQ(sut.removeEntry("obj1"), true);
CHECK_EQ(sut.removeEntry("obj3"), true);
const auto & repo = sut.getRepo();
CHECK_EQ(sut.getSize(), 2);
CHECK_EQ(repo.size(), 2);
CHECK_EQ(repo.at("obj2")->val, 2);
CHECK_EQ(repo.at("obj4")->val, 4);
}
auto obj5 = make_unique<SomeType>("obj5", 5, 0);
sut.addEntry(move(obj5));
{
const auto & repo = sut.getRepo();
CHECK_EQ(sut.getSize(), 3);
CHECK_EQ(repo.size(), 3);
CHECK_EQ(repo.at("obj2")->val, 2);
CHECK_EQ(repo.at("obj4")->val, 4);
CHECK_EQ(repo.at("obj5")->val, 5);
}
{
sut.clearEntries();
CHECK_EQ(sut.getSize(), 0);
}
auto obj6 = make_unique<SomeType>("obj6", 6, 0);
sut.addEntry(move(obj6));
{ // Still works after clear
CHECK_EQ(sut.getSize(), 1);
CHECK_EQ(sut.hasEntry("obj6"), true);
CHECK_EQ(sut.getEntry("obj6")->val, 6);
CHECK_EQ(sut.getEntryRef("obj6").val, 6);
}
}
+11
View File
@@ -0,0 +1,11 @@
/**
* @file uTestMain.cpp
* @author Gustice
* @brief
*/
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest/doctest.h"
TEST_CASE("TestTest") {
CHECK(true == true);
}
+88
View File
@@ -0,0 +1,88 @@
/**
* @file spitRepoTest.cpp
* @author Gustice
* @brief
*/
#include "doctest/doctest.h"
#include <fmt/core.h>
#include <iostream>
#include "spit_type.hpp"
using namespace spit;
using namespace std;
class SpitTypetFix {
protected:
};
TEST_CASE_FIXTURE(SpitTypetFix, "check confirmend behaviour") {
{
SType sut("#Id#123");
CHECK(sut.getID() == "#Id#123");
CHECK(sut.getConfirmed() == false);
sut.setConfirmed(true);
CHECK(sut.getConfirmed() == true);
CHECK(sut.GroupName.set("G123") == true);
CHECK(sut.GroupName.get() == "G123");
CHECK(sut.getConfirmed() == false);
sut.setConfirmed(true);
CHECK(sut.getConfirmed() == true);
CHECK(sut.GroupName.set("G123") == false);
CHECK(sut.getConfirmed() == true);
CHECK(sut.GroupName.set("G321") == true);
CHECK(sut.GroupName.get() == "G321");
CHECK(sut.getConfirmed() == false);
}
{
SType sut("#Id#123");
CHECK(sut.getID() == "#Id#123");
CHECK(sut.getConfirmed() == false);
sut.setConfirmed(true);
CHECK(sut.getConfirmed() == true);
CHECK(sut.Fixed.set(true) == true);
CHECK(sut.Fixed.get() == true);
CHECK(sut.getConfirmed() == false);
sut.setConfirmed(true);
CHECK(sut.getConfirmed() == true);
CHECK(sut.Fixed.set(true) == false);
CHECK(sut.getConfirmed() == true);
CHECK(sut.Fixed.set(false) == true);
CHECK(sut.Fixed.get() == false);
CHECK(sut.getConfirmed() == false);
}
{
SType sut("#Id#123");
CHECK(sut.getID() == "#Id#123");
CHECK(sut.getConfirmed() == false);
sut.setConfirmed(true);
CHECK(sut.getConfirmed() == true);
CHECK(sut.DefaultDelay.set(123) == true);
CHECK(sut.DefaultDelay.get() == 123);
CHECK(sut.getConfirmed() == false);
sut.setConfirmed(true);
CHECK(sut.getConfirmed() == true);
CHECK(sut.DefaultDelay.set(123) == false);
CHECK(sut.getConfirmed() == true);
CHECK(sut.DefaultDelay.set(321) == true);
CHECK(sut.DefaultDelay.get() == 321);
CHECK(sut.getConfirmed() == false);
}
// ...
{
SType sut("#Id#123", "grp123", "name123", true, false, 1234, 4321);
CHECK(sut.getID() == "#Id#123");
CHECK(sut.GroupName.get() == "grp123");
CHECK(sut.Name.get() == "name123");
CHECK(sut.Fixed.get() == true);
CHECK(sut.ProjectIndepended.get() == false);
CHECK(sut.DefaultDelay.get() == 1234);
CHECK(sut.DefaultDuration.get() == 4321);
}
}
+4
View File
@@ -0,0 +1,4 @@
idf_component_register(
INCLUDE_DIRS "include"
REQUIRES devHal
)
@@ -0,0 +1,70 @@
#pragma once
#include "stepperDrv.hpp"
#include "stepperMotor.hpp"
#include <cmath>
#include "logging.hpp"
class WatchMotor {
static constexpr uint8_t Positions = 6;
public:
using AlmostFinishedCb = void (*)(uint8_t newIdx);
WatchMotor(IStepperMotor &stepper, AlmostFinishedCb onDone, ILogging &logger)
: _stepper(stepper),
_onFinCb(onDone),
_logger(logger) {
}
void moveTo(uint8_t position, uint8_t profile, int minJumps, float accel, float speed) {
auto sign = position % 6;
int minTurns = minJumps / 6;
float corr = 0.0F;
float currentPos = _stepper.getScaledPos() * 6;
if ((_currentIndex > 4) && (currentPos < 2)) {
corr = 6 - _currentIndex - currentPos;
} else if ((_currentIndex < 2) && (currentPos > 2)) {
corr = _currentIndex + 6 - currentPos;
} else {
corr = _currentIndex - currentPos;
}
int steps = 0;
if (_currentIndex < sign) {
steps = (sign - _currentIndex);
} else if (_currentIndex >= sign) {
steps = (sign + 6 - _currentIndex);
}
steps += minTurns * 6;
if (steps < minJumps) {
steps += 6;
}
float delta = 2.0F * std::numbers::pi / 6 * steps;
ESP_LOGD("MV", "Moving to %d by %.2f + %.2f", sign, delta, corr);
_stepper.moveBy(delta + corr, profile, accel, speed, [&](int diff) {
_logger.logv(ILogging::Severity::Debug,"Almost done with head=%i",diff);
if (diff < 6) {
_logger.logv(ILogging::Severity::Warning,"Low headroom in Profile pipeline=%i",diff);
}
if (_onFinCb) {
_onFinCb(position);
}
});
_currentIndex = sign;
}
float getScaled() { return _stepper.getScaledPos() * 6; }
uint8_t getIndex() { return _currentIndex; }
private:
IStepperMotor &_stepper;
uint8_t _currentIndex{};
AlmostFinishedCb _onFinCb{};
ILogging &_logger;
};
+1
View File
@@ -0,0 +1 @@
// Dummy file
@@ -0,0 +1,5 @@
UnitTestsFrame()
target_link_libraries(${moduleName} PUBLIC
devHal
bases
)
@@ -0,0 +1,112 @@
/**
* @file TestableUTest.cpp
* @author Gustice
* @brief
*
* @copyright Copyright (c) 2024
*/
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest/doctest.h"
#include <iostream>
#include <numbers>
#include "watchFace.hpp"
class StepperMotorMock : public IStepperMotor {
public:
void moveBy(float angle, uint8_t profile = 0) override {
lastAngle = angle;
lastProfile = profile;
}
void moveBy(float angle, uint8_t profile, float accel, float speed, std::function<void(int diff)> profileDone = nullptr) override {
lastAngle = angle;
lastProfile = profile;
}
float getScaledPos() const override {
return nextPos;
}
uint16_t getQuanted() const override {
return 0;
}
float lastAngle{};
uint8_t lastProfile{};
float lastAccel{};
float lastSpeed{};
float nextPos{};
};
class LoggerMock : public ILogging {
void output(Severity severity, std::string message) override {
// do nothing
}
};
void onFin(uint8_t idx) {
}
class WatchFaceFixture {
using PositionStore = StepperMotor::Position<50, 5>;
public:
protected:
static constexpr int MicroSteps = 32;
StepperMotorMock motor;
LoggerMock logger;
WatchMotor sut{motor, onFin, logger};
private:
};
TEST_CASE_FIXTURE(WatchFaceFixture, "Moving simple steps") {
auto next = 0*std::numbers::pi/3;
motor.nextPos = next;
sut.moveTo(1, 0, 0, 1, 1);
auto delta = 1*std::numbers::pi/3;
CHECK(motor.lastAngle == doctest::Approx(delta).epsilon(0.000625)); // MicroStep tolerance
motor.nextPos = 1.0F/6;
sut.moveTo(2, 0, 0, 1, 1);
CHECK(motor.lastAngle == doctest::Approx(delta).epsilon(0.000625)); // MicroStep tolerance
motor.nextPos = 2.0F/6;
sut.moveTo(3, 0, 0, 1, 1);
CHECK(motor.lastAngle == doctest::Approx(delta).epsilon(0.000625)); // MicroStep tolerance
motor.nextPos = 3.0F/6;
sut.moveTo(4, 0, 0, 1, 1);
CHECK(motor.lastAngle == doctest::Approx(delta).epsilon(0.000625)); // MicroStep tolerance
motor.nextPos = 4.0F/6;
sut.moveTo(5, 0, 0, 1, 1);
CHECK(motor.lastAngle == doctest::Approx(delta).epsilon(0.000625)); // MicroStep tolerance
motor.nextPos = 5.0F/6;
sut.moveTo(6, 0, 0, 1, 1);
CHECK(motor.lastAngle == doctest::Approx(delta).epsilon(0.000625)); // MicroStep tolerance
motor.nextPos = 6.0F/6;
sut.moveTo(1, 0, 0, 1, 1);
CHECK(motor.lastAngle == doctest::Approx(delta).epsilon(0.000625)); // MicroStep tolerance
motor.nextPos = 1.0F/6;
sut.moveTo(2, 0, 0, 1, 1);
CHECK(motor.lastAngle == doctest::Approx(delta).epsilon(0.000625)); // MicroStep tolerance
}
TEST_CASE_FIXTURE(WatchFaceFixture, "Moving multiple turns") {
auto next = 0*std::numbers::pi/3;
motor.nextPos = next;
sut.moveTo(1, 0, 0, 1, 1);
auto delta = 1*std::numbers::pi/3;
CHECK(motor.lastAngle == doctest::Approx(delta).epsilon(0.000625)); // MicroStep tolerance
motor.nextPos = 1.0F/6;
sut.moveTo(1, 0, 0, 1, 1);
CHECK(motor.lastAngle == doctest::Approx(6*delta).epsilon(0.000625)); // MicroStep tolerance
motor.nextPos = 1.0F/6;
sut.moveTo(1, 0, 7, 1, 1);
CHECK(motor.lastAngle == doctest::Approx(12*delta).epsilon(0.000625)); // MicroStep tolerance
}