248 lines
5.7 KiB
C++
248 lines
5.7 KiB
C++
#include "RuntimeServices.h"
|
|
|
|
#include "ControlServer.h"
|
|
#include "OscServer.h"
|
|
#include "RuntimeControlBridge.h"
|
|
#include "RuntimeHost.h"
|
|
#include <windows.h>
|
|
|
|
RuntimeServices::RuntimeServices() :
|
|
mControlServer(std::make_unique<ControlServer>()),
|
|
mOscServer(std::make_unique<OscServer>()),
|
|
mPollRunning(false),
|
|
mRegistryChanged(false),
|
|
mReloadRequested(false),
|
|
mPollFailed(false)
|
|
{
|
|
}
|
|
|
|
RuntimeServices::~RuntimeServices()
|
|
{
|
|
Stop();
|
|
}
|
|
|
|
bool RuntimeServices::Start(OpenGLComposite& composite, RuntimeHost& runtimeHost, std::string& error)
|
|
{
|
|
Stop();
|
|
|
|
if (!StartRuntimeControlServices(composite, runtimeHost, *this, *mControlServer, *mOscServer, error))
|
|
{
|
|
Stop();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void RuntimeServices::BeginPolling(RuntimeHost& runtimeHost)
|
|
{
|
|
StartPolling(runtimeHost);
|
|
}
|
|
|
|
void RuntimeServices::Stop()
|
|
{
|
|
StopPolling();
|
|
|
|
if (mOscServer)
|
|
mOscServer->Stop();
|
|
|
|
if (mControlServer)
|
|
mControlServer->Stop();
|
|
}
|
|
|
|
void RuntimeServices::BroadcastState()
|
|
{
|
|
if (mControlServer)
|
|
mControlServer->BroadcastState();
|
|
}
|
|
|
|
void RuntimeServices::RequestBroadcastState()
|
|
{
|
|
if (mControlServer)
|
|
mControlServer->RequestBroadcastState();
|
|
}
|
|
|
|
bool RuntimeServices::QueueOscUpdate(const std::string& layerKey, const std::string& parameterKey, const std::string& valueJson, std::string& error)
|
|
{
|
|
(void)error;
|
|
|
|
PendingOscUpdate update;
|
|
update.layerKey = layerKey;
|
|
update.parameterKey = parameterKey;
|
|
update.valueJson = valueJson;
|
|
|
|
const std::string routeKey = layerKey + "\n" + parameterKey;
|
|
{
|
|
std::lock_guard<std::mutex> lock(mPendingOscMutex);
|
|
mPendingOscUpdates[routeKey] = std::move(update);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool RuntimeServices::ApplyPendingOscUpdates(std::vector<AppliedOscUpdate>& appliedUpdates, std::string& error)
|
|
{
|
|
appliedUpdates.clear();
|
|
|
|
std::map<std::string, PendingOscUpdate> pending;
|
|
{
|
|
std::lock_guard<std::mutex> lock(mPendingOscMutex);
|
|
if (mPendingOscUpdates.empty())
|
|
return true;
|
|
pending.swap(mPendingOscUpdates);
|
|
}
|
|
|
|
for (const auto& entry : pending)
|
|
{
|
|
JsonValue targetValue;
|
|
std::string parseError;
|
|
if (!ParseJson(entry.second.valueJson, targetValue, parseError))
|
|
{
|
|
OutputDebugStringA(("OSC queued value parse failed: " + parseError + "\n").c_str());
|
|
continue;
|
|
}
|
|
|
|
AppliedOscUpdate appliedUpdate;
|
|
appliedUpdate.routeKey = entry.first;
|
|
appliedUpdate.layerKey = entry.second.layerKey;
|
|
appliedUpdate.parameterKey = entry.second.parameterKey;
|
|
appliedUpdate.targetValue = targetValue;
|
|
appliedUpdates.push_back(std::move(appliedUpdate));
|
|
}
|
|
|
|
(void)error;
|
|
return true;
|
|
}
|
|
|
|
bool RuntimeServices::QueueOscCommit(const std::string& routeKey, const std::string& layerKey, const std::string& parameterKey, const JsonValue& value, uint64_t generation, std::string& error)
|
|
{
|
|
(void)error;
|
|
|
|
PendingOscCommit commit;
|
|
commit.routeKey = routeKey;
|
|
commit.layerKey = layerKey;
|
|
commit.parameterKey = parameterKey;
|
|
commit.value = value;
|
|
commit.generation = generation;
|
|
|
|
{
|
|
std::lock_guard<std::mutex> lock(mPendingOscCommitMutex);
|
|
mPendingOscCommits[routeKey] = std::move(commit);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void RuntimeServices::ClearOscState()
|
|
{
|
|
{
|
|
std::lock_guard<std::mutex> lock(mPendingOscMutex);
|
|
mPendingOscUpdates.clear();
|
|
}
|
|
{
|
|
std::lock_guard<std::mutex> lock(mPendingOscCommitMutex);
|
|
mPendingOscCommits.clear();
|
|
}
|
|
{
|
|
std::lock_guard<std::mutex> lock(mCompletedOscCommitMutex);
|
|
mCompletedOscCommits.clear();
|
|
}
|
|
}
|
|
|
|
void RuntimeServices::ConsumeCompletedOscCommits(std::vector<CompletedOscCommit>& completedCommits)
|
|
{
|
|
completedCommits.clear();
|
|
|
|
std::lock_guard<std::mutex> lock(mCompletedOscCommitMutex);
|
|
if (mCompletedOscCommits.empty())
|
|
return;
|
|
|
|
completedCommits.swap(mCompletedOscCommits);
|
|
}
|
|
|
|
RuntimePollEvents RuntimeServices::ConsumePollEvents()
|
|
{
|
|
RuntimePollEvents events;
|
|
events.registryChanged = mRegistryChanged.exchange(false);
|
|
events.reloadRequested = mReloadRequested.exchange(false);
|
|
events.failed = mPollFailed.exchange(false);
|
|
|
|
if (events.failed)
|
|
{
|
|
std::lock_guard<std::mutex> lock(mPollErrorMutex);
|
|
events.error = mPollError;
|
|
}
|
|
|
|
return events;
|
|
}
|
|
|
|
void RuntimeServices::StartPolling(RuntimeHost& runtimeHost)
|
|
{
|
|
if (mPollRunning.exchange(true))
|
|
return;
|
|
|
|
mPollThread = std::thread([this, &runtimeHost]() { PollLoop(runtimeHost); });
|
|
}
|
|
|
|
void RuntimeServices::StopPolling()
|
|
{
|
|
if (!mPollRunning.exchange(false))
|
|
return;
|
|
|
|
if (mPollThread.joinable())
|
|
mPollThread.join();
|
|
}
|
|
|
|
void RuntimeServices::PollLoop(RuntimeHost& runtimeHost)
|
|
{
|
|
while (mPollRunning)
|
|
{
|
|
std::map<std::string, PendingOscCommit> pendingCommits;
|
|
{
|
|
std::lock_guard<std::mutex> lock(mPendingOscCommitMutex);
|
|
pendingCommits.swap(mPendingOscCommits);
|
|
}
|
|
for (const auto& entry : pendingCommits)
|
|
{
|
|
std::string commitError;
|
|
if (runtimeHost.UpdateLayerParameterByControlKey(
|
|
entry.second.layerKey,
|
|
entry.second.parameterKey,
|
|
entry.second.value,
|
|
false,
|
|
commitError))
|
|
{
|
|
CompletedOscCommit completedCommit;
|
|
completedCommit.routeKey = entry.second.routeKey;
|
|
completedCommit.generation = entry.second.generation;
|
|
std::lock_guard<std::mutex> lock(mCompletedOscCommitMutex);
|
|
mCompletedOscCommits.push_back(std::move(completedCommit));
|
|
}
|
|
else if (!commitError.empty())
|
|
{
|
|
OutputDebugStringA(("OSC commit failed: " + commitError + "\n").c_str());
|
|
}
|
|
}
|
|
|
|
bool registryChanged = false;
|
|
bool reloadRequested = false;
|
|
std::string runtimeError;
|
|
if (!runtimeHost.PollFileChanges(registryChanged, reloadRequested, runtimeError))
|
|
{
|
|
{
|
|
std::lock_guard<std::mutex> lock(mPollErrorMutex);
|
|
mPollError = runtimeError;
|
|
}
|
|
mPollFailed = true;
|
|
}
|
|
else
|
|
{
|
|
if (registryChanged)
|
|
mRegistryChanged = true;
|
|
if (reloadRequested)
|
|
mReloadRequested = true;
|
|
}
|
|
|
|
for (int i = 0; i < 25 && mPollRunning; ++i)
|
|
Sleep(10);
|
|
}
|
|
}
|