#pragma once #include "sad_DetectedServer.hpp" #include "spit_object.hpp" #include "spit_player.hpp" #include "spit_socket.hpp" #include "spit_type.hpp" #include "spit_message.hpp" #include "spit_repo.hpp" #include #include #include #include "syslog.hpp" using namespace std; namespace spit { class SClient : ISadClient { public: /** Maximal length of a SPITMessage*/ static const int MAXSPITMESSAGEBYTECOUNT = 512; 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.
Either no connect tried or disconnected NoneServer = 1, // Server has disconnected Lost = 2, // The connection is lost
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 onServerEvent; function onConnectionStatus; function onProjectUpdate; function onSpitType; function onSpitObject; function onPlay; function onPlaySpitType; function onPlayRemoteObjectStatus; function onCheckSpitTypeVeto; function onCheckSpitObjectVeto; }; //------ 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 constexpr char PLAYERTYPE_MAIN = 0x02; /** continous player. This player plays SPIT objects sometime during the project.*/ static constexpr char PLAYERTYPE_CONTINOUS = 0x01; /** diescrete player. This player is used to transmit single discrete values.*/ static constexpr char PLAYERTYPE_DISCRETE = 0x00; //------ Value Flag ------ //------ used by sendRemoteObject public: /**Set the given value directly*/ static constexpr 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 constexpr 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 constexpr char VALUEFLAG_DECREASE = 0x02; static constexpr int MAX_OVERTAKING_SPIT_MESSAGES = 10; //----- SClient ----- public: SClient(string name, string projName, ISocket &socket, Callbacks calls); virtual ~SClient(); private: Callbacks mCallbacks; fos::TaskT *mThreadReceive{}; fos::TaskT *mThreadProcess{}; bool mStopReceiveMessages{}; Connection mConnectionStatus{Connection::None}; bool mDisconnectedByServer{}; // flag true = when server disconnects string mID; string mName; string mProjName; string mComputerName; string mClientProjectName; uint8_t mMessageNumberSend{1}; int mMessageNumberReceived{-1}; // set to -1 because the first received message will set the number (1..255) vector> mOvertakingMessages; // Server's info filled by parseConnectAnswer string mServerID{}; string mServerName{}; string mServerComputerName{}; int mServerStepTime{-1}; //--- Server Project --- string mServerProjectID; string mServerProjectName; int mServerFramesPerSecond; public: /** The ID of the client.
Every client instance should set an unique id*/ string getID() { return mID; } /** The name of the client, should be set by the user app*/ string getName() { return mName; } //----- Computer Params ----- /** The name of the client's host computer*/ string getComputerName() { return mComputerName; } 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.
* When the server plays objects, it will do so at discrete time steps.
* 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 setActionSPITType(string spitypeID, bool actionAssigned); void setActionSPITObject(string objectID, bool 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 sendSPITMessage(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.
* 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(unique_ptr message); void checkParseMessage(unique_ptr message); void addOvertakingMessage(unique_ptr 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 --------------- private: void parseReportProject(Message::ReportProject &message); void parseCloseProject(Message::CloseProject &message); 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(); private: void parseRequestProjectTransfer(Message::RequestProjectTransfer &message); void parseStartProjectTransfer(Message::StartProjectTransfer &message); void parseEndProjectTransfer(Message::EndProjectTransfer &message); public: static void sendProject(SClient *p_PtrSClient); // --------------- 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. */ SType *createTypeFixed(string iD, string groupName, string name, uint64_t defaultDelay, uint64_t 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. */ SType *createType(string iD, string groupName, string name, uint64_t defaultDelay, uint64_t defaultDuration, bool fixed, bool projectIndepended); private: // receive SPIT-Types void parseReportType(Message::ReportType &message); void parseRemoveSPITType(Message::RemoveType &message); int sendSPITTypeInternal(SType &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 spitypeID the id of the SPITType to remove. * @return 0 = is sended, -1 = is not sended */ bool changeSPITType(SType *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 spitypeID the id of the SPITType to remove. * @return 0 = is sended, -1 = is not sended */ int removeSPITType(string spitypeID); public: //--------------- SPIT Objects --------------- private: void parseReportSPITObject(Message::ReportObject &message); void parseRemoveSPITObject(Message::RemoveObject &message); int sendSPITObjectInternal(SObject *ptrObject); 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 spitObject the pointer to the changed SPITObject. * @return 0 = sended, -1 = not sended */ bool changeSPITObject(SObject *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 objectID the id of the SPITObject to remove. * @return 0 = sended, -1 = not sended */ int removeSPITObject(string objectID); public: //--------------- Play SPIT Objects --------------- private: void parsePlaySPITStart(Message::PlayStart &message); void parsePlaySPITObject(Message::PlayObject &message); void parsePlaySPITEnd(Message::PlayEnd &message); void parseReportPlayStatus(Message::ReportPlayStatus &message); public: //--------- Remote ---------- void sendRemoteObject(string objectID, int valuePpm = 0, char valueFlag = 0x00); private: static void processSocketTask(SClient &instance); fos::TaskT *sThread{}; bool mAbortSearch{}; bool sAnswered{}; static fos::Semaphore sStartSad; ISocket &mSocket; void answerWatchdog() { sAnswered = true; } SpitRepo mSpitObjects; SpitRepo mRemoteSpitObjects; SpitRepo mSpitTypes; SpitRepo mRemoteSpitTypes; }; }