/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ /* * File: SPIT_Client.h * Author: Hagi * * Created on 7. April 2019, 08:50 */ #ifndef SPIT_CLIENT_H #define SPIT_CLIENT_H #include "_SPITMACROS.h" #include "UnixSocket.h" #include "SPITSocket.h" #include "SAD_Client.h" #include "SAD_ClientInterface.h" #include "SAD_DetectedServer.h" #include "SPIT_ClientInterface.h" #include "SPIT_Message.h" #include "Watchdog.h" #include "SPITType.h" #include "SPITObject.h" #include "SPIT_Handler.h" #include "SPITPlayer.h" #include using namespace std; //static char nullChar[] = "\0"; class SPIT_Client : SAD_ClientInterface, Watchdog::Listener { public: /** Maximal length of a SPITMessage*/ static const int MAXSPITMESSAGEBYTECOUNT = 512; //----- Connection Status ----- public: /** No Connection established.
Either no connect tried or disconnected*/ static const int CONNECTION_NONE = 0; /** Server has disconnected*/ static const int CONNECTION_NONESERVER = 1; /** The connection is lost
Either because of a network crash or because the server no longer responds.*/ static const int CONNECTION_LOST = 2; /** Connection to the server is established.*/ static const int CONNECTION_OK = 3; /** A connection to the server is tried.*/ static const int CONNECTION_TRYCONNECTION = 4; //------ PlayerType ------ public: /** continous main player, time value can be used like a timecode.
The time value is the position in server's project.
A server should use only one main player.*/ static const char PLAYERTYPE_MAIN = 0x02; /** continous player. This player plays SPIT objects sometime during the project.*/ static const char PLAYERTYPE_CONTINOUS = 0x01; /** diescrete player. This player is used to transmit single discrete values.*/ static const char PLAYERTYPE_DISCRETE = 0x00; //------ Value Flag ------ //------ used by sendRemoteObject public: /**Set the given value directly*/ static const char VALUEFLAG_DIRECT = 0x00; /**Increase the existing value by the given value.
This is usefull e.g. when instead of a fader a endless wheel is used*/ static const char VALUEFLAG_INCREASE = 0x01; /**Decrease the existing value by the given value.
This is usefull e.g. when instead of a fader a endless wheel is used*/ static const char VALUEFLAG_DECREASE = 0x02; //----- SPIT_Client ----- public: static SPITSocket* createSPITSocket(); public: SPIT_Client(SPIT_ClientInterface* p_SPITClientInterface); virtual ~SPIT_Client(); private: /** SPITClientInterface, normally the user app should implement this*/ SPIT_ClientInterface* m_SPITClientInterface; SPITSocket* m_SPITSocket; recursive_mutex m_MutexSPITSocket; thread* m_ThreadReceive; bool m_StopReceiveSPITMessages; int m_ConnectionStatus; bool m_DisconnectedByServer; //flag true = when server disconnects string m_ID; string m_Name; string m_ComputerName; //Server's info filled by parseConnectAnswer string m_ServerID; string m_ServerName; string m_ServerComputerName; int m_ServerStepTime; Watchdog* m_Watchdog; //--- Server Project --- string m_ServerProjectID; string m_ServerProjectName; int m_ServerFramesPerSecond; public: SPIT_ClientInterface* getSPITClientInterface() { return m_SPITClientInterface; } public: /** The ID of the client.
Every client instance should set an unique id*/ string getID(); /** Set the name of the client, should be done by the user app*/ void setName(string p_Name); /** The name of the client, should be set by the user app*/ string getName(); //----- Computer Params ----- /** The name of the client's host computer*/ string getComputerName(); //connected Server Infos /** The ID of the connected SPITServer*/ string getServerID(); /** The host computername of the connected SPITServer*/ string getServerComputerName(); /** The name of the connected SPITServer*/ string getServerName(); /** Expected Refreah Rate of the connectd SPITServer.
* When the server plays objects, it will do so at discrete time steps.
* Play events will take place in these time steps.. */ int getServerStepTime(); /** 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(); /** * * @param p_SPITTypeID * @param p_ActionAssigned */ void setActionSPITType(string p_SPITTypeID, bool p_ActionAssigned); void setActionSPITObject(string p_SPITObjectID, bool p_ActionAssigned); //--------------- SPITMessage --------------- private: bool startReceiveSPITMessages(); void stopReceiveSPITMessages(); void receiveSPITMessage(); int sendSPITMessage(SPIT_Message::OPPart* p_SPITMessageOPPart); void fillHeader(SPIT_Message::Header* p_Header); //--------------- 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(string p_ID, string p_Name, string p_ComputerName, string p_IP, string p_Port, string p_Addition, string p_ClientIP, bool p_isLoopback, bool p_IsIP4, bool p_IsIP6); /** Called every time a prior detected SPITServer is is no longer found.*/ void eventServerLost(string p_ID, string p_Name, string p_ComputerName, string p_IP, string p_Port, string p_Addition, string p_ClientIP, bool p_isLoopback, bool p_IsIP4, bool p_IsIP6); public: /** Helper class to notify the SPITClientInterface/user's app*/ class NotifyServerDetected: virtual public SPIT_Handler::Runnable { private: SPIT_ClientInterface* m_SPITClientInterface; SAD_DetectedServer m_DetectedServer; public: NotifyServerDetected(SPIT_ClientInterface* p_SPITClientInterface, const SAD_DetectedServer& p_DetetctedServer) { m_SPITClientInterface = p_SPITClientInterface; m_DetectedServer = p_DetetctedServer; } ~NotifyServerDetected(){} void run() { if (m_SPITClientInterface != NULL) { m_SPITClientInterface->eventServerDetected(m_DetectedServer); } } }; public: /** Helper class to notify the SPITClientInterface/user's app*/ class NotifyServerLost: virtual public SPIT_Handler::Runnable { private: SPIT_ClientInterface* m_SPITClientInterface; SAD_DetectedServer m_DetectedServer; public: NotifyServerLost(SPIT_ClientInterface* p_SPITClientInterface, const SAD_DetectedServer& p_DetetctedServer) { m_SPITClientInterface = p_SPITClientInterface; m_DetectedServer = p_DetetctedServer; } ~NotifyServerLost() {} void run() { if (m_SPITClientInterface != NULL) { m_SPITClientInterface->eventServerLost(m_DetectedServer); } } }; //--------------- 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.
* false = something is going wrong. (client could not bind) */ bool connect(string p_ServerIP, string p_ServerPort, string p_ClientIP, string p_ClientPort, string p_LoginName = "", string p_LoginPassword = ""); /** * Terminates the connection to the SPITServer. */ void disconnect(); private: void disconnectInternal(int p_Cause); void parseSPIT_Message(SPIT_Message* p_SPITMessage); void parseConnectAnswer(SPIT_Message::ConnectAnswer* p_ConnectAnswer); void parseDisconnect(SPIT_Message::Disconnect* p_Disconnect); void parseWatchdog(SPIT_Message::Watchdog* p_Watchdog); void parseWatchdogAnswer(SPIT_Message::WatchdogAnswer* p_WatchdogAnswer); public: /** Used internally*/ void sendWatchdog(); /** Used internally. When the connection to the SPITServer is lost an eventConnectionStatus will occur*/ void eventWatchdogLost(); public: /** Helper class to notify the SPITClientInterface/user's app*/ class NotifyConnectionStatus : public SPIT_Handler::Runnable { public: NotifyConnectionStatus(SPIT_ClientInterface* p_SPITClientInterface, string p_ServerID, string p_ServerComputerName, string p_ServerName, int p_ServerStepTime, int p_NewConnectionStatus, int p_OldConnectionStatus) { m_SPITClientInterface = p_SPITClientInterface; m_ServerID = p_ServerID; m_ServerComputerName = p_ServerComputerName; m_ServerName = p_ServerName; m_ServerStepTime = p_ServerStepTime; m_NewConnectionStatus = p_NewConnectionStatus; m_OldConnectionStatus = p_OldConnectionStatus; } ~NotifyConnectionStatus() {} void run (){ if (m_SPITClientInterface != NULL) { m_SPITClientInterface->eventConnectionStatus(m_ServerComputerName, m_ServerID, m_ServerName, m_ServerStepTime, m_NewConnectionStatus, m_OldConnectionStatus); } } private: SPIT_ClientInterface* m_SPITClientInterface; string m_ServerID; string m_ServerComputerName; string m_ServerName; int m_ServerStepTime; int m_NewConnectionStatus; int m_OldConnectionStatus; }; //--------------- SPIT Project --------------- private: void parseReportSPITProject(SPIT_Message::ReportSPITProject* p_ReportSPITProject); void parseCloseSPITProject(SPIT_Message::CloseSPITProject* p_CloseSPITProject); public: /** Returns the SPITServer's project ID - may be the same as the project's name*/ string getServerProjectID(); /** Returns the SPITServer's project name*/ string 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(); public: /** Helper class to notify the SPITClientInterface/user's app*/ class NotifyReportProject : public SPIT_Handler::Runnable { public: NotifyReportProject(SPIT_ClientInterface* p_SPITClientInterface, string p_ProjectID, string p_ProjectName, int p_FramesPerSecond) { m_SPITClientInterface = p_SPITClientInterface; m_ProjectID = p_ProjectID; m_ProjectName = p_ProjectName; m_FramesPerSecond = p_FramesPerSecond; } ~NotifyReportProject() {} void run() { if (m_SPITClientInterface != NULL) { m_SPITClientInterface->eventProjectReport(m_ProjectID, m_ProjectName, m_FramesPerSecond); } } private: SPIT_ClientInterface* m_SPITClientInterface; string m_ProjectID; string m_ProjectName; int m_FramesPerSecond; }; public: /** Helper class to notify the SPITClientInterface/user's app*/ class NotifyProjectClosed : public SPIT_Handler::Runnable { public: NotifyProjectClosed(SPIT_ClientInterface* p_SPITClientInterface, string p_ProjectID) { m_SPITClientInterface = p_SPITClientInterface; m_ProjectID = p_ProjectID; } ~NotifyProjectClosed() {} void run() { if (m_SPITClientInterface != NULL) { m_SPITClientInterface->eventProjectClosed(m_ProjectID); } } private: SPIT_ClientInterface* m_SPITClientInterface; string m_ProjectID; }; private: void parseRequestProjectTransfer(SPIT_Message::RequestProjectTransfer* p_RequestProjectTransfer); void parseStartProjectTransfer(SPIT_Message::StartProjectTransfer* p_StartProjectTransfer); void parseEndProjectTransfer(SPIT_Message::EndProjectTransfer* p_EndProjectTransfer); public: /** Helper class to notify the SPITClientInterface/user's app*/ class NotifyStartProjectTransfer : public SPIT_Handler::Runnable { public: NotifyStartProjectTransfer(SPIT_ClientInterface* p_SPITClientInterface) { m_SPITClientInterface = p_SPITClientInterface; } ~NotifyStartProjectTransfer() {} void run() { if (m_SPITClientInterface != NULL) { m_SPITClientInterface->eventStartProjectTransfer(); } } private: SPIT_ClientInterface* m_SPITClientInterface; }; public: /** Helper class to notify the SPITClientInterface/user's app*/ class NotifyEndProjectTransfer : public SPIT_Handler::Runnable { public: NotifyEndProjectTransfer(SPIT_ClientInterface* p_SPITClientInterface) { m_SPITClientInterface = p_SPITClientInterface; } ~NotifyEndProjectTransfer() {} void run() { if (m_SPITClientInterface != NULL) { m_SPITClientInterface->eventEndProjectTransfer(); } } private: SPIT_ClientInterface* m_SPITClientInterface; }; // --------------- SPIT Types --------------- public: /** * Creates a fixed SPITType for application actions that are always present and project independent.
* Fixed SPIT types cannot be changed or deleted by the server.
* 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. */ SPITType* createSPITTypeFixed(string p_ID, string p_GroupName, string p_Name, long long p_DefaultDelay, long long p_DefaultDuration); /** * Creates a SPITType for application actions.
* 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. */ SPITType* createSPITType(string p_ID, string p_GroupName, string p_Name, long long p_DefaultDelay, long long p_DefaultDuration, bool p_Fixed, bool p_ProjectIndepended); private: //receive SPITTypes void parseReportSPITType(SPIT_Message::ReportSPITType* p_ReportSPITType); void parseRemoveSPITType(SPIT_Message::RemoveSPITType* p_RemoveSPITType); int sendSPITTypeInternal(SPITType* p_SPITType); public: /** * If a client wants to change a SPITType, he should send it to the server.
* 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.
* @param p_SPITTypeID the id of the SPITType to remove. * @return 0 = is sended, -1 = is not sended */ bool changeSPITType(SPITType* p_SPITType); /** * If a client wants to remove a SPITType, he should send it to the server.
* 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.
* @param p_SPITTypeID the id of the SPITType to remove. * @return 0 = is sended, -1 = is not sended */ int removeSPITType(string p_SPITTypeID); public: /** Helper class to notify the SPITClientInterface/user's app*/ class NotifyReportSPITType : public SPIT_Handler::Runnable { public: NotifyReportSPITType(bool p_Remote, bool p_New, SPIT_ClientInterface* p_SPITClientInterface, const SPITType& p_SPITType) { m_SPITClientInterface = p_SPITClientInterface; m_Remote = p_Remote; m_New = p_New; m_SPITType = p_SPITType; } ~NotifyReportSPITType() {} void run() { if (m_SPITClientInterface != NULL) { if (m_Remote == false) { m_SPITClientInterface->eventSPITType(m_New, m_SPITType); } else { m_SPITClientInterface->eventSPITTypeRemote(m_New, m_SPITType); } } } private: SPIT_ClientInterface* m_SPITClientInterface; bool m_Remote; bool m_New; SPITType m_SPITType; }; public: /** Helper class to notify the SPITClientInterface/user's app*/ class NotifyRemoveSPITType : public SPIT_Handler::Runnable { public: NotifyRemoveSPITType(bool p_Remote, SPIT_ClientInterface* p_SPITClientInterface, const SPITType& p_SPITType) { m_SPITClientInterface = p_SPITClientInterface; m_Remote = p_Remote; m_SPITType = p_SPITType; } ~NotifyRemoveSPITType() {} void run() { if (m_SPITClientInterface != NULL) { if (m_Remote == false) { m_SPITClientInterface->eventSPITTypeRemoved(m_SPITType); } else { m_SPITClientInterface->eventSPITTypeRemoteRemoved(m_SPITType); } } } private: SPIT_ClientInterface* m_SPITClientInterface; bool m_Remote; SPITType m_SPITType; }; //--------------- SPIT Objects --------------- private: void parseReportSPITObject(SPIT_Message::ReportSPITObject* p_ReportSPITObject); void parseRemoveSPITObject(SPIT_Message::RemoveSPITObject* p_RemoveSPITObject); int sendSPITObjectInternal(SPITObject* ptrSPITObject); public: /** * When the client changes a SPITObject (normally the client should not do this), then it should send the changes to the SPITServer.
* The SPITServer may discard the changes and an eventSPITObject occurs.
* @param p_SPITObject the pointer to the changed SPITObject. * @return 0 = sended, -1 = not sended */ bool changeSPITObject(SPITObject* p_SPITObject); /** * When the client removes a SPITObject (normally the client should not do this), then he should send it to the SPITServer.
* The SPITServer may discard the deletion and an eventSPITObject occurs.
* @param p_SPITObjectID the id of the SPITObject to remove. * @return 0 = sended, -1 = not sended */ int removeSPITObject(string p_SPITObjectID); public: /** Helper class to notify the SPITClientInterface/user's app*/ class NotifyReportSPITObject : public SPIT_Handler::Runnable { public: NotifyReportSPITObject(bool p_Remote, bool p_New, SPIT_ClientInterface* p_SPITClientInterface, const SPITType& p_SPITType, const SPITObject& p_SPITObject) { m_SPITClientInterface = p_SPITClientInterface; m_Remote = p_Remote; m_New = p_New; m_SPITType = p_SPITType; m_SPITObject = p_SPITObject; } ~NotifyReportSPITObject() {} void run() { if (m_SPITClientInterface != NULL) { if (m_Remote == false) { m_SPITClientInterface->eventSPITObject(m_New, m_SPITType, m_SPITObject); } else { m_SPITClientInterface->eventSPITObjectRemote(m_New, m_SPITType, m_SPITObject); } } } private: SPIT_ClientInterface* m_SPITClientInterface; bool m_Remote; bool m_New; SPITType m_SPITType; SPITObject m_SPITObject; }; public: /** Helper class to notify the SPITClientInterface/user's app*/ class NotifyRemoveSPITObject : public SPIT_Handler::Runnable { public: NotifyRemoveSPITObject(bool p_Remote, SPIT_ClientInterface* p_SPITClientInterface, const SPITType& p_SPITType, const SPITObject& p_SPITObject) { m_SPITClientInterface = p_SPITClientInterface; m_Remote = p_Remote; m_SPITType = p_SPITType; m_SPITObject = p_SPITObject; } ~NotifyRemoveSPITObject() {} void run() { if (m_SPITClientInterface != NULL) { if (m_Remote == false) { m_SPITClientInterface->eventSPITObjectRemoved(m_SPITType, m_SPITObject); } else { m_SPITClientInterface->eventSPITObjectRemoteRemoved(m_SPITType, m_SPITObject); } } } private: SPIT_ClientInterface* m_SPITClientInterface; bool m_Remote; SPITType m_SPITType; SPITObject m_SPITObject; }; //--------------- Play SPIT Objects --------------- private: void parsePlaySPITStart(SPIT_Message::PlaySPITStart* p_PlaySPITStart); void parsePlaySPITObject(SPIT_Message::PlaySPITObject* p_PlaySPITObject); void parsePlaySPITEnd(SPIT_Message::PlaySPITEnd* p_PlaySPITEnd); void parseReportPlayStatus(SPIT_Message::ReportPlayStatus* p_ReportPlayStatus); public: /** Helper class to notify the SPITClientInterface/user's app*/ class NotifyPlaySPITStart : public SPIT_Handler::Runnable { public: NotifyPlaySPITStart(SPIT_ClientInterface* p_SPITClientInterface, string p_PlayerID, string p_PlayerName, char p_PlayerType, bool p_IsRunning, long long p_TimeFrame) { m_SPITClientInterface = p_SPITClientInterface; m_PlayerID = p_PlayerID; m_PlayerName = p_PlayerName; m_PlayerType = p_PlayerType; m_IsRunning = p_IsRunning; m_TimeFrames = p_TimeFrame; } ~NotifyPlaySPITStart() {} void run() { if (m_SPITClientInterface != NULL) { m_SPITClientInterface->eventPlay(m_PlayerID, m_PlayerName, m_PlayerType, m_IsRunning, m_TimeFrames); } } private: SPIT_ClientInterface* m_SPITClientInterface; string m_PlayerID; string m_PlayerName; char m_PlayerType; bool m_IsRunning; long long m_TimeFrames; }; public: /** Helper class to notify the SPITClientInterface/user's app*/ class NotifyPlaySPITObject : public SPIT_Handler::Runnable { public: NotifyPlaySPITObject(bool p_Started, bool p_Stopped, SPIT_ClientInterface* p_SPITClientInterface, const SPITType& p_SPITType, const SPITObject& p_SPITObject, const SPITPlayer::PlayObject& p_PlayObject) { m_Started = p_Started; m_Stopped = p_Stopped; m_SPITClientInterface = p_SPITClientInterface; m_SPITType = p_SPITType; m_SPITObject = p_SPITObject; m_PlayObject = p_PlayObject; } ~NotifyPlaySPITObject() {} void run() { if (m_SPITClientInterface != NULL) { m_SPITClientInterface->eventPlayObject(m_PlayObject.getPlayerID(), m_Started, m_Stopped, m_SPITType, m_SPITObject, m_PlayObject); } } private: bool m_Started; bool m_Stopped; SPIT_ClientInterface* m_SPITClientInterface; SPITObject m_SPITObject; SPITType m_SPITType; SPITPlayer::PlayObject m_PlayObject; }; public: /** Helper class to notify the SPITClientInterface/user's app*/ class NotifyReportPlayStatus: public SPIT_Handler::Runnable { public: NotifyReportPlayStatus(SPIT_ClientInterface* p_SPITClientInterface, const SPITType& p_SPITType, const SPITObject& p_SPITObject, long long p_FrameInsideObject, int p_Value) { m_SPITClientInterface = p_SPITClientInterface; m_SPITObject = p_SPITObject; m_SPITType = p_SPITType; m_FrameInsideObject = p_FrameInsideObject; m_Value = p_Value; } ~NotifyReportPlayStatus() {} private: SPIT_ClientInterface* m_SPITClientInterface; SPITObject m_SPITObject; SPITType m_SPITType; long long m_FrameInsideObject; int m_Value; public: void run() { if (m_SPITClientInterface != NULL) { m_SPITClientInterface->eventPlayRemoteObjectStatus(m_SPITType, m_SPITObject, m_FrameInsideObject, m_Value); } } }; //--------- Remote ---------- void sendRemoteObject(string p_SPITObjectID, int p_Valueppm = 0, char p_ValueFlag = 0x00); }; #endif /* SPIT_CLIENT_H */