refactor: moving SPIT-App parts to main app

This commit is contained in:
2025-03-29 14:57:01 +01:00
parent 810264aa71
commit 2f5497c432
19 changed files with 6245 additions and 6516 deletions
+356 -13
View File
@@ -27,6 +27,33 @@ struct {
const char *changingPosition = /* <mqttPrefix>/ */ "watch/changingPosition";
} MqttTopics;
struct {
const char *expectedServer = "";
struct {
const char *group = "CatasWatchMovement";
const char *startShuffle = "ActionStartShuffle";
const char *moveToX[6] = {
"ActionMoveTo1",
"ActionMoveTo2",
"ActionMoveTo3",
"ActionMoveTo4",
"ActionMoveTo5",
"ActionMoveTo6",
};
} Provided;
struct {
const char *changingPos = "ChangingPositionScene";
const char *inPosX[6] = {
"InPosition1Scene",
"InPosition2Scene",
"InPosition3Scene",
"InPosition4Scene",
"InPosition5Scene",
"InPosition6Scene",
};
} Expected;
} SpitTypes;
// ToDo:
// - MQTT Integration
// - OTA
@@ -36,8 +63,8 @@ struct {
#include "watchMain.hpp"
string MyApp::s_ClientName = "CatasWatchDev";
string MyApp::s_ProjectName = "CatastrphicProj";
string ClientName = "CatasWatchDev";
string ProjectName = "CatastrphicProj";
extern "C" { // This switch allows the ROS C-implementation to find this main
void app_main(void);
@@ -59,8 +86,18 @@ enum class MoveType {
Shuffle,
Demo
};
enum class ResponseType {
StepAt,
StartMoving,
};
struct MoveCommand {
MoveType type;
// 1..6
uint8_t pos;
};
struct Arrived {
ResponseType type;
// 1..6
uint8_t pos;
};
struct PictoIllumCommand {
@@ -68,6 +105,7 @@ struct PictoIllumCommand {
};
static fos::Queue<MoveCommand> commandQueue(8);
static fos::Queue<Arrived> responseQueue(8);
static fos::Queue<PictoIllumCommand> pictoIllumQue(2);
DeviceState activeState;
@@ -346,6 +384,7 @@ static void dmxSocketTask(int addrFamily) {
}
fos::Semaphore etherActive;
fos::Semaphore spitProjectFinish;
/** Event handler for IP_EVENT_ETH_GOT_IP */
static void got_ip_event_handler(void *arg, esp_event_base_t event_base,
@@ -587,9 +626,11 @@ void MovementTask() {
"Running Stepper by %i steps with prof=%i a=%.2f w=%.2f",
delta, activeState.profile, activeState.accel, activeState.speed);
pictoIllumQue.enqueue(std::make_unique<PictoIllumCommand>(0));
responseQueue.enqueue(std::make_unique<Arrived>(ResponseType::StartMoving, 0));
stepper.moveBy(delta * std::numbers::pi / 3,
activeState.profile, activeState.accel, activeState.speed);
pictoIllumQue.enqueue(std::make_unique<PictoIllumCommand>(newPos));
responseQueue.enqueue(std::make_unique<Arrived>(ResponseType::StepAt, newPos));
activeState.pictogram = newPos;
}
} else if (cmd->type == MoveType::Demo) {
@@ -616,20 +657,322 @@ void MovementTask() {
}
}
}
struct LocalSpitObj {
string iD;
string groupName;
string name;
};
std::map<string, MoveCommand> ProvidedSpitActions;
std::map<string, string> ExpectedObjects;
void onServerEvent(SPIT_Client::ServerEvent event, const SAD_DetectedServer &p_DetectedServer) {
switch (event) {
case SPIT_Client::ServerEvent::Detect:
ESP_LOGI(TAG, "SERVER DETECTED");
ESP_LOGI(TAG, " Name: %s", p_DetectedServer.getName().data());
ESP_LOGI(TAG, " ID: %s", p_DetectedServer.getID().data());
ESP_LOGI(TAG, " Computer: %s", p_DetectedServer.getComputerName().data());
ESP_LOGI(TAG, " IP: %s", p_DetectedServer.getIP().data());
ESP_LOGI(TAG, " Port: %s", p_DetectedServer.getPort().data());
ESP_LOGI(TAG, " Addition: %s", p_DetectedServer.getAdditions().data());
ESP_LOGI(TAG, " ClientIP: %s", p_DetectedServer.getClientIP().data());
break;
case SPIT_Client::ServerEvent::Lost:
ESP_LOGW(TAG, "Server lost Name: %s IP: %s", p_DetectedServer.getName().data(), p_DetectedServer.getIP().data());
break;
case SPIT_Client::ServerEvent::Changed:
ESP_LOGW(TAG, "Server lost Name: %s IP: %s", p_DetectedServer.getName().data(), p_DetectedServer.getIP().data());
break;
default:
break;
}
}
void onConnectionStatus(string p_ServerID, string p_ServerComputerName, string p_ServerName, int p_ServerStepTime, int p_ConnectionStatus, int p_OldConnectionStatus) {
ESP_LOGI(TAG, "MySPITApp: eventConnectionStatus %d ", p_ConnectionStatus);
}
// The transfer of the server's SPITTypes and SPITObjects is finished.
void onProjectUpdate(SPIT_Client::ProjUpdate state, string projectId) {
switch (state) {
case SPIT_Client::ProjUpdate::ServerLoaded:
ESP_LOGI(TAG, "eventProjectReport ID: %s ", projectId.data());
break;
case SPIT_Client::ProjUpdate::StartTransfer:
ESP_LOGI(TAG, "eventStartProjectTransfer");
break;
case SPIT_Client::ProjUpdate::EndTransfer:
ESP_LOGI(TAG, "eventEndProjectTransfer");
spitProjectFinish.give();
break;
case SPIT_Client::ProjUpdate::ServerClosed:
ESP_LOGI(TAG, "eventProjectClosed ID: %s", projectId.data());
// If we don't save SPITServer's project specific information
// perhaps close all SPITObjects and RemoteSPITObjects to avoid conflicts, when the SPITServer loads a new project
// SPITObject::clearSPITObjects();
// SPITObject::clearSPITObjectsRemote();
break;
default:
break;
}
}
/**
* The SPITCLientInterface (implemented by this) will be asked to veto the SPITType while SPIT_Client.parseReportSPITType.<br>
* @param p_ReportSPITType
* @return false = the SPITType is denied, true = the SPITType is accepted
*/
bool onCheckSpitTypeVeto(SPIT_Message::ReportSPITType *p_ReportSPITType) {
if (p_ReportSPITType == NULL)
return true;
SPITType *ptrSPITType;
ptrSPITType = SPITType::getSPITType(p_ReportSPITType->getID());
// Don't allow foreign SPITTypes
if (ptrSPITType == NULL)
return false;
if (ptrSPITType->getCreatedByClient() == false)
return false;
return true;
}
bool onCheckSpitObjectVeto(SPIT_Message::ReportSPITObject *p_ReportSPITObject) {
if (p_ReportSPITObject == NULL)
return true;
SPITType *ptrSPITType = SPITType::getSPITType(p_ReportSPITObject->getSPITTypeID());
if (ptrSPITType == NULL)
return false;
// Don't allow SPITObjects which refers to a SPITType which is not created by this client - other SPITTypes cannot be handled here
if (ptrSPITType->getCreatedByClient() == false)
return false;
SPITObject *ptrSPITObject = SPITObject::getSPITObject(p_ReportSPITObject->getID());
// Don't allow foreign SPITTypes
if (ptrSPITObject == NULL)
return false;
return true;
}
// Called when the server has created, loaded, changed or deleted a SPITType.
// May also occur when the server confirms a SPITType created or changed by the client.
void onSpitType(SPIT_Client::ObjectEvent event, const SPITType &p_SPITType) {
if (event == SPIT_Client::ObjectEvent::Created) {
// Whenever you provide an action for this SPITType take this over in your code.
// call _SpitClient.setActionSPITType(p_SPITType.getID(), true);
ESP_LOGI(TAG, "NEW SPIT-Type ID: %s GroupName: %s Name: %s confirmed: %d", p_SPITType.getID().data(), p_SPITType.getGroupName().data(), p_SPITType.getName().data(), p_SPITType.getConfirmed());
auto typeName = p_SPITType.getName();
if (ExpectedObjects.contains(typeName)) {
ExpectedObjects[typeName] = p_SPITType.getID();
}
} else if (event == SPIT_Client::ObjectEvent::Changed) {
ESP_LOGI(TAG, "SPIT-Type CHANGED ID: %s GroupName: %s Name: %s confirmed: %d", p_SPITType.getID().data(), p_SPITType.getGroupName().data(), p_SPITType.getName().data(), p_SPITType.getConfirmed());
} else if (event == SPIT_Client::ObjectEvent::Deleted) {
ESP_LOGI(TAG, "SPIT-Type Removed ID: %s GroupName: %s Name: %s\n", p_SPITType.getID().data(), p_SPITType.getGroupName().data(), p_SPITType.getName().data());
}
}
/**
* Called when the server has created or changed a Remote SPITType.
* Remote SPITType are used in Remote SPITObjects. A Remote SPITObject is used to remote control the SPITServer.
* @param p_New true = server created a new SPITType / false = server changed a existing SPITType
*/
void onSpitTypeRemote(SPIT_Client::ObjectEvent event, const SPITType &p_SPITType) {
if (event == SPIT_Client::ObjectEvent::Created) {
ESP_LOGI(TAG, "NEW SPIT-REMOTE-Type ID: %s GroupName: %s Name: %s confirmed: %d", p_SPITType.getID().data(), p_SPITType.getGroupName().data(), p_SPITType.getName().data(), p_SPITType.getConfirmed());
} else if (event == SPIT_Client::ObjectEvent::Changed) {
// we had to inform all Remote SPITObjects using the p_SPITType
// perhaps the SPITObject uses information from the SPITType
SPITObject *ptrSPITObject;
int intSPITObjectCount;
intSPITObjectCount = SPITObject::getSPITObjectRemoteCount();
for (int intObject = 0; intObject < intSPITObjectCount; intObject++) {
ptrSPITObject = SPITObject::getSPITObjectRemote(intObject);
if (ptrSPITObject == NULL)
continue;
if (ptrSPITObject->getSPITTypeID() == p_SPITType.getID()) {
// call ecentSPITObjectRemote
// onSpitObjectRemote(SPIT_Client::ObjectEvent::Changed, p_SPITType, *ptrSPITObject);
}
}
ESP_LOGI(TAG, "SPIT-REMOTE-Type CHANGED ID: %s GroupName: %s Name: %s confirmed: %d", p_SPITType.getID().data(), p_SPITType.getGroupName().data(), p_SPITType.getName().data(), p_SPITType.getConfirmed());
} else if (event == SPIT_Client::ObjectEvent::Deleted) {
ESP_LOGD(TAG, "SPIT-REMOTE-Type Removed ID: %s GroupName: %s Name: %s\n", p_SPITType.getID().data(), p_SPITType.getGroupName().data(), p_SPITType.getName().data());
}
}
/**
* Called when the server has created, loaded or changed a SPITObject.
* May also occur when the server confirms a SPITObject created by the client, what the client should never do!!.
* false = server changed a existing SPITObject
* @param p_SPITType a reference to a copy of the associated SPITType instance.
* @param p_SPITObject a reference to a copy of the SPITObject instance.
*/
void onSpitObject(SPIT_Client::ObjectEvent event, const SPITType &p_SPITType, const SPITObject &p_SPITObject) {
if (event == SPIT_Client::ObjectEvent::Created) {
ESP_LOGI(TAG, "NEW SPIT-Object ID: %s Name: %s -> SPITType: %s", p_SPITObject.getID().data(), p_SPITObject.getName().data(), p_SPITType.getName().data());
// check your actions
// Whenever you provide an action for this SPITObject take this over in your code.
// call _SpitClient.setActionSPITObject(p_SPITObject.getID(), true);
// for our little excample as described in loadClientProject:
// We have only actions for SPITTypes - means every SPITObject with the associated "Actionx"-SPITTypes will trigger the action when it is played
//-> no need to do something here, because setActionSPITType has been set prior and the SPITObject will automatically inherits this
// SPITObjects with other SPITTypes asscciated will trigger no actions
} else if (event == SPIT_Client::ObjectEvent::Changed) {
ESP_LOGI(TAG, "SPIT-Object CHANGED ID: %s Name: %s -> SPITType: %s", p_SPITObject.getID().data(), p_SPITObject.getName().data(), p_SPITType.getName().data());
} else if (event == SPIT_Client::ObjectEvent::Deleted) {
ESP_LOGD(TAG, "SPIT-Object Removed ID: %s Name: %s\n", p_SPITObject.getID().data(), p_SPITObject.getName().data());
// Check your actions
// take the removal of the SPITObject over in your code
// CAUTION: Deleted SPITObjects can be added later again (perhaps per UNDO on the SPITServer)
//-> don't remove your assigned action immediately - keep it to reassign it later
}
}
/**
* Called when the server has created or changed a Remote SPITObject.
* A Remote SPITObject is used to remote control the SPITServer.
* @param p_New true = server created a new remote SPITObject
* false = server changed a existing remote SPITObject
* @param p_SPITType a reference to a copy of the associated SPITType instance.
* @param p_SPITObject a reference to a copy of the SPITObject instance.
*/
void onSpitObjectRemote(SPIT_Client::ObjectEvent event, const SPITType &p_SPITType, const SPITObject &p_SPITObject) {
if (event == SPIT_Client::ObjectEvent::Created) {
ESP_LOGI(TAG, "NEW SPIT-REMOTE-Object ID: %s Name: %s -> SPITType: %s", p_SPITObject.getID().data(), p_SPITObject.getName().data(), p_SPITType.getName().data());
// This is a remote SPITObject which can be used to remote control the server.
// If you want you can list this for later use
} else if (event == SPIT_Client::ObjectEvent::Changed) {
ESP_LOGI(TAG, "SPIT-REMOTE-Object CHANGED ID: %s Name: %s -> SPITType: %s", p_SPITObject.getID().data(), p_SPITObject.getName().data(), p_SPITType.getName().data());
} else if (event == SPIT_Client::ObjectEvent::Deleted) {
ESP_LOGD(TAG, "SPIT-REMOTE-Object Removed ID: %s Name: %s\n", p_SPITObject.getID().data(), p_SPITObject.getName().data());
// This is a remote SPITObject
// If you have listet this prior you should remove it from the list
}
}
// SPITServer may use several methods for playing SPITObjects.
// e.g. liveSHOWsoftware has the timeline and jingles which plays objects, where the timeline the main player is.
// SPIT distinguishes between the different players. All eventPlay... functions contain the information which player plays the SPIT object.
void onPlay(string p_PlayerID, string p_PlayerName, char p_PlayerType, bool p_PlayerIsRunning, long long p_TimeFrames) {
ESP_LOGD(TAG, "eventPlay Player: %s Runs: %d Time: %lld", p_PlayerName.data(), p_PlayerIsRunning, p_TimeFrames);
switch (p_PlayerType) {
case SPIT_Client::PLAYERTYPE_MAIN:
// p_FrameTimes contains the position inside the server's project, we can use it like a timecode
// int intMilliseconds = (int)(((double)p_TimeFrames / (double)m_ServerFramesPerSecond) * 1000);
break;
case SPIT_Client::PLAYERTYPE_CONTINOUS:
// The olayer is playing only single Object sequences sometime during the project
break;
case SPIT_Client::PLAYERTYPE_DISCRETE:
// Only discrete values where submitted, e.g the fader position of remote faders.
break;
}
}
/**
* Called when the SPITServer plays a SPITObject. For every played SPITObject one eventPlayObject is called.
* You can decide here which action should be done or not.
*/
void onPlaySpitType(string p_PlayerID, string spitTypeId, bool p_PlayerIsRunning, bool p_Started, bool p_Ended, double p_Value) {
// double doubleValue = ((double)p_PlayObject.getValue() / 1000000) * ((double)p_PlayObject.getValueFactor() / 1000000);
ESP_LOGD(TAG, "eventPlayObject Player: %s is running: %d Object Started: %d Stopped: %d", p_PlayerID.data(), p_PlayerIsRunning, p_Started, p_Ended);
// ESP_LOGD(TAG, " SPITType: %s SPITObject: %s Position: %lld Value: %f\n", spitTypeId.data(), p_SPITObject.getName().data(), p_PlayObject.getFrameInside(), doubleValue);
ESP_LOGD(TAG, " SPITType: %s Value: %f\n", spitTypeId.data(), p_Value);
if (!ProvidedSpitActions.contains(spitTypeId)) {
ESP_LOGE(TAG, "SPIT ID %s is not accepted", spitTypeId.c_str());
return;
}
if (p_Started == true) {
auto command = ProvidedSpitActions[spitTypeId];
ESP_LOGI(TAG, "Player: %s is running: %s Object", p_PlayerID.data(), spitTypeId.c_str());
commandQueue.enqueue(std::make_unique<MoveCommand>(command));
ESP_LOGI(TAG, "MyApp AKTION 1 started Value: %f and running: %d", p_Value, p_PlayerIsRunning);
}
}
/**
* Called when a remote SpitObject which value function is changed by the server.
* This allows the Client to adjust its display for the remote command.
* @param p_SPITType the assigned remote SPITType of p_SPITObject
* @param p_SPITObject the remote SPITObject which is changed
* @param p_FrameInsideObject can be discarded
* @param p_Value the new value
*/
void onPlayRemoteObjectStatus(const SPITType &p_SPITType, const SPITObject &p_SPITObject, long long p_FrameInsideObject, int p_Value) {
ESP_EARLY_LOGI(TAG, "eventPlayRemoteObjectStatus RemoteSPITType: %s RemoteSPITObject: %s Position: %lld Value: %d", p_SPITType.getName().data(), p_SPITObject.getName().data(), p_FrameInsideObject, p_Value);
}
void SpitClientApp() {
ESP_LOGI(TAG, "Starting SPIT task");
auto ptrMyApp = std::make_unique<MyApp>();
ESP_LOGI(TAG, "Starting SPIT task");
auto &provided = SpitTypes.Provided;
auto &expected = SpitTypes.Expected;
std::vector<LocalSpitObj> ProvidedObj;
ProvidedSpitActions[provided.startShuffle] = MoveCommand{.type = MoveType::Shuffle, .pos = 0};
ProvidedObj.push_back(LocalSpitObj{.iD = provided.startShuffle, .groupName = provided.group, .name = provided.startShuffle});
ExpectedObjects[expected.changingPos] = "";
for (size_t i = 0; i < 6; i++) {
ProvidedObj.push_back(LocalSpitObj{.iD = provided.moveToX[i], .groupName = provided.group, .name = provided.moveToX[i]});
ProvidedSpitActions[provided.moveToX[i]] = MoveCommand{.type = MoveType::StepTo, .pos = (uint8_t)(i + 1)};
ExpectedObjects[expected.inPosX[i]] = "";
}
// load a client project and start auto detection of SPITServers
ptrMyApp->init();
while (true) {
fos::Task::delayMs(10000);
ESP_LOGI(TAG, "Test LiveShow");
ptrMyApp->testLiveShowRemote(); //This is an excample how to remote control the liveSHOWsoftware
}
SPIT_Client spitClient(ClientName, ProjectName,
{
.onServerEvent = onServerEvent,
.onConnectionStatus = onConnectionStatus,
.onProjectUpdate = onProjectUpdate,
.onSpitType = onSpitType,
.onSpitTypeRemote = onSpitTypeRemote,
.onSpitObject = onSpitObject,
.onSpitObjectRemote = onSpitObjectRemote,
.onPlay = onPlay,
.onPlaySpitType = onPlaySpitType,
.onPlayRemoteObjectStatus = onPlayRemoteObjectStatus,
.onCheckSpitTypeVeto = onCheckSpitTypeVeto,
.onCheckSpitObjectVeto = onCheckSpitObjectVeto,
});
for (auto &obj : ProvidedObj) {
SPITType *ptrSPITType1Fix = spitClient.createSPITTypeFixed(obj.iD, obj.groupName, obj.name, 0, 0);
spitClient.setActionSPITType(ptrSPITType1Fix->getID(), true); // should be executed whenever a SPITObject with this SPITType is played
}
spitClient.clientProjectLoaded();
spitClient.startServerAutoDetect();
spitProjectFinish.take();
string serverProjectID = spitClient.getServerProjectID();
string serverProjectName = spitClient.getClientProjectName();
int serverFramesPerSecond = spitClient.getServerFramesPerSecond(); // set when the server reports his project, see eventProjectReport
ESP_LOGI(TAG, "eventProjectReport ID: %s Name: %s FramesPerSecond: %d", serverProjectID.data(), serverProjectName.data(), serverFramesPerSecond);
for (auto &&obj : ExpectedObjects) {
if (obj.second == "") {
ESP_LOGE(TAG, "No matching Remote ID for %s", obj.first.c_str());
}
}
while (true) {
fos::Task::delayMs(10000);
ESP_LOGI(TAG, "Test LiveShow");
auto res = responseQueue.dequeue();
if (res->type == ResponseType::StartMoving) {
spitClient.sendRemoteObject(ExpectedObjects[expected.changingPos]);
} else if (res->type == ResponseType::StepAt) {
spitClient.sendRemoteObject(ExpectedObjects[expected.inPosX[res->pos - 1]]);
}
}
}
void app_main(void) {
@@ -651,7 +994,7 @@ void app_main(void) {
static fos::TaskT<int> telnetSocket(telnetSocketTask, ipVersion, "tcpServer", 4096);
static fos::TaskT<int> dmxNode(dmxSocketTask, ipVersion, "dmxNode", 4096);
// static Task otaUpdate(SystemFunctions::OtaTaskEntry, "otaTask", 8192);
static fos::Task spit(SpitClientApp,"SpitClient");
static fos::Task spit(SpitClientApp, "SpitClient");
auto &store = NvsApi::getInstance();
auto logger = store.getValue(ConfigKeys.syslogDest);