This commit is contained in:
sylvieee-iot 2025-02-08 16:47:29 +01:00 committed by GitHub
commit b639799a59
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 26382 additions and 0 deletions

3
.gitmodules vendored
View file

@ -67,6 +67,9 @@
[submodule "third_party/capstone"]
path = third_party/capstone
url = https://github.com/capstone-engine/capstone
[submodule "third_party/open-bp-cpp/third_party/IXWebSocket"]
path = third_party/open-bp-cpp/third_party/IXWebSocket
url = https://github.com/machinezone/IXWebSocket
[submodule "third_party/hips"]
path = third_party/hips
url = https://github.com/wheremyfoodat/Hips

View file

@ -403,6 +403,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp
include/audio/audio_interpolation.hpp include/audio/hle_mixer.hpp include/audio/dsp_simd.hpp
include/services/dsp_firmware_db.hpp include/frontend_settings.hpp include/fs/archive_twl_photo.hpp
include/fs/archive_twl_sound.hpp include/fs/archive_card_spi.hpp include/services/ns.hpp
include/external_haptics_manager.hpp
)
cmrc_add_resource_library(
@ -432,6 +433,11 @@ if(ENABLE_LUAJIT AND NOT ANDROID)
add_subdirectory(third_party/libuv)
target_link_libraries(AlberCore PRIVATE uv_a)
include_directories(third_party/open-bp-cpp/include)
include_directories(third_party/open-bp-cpp/third_party/IXWebSocket/ixwebsocket)
add_subdirectory(third_party/open-bp-cpp)
target_link_libraries(AlberCore PRIVATE buttplugCpp)
endif()
set(GL_CONTEXT_SOURCE_FILES "")

View file

@ -0,0 +1,51 @@
#pragma once
#include <chrono>
#include <cstdio>
#include <string>
#include <thread>
#include <vector>
#include "buttplugclient.h"
class ExternalHapticsManager {
Client client;
std::vector<DeviceClass> devices;
SensorClass sensors;
public:
ExternalHapticsManager() : client("ws://127.0.0.1", 12345) {}
void connect() {
client.connect([](const mhl::Messages message) {
switch (message.messageType) {
case mhl::MessageTypes::DeviceList: std::printf("Device List callback\n"); break;
case mhl::MessageTypes::DeviceAdded: std::printf("Device List callback\n"); break;
case mhl::MessageTypes::ServerInfo: std::printf("Server info callback\n"); break;
case mhl::MessageTypes::DeviceRemoved: std::printf("Device Removed callback\n"); break;
case mhl::MessageTypes::SensorReading: std::printf("Sensor reading callback\n"); break;
default: std::printf("Unknown message");
}
});
}
void requestDeviceList() { client.requestDeviceList(); }
void startScan() { client.startScan(); }
void stopScan() { client.stopScan(); }
void stopAllDevices() { client.stopAllDevices(); }
void getDevices() { devices = client.getDevices(); }
void getSensors() { sensors = client.getSensors(); }
void sendScalar(int index, double value) {
if (index < devices.size()) {
client.sendScalar(devices[index], value);
}
}
void stopDevice(int index) {
if (index < devices.size()) {
client.stopDevice(devices[index]);
}
}
};

View file

@ -2,6 +2,7 @@
#include <teakra/disassembler.h>
#include <array>
#include <memory>
#include "capstone.hpp"
#include "emulator.hpp"
@ -11,6 +12,8 @@
extern "C" {
#include "luv.h"
}
#include "external_haptics_manager.hpp"
#endif
void LuaManager::initialize() {
@ -268,6 +271,80 @@ static int disassembleTeakThunk(lua_State* L) {
return 1;
}
#ifndef __ANDROID__
// Haptics functions
namespace Haptics {
std::unique_ptr<ExternalHapticsManager> hapticsManager = nullptr;
static int initHapticsThunk(lua_State* L) {
if (hapticsManager == nullptr) {
hapticsManager.reset(new ExternalHapticsManager());
}
return 0;
}
#define HAPTICS_THUNK(func) \
static int func##Thunk(lua_State* L) { \
if (hapticsManager != nullptr) { \
hapticsManager->func(); \
} \
\
return 0; \
}
HAPTICS_THUNK(startScan)
HAPTICS_THUNK(stopScan)
HAPTICS_THUNK(connect)
HAPTICS_THUNK(requestDeviceList)
HAPTICS_THUNK(getDevices)
HAPTICS_THUNK(getSensors)
HAPTICS_THUNK(stopAllDevices)
#undef HAPTICS_THUNK
static int sendScalarThunk(lua_State* L) {
const int device = (int)lua_tonumber(L, 1);
const auto value = lua_tonumber(L, 2);
if (hapticsManager != nullptr) {
hapticsManager->sendScalar(device, value);
}
return 2;
}
static int stopDeviceThunk(lua_State* L) {
const int device = (int)lua_tonumber(L, 1);
if (hapticsManager != nullptr) {
hapticsManager->stopDevice(device);
}
return 1;
}
// clang-format off
static constexpr luaL_Reg functions[] = {
{ "initHaptics", initHapticsThunk },
{ "startScan", startScanThunk },
{ "stopScan", stopScanThunk },
{ "connect", connectThunk },
{ "requestDeviceList", requestDeviceListThunk },
{ "getDevices", getDevicesThunk },
{ "getSensors", getSensorsThunk },
{ "stopAllDevices", stopAllDevicesThunk },
{ "sendScalar", sendScalarThunk },
{ "stopDevice", stopDeviceThunk },
};
// clang-format on
void registerFunctions(lua_State* L) {
luaL_register(L, "HAPTICS", functions);
}
} // namespace Haptics
#endif
// clang-format off
static constexpr luaL_Reg functions[] = {
{ "__read8", read8Thunk },
@ -345,6 +422,20 @@ void LuaManager::initializeThunks() {
ButtonLeft = __ButtonLeft,
ButtonRight= __ButtonRight,
}
Buzz = {
initHaptics = function() HAPTICS.initHaptics() end,
startScan = function() HAPTICS.startScan() end,
stopScan = function() HAPTICS.stopScan() end,
connect = function() HAPTICS.connect() end,
requestDeviceList = function() HAPTICS.requestDeviceList() end,
getDevices = function() HAPTICS.getDevices() end,
getSensors = function() HAPTICS.getSensors() end,
stopAllDevices = function() HAPTICS.stopAllDevices() end,
sendScalar = function(device, value) HAPTICS.sendScalar(device, value) end,
stopDevice = function(device) HAPTICS.stopDevice(device) end,
}
)";
auto addIntConstant = [&]<typename T>(T x, const char* name) {
@ -353,6 +444,10 @@ void LuaManager::initializeThunks() {
};
luaL_register(L, "GLOBALS", functions);
#ifndef __ANDROID__
Haptics::registerFunctions(L);
#endif
// Add values for event enum
addIntConstant(LuaEvent::Frame, "__Frame");

63
third_party/open-bp-cpp/.gitattributes vendored Normal file
View file

@ -0,0 +1,63 @@
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

40
third_party/open-bp-cpp/.gitignore vendored Normal file
View file

@ -0,0 +1,40 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
/.vs
/out/build/x64-debug
/CMakeFiles
/_3rdParty
/CMakeCache.txt
/cmake_install.cmake
/Makefile
/buttplugCpp

35
third_party/open-bp-cpp/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,35 @@
# CMakeList.txt : CMake project for buttplugCpp, include source and define
# project specific logic here.
#
cmake_minimum_required (VERSION 3.8)
# Enable Hot Reload for MSVC compilers if supported.
if (POLICY CMP0141)
cmake_policy(SET CMP0141 NEW)
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")
endif()
project(buttplugCpp)
# Add source to this project's executable.
file(GLOB SRC_FILES
"source/*.cpp"
"include/*.h"
"include/*.hpp"
)
add_library(buttplugCpp STATIC ${SRC_FILES})
set(USE_ZLIB FALSE)
target_include_directories(buttplugCpp PUBLIC third_party/IXWebSocket/ixwebsocket)
target_include_directories(buttplugCpp PUBLIC include)
add_subdirectory(third_party/IXWebSocket)
find_package(Threads REQUIRED)
target_link_libraries(buttplugCpp PUBLIC ixwebsocket::ixwebsocket Threads::Threads)
if (CMAKE_VERSION VERSION_GREATER 3.12)
set_property(TARGET buttplugCpp PROPERTY CXX_STANDARD 11)
endif()
# TODO: Add tests and install targets if needed.

View file

@ -0,0 +1,61 @@
{
"version": 3,
"configurePresets": [
{
"name": "windows-base",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/out/build/${presetName}",
"installDir": "${sourceDir}/out/install/${presetName}",
"cacheVariables": {
"CMAKE_C_COMPILER": "cl.exe",
"CMAKE_CXX_COMPILER": "cl.exe"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
},
{
"name": "x64-debug",
"displayName": "x64 Debug",
"inherits": "windows-base",
"architecture": {
"value": "x64",
"strategy": "external"
},
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "x64-release",
"displayName": "x64 Release",
"inherits": "x64-debug",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "x86-debug",
"displayName": "x86 Debug",
"inherits": "windows-base",
"architecture": {
"value": "x86",
"strategy": "external"
},
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "x86-release",
"displayName": "x86 Release",
"inherits": "x86-debug",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
}
]
}

17
third_party/open-bp-cpp/README.md vendored Normal file
View file

@ -0,0 +1,17 @@
# buttplugCpp
## How to use
I made this project in Visual Studio 2022, but it uses CMake. I currently am struggling to get a proper library CMake going, so simply include the src and include directories in your project and add them with:
```
file(GLOB SRC_FILES
"src/*.cpp"
"include/*.h"
)
target_sources(buttplugCpp PUBLIC
${SRC_FILES}
)
```
in your CMake file. Also make sure to build IXWebSockets (without zlib, unless you want to deal with getting it working with CMake). Nlohmann Json dependency is handled by Hunter, so make sure you have Hunter in your project too. I provide an example CMake file which makes the example program in example directory.
## More info
Currently this library was tested (not very extensively) with Linux and Windows. The C++ version is C++11. This library and its documentation is still WIP.

View file

@ -0,0 +1,42 @@
// buttplugCpp.cpp : Defines the entry point for the application.
//
#include "buttplugCpp.h"
#include <cstdio>
void callbackFunction(const mhl::Messages msg) {
switch (msg.messageType) {
case mhl::MessageTypes::DeviceList: std::printf("Device List callback\n"); break;
case mhl::MessageTypes::DeviceAdded: std::printf("Device List callback\n"); break;
case mhl::MessageTypes::ServerInfo: std::printf("Server info callback\n"); break;
case mhl::MessageTypes::DeviceRemoved: std::printf("Device Removed callback\n"); break;
case mhl::MessageTypes::SensorReading: std::printf("Sensor reading callback\n"); break;
default: printf("Unknown message")
}
}
int main()
{
std::string url = "ws://127.0.0.1";
std::cout << "\n";
Client client(url, 12345, "test.txt");
client.connect(callbackFunction);
client.requestDeviceList();
client.startScan();
while (1) {
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
client.stopScan();
break;
}
//std::vector<DeviceClass> myDevices = client.getDevices();
//client.sendScalar(myDevices[0], 0.5);
//client.sendScalar(myDevices[1], 0.5);
//client.sensorSubscribe(myDevices[0], 0);
//std::this_thread::sleep_for(std::chrono::milliseconds(20000));
//client.sensorUnsubscribe(myDevices[0], 0);
//client.stopAllDevices();
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
return 0;
}

View file

@ -0,0 +1,9 @@
// buttplugCpp.h : Include file for standard system include files,
// or project specific include files.
#pragma once
#include <iostream>
#include "../include/buttplugclient.h"
// TODO: Reference additional headers your program requires here.

View file

@ -0,0 +1,113 @@
#include <string>
#include <functional>
#include <iostream>
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <sstream>
#include <vector>
#include <queue>
#include <IXWebSocket.h>
#ifdef _WIN32
#include <IXNetSystem.h>
#endif
#include "messageHandler.h"
#include "log.h"
typedef msg::SensorReading SensorClass;
// Helper class to store devices and access them outside of the library.
class DeviceClass {
public:
std::string deviceName;
std::string displayName;
std::vector<std::string> commandTypes;
std::vector<std::string> sensorTypes;
unsigned int deviceID;
};
// Main client class
class Client {
public:
// Constructor which initialized websockets for Windows. Add an IFDEF depending on compilation OS for portability.
Client(std::string url, unsigned int port) {
#ifdef _WIN32
ix::initNetSystem();
#endif
lUrl = url;
lPort = port;
}
Client(std::string url, unsigned int port, std::string logfile) {
#ifdef _WIN32
ix::initNetSystem();
#endif
lUrl = url;
lPort = port;
logging = 1;
if (!logfile.empty()) logInfo.init(logfile);
else logInfo.init("log.txt");
}
~Client() {
#ifdef _WIN32
ix::uninitNetSystem();
#endif
}
int connect(void (*callFunc)(const mhl::Messages));
// Atomic variables to store connection status. Can be accessed outside library too since atomic.
std::atomic<int> wsConnected{0};
std::atomic<int> isConnecting{0};
std::atomic<int> clientConnected{0};
// Condition variables for the atomics, we want C++11 support
std::condition_variable condClient;
std::condition_variable condWs;
// Public functions that send requests to server.
void startScan();
void stopScan();
void requestDeviceList();
void stopDevice(DeviceClass dev);
void stopAllDevices();
void sendScalar(DeviceClass dev, double str);
void sensorRead(DeviceClass dev, int senIndex);
void sensorSubscribe(DeviceClass dev, int senIndex);
void sensorUnsubscribe(DeviceClass dev, int senIndex);
// Mutex blocked function which grabs the currently connected devices and sensor reads.
std::vector<DeviceClass> getDevices();
SensorClass getSensors();
private:
// URL variables for the websocket.
std::string FullUrl;
std::string lUrl;
unsigned int lPort;
int logging = 0;
Logger logInfo;
ix::WebSocket webSocket;
// Message handler class, which takes messages, parses them and makes them to classes.
mhl::Messages messageHandler;
// Queue variable for passing received messages from server.
std::queue<std::string> q;
// Condition variabel to wait for received messages in the queue.
std::condition_variable cond;
// Mutex to ensure no race conditions.
std::mutex msgMx;
// Callback function for when a message is received and handled.
std::function<void(const mhl::Messages&)> messageCallback;
// Device and sensor class vector which is grabbed outside of the library.
std::vector<DeviceClass> devices;
SensorClass sensorData;
void connectServer();
void callbackFunction(const ix::WebSocketMessagePtr& msg);
void messageHandling();
void sendMessage(json msg, mhl::MessageTypes mType);
void updateDevices();
int findDevice(DeviceClass dev);
};

View file

@ -0,0 +1,42 @@
#include <string>
#include <vector>
#ifdef DEBUG
#define DEBUG_MSG(str) do { std::cout << str << std::endl; } while( false )
#else
#define DEBUG_MSG(str) do { } while ( false )
#endif
class DeviceCmdAttr {
public:
std::string FeatureDescriptor = "";
unsigned int StepCount = 0;
std::string ActuatorType = "";
std::string SensorType = "";
std::vector<int> SensorRange; // every two steps
//std::vector<std::string> Endpoints;
};
class DeviceCmd {
public:
std::string CmdType = "";
std::string StopDeviceCmd = "";
std::vector<DeviceCmdAttr> DeviceCmdAttributes;
};
class Device {
public:
std::string DeviceName;
unsigned int DeviceIndex;
unsigned int DeviceMessageTimingGap = 0;
std::string DeviceDisplayName = "";
std::vector<DeviceCmd> DeviceMessages;
};
class Scalar {
public:
unsigned int Index;
double ScalarVal;
std::string ActuatorType;
};

24766
third_party/open-bp-cpp/include/json.hpp vendored Normal file

File diff suppressed because it is too large Load diff

31
third_party/open-bp-cpp/include/log.h vendored Normal file
View file

@ -0,0 +1,31 @@
#include <fstream>
#include <vector>
#include <chrono>
class RequestQueue {
public:
unsigned int id = 1;
std::string requestType;
};
class Logger {
public:
Logger() {
start = std::chrono::system_clock::now();
}
~Logger() {
logFile.close();
}
void init(std::string filename);
void logSentMessage(std::string rqType, unsigned int id);
void logReceivedMessage(std::string repType, unsigned int id);
private:
std::fstream logFile;
std::vector<RequestQueue> rQueue;
// Using time point and system_clock
std::chrono::time_point<std::chrono::system_clock> start, end;
};

View file

@ -0,0 +1,95 @@
#include<string>
#include<map>
#include "messages.h"
namespace mhl {
// Enum class for message types for convenience.
enum class MessageTypes {
Ok,
Error,
Ping,
RequestServerInfo,
ServerInfo,
StartScanning,
StopScanning,
ScanningFinished,
RequestDeviceList,
DeviceList,
DeviceAdded,
DeviceRemoved,
StopDeviceCmd,
StopAllDevices,
ScalarCmd,
LinearCmd,
RotateCmd,
SensorReadCmd,
SensorReading,
SensorSubscribeCmd,
SensorUnsubscribeCmd
};
typedef std::map<MessageTypes, std::string> MessageMap_t;
// Class for request messages.
class Requests {
public:
msg::RequestServerInfo requestServerInfo;
msg::StartScanning startScanning;
msg::StopScanning stopScanning;
msg::ScanningFinished scanningFinished;
msg::RequestDeviceList requestDeviceList;
msg::DeviceRemoved deviceRemoved;
msg::StopDeviceCmd stopDeviceCmd;
msg::StopAllDevices stopAllDevices;
msg::ScalarCmd scalarCmd;
msg::SensorReadCmd sensorReadCmd;
msg::SensorSubscribeCmd sensorSubscribeCmd;
msg::SensorUnsubscribeCmd sensorUnsubscribeCmd;
};
// Class for messages received and for handling all types of messages.
class Messages {
public:
MessageTypes messageType = MessageTypes::Ok;
unsigned int Id;
// A map for message strings corresponding to enum. This is in this class since it is more convenient.
MessageMap_t messageMap = {
{MessageTypes::Ok, "Ok"},
{MessageTypes::Error, "Error"},
{MessageTypes::Ping, "Ping"},
{MessageTypes::RequestServerInfo, "RequestServerInfo"},
{MessageTypes::ServerInfo, "ServerInfo"},
{MessageTypes::StartScanning, "StartScanning"},
{MessageTypes::StopScanning, "StopScanning"},
{MessageTypes::ScanningFinished, "ScanningFinished"},
{MessageTypes::RequestDeviceList, "RequestDeviceList"},
{MessageTypes::DeviceList, "DeviceList"},
{MessageTypes::DeviceAdded, "DeviceAdded"},
{MessageTypes::DeviceRemoved, "DeviceRemoved"},
{MessageTypes::StopDeviceCmd, "StopDeviceCmd"},
{MessageTypes::StopAllDevices, "StopAllDevices"},
{MessageTypes::ScalarCmd, "ScalarCmd"},
{MessageTypes::LinearCmd, "LinearCmd"},
{MessageTypes::RotateCmd, "RotateCmd"},
{MessageTypes::SensorReadCmd, "SensorReadCmd"},
{MessageTypes::SensorReading, "SensorReading"},
{MessageTypes::SensorSubscribeCmd, "SensorSubscribeCmd"},
{MessageTypes::SensorUnsubscribeCmd, "SensorUnsubscribeCmd"}
};
msg::Ok ok;
msg::Error error;
msg::ServerInfo serverInfo;
msg::DeviceList deviceList;
msg::DeviceAdded deviceAdded;
msg::DeviceRemoved deviceRemoved;
msg::SensorReading sensorReading;
// Both server message and requests are handled in this class.
void handleServerMessage(json& msg);
json handleClientRequest(Requests req);
private:
};
}

View file

@ -0,0 +1,151 @@
#include "helperClasses.h"
#include "json.hpp"
#include <iostream>
#include <string>
#include <vector>
using json = nlohmann::json;
// Classes for the various message types and functions to convert to/from json.
namespace msg {
class Ok {
public:
unsigned int Id = 1;
//NLOHMANN_DEFINE_TYPE_INTRUSIVE(Ok, Id);
};
class Error {
public:
unsigned int Id = 1;
std::string ErrorMessage = "";
int ErrorCode = 0;
//NLOHMANN_DEFINE_TYPE_INTRUSIVE(Error, Id, ErrorMessage, ErrorCode);
};
class RequestServerInfo {
public:
unsigned int Id = 1;
std::string ClientName = "Default Client";
unsigned int MessageVersion = 3;
};
class ServerInfo {
public:
unsigned int Id;
std::string ServerName;
unsigned int MessageVersion;
unsigned int MaxPingTime;
//NLOHMANN_DEFINE_TYPE_INTRUSIVE(ServerInfo, Id, ServerName, MessageVersion, MaxPingTime);
};
class StartScanning {
public:
unsigned int Id = 1;
};
class StopScanning {
public:
unsigned int Id = 1;
};
class ScanningFinished {
public:
unsigned int Id = 1;
//NLOHMANN_DEFINE_TYPE_INTRUSIVE(ScanningFinished, Id);
};
class RequestDeviceList {
public:
unsigned int Id = 1;
};
class DeviceList {
public:
unsigned int Id = 1;
std::vector<Device> Devices;
};
class DeviceAdded {
public:
unsigned int Id = 1;
Device device;
};
class DeviceRemoved {
public:
unsigned int Id = 1;
unsigned int DeviceIndex;
};
class StopDeviceCmd {
public:
unsigned int Id = 1;
unsigned int DeviceIndex;
};
class StopAllDevices {
public:
unsigned int Id = 1;
};
class ScalarCmd {
public:
unsigned int Id = 1;
unsigned int DeviceIndex;
std::vector<Scalar> Scalars;
};
class SensorReadCmd {
public:
unsigned int Id = 1;
unsigned int DeviceIndex;
unsigned int SensorIndex;
std::string SensorType;
};
class SensorReading {
public:
unsigned int Id = 1;
unsigned int DeviceIndex;
unsigned int SensorIndex;
std::string SensorType;
std::vector<int> Data;
};
class SensorSubscribeCmd {
public:
unsigned int Id = 1;
unsigned int DeviceIndex;
unsigned int SensorIndex;
std::string SensorType;
};
class SensorUnsubscribeCmd {
public:
unsigned int Id = 1;
unsigned int DeviceIndex;
unsigned int SensorIndex;
std::string SensorType;
};
extern void to_json(json& j, const RequestServerInfo& k);
extern void to_json(json& j, const StartScanning& k);
extern void to_json(json& j, const StopScanning& k);
extern void to_json(json& j, const RequestDeviceList& k);
extern void to_json(json& j, const StopDeviceCmd& k);
extern void to_json(json& j, const StopAllDevices& k);
extern void to_json(json& j, const ScalarCmd& k);
extern void to_json(json& j, const SensorReadCmd& k);
extern void to_json(json& j, const SensorSubscribeCmd& k);
extern void to_json(json& j, const SensorUnsubscribeCmd& k);
extern void from_json(const json& j, Ok& k);
extern void from_json(const json& j, Error& k);
extern void from_json(const json& j, ServerInfo& k);
extern void from_json(const json& j, DeviceList& k);
extern void from_json(const json& j, DeviceAdded& k);
extern void from_json(const json& j, DeviceRemoved& k);
extern void from_json(const json& j, SensorReading& k);
}

View file

@ -0,0 +1,423 @@
#include "../include/buttplugclient.h"
/*
TODO: Let the user access the devices in more details, that is, how many scalar cmds,
how many sensor cmds and their types. Implement some kind of logging to track whether
commands are successful. Implement Linear and Rotation cmds. Port to Linux.
Investigate whether push back won't ruin sending scalar and sensor cmds, since the
device list does not provide scalar or sensor indices (don't think it will).
*/
// Connection function with a function parameter which acts as a callback.
int Client::connect(void (*callFunc)(const mhl::Messages)) {
FullUrl = lUrl + ":" + std::to_string(lPort);
webSocket.setUrl(FullUrl);
// Ping interval option.
webSocket.setPingInterval(10);
// Per message deflate connection is enabled by default. You can tweak its parameters or disable it
webSocket.disablePerMessageDeflate();
// Set the callback function of the websocket
std::function<void(const ix::WebSocketMessagePtr&)> callback = std::bind(&Client::callbackFunction, this, std::placeholders::_1);
messageCallback = callFunc;
webSocket.setOnMessageCallback(callback);
// Start websocket and indicate that it is connecting (non blocking function, other functions must wait for it to connect).
webSocket.start();
isConnecting = 1;
// Start a message handler thread and detach it.
std::thread messageHandler(&Client::messageHandling, this);
messageHandler.detach();
// Connect to server, specifically send a RequestServerInfo
connectServer();
return 0;
}
// Websocket callback function.
void Client::callbackFunction(const ix::WebSocketMessagePtr& msg) {
// If a message is received to the websocket, pass it to the message handler and notify to stop waiting.
if (msg->type == ix::WebSocketMessageType::Message)
{
// Mutex lock this scope.
std::lock_guard<std::mutex> lock{msgMx};
// Push message.
q.push(msg->str);
// Notify conditional variable to stop waiting.
cond.notify_one();
}
// Handle websocket errors.
if (msg->type == ix::WebSocketMessageType::Error)
{
std::stringstream ss;
ss << "Error: " << msg->errorInfo.reason << std::endl;
ss << "#retries: " << msg->errorInfo.retries << std::endl;
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
std::cout << ss.str() << std::endl;
}
// Set atomic variable that websocket is connected once it is open.
if (msg->type == ix::WebSocketMessageType::Open) {
wsConnected = 1;
condWs.notify_all();
}
// Set atomic variable that websocket is not connected if socket closes.
if (msg->type == ix::WebSocketMessageType::Close) {
wsConnected = 0;
}
}
// Function to start scanning in the server.
void Client::startScan() {
// Mutex lock scope.
std::lock_guard<std::mutex> lock{msgMx};
// Get a request class from message handling header.
mhl::Requests req;
// Set the ID of message according to the message enum in message handling header.
req.startScanning.Id = static_cast<unsigned int>(mhl::MessageTypes::StartScanning);
// Set message type for the handler to recognize what message it is.
messageHandler.messageType = mhl::MessageTypes::StartScanning;
// Convert the returned handled request message class to json.
json j = json::array({ messageHandler.handleClientRequest(req) });
std::cout << j << std::endl;
// Start a thread that sends the message.
std::thread sendHandler(&Client::sendMessage, this, j, messageHandler.messageType);
sendHandler.detach();
}
// Function to stop scanning, same as before but different type.
void Client::stopScan() {
std::lock_guard<std::mutex> lock{msgMx};
mhl::Requests req;
req.stopScanning.Id = static_cast<unsigned int>(mhl::MessageTypes::StopScanning);
messageHandler.messageType = mhl::MessageTypes::StopScanning;
json j = json::array({ messageHandler.handleClientRequest(req) });
std::cout << j << std::endl;
std::thread sendHandler(&Client::sendMessage, this, j, messageHandler.messageType);
sendHandler.detach();
}
// Function to get device list, same as before but different type.
void Client::requestDeviceList() {
std::lock_guard<std::mutex> lock{msgMx};
mhl::Requests req;
req.stopScanning.Id = static_cast<unsigned int>(mhl::MessageTypes::RequestDeviceList);
messageHandler.messageType = mhl::MessageTypes::RequestDeviceList;
json j = json::array({ messageHandler.handleClientRequest(req) });
std::cout << j << std::endl;
std::thread sendHandler(&Client::sendMessage, this, j, messageHandler.messageType);
sendHandler.detach();
}
// Function to send RequestServerInfo, same as before but different type.
void Client::connectServer() {
std::lock_guard<std::mutex> lock{msgMx};
mhl::Requests req;
req.requestServerInfo.Id = static_cast<unsigned int>(mhl::MessageTypes::RequestServerInfo);
req.requestServerInfo.ClientName = "Testing";
req.requestServerInfo.MessageVersion = 3;
messageHandler.messageType = mhl::MessageTypes::RequestServerInfo;
json j = json::array({ messageHandler.handleClientRequest(req) });
std::cout << j << std::endl;
std::thread sendHandler(&Client::sendMessage, this, j, messageHandler.messageType);
sendHandler.detach();
}
// Function that actually sends the message.
void Client::sendMessage(json msg, mhl::MessageTypes mType) {
// First check whether a connection process is started.
if (!isConnecting && !wsConnected) {
std::cout << "Client is not connected and not started, start before sending a message" << std::endl;
return;
}
// If started, wait for the socket to connect first.
if (!wsConnected && isConnecting) {
std::unique_lock<std::mutex> lock{msgMx};
DEBUG_MSG("Waiting for socket to connect");
auto wsConnStatus = [this]() {return wsConnected == 1; };
condWs.wait(lock, wsConnStatus);
std::cout << "Connected to socket" << std::endl;
//webSocket.send(msg.dump());
}
// Once socket is connected, either wait for client to connect, or send a message if the message type
// is a request server info, since this is our client connection message.
if (!clientConnected && isConnecting) {
std::cout << "Waiting for client to connect" << std::endl;
if (mType == mhl::MessageTypes::RequestServerInfo) {
webSocket.send(msg.dump());
if (logging) logInfo.logSentMessage("RequestServerInfo", static_cast<unsigned int>(mType));
std::cout << "Started connection to client" << std::endl;
return;
}
std::unique_lock<std::mutex> lock{msgMx};
auto clientConnStatus = [this]() {return clientConnected == 1; };
condClient.wait(lock, clientConnStatus);
std::cout << "Connected to client" << std::endl;
webSocket.send(msg.dump());
}
// If everything is connected, simply send message and log request if enabled.
else if (wsConnected && clientConnected) webSocket.send(msg.dump());
if (logging) {
auto result = std::find_if(
messageHandler.messageMap.begin(),
messageHandler.messageMap.end(),
[mType](std::pair<const mhl::MessageTypes, std::basic_string<char> > mo) {return mo.first == mType; });
std::string msgTypeText = result->second;
logInfo.logSentMessage(msgTypeText, static_cast<unsigned int>(mType));
}
}
// This takes the device data from message handler and puts it in to main device class
// for user access.
void Client::updateDevices() {
std::vector<DeviceClass> tempDeviceVec;
// Iterate through available devices.
for (auto& el : messageHandler.deviceList.Devices) {
DeviceClass tempDevice;
// Set the appropriate class variables.
tempDevice.deviceID = el.DeviceIndex;
tempDevice.deviceName = el.DeviceName;
tempDevice.displayName = el.DeviceDisplayName;
if (el.DeviceMessages.size() > 0) {
for (auto& el2 : el.DeviceMessages) tempDevice.commandTypes.push_back(el2.CmdType);
}
// Push back the device in vector.
tempDeviceVec.push_back(tempDevice);
}
devices = tempDeviceVec;
}
// Mutex locked function to provide the user with available devices.
std::vector<DeviceClass> Client::getDevices() {
std::lock_guard<std::mutex> lock{msgMx};
return devices;
}
SensorClass Client::getSensors() {
std::lock_guard<std::mutex> lock{msgMx};
return sensorData;
}
int Client::findDevice(DeviceClass dev) {
for (int i = 0; i < messageHandler.deviceList.Devices.size(); i++)
if (messageHandler.deviceList.Devices[i].DeviceIndex == dev.deviceID)
return i;
return -1;
}
void Client::stopDevice(DeviceClass dev) {
std::lock_guard<std::mutex> lock{msgMx};
mhl::Requests req;
req.stopDeviceCmd.Id = static_cast<unsigned int>(mhl::MessageTypes::StopDeviceCmd);
req.stopDeviceCmd.DeviceIndex = dev.deviceID;
messageHandler.messageType = mhl::MessageTypes::StopDeviceCmd;
json j = json::array({ messageHandler.handleClientRequest(req) });
std::cout << j << std::endl;
std::thread sendHandler(&Client::sendMessage, this, j, messageHandler.messageType);
sendHandler.detach();
}
void Client::stopAllDevices() {
std::lock_guard<std::mutex> lock{msgMx};
mhl::Requests req;
req.stopDeviceCmd.Id = static_cast<unsigned int>(mhl::MessageTypes::StopAllDevices);
messageHandler.messageType = mhl::MessageTypes::StopAllDevices;
json j = json::array({ messageHandler.handleClientRequest(req) });
std::cout << j << std::endl;
std::thread sendHandler(&Client::sendMessage, this, j, messageHandler.messageType);
sendHandler.detach();
}
void Client::sendScalar(DeviceClass dev, double str) {
std::lock_guard<std::mutex> lock{msgMx};
int idx = findDevice(dev);
if (idx > -1) {
mhl::Requests req;
for (auto& el1 : messageHandler.deviceList.Devices[idx].DeviceMessages) {
std::string testScalar = "ScalarCmd";
if (!el1.CmdType.compare(testScalar)) {
req.scalarCmd.DeviceIndex = messageHandler.deviceList.Devices[idx].DeviceIndex;
req.scalarCmd.Id = static_cast<unsigned int>(mhl::MessageTypes::ScalarCmd);
int i = 0;
for (auto& el2: el1.DeviceCmdAttributes) {
Scalar sc;
sc.ActuatorType = el2.ActuatorType;
sc.ScalarVal = str;
sc.Index = i;
req.scalarCmd.Scalars.push_back(sc);
i++;
}
messageHandler.messageType = mhl::MessageTypes::ScalarCmd;
json j = json::array({ messageHandler.handleClientRequest(req) });
std::cout << j << std::endl;
std::thread sendHandler(&Client::sendMessage, this, j, messageHandler.messageType);
sendHandler.detach();
}
}
}
}
void Client::sensorRead(DeviceClass dev, int senIndex) {
std::lock_guard<std::mutex> lock{msgMx};
int idx = findDevice(dev);
if (idx > -1) {
mhl::Requests req;
for (auto& el1 : messageHandler.deviceList.Devices[idx].DeviceMessages) {
std::string testSensor = "SensorReadCmd";
if (!el1.CmdType.compare(testSensor)) {
req.sensorReadCmd.DeviceIndex = messageHandler.deviceList.Devices[idx].DeviceIndex;
req.sensorReadCmd.Id = static_cast<unsigned int>(mhl::MessageTypes::SensorReadCmd);
req.sensorReadCmd.SensorIndex = senIndex;
req.sensorReadCmd.SensorType = el1.DeviceCmdAttributes[senIndex].SensorType;
int i = 0;
messageHandler.messageType = mhl::MessageTypes::SensorReadCmd;
json j = json::array({ messageHandler.handleClientRequest(req) });
std::cout << j << std::endl;
std::thread sendHandler(&Client::sendMessage, this, j, messageHandler.messageType);
sendHandler.detach();
}
}
}
}
void Client::sensorSubscribe(DeviceClass dev, int senIndex) {
std::lock_guard<std::mutex> lock{msgMx};
int idx = findDevice(dev);
if (idx > -1) {
mhl::Requests req;
for (auto& el1 : messageHandler.deviceList.Devices[idx].DeviceMessages) {
std::string testSensor = "SensorReadCmd";
if (!el1.CmdType.compare(testSensor)) {
req.sensorSubscribeCmd.DeviceIndex = messageHandler.deviceList.Devices[idx].DeviceIndex;
req.sensorSubscribeCmd.Id = static_cast<unsigned int>(mhl::MessageTypes::SensorSubscribeCmd);
req.sensorSubscribeCmd.SensorIndex = senIndex;
req.sensorSubscribeCmd.SensorType = el1.DeviceCmdAttributes[senIndex].SensorType;
int i = 0;
messageHandler.messageType = mhl::MessageTypes::SensorSubscribeCmd;
json j = json::array({ messageHandler.handleClientRequest(req) });
std::cout << j << std::endl;
std::thread sendHandler(&Client::sendMessage, this, j, messageHandler.messageType);
sendHandler.detach();
}
}
}
}
void Client::sensorUnsubscribe(DeviceClass dev, int senIndex) {
std::lock_guard<std::mutex> lock{msgMx};
int idx = findDevice(dev);
if (idx > -1) {
mhl::Requests req;
for (auto& el1 : messageHandler.deviceList.Devices[idx].DeviceMessages) {
std::string testSensor = "SensorReadCmd";
if (!el1.CmdType.compare(testSensor)) {
req.sensorUnsubscribeCmd.DeviceIndex = messageHandler.deviceList.Devices[idx].DeviceIndex;
req.sensorUnsubscribeCmd.Id = static_cast<unsigned int>(mhl::MessageTypes::SensorUnsubscribeCmd);
req.sensorUnsubscribeCmd.SensorIndex = senIndex;
req.sensorUnsubscribeCmd.SensorType = el1.DeviceCmdAttributes[senIndex].SensorType;
int i = 0;
messageHandler.messageType = mhl::MessageTypes::SensorUnsubscribeCmd;
json j = json::array({ messageHandler.handleClientRequest(req) });
std::cout << j << std::endl;
std::thread sendHandler(&Client::sendMessage, this, j, messageHandler.messageType);
sendHandler.detach();
}
}
}
}
// Message handling function.
// TODO: add client disconnect which stops this thread too.
void Client::messageHandling() {
// Start infinite loop.
while (1) {
std::unique_lock<std::mutex> lock{msgMx};
// A lambda that waits to receive messages in the queue.
cond.wait(
lock,
[this] {
return !q.empty();
}
);
// If received, grab the message and pop it out.
std::string value = q.front();
q.pop();
// Handle the message.
json j = json::parse(value);
// Iterate through messages since server can send array.
for (auto& el : j.items()) {
// Pass the message to actual handler.
messageHandler.handleServerMessage(el.value());
// If server info received, it means client is connected so set the connection atomic variables
// and notify all send threads that they are good to go.
if (messageHandler.messageType == mhl::MessageTypes::ServerInfo) {
isConnecting = 0;
clientConnected = 1;
condClient.notify_all();
}
// If a device updated message, make sure to update the devices for the user.
if (messageHandler.messageType == mhl::MessageTypes::DeviceAdded ||
messageHandler.messageType == mhl::MessageTypes::DeviceList ||
messageHandler.messageType == mhl::MessageTypes::DeviceRemoved) updateDevices();
if (messageHandler.messageType == mhl::MessageTypes::SensorReading) sensorData = messageHandler.sensorReading;
// Log if logging is enabled.
if (logging)
logInfo.logReceivedMessage(el.value().begin().key(), static_cast<unsigned int>(messageHandler.messageType));
// Callback function for the user.
messageCallback(messageHandler);
}
lock.unlock();
std::cout << "[subscriber] Received " << value << std::endl;
}
}

20
third_party/open-bp-cpp/source/log.cpp vendored Normal file
View file

@ -0,0 +1,20 @@
#include "../include/log.h"
void Logger::init(std::string filename) {
logFile.open(filename, std::fstream::out);
}
void Logger::logSentMessage(std::string rqType, unsigned int id) {
end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds = end - start;
RequestQueue tempQ;
tempQ.id = id;
tempQ.requestType = rqType;
rQueue.push_back(tempQ);
logFile << elapsed_seconds.count() << " s, Request type sent: " << rqType << ", ID: " << id << std::endl;
}
void Logger::logReceivedMessage(std::string repType, unsigned int id) {
//end = std::chrono::system_clock::now();
}

View file

@ -0,0 +1,100 @@
#include "../include/messageHandler.h"
namespace mhl {
// Function that handles messages received from server.
void Messages::handleServerMessage(json& msg) {
// Grab the string of message type and find it in the map.
auto msgType = msg.begin().key();
auto result = std::find_if(
messageMap.begin(),
messageMap.end(),
[msgType](std::pair<const mhl::MessageTypes, std::basic_string<char> > mo) {return mo.second == msgType; });
auto msgEnumType = result->first;
int i = 0;
// Switch that converts message to class.
switch (msgEnumType) {
case mhl::MessageTypes::Ok:
messageType = mhl::MessageTypes::Ok;
ok = msg.get<msg::Ok>();
break;
case mhl::MessageTypes::Error:
messageType = mhl::MessageTypes::Error;
error = msg.get<msg::Error>();
break;
case mhl::MessageTypes::ServerInfo:
// Set message type and convert to class from json.
std::cout << "Server info!" << std::endl;
messageType = mhl::MessageTypes::ServerInfo;
serverInfo = msg.get<msg::ServerInfo>();
break;
case mhl::MessageTypes::ScanningFinished:
break;
case mhl::MessageTypes::DeviceList:
std::cout << "Device list!" << std::endl;
messageType = mhl::MessageTypes::DeviceList;
deviceList = msg.get<msg::DeviceList>();
break;
case mhl::MessageTypes::DeviceAdded:
deviceAdded = msg.get<msg::DeviceAdded>();
messageType = mhl::MessageTypes::DeviceAdded;
// Push back to message handler class device list the newly added device.
deviceList.Devices.push_back(deviceAdded.device);
break;
case mhl::MessageTypes::DeviceRemoved:
deviceRemoved = msg.get<msg::DeviceRemoved>();
messageType = mhl::MessageTypes::DeviceRemoved;
// Erase device from message handler class device list.
for (auto& el : deviceList.Devices) {
if (deviceRemoved.DeviceIndex == el.DeviceIndex) {
deviceList.Devices.erase(deviceList.Devices.begin() + i);
break;
}
i++;
}
break;
case mhl::MessageTypes::SensorReading:
sensorReading = msg.get<msg::SensorReading>();
messageType = mhl::MessageTypes::SensorReading;
break;
}
}
// Convert client request classes to json.
json Messages::handleClientRequest(Requests req) {
json j;
switch (messageType) {
case mhl::MessageTypes::RequestServerInfo:
j = req.requestServerInfo;
break;
case mhl::MessageTypes::RequestDeviceList:
j = req.requestDeviceList;
break;
case mhl::MessageTypes::StartScanning:
j = req.startScanning;
break;
case mhl::MessageTypes::StopScanning:
j = req.stopScanning;
break;
case mhl::MessageTypes::StopDeviceCmd:
j = req.stopDeviceCmd;
break;
case mhl::MessageTypes::StopAllDevices:
j = req.stopAllDevices;
break;
case mhl::MessageTypes::ScalarCmd:
j = req.scalarCmd;
break;
case mhl::MessageTypes::SensorReadCmd:
j = req.sensorReadCmd;
break;
case mhl::MessageTypes::SensorSubscribeCmd:
j = req.sensorSubscribeCmd;
break;
case mhl::MessageTypes::SensorUnsubscribeCmd:
j = req.sensorUnsubscribeCmd;
break;
}
return j;
}
}

View file

@ -0,0 +1,218 @@
#include "../include/messages.h"
// Function definitions for json conversions.
namespace msg {
void to_json(json& j, const RequestServerInfo& k) {
j["RequestServerInfo"] = { {"Id", k.Id}, {"ClientName", k.ClientName}, {"MessageVersion", k.MessageVersion} };
}
void to_json(json& j, const StartScanning& k) {
j["StartScanning"] = { {"Id", k.Id} };
}
void to_json(json& j, const StopScanning& k) {
j["StopScanning"] = { {"Id", k.Id} };
}
void to_json(json& j, const RequestDeviceList& k) {
j["RequestDeviceList"] = { {"Id", k.Id} };
}
void to_json(json& j, const StopDeviceCmd& k) {
j["StopDeviceCmd"] = { {"Id", k.Id}, {"DeviceIndex", k.DeviceIndex} };
}
void to_json(json& j, const StopAllDevices& k) {
j["StopAllDevices"] = { {"Id", k.Id} };
}
void to_json(json& j, const ScalarCmd& k) {
j["ScalarCmd"] = { {"Id", k.Id}, {"DeviceIndex", k.DeviceIndex} };
j["ScalarCmd"]["Scalars"] = json::array();
for (auto& vec : k.Scalars) {
json jTemp = { { "Index", vec.Index }, { "Scalar", vec.ScalarVal }, { "ActuatorType", vec.ActuatorType } };
j["ScalarCmd"]["Scalars"].insert(j["ScalarCmd"]["Scalars"].end(), jTemp);
}
}
void to_json(json& j, const SensorReadCmd& k) {
j["SensorReadCmd"] = { {"Id", k.Id}, {"DeviceIndex", k.DeviceIndex}, {"SensorIndex", k.SensorIndex}, {"SensorType", k.SensorType}};
}
void to_json(json& j, const SensorSubscribeCmd& k) {
j["SensorSubscribeCmd"] = { {"Id", k.Id}, {"DeviceIndex", k.DeviceIndex}, {"SensorIndex", k.SensorIndex}, {"SensorType", k.SensorType} };
}
void to_json(json& j, const SensorUnsubscribeCmd& k) {
j["SensorUnsubscribeCmd"] = { {"Id", k.Id}, {"DeviceIndex", k.DeviceIndex}, {"SensorIndex", k.SensorIndex}, {"SensorType", k.SensorType} };
}
void from_json(const json& j, ServerInfo& k) {
json jTemp;
j.at("ServerInfo").get_to(jTemp);
jTemp.at("Id").get_to(k.Id);
jTemp.at("ServerName").get_to(k.ServerName);
jTemp.at("MessageVersion").get_to(k.MessageVersion);
jTemp.at("MaxPingTime").get_to(k.MaxPingTime);
}
void from_json(const json& j, Ok& k) {
json jTemp;
j.at("Ok").get_to(jTemp);
jTemp.at("Id").get_to(k.Id);
}
void from_json(const json& j, Error& k) {
json jTemp;
j.at("Error").get_to(jTemp);
jTemp.at("Id").get_to(k.Id);
jTemp.at("ErrorCode").get_to(k.ErrorCode);
jTemp.at("ErrorMessage").get_to(k.ErrorMessage);
}
void from_json(const json& j, DeviceRemoved& k) {
json jTemp;
j.at("DeviceRemoved").get_to(jTemp);
jTemp.at("Id").get_to(k.Id);
jTemp.at("DeviceIndex").get_to(k.DeviceIndex);
}
// The device conversion functions are slightly more complicated since they have some
// nested objects and arrays, but otherwise it is simply parsing, just a lot of it.
void from_json(const json& j, DeviceList& k) {
json jTemp;
j.at("DeviceList").get_to(jTemp);
jTemp.at("Id").get_to(k.Id);
if (jTemp["Devices"].size() > 0) {
for (auto& el : jTemp["Devices"].items()) {
Device tempD;
//std::cout << el.value() << std::endl;
auto test = el.value().contains("DeviceMessageTimingGap");
if (el.value().contains("DeviceName")) tempD.DeviceName = el.value()["DeviceName"];
if (el.value().contains("DeviceIndex")) tempD.DeviceIndex = el.value()["DeviceIndex"];
if (el.value().contains("DeviceMessageTimingGap")) tempD.DeviceMessageTimingGap = el.value()["DeviceMessageTimingGap"];
if (el.value().contains("DeviceDisplayName")) tempD.DeviceName = el.value()["DeviceDisplayName"];
if (el.value().contains("DeviceMessages")) {
json jTemp2;
jTemp2 = el.value()["DeviceMessages"];
for (auto& el2 : jTemp2.items()) {
DeviceCmd tempCmd;
tempCmd.CmdType = el2.key();
if (!el2.key().compare("StopDeviceCmd")) {
// Do something, not sure what yet
continue;
}
for (auto& el3 : el2.value().items()) {
DeviceCmdAttr tempAttr;
//std::cout << el3.value() << std::endl;
if (el3.value().contains("FeatureDescriptor")) tempAttr.FeatureDescriptor = el3.value()["FeatureDescriptor"];
if (el3.value().contains("StepCount")) tempAttr.StepCount = el3.value()["StepCount"];
if (el3.value().contains("ActuatorType")) tempAttr.ActuatorType = el3.value()["ActuatorType"];
if (el3.value().contains("SensorType")) tempAttr.SensorType = el3.value()["SensorType"];
if (el3.value().contains("SensorRange")) {
//std::cout << el3.value()["SensorRange"] << std::endl;
for (auto& el4 : el3.value()["SensorRange"].items()) {
tempAttr.SensorRange.push_back(el4.value()[0]);
tempAttr.SensorRange.push_back(el4.value()[1]);
}
//tempCmd.SensorRange.push_back(el2.value()["SensorRange"]);
}
//if (el2.value().contains("Endpoints")) tempCmd.Endpoints = el2.value()["Endpoints"];
//std::cout << el2.key() << std::endl;
//std::cout << el2.value() << std::endl;
tempCmd.DeviceCmdAttributes.push_back(tempAttr);
}
tempD.DeviceMessages.push_back(tempCmd);
}
}
k.Devices.push_back(tempD);
}
}
}
void from_json(const json& j, DeviceAdded& k) {
json jTemp;
j.at("DeviceAdded").get_to(jTemp);
jTemp.at("Id").get_to(k.Id);
Device tempD;
if (jTemp.contains("DeviceName")) k.device.DeviceName = jTemp["DeviceName"];
if (jTemp.contains("DeviceIndex")) k.device.DeviceIndex = jTemp["DeviceIndex"];
if (jTemp.contains("DeviceMessageTimingGap")) k.device.DeviceMessageTimingGap = jTemp["DeviceMessageTimingGap"];
if (jTemp.contains("DeviceDisplayName")) k.device.DeviceName = jTemp["DeviceDisplayName"];
if (jTemp.contains("DeviceMessages")) {
json jTemp2;
jTemp2 = jTemp["DeviceMessages"];
for (auto& el2 : jTemp2.items()) {
DeviceCmd tempCmd;
tempCmd.CmdType = el2.key();
if (!el2.key().compare("StopDeviceCmd")) {
// Do something, not sure what yet
continue;
}
for (auto& el3 : el2.value().items()) {
DeviceCmdAttr tempAttr;
//std::cout << el3.value() << std::endl;
if (el3.value().contains("FeatureDescriptor")) tempAttr.FeatureDescriptor = el3.value()["FeatureDescriptor"];
if (el3.value().contains("StepCount")) tempAttr.StepCount = el3.value()["StepCount"];
if (el3.value().contains("ActuatorType")) tempAttr.ActuatorType = el3.value()["ActuatorType"];
if (el3.value().contains("SensorType")) tempAttr.SensorType = el3.value()["SensorType"];
if (el3.value().contains("SensorRange")) {
//std::cout << el3.value()["SensorRange"] << std::endl;
for (auto& el4 : el3.value()["SensorRange"].items()) {
tempAttr.SensorRange.push_back(el4.value()[0]);
tempAttr.SensorRange.push_back(el4.value()[1]);
}
}
//if (el2.value().contains("Endpoints")) tempCmd.Endpoints = el2.value()["Endpoints"];
//std::cout << el2.key() << std::endl;
//std::cout << el2.value() << std::endl;
tempCmd.DeviceCmdAttributes.push_back(tempAttr);
}
k.device.DeviceMessages.push_back(tempCmd);
}
}
}
void from_json(const json& j, SensorReading& k) {
json jTemp;
j.at("SensorReading").get_to(jTemp);
jTemp.at("Id").get_to(k.Id);
jTemp.at("DeviceIndex").get_to(k.DeviceIndex);
jTemp.at("SensorIndex").get_to(k.SensorIndex);
jTemp.at("SensorType").get_to(k.SensorType);
for (auto& el : jTemp["Data"].items())
k.Data.push_back(el.value());
}
}

@ -0,0 +1 @@
Subproject commit 1d210c013937b5e1e932302164ddf44af79ae94f