Files
2025-11-15 13:50:18 +01:00

255 lines
11 KiB
C++

#pragma once
#include "sad_DetectedServer.hpp"
#include "spit_object.hpp"
#include "spit_player.hpp"
#include "spit_processor.hpp"
#include "spit_socket.hpp"
#include "spit_type.hpp"
#include "spit_message.hpp"
#include "spit_repo.hpp"
#include <memory>
#include <vector>
#include <functional>
using namespace std;
namespace spit {
class SClient : ISadClient {
public:
/** Maximal length of a SPITMessage*/
static const int MAXSPITMESSAGEBYTECOUNT = 512;
using Callbacks = SProcessor::Callbacks;
using ProjUpdate = SProcessor::ProjUpdate;
using ServerEvent = SProcessor::ServerEvent;
using ObjectEvent = SProcessor::ObjectEvent;
using Connection = SProcessor::Connection;
//------ PlayerType ------
public:
/** 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.*/
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.<br>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.<br>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<SClient &> *mThreadReceive{};
fos::TaskT<SClient &> *mThreadProcess{};
bool mStopReceiveMessages{};
Connection mConnectionStatus{Connection::None};
bool mDisconnectedByServer{}; // flag true = when server disconnects
string mID;
string mName;
string mProjName;
string mComputerName;
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)
vector<unique_ptr<Message>> mOvertakingMessages;
// 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 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.<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 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 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(unique_ptr<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();
/** 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:
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);
/**
* 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);
private:
// receive SPIT-Types
void parseReportType(Message::ReportType &message);
int sendTypeInternal(SType &spitType);
public:
bool changeType(SType *spitType) { return mProcessor.changeType(spitType); }
int removeType(string spitypeID) { return mProcessor.removeType(spitypeID); }
public:
//--------------- SPIT Objects ---------------
private:
int sendObjectInternal(SObject *sObject);
public:
/**
* When the client changes a SPITObject (normally the client should not do this), then it should send the changes to the SPITServer.<br>
* The SPITServer may discard the changes and an eventSPITObject occurs.<br>
* @param spitObject the pointer to the changed SPITObject.
* @return 0 = sended, -1 = not sended
*/
bool changeSPITObject(SObject *spitObject);
public:
//--------- Remote ----------
void sendRemoteObject(string objectID, int valuePpm = 0, char valueFlag = 0x00);
private:
static void processSocketTask(SClient &instance);
fos::TaskT<SClient &> *sThread{};
bool mAbortSearch{};
bool sAnswered{};
static fos::Semaphore sStartSad;
ISocket &mSocket;
void answerWatchdog() {
sAnswered = true;
}
};
}