#include <string.h>
#include <string>
#include <thread>
#include <memory>
#include <deque>
#include <mutex>
#include "Init.h"
#include "Logger.h"
#include "Server.h"
namespace ggk
{
static const int kMaxAsyncInitCheckIntervalMS = 10;
static std::thread serverThread;
volatile static GGKServerRunState serverRunState = EUninitialized;
volatile static GGKServerHealth serverHealth = EOk;
static GPrintFunc printHandlerGLib;
static GPrintFunc printerrHandlerGLib;
static GLogFunc logHandlerGLib;
typedef std::tuple<std::string, std::string> QueueEntry;
std::deque<QueueEntry> updateQueue;
std::mutex updateQueueMutex;
void setServerRunState(GGKServerRunState newState)
{
Logger::status(SSTR << "** SERVER RUN STATE CHANGED: " << ggkGetServerRunStateString(serverRunState) << " -> " << ggkGetServerRunStateString(newState));
serverRunState = newState;
}
void setServerHealth(GGKServerHealth newState)
{
serverHealth = newState;
}
};
using namespace ggk;
void ggkLogRegisterDebug(GGKLogReceiver receiver) { Logger::registerDebugReceiver(receiver); }
void ggkLogRegisterInfo(GGKLogReceiver receiver) { Logger::registerInfoReceiver(receiver); }
void ggkLogRegisterStatus(GGKLogReceiver receiver) { Logger::registerStatusReceiver(receiver); }
void ggkLogRegisterWarn(GGKLogReceiver receiver) { Logger::registerWarnReceiver(receiver); }
void ggkLogRegisterError(GGKLogReceiver receiver) { Logger::registerErrorReceiver(receiver); }
void ggkLogRegisterFatal(GGKLogReceiver receiver) { Logger::registerFatalReceiver(receiver); }
void ggkLogRegisterTrace(GGKLogReceiver receiver) { Logger::registerTraceReceiver(receiver); }
void ggkLogRegisterAlways(GGKLogReceiver receiver) { Logger::registerAlwaysReceiver(receiver); }
int ggkNofifyUpdatedCharacteristic(const char *pObjectPath)
{
return ggkPushUpdateQueue(pObjectPath, "org.bluez.GattCharacteristic1") != 0;
}
int ggkNofifyUpdatedDescriptor(const char *pObjectPath)
{
return ggkPushUpdateQueue(pObjectPath, "org.bluez.GattDescriptor1") != 0;
}
int ggkPushUpdateQueue(const char *pObjectPath, const char *pInterfaceName)
{
QueueEntry t(pObjectPath, pInterfaceName);
std::lock_guard<std::mutex> guard(updateQueueMutex);
updateQueue.push_front(t);
return 1;
}
int ggkPopUpdateQueue(char *pElementBuffer, int elementLen, int keep)
{
std::string result;
{
std::lock_guard<std::mutex> guard(updateQueueMutex);
if (updateQueue.empty()) { return 0; }
QueueEntry t = updateQueue.back();
result = std::get<0>(t) + "|" + std::get<1>(t);
if (result.length() + 1 > static_cast<size_t>(elementLen)) { return -1; }
if (keep == 0)
{
updateQueue.pop_back();
}
}
memcpy(pElementBuffer, result.c_str(), result.length() + 1);
return 1;
}
int ggkUpdateQueueIsEmpty()
{
std::lock_guard<std::mutex> guard(updateQueueMutex);
return updateQueue.empty() ? 1 : 0;
}
int ggkUpdateQueueSize()
{
std::lock_guard<std::mutex> guard(updateQueueMutex);
return updateQueue.size();
}
void ggkUpdateQueueClear()
{
std::lock_guard<std::mutex> guard(updateQueueMutex);
updateQueue.clear();
}
GGKServerRunState ggkGetServerRunState()
{
return serverRunState;
}
const char *ggkGetServerRunStateString(GGKServerRunState state)
{
switch(state)
{
case EUninitialized: return "Uninitialized";
case EInitializing: return "Initializing";
case ERunning: return "Running";
case EStopping: return "Stopping";
case EStopped: return "Stopped";
default: return "Unknown";
}
}
int ggkIsServerRunning()
{
return serverRunState == ERunning ? 1 : 0;
}
GGKServerHealth ggkGetServerHealth()
{
return serverHealth;
}
const char *ggkGetServerHealthString(GGKServerHealth state)
{
switch(state)
{
case EOk: return "Ok";
case EFailedInit: return "Failed initialization";
case EFailedRun: return "Failed run";
default: return "Unknown";
}
}
void ggkTriggerShutdown()
{
shutdown();
}
int ggkShutdownAndWait()
{
ggkTriggerShutdown();
return ggkWait();
}
int ggkWait()
{
int result = 0;
try
{
Logger::info("Stopping GGK server");
serverThread.join();
result = 1;
}
catch(std::system_error &ex)
{
if (ex.code() == std::errc::invalid_argument)
{
Logger::warn(SSTR << "Server thread was not joinable during stop(): " << ex.what());
}
else if (ex.code() == std::errc::no_such_process)
{
Logger::warn(SSTR << "Server thread was not valid during stop(): " << ex.what());
}
else if (ex.code() == std::errc::resource_deadlock_would_occur)
{
Logger::warn(SSTR << "Deadlock avoided in call to stop() (did the server thread try to stop itself?): " << ex.what());
}
else
{
Logger::warn(SSTR << "Unknown system_error code (" << ex.code() << "): " << ex.what());
}
}
g_set_print_handler(printHandlerGLib);
g_set_printerr_handler(printerrHandlerGLib);
g_log_set_default_handler(logHandlerGLib, nullptr);
return result;
}
int ggkStart(const char *pServiceName, const char *pAdvertisingName, const char *pAdvertisingShortName,
GGKServerDataGetter getter, GGKServerDataSetter setter, int maxAsyncInitTimeoutMS)
{
printHandlerGLib = g_set_print_handler([](const gchar *string)
{
Logger::info(string);
});
printerrHandlerGLib = g_set_printerr_handler([](const gchar *string)
{
Logger::error(string);
});
logHandlerGLib = g_log_set_default_handler([](const gchar *log_domain, GLogLevelFlags log_levels, const gchar *message, gpointer )
{
std::string str = std::string(log_domain) + ": " + message;
if ((log_levels & (G_LOG_FLAG_RECURSION|G_LOG_FLAG_FATAL)) != 0)
{
Logger::fatal(str);
}
else if ((log_levels & (G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_ERROR)) != 0)
{
Logger::error(str);
}
else if ((log_levels & G_LOG_LEVEL_WARNING) != 0)
{
Logger::warn(str);
}
else if ((log_levels & G_LOG_LEVEL_DEBUG) != 0)
{
Logger::debug(str);
}
else
{
Logger::info(str);
}
}, nullptr);
Logger::info(SSTR << "Starting GGK server '" << pAdvertisingName << "'");
TheServer = std::make_shared<Server>(pServiceName, pAdvertisingName, pAdvertisingShortName, getter, setter);
try
{
serverThread = std::thread(runServerThread);
}
catch(std::system_error &ex)
{
if (ex.code() == std::errc::resource_unavailable_try_again)
{
Logger::error(SSTR << "Server thread was unable to start during start(): " << ex.what());
setServerRunState(EStopped);
return 0;
}
}
int retryTimeMS = 0;
while (retryTimeMS < maxAsyncInitTimeoutMS && ggkGetServerRunState() <= EInitializing)
{
std::this_thread::sleep_for(std::chrono::milliseconds(kMaxAsyncInitCheckIntervalMS));
retryTimeMS += kMaxAsyncInitCheckIntervalMS;
}
if (ggkGetServerRunState() < ERunning || ggkGetServerHealth() != EOk)
{
setServerHealth(EFailedInit);
if (!ggkShutdownAndWait())
{
Logger::warn(SSTR << "Unable to stop the server from error in start()");
}
return 0;
}
return 1;
}