Created
September 18, 2020 22:58
-
-
Save westlakem/17910065569d21066d038667537e48ee to your computer and use it in GitHub Desktop.
Updated addon.cc file for using node-simconnect in electron
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "addon.h" | |
#include <winternl.h> | |
#include <stack> | |
uv_loop_t *loop; | |
uv_async_t async; | |
std::map<DWORD, DataDefinition> dataDefinitions; | |
std::map<DWORD, Nan::Callback *> systemEventCallbacks; | |
std::map<DWORD, Nan::Callback *> systemStateCallbacks; | |
std::map<DWORD, Nan::Callback *> dataRequestCallbacks; | |
Nan::Callback *errorCallback; | |
// Special events to listen for from the beginning | |
SIMCONNECT_CLIENT_EVENT_ID openEventId; | |
SIMCONNECT_CLIENT_EVENT_ID quitEventId; | |
SIMCONNECT_CLIENT_EVENT_ID exceptionEventId; | |
// Counters for creating unique IDs for SimConnect | |
SIMCONNECT_DATA_DEFINITION_ID defineIdCounter; | |
SIMCONNECT_CLIENT_EVENT_ID eventIdCounter; | |
SIMCONNECT_DATA_REQUEST_ID requestIdCounter; | |
std::stack<SIMCONNECT_DATA_REQUEST_ID> unusedReqIds; | |
// Semaphores | |
uv_sem_t workerSem; | |
uv_sem_t defineIdSem; | |
uv_sem_t eventIdSem; | |
uv_sem_t reqIdSem; | |
HANDLE ghSimConnect = NULL; | |
class DispatchWorker : public Nan::AsyncWorker | |
{ | |
public: | |
DispatchWorker(Nan::Callback *callback) : AsyncWorker(callback) | |
{ | |
} | |
~DispatchWorker() {} | |
void Execute() | |
{ | |
uv_async_init(loop, &async, messageReceiver); // Must be called from worker thread | |
while (true) | |
{ | |
if (ghSimConnect) | |
{ | |
uv_sem_wait(&workerSem); // Wait for mainthread to process the previous dispatch | |
SIMCONNECT_RECV *pData; | |
DWORD cbData; | |
HRESULT hr = SimConnect_GetNextDispatch(ghSimConnect, &pData, &cbData); | |
if (SUCCEEDED(hr)) | |
{ | |
CallbackData data; | |
data.pData = pData; | |
data.cbData = cbData; | |
data.ntstatus = 0; | |
async.data = &data; | |
uv_async_send(&async); | |
} | |
else if (NT_ERROR(hr)) | |
{ | |
CallbackData data; | |
data.ntstatus = (NTSTATUS)hr; | |
async.data = &data; | |
uv_async_send(&async); | |
} | |
else | |
{ | |
uv_sem_post(&workerSem); // Continue | |
Sleep(1); | |
} | |
} | |
else | |
{ | |
Sleep(10); | |
} | |
} | |
} | |
}; | |
SIMCONNECT_DATA_DEFINITION_ID getUniqueDefineId() | |
{ | |
uv_sem_wait(&defineIdSem); | |
SIMCONNECT_DATA_DEFINITION_ID id = defineIdCounter; | |
defineIdCounter++; | |
uv_sem_post(&defineIdSem); | |
return id; | |
} | |
SIMCONNECT_CLIENT_EVENT_ID getUniqueEventId() | |
{ | |
uv_sem_wait(&eventIdSem); | |
SIMCONNECT_CLIENT_EVENT_ID id = eventIdCounter; | |
eventIdCounter++; | |
uv_sem_post(&eventIdSem); | |
return id; | |
} | |
SIMCONNECT_DATA_REQUEST_ID getUniqueRequestId() | |
{ | |
uv_sem_wait(&reqIdSem); | |
SIMCONNECT_DATA_REQUEST_ID id; | |
if (!unusedReqIds.empty()) | |
{ | |
id = unusedReqIds.top(); | |
unusedReqIds.pop(); | |
} | |
else | |
{ | |
id = requestIdCounter; | |
requestIdCounter++; | |
} | |
uv_sem_post(&reqIdSem); | |
return id; | |
} | |
// Runs on main thread after uv_async_send() is called | |
void messageReceiver(uv_async_t *handle) | |
{ | |
Nan::HandleScope scope; | |
v8::Isolate *isolate = v8::Isolate::GetCurrent(); | |
CallbackData *data = (CallbackData *)handle->data; | |
if (NT_SUCCESS(data->ntstatus)) | |
{ | |
switch (data->pData->dwID) | |
{ | |
case SIMCONNECT_RECV_ID_EVENT: | |
handleReceived_Event(isolate, data->pData, data->cbData); | |
break; | |
case SIMCONNECT_RECV_ID_SIMOBJECT_DATA: | |
handleReceived_Data(isolate, data->pData, data->cbData); | |
break; | |
case SIMCONNECT_RECV_ID_QUIT: | |
handleReceived_Quit(isolate); | |
break; | |
case SIMCONNECT_RECV_ID_EXCEPTION: | |
handleReceived_Exception(isolate, data->pData, data->cbData); | |
break; | |
case SIMCONNECT_RECV_ID_EVENT_FILENAME: | |
handleReceived_Filename(isolate, data->pData, data->cbData); | |
break; | |
case SIMCONNECT_RECV_ID_OPEN: | |
handleReceived_Open(isolate, data->pData, data->cbData); | |
break; | |
case SIMCONNECT_RECV_ID_SYSTEM_STATE: | |
handleReceived_SystemState(isolate, data->pData, data->cbData); | |
break; | |
case SIMCONNECT_RECV_ID_SIMOBJECT_DATA_BYTYPE: | |
handleReceived_DataByType(isolate, data->pData, data->cbData); | |
break; | |
case SIMCONNECT_RECV_ID_EVENT_FRAME: | |
handleReceived_Frame(isolate, data->pData, data->cbData); | |
break; | |
default: | |
printf("Unexpected message received (dwId: %i)\n", data->pData->dwID); | |
break; | |
} | |
} | |
else | |
{ | |
handle_Error(isolate, data->ntstatus); | |
} | |
uv_sem_post(&workerSem); // The dispatch-worker can now continue | |
} | |
// Handles data requested with requestDataOnSimObject or requestDataOnSimObjectType | |
void handleReceived_Data(Isolate *isolate, SIMCONNECT_RECV *pData, DWORD cbData) | |
{ | |
SIMCONNECT_RECV_SIMOBJECT_DATA *pObjData = (SIMCONNECT_RECV_SIMOBJECT_DATA *)pData; | |
int numVars = dataDefinitions[pObjData->dwDefineID].num_values; | |
std::vector<SIMCONNECT_DATATYPE> valTypes = dataDefinitions[pObjData->dwDefineID].datum_types; | |
std::vector<std::string> valIds = dataDefinitions[pObjData->dwDefineID].datum_names; | |
Local<Object> result_list = Object::New(isolate); | |
int dataValueOffset = 0; | |
for (int i = 0; i < numVars; i++) | |
{ | |
int varSize = 0; | |
if (valTypes[i] == SIMCONNECT_DATATYPE_STRINGV) | |
{ | |
dataValueOffset += 8; // Not really sure why this is needed, but it fixes problems like this: "F-22 RapF-22 Raptor - 525th Fighter Squadron" | |
char *pOutString; | |
DWORD cbString; | |
char *pStringv = ((char *)(&pObjData->dwData)); | |
HRESULT hr = SimConnect_RetrieveString(pData, cbData, dataValueOffset + pStringv, &pOutString, &cbString); | |
if (NT_ERROR(hr)) | |
{ | |
handle_Error(isolate, hr); | |
return; | |
} | |
const v8::Local<v8::String> key = String::NewFromUtf8(isolate, valIds.at(i).c_str()).ToLocalChecked(); | |
const v8::Local<v8::Context> context = isolate->GetCurrentContext(); | |
try | |
{ | |
const v8::Local<v8::String> value = String::NewFromOneByte(isolate, (const uint8_t *)pOutString, v8::NewStringType::kNormal).ToLocalChecked(); | |
result_list->Set(context, key, value); | |
} | |
catch (...) | |
{ | |
v8::Local<v8::String> value = String::NewFromUtf8(isolate, "ERROR").ToLocalChecked(); | |
result_list->Set(context, key, value); | |
} | |
varSize = cbString; | |
} | |
else | |
{ | |
//printf("------ %s -----\n", valIds.at(i).c_str()); | |
varSize = sizeMap[valTypes[i]]; | |
char *p = ((char *)(&pObjData->dwData) + dataValueOffset); | |
double *var = (double *)p; | |
const v8::Local<v8::Context> context = isolate->GetCurrentContext(); | |
result_list->Set(context, String::NewFromUtf8(isolate, valIds.at(i).c_str()).ToLocalChecked(), Number::New(isolate, *var)); | |
} | |
dataValueOffset += varSize; | |
} | |
const int argc = 1; | |
Local<Value> argv[argc] = { | |
result_list}; | |
dataRequestCallbacks[pObjData->dwRequestID]->Call(isolate->GetCurrentContext()->Global(), argc, argv); | |
} | |
void handleReceived_DataByType(Isolate *isolate, SIMCONNECT_RECV *pData, DWORD cbData) | |
{ | |
SIMCONNECT_RECV_SIMOBJECT_DATA *pObjData = (SIMCONNECT_RECV_SIMOBJECT_DATA *)pData; | |
handleReceived_Data(isolate, pData, cbData); | |
unusedReqIds.push(pObjData->dwRequestID); // The id can be re-used in next request | |
} | |
void handleReceived_Frame(Isolate *isolate, SIMCONNECT_RECV *pData, DWORD cbData) | |
{ | |
SIMCONNECT_RECV_EVENT_FRAME *pFrame = (SIMCONNECT_RECV_EVENT_FRAME *)pData; | |
// printf("frame data recived: %f FPS\n",pFrame->fFrameRate); | |
const int argc = 2; | |
Local<Value> argv[argc] = { | |
Number::New(isolate, pFrame->fFrameRate), | |
Number::New(isolate, pFrame->fSimSpeed)}; | |
systemEventCallbacks[pFrame->uEventID]->Call(isolate->GetCurrentContext()->Global(), argc, argv); | |
// Local<Object> obj = Object::New(isolate); | |
// float fFrameRate; | |
// float fSimSpeed; | |
// DWORD dwFlags; | |
// obj->Set(String::NewFromUtf8(isolate, "float"), Number::New(isolate, pFrame->fFrameRate)); | |
// obj->Set(String::NewFromUtf8(isolate, "float"), Number::New(isolate, pFrame->fSimSpeed)); | |
// Local<Value> argv[1] = { obj }; | |
// unusedReqIds.push(pFrame->dwRequestID); // The id can be re-used in next request | |
} | |
void handle_Error(Isolate *isolate, NTSTATUS code) | |
{ | |
// Codes found so far: 0xC000014B, 0xC000020D, 0xC000013C | |
ghSimConnect = NULL; | |
char errorCode[32]; | |
sprintf(errorCode, "0x%08X", code); | |
const int argc = 1; | |
Local<Value> argv[argc] = { | |
String::NewFromUtf8(isolate, errorCode).ToLocalChecked()}; | |
errorCallback->Call(isolate->GetCurrentContext()->Global(), argc, argv); | |
} | |
void handleReceived_Event(Isolate *isolate, SIMCONNECT_RECV *pData, DWORD cbData) | |
{ | |
SIMCONNECT_RECV_EVENT *myEvent = (SIMCONNECT_RECV_EVENT *)pData; | |
const int argc = 1; | |
Local<Value> argv[argc] = { | |
Number::New(isolate, myEvent->dwData)}; | |
systemEventCallbacks[myEvent->uEventID]->Call(isolate->GetCurrentContext()->Global(), argc, argv); | |
} | |
void handleReceived_Exception(Isolate *isolate, SIMCONNECT_RECV *pData, DWORD cbData) | |
{ | |
SIMCONNECT_RECV_EXCEPTION *except = (SIMCONNECT_RECV_EXCEPTION *)pData; | |
const v8::Local<v8::Context> context = isolate->GetCurrentContext(); | |
Local<Object> obj = Object::New(isolate); | |
obj->Set(context, String::NewFromUtf8(isolate, "dwException").ToLocalChecked(), Number::New(isolate, except->dwException)); | |
obj->Set(context, String::NewFromUtf8(isolate, "dwSendID").ToLocalChecked(), Number::New(isolate, except->dwSendID)); | |
obj->Set(context, String::NewFromUtf8(isolate, "dwIndex").ToLocalChecked(), Number::New(isolate, except->dwIndex)); | |
obj->Set(context, String::NewFromUtf8(isolate, "cbData").ToLocalChecked(), Number::New(isolate, cbData)); | |
obj->Set(context, String::NewFromUtf8(isolate, "cbVersion").ToLocalChecked(), Number::New(isolate, except->dwException)); | |
obj->Set(context, String::NewFromUtf8(isolate, "name").ToLocalChecked(), String::NewFromUtf8(isolate, exceptionNames[SIMCONNECT_EXCEPTION(except->dwException)]).ToLocalChecked()); | |
Local<Value> argv[1] = {obj}; | |
systemEventCallbacks[exceptionEventId]->Call(isolate->GetCurrentContext()->Global(), 1, argv); | |
} | |
void handleReceived_Filename(Isolate *isolate, SIMCONNECT_RECV *pData, DWORD cbData) | |
{ | |
SIMCONNECT_RECV_EVENT_FILENAME *fileName = (SIMCONNECT_RECV_EVENT_FILENAME *)pData; | |
const int argc = 1; | |
Local<Value> argv[argc] = { | |
String::NewFromUtf8(isolate, (const char *)fileName->szFileName).ToLocalChecked()}; | |
systemEventCallbacks[fileName->uEventID]->Call(isolate->GetCurrentContext()->Global(), argc, argv); | |
} | |
void handleReceived_Open(Isolate *isolate, SIMCONNECT_RECV *pData, DWORD cbData) | |
{ | |
SIMCONNECT_RECV_OPEN *pOpen = (SIMCONNECT_RECV_OPEN *)pData; | |
char simconnVersion[32]; | |
sprintf(simconnVersion, "%d.%d.%d.%d", pOpen->dwSimConnectVersionMajor, pOpen->dwSimConnectVersionMinor, pOpen->dwSimConnectBuildMajor, pOpen->dwSimConnectBuildMinor); | |
const int argc = 2; | |
Local<Value> argv[argc] = { | |
String::NewFromOneByte(isolate, (const uint8_t *)pOpen->szApplicationName, v8::NewStringType::kNormal).ToLocalChecked(), | |
String::NewFromUtf8(isolate, simconnVersion).ToLocalChecked()}; | |
systemEventCallbacks[openEventId]->Call(isolate->GetCurrentContext()->Global(), argc, argv); | |
} | |
void handleReceived_SystemState(Isolate *isolate, SIMCONNECT_RECV *pData, DWORD cbData) | |
{ | |
SIMCONNECT_RECV_SYSTEM_STATE *pState = (SIMCONNECT_RECV_SYSTEM_STATE *)pData; | |
Local<Object> obj = Object::New(isolate); | |
const v8::Local<v8::Context> context = isolate->GetCurrentContext(); | |
obj->Set(context, String::NewFromUtf8(isolate, "integer").ToLocalChecked(), Number::New(isolate, pState->dwInteger)); | |
obj->Set(context, String::NewFromUtf8(isolate, "float").ToLocalChecked(), Number::New(isolate, pState->fFloat)); | |
obj->Set(context, String::NewFromUtf8(isolate, "string").ToLocalChecked(), String::NewFromUtf8(isolate, "string").ToLocalChecked()); | |
Local<Value> argv[1] = {obj}; | |
systemStateCallbacks[openEventId]->Call(isolate->GetCurrentContext()->Global(), 1, argv); | |
} | |
void handleReceived_Quit(Isolate *isolate) | |
{ | |
ghSimConnect = NULL; | |
systemEventCallbacks[quitEventId]->Call(isolate->GetCurrentContext()->Global(), 0, NULL); | |
} | |
void handleSimDisconnect(Isolate *isolate) | |
{ | |
} | |
// Wrapped SimConnect-functions ////////////////////////////////////////////////////// | |
void Open(const v8::FunctionCallbackInfo<v8::Value> &args) | |
{ | |
uv_sem_init(&workerSem, 1); | |
uv_sem_init(&defineIdSem, 1); | |
uv_sem_init(&eventIdSem, 1); | |
uv_sem_init(&reqIdSem, 1); | |
defineIdCounter = 0; | |
eventIdCounter = 0; | |
requestIdCounter = 0; | |
Isolate *isolate = args.GetIsolate(); | |
v8::Local<v8::Context> ctx = Nan::GetCurrentContext(); | |
// Get arguments | |
v8::String::Utf8Value appName(isolate, args[0]->ToString(ctx).ToLocalChecked()); | |
openEventId = getUniqueEventId(); | |
systemEventCallbacks[openEventId] = {new Nan::Callback(args[1].As<Function>())}; | |
quitEventId = getUniqueEventId(); | |
systemEventCallbacks[quitEventId] = {new Nan::Callback(args[2].As<Function>())}; | |
exceptionEventId = getUniqueEventId(); | |
systemEventCallbacks[exceptionEventId] = {new Nan::Callback(args[3].As<Function>())}; | |
errorCallback = {new Nan::Callback(args[4].As<Function>())}; | |
// Create dispatch looper thread | |
loop = uv_default_loop(); | |
Nan::AsyncQueueWorker(new DispatchWorker(NULL)); | |
// Open connection | |
HRESULT hr = SimConnect_Open(&ghSimConnect, *appName, NULL, 0, 0, 0); | |
// Return true if success | |
Local<Boolean> retval = v8::Boolean::New(isolate, SUCCEEDED(hr)); | |
args.GetReturnValue().Set(retval); | |
} | |
void Close(const v8::FunctionCallbackInfo<v8::Value> &args) | |
{ | |
if (ghSimConnect) | |
{ | |
Isolate *isolate = args.GetIsolate(); | |
printf("Trying to close..\n"); | |
HRESULT hr = SimConnect_Close(&ghSimConnect); | |
if (NT_ERROR(hr)) | |
{ | |
handle_Error(isolate, hr); | |
return; | |
} | |
printf("Closed: %i\n", hr); | |
ghSimConnect = NULL; | |
args.GetReturnValue().Set(v8::Boolean::New(isolate, SUCCEEDED(hr))); | |
} | |
} | |
void isConnected(const v8::FunctionCallbackInfo<v8::Value> &args) | |
{ | |
Isolate *isolate = args.GetIsolate(); | |
args.GetReturnValue().Set(v8::Boolean::New(isolate, ghSimConnect)); | |
} | |
void RequestSystemState(const v8::FunctionCallbackInfo<v8::Value> &args) | |
{ | |
if (ghSimConnect) | |
{ | |
Isolate *isolate = args.GetIsolate(); | |
v8::Local<v8::Context> ctx = Nan::GetCurrentContext(); | |
v8::String::Utf8Value stateName(isolate, args[0]->ToString(ctx).ToLocalChecked()); | |
SIMCONNECT_DATA_REQUEST_ID reqId = getUniqueRequestId(); | |
systemStateCallbacks[reqId] = new Nan::Callback(args[1].As<Function>()); | |
HRESULT hr = SimConnect_RequestSystemState(ghSimConnect, reqId, *stateName); | |
if (NT_ERROR(hr)) | |
{ | |
handle_Error(isolate, hr); | |
return; | |
} | |
args.GetReturnValue().Set(v8::Number::New(isolate, reqId)); | |
} | |
} | |
void FlightLoad(const v8::FunctionCallbackInfo<v8::Value> &args) | |
{ | |
if (ghSimConnect) | |
{ | |
Isolate *isolate = args.GetIsolate(); | |
v8::Local<v8::Context> ctx = Nan::GetCurrentContext(); | |
v8::String::Utf8Value szFileName(isolate, args[0]->ToString(ctx).ToLocalChecked()); | |
HRESULT hr = SimConnect_FlightLoad(ghSimConnect, *szFileName); | |
if (NT_ERROR(hr)) | |
{ | |
handle_Error(isolate, hr); | |
return; | |
} | |
args.GetReturnValue().Set(v8::Boolean::New(isolate, SUCCEEDED(hr))); | |
} | |
} | |
void TransmitClientEvent(const v8::FunctionCallbackInfo<v8::Value> &args) | |
{ | |
if (ghSimConnect) | |
{ | |
Isolate *isolate = args.GetIsolate(); | |
v8::Local<v8::Context> ctx = Nan::GetCurrentContext(); | |
v8::String::Utf8Value eventName(isolate, args[0]->ToString(ctx).ToLocalChecked()); | |
DWORD data = args.Length() > 1 ? args[1]->Int32Value(ctx).ToChecked() : 0; | |
SIMCONNECT_CLIENT_EVENT_ID id = getUniqueEventId(); | |
HRESULT hr = SimConnect_MapClientEventToSimEvent(ghSimConnect, id, *eventName); | |
if (NT_ERROR(hr)) | |
{ | |
handle_Error(isolate, hr); | |
return; | |
} | |
hr = SimConnect_TransmitClientEvent(ghSimConnect, SIMCONNECT_OBJECT_ID_USER, id, data, SIMCONNECT_GROUP_PRIORITY_HIGHEST, SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY); | |
if (NT_ERROR(hr)) | |
{ | |
handle_Error(isolate, hr); | |
return; | |
} | |
args.GetReturnValue().Set(v8::Boolean::New(isolate, SUCCEEDED(hr))); | |
} | |
} | |
void SubscribeToSystemEvent(const v8::FunctionCallbackInfo<v8::Value> &args) | |
{ | |
if (ghSimConnect) | |
{ | |
v8::Isolate *isolate = args.GetIsolate(); | |
v8::Local<v8::Context> ctx = Nan::GetCurrentContext(); | |
SIMCONNECT_CLIENT_EVENT_ID eventId = getUniqueEventId(); | |
v8::String::Utf8Value systemEventName(isolate, args[0]->ToString(ctx).ToLocalChecked()); | |
systemEventCallbacks[eventId] = {new Nan::Callback(args[1].As<Function>())}; | |
HANDLE hSimConnect = ghSimConnect; | |
HRESULT hr = SimConnect_SubscribeToSystemEvent(hSimConnect, eventId, *systemEventName); | |
if (NT_ERROR(hr)) | |
{ | |
handle_Error(isolate, hr); | |
return; | |
} | |
args.GetReturnValue().Set(v8::Integer::New(isolate, eventId)); | |
} | |
} | |
void RequestDataOnSimObject(const v8::FunctionCallbackInfo<v8::Value> &args) | |
{ | |
if (ghSimConnect) | |
{ | |
v8::Isolate *isolate = args.GetIsolate(); | |
v8::Local<v8::Context> ctx = Nan::GetCurrentContext(); | |
Local<Array> reqValues = v8::Local<v8::Array>::Cast(args[0]); | |
auto callback = new Nan::Callback(args[1].As<Function>()); | |
int objectId = args.Length() > 2 ? args[2]->Int32Value(ctx).ToChecked() : SIMCONNECT_OBJECT_ID_USER; | |
int periodId = args.Length() > 3 ? args[3]->Int32Value(ctx).ToChecked() : SIMCONNECT_PERIOD_SIM_FRAME; | |
int flags = args.Length() > 4 ? args[4]->Int32Value(ctx).ToChecked() : 0; | |
int origin = args.Length() > 5 ? args[5]->Int32Value(ctx).ToChecked() : 0; | |
int interval = args.Length() > 6 ? args[6]->Int32Value(ctx).ToChecked() : 0; | |
DWORD limit = args.Length() > 7 ? args[7]->NumberValue(ctx).ToChecked() : 0; | |
SIMCONNECT_DATA_REQUEST_ID reqId = getUniqueRequestId(); | |
DataDefinition definition = generateDataDefinition(isolate, ghSimConnect, reqValues); | |
HRESULT hr = SimConnect_RequestDataOnSimObject(ghSimConnect, reqId, definition.id, objectId, SIMCONNECT_PERIOD(periodId), flags, origin, interval, limit); | |
if (NT_ERROR(hr)) | |
{ | |
handle_Error(isolate, hr); | |
return; | |
} | |
args.GetReturnValue().Set(v8::Boolean::New(isolate, SUCCEEDED(hr))); | |
dataDefinitions[definition.id] = definition; | |
dataRequestCallbacks[reqId] = callback; | |
} | |
} | |
void RequestDataOnSimObjectType(const v8::FunctionCallbackInfo<v8::Value> &args) | |
{ | |
if (ghSimConnect) | |
{ | |
v8::Isolate *isolate = args.GetIsolate(); | |
v8::Local<v8::Context> ctx = Nan::GetCurrentContext(); | |
DataDefinition definition; | |
if (args[0]->IsArray()) | |
{ | |
Local<Array> reqValues = v8::Local<v8::Array>::Cast(args[0]); | |
definition = generateDataDefinition(isolate, ghSimConnect, reqValues); | |
} | |
else if (args[0]->IsNumber()) | |
{ | |
definition = dataDefinitions[args[0]->NumberValue(ctx).ToChecked()]; | |
} | |
auto callback = new Nan::Callback(args[1].As<Function>()); | |
DWORD radius = args.Length() > 2 ? args[2]->Int32Value(ctx).ToChecked() : 0; | |
int typeId = args.Length() > 3 ? args[3]->Int32Value(ctx).ToChecked() : SIMCONNECT_SIMOBJECT_TYPE_USER; | |
SIMCONNECT_DATA_REQUEST_ID reqId = getUniqueRequestId(); | |
HRESULT hr = SimConnect_RequestDataOnSimObjectType(ghSimConnect, reqId, definition.id, radius, SIMCONNECT_SIMOBJECT_TYPE(typeId)); | |
if (NT_ERROR(hr)) | |
{ | |
handle_Error(isolate, hr); | |
return; | |
} | |
args.GetReturnValue().Set(v8::Boolean::New(isolate, SUCCEEDED(hr))); | |
dataDefinitions[definition.id] = definition; | |
dataRequestCallbacks[reqId] = callback; | |
} | |
} | |
void CreateDataDefinition(const v8::FunctionCallbackInfo<v8::Value> &args) | |
{ | |
if (ghSimConnect) | |
{ | |
v8::Isolate *isolate = args.GetIsolate(); | |
Local<Array> reqValues = v8::Local<v8::Array>::Cast(args[0]); | |
DataDefinition definition = generateDataDefinition(isolate, ghSimConnect, reqValues); | |
args.GetReturnValue().Set(v8::Number::New(isolate, definition.id)); | |
dataDefinitions[definition.id] = definition; | |
} | |
} | |
void SetDataOnSimObject(const v8::FunctionCallbackInfo<v8::Value> &args) | |
{ | |
if (ghSimConnect) | |
{ | |
v8::Isolate *isolate = args.GetIsolate(); | |
v8::Local<v8::Context> ctx = Nan::GetCurrentContext(); | |
v8::String::Utf8Value name(isolate, args[0]->ToString(ctx).ToLocalChecked()); | |
v8::String::Utf8Value unit(isolate, args[1]->ToString(ctx).ToLocalChecked()); | |
double value = args[2]->NumberValue(ctx).ToChecked(); | |
int objectId = args.Length() > 3 ? args[3]->Int32Value(ctx).FromMaybe(SIMCONNECT_OBJECT_ID_USER) : SIMCONNECT_OBJECT_ID_USER; | |
int flags = args.Length() > 4 ? args[4]->Int32Value(ctx).ToChecked() : 0; | |
SIMCONNECT_DATA_DEFINITION_ID defId = getUniqueDefineId(); | |
HRESULT hr = SimConnect_AddToDataDefinition(ghSimConnect, defId, *name, *unit); | |
if (NT_ERROR(hr)) | |
{ | |
handle_Error(isolate, hr); | |
return; | |
} | |
hr = SimConnect_SetDataOnSimObject(ghSimConnect, defId, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(value), &value); | |
if (NT_ERROR(hr)) | |
{ | |
handle_Error(isolate, hr); | |
return; | |
} | |
args.GetReturnValue().Set(v8::Boolean::New(isolate, SUCCEEDED(hr))); | |
} | |
} | |
// Generates a SimConnect data definition for the collection of requests. | |
DataDefinition generateDataDefinition(Isolate *isolate, HANDLE hSimConnect, Local<Array> requestedValues) | |
{ | |
SIMCONNECT_DATA_DEFINITION_ID definitionId = getUniqueDefineId(); | |
v8::Local<v8::Context> ctx = Nan::GetCurrentContext(); | |
const v8::Local<v8::Context> context = isolate->GetCurrentContext(); | |
HRESULT hr = -1; | |
bool success = true; | |
unsigned int numValues = requestedValues->Length(); | |
std::vector<std::string> datumNames; | |
std::vector<SIMCONNECT_DATATYPE> datumTypes; | |
for (unsigned int i = 0; i < requestedValues->Length(); i++) | |
{ | |
Local<Array> value = v8::Local<v8::Array>::Cast(requestedValues->Get(context, i).ToLocalChecked()); | |
if (value->IsArray()) | |
{ | |
int len = value->Length(); | |
if (len > 1) | |
{ | |
Local<Array> v0 = v8::Local<v8::Array>::Cast(value->Get(context, 0).ToLocalChecked()); | |
v8::String::Utf8Value datumName(isolate, v0->ToString(ctx).ToLocalChecked()); | |
const char *sDatumName = *datumName; | |
const char *sUnitsName = NULL; | |
Local<Array> v1 = v8::Local<v8::Array>::Cast(value->Get(context, 1).ToLocalChecked()); | |
if (!v1->IsNull()) | |
{ // Should be NULL for string | |
v8::String::Utf8Value unitsName(isolate, v1->ToString(ctx).ToLocalChecked()); | |
sUnitsName = *unitsName; | |
} | |
SIMCONNECT_DATATYPE datumType = SIMCONNECT_DATATYPE_FLOAT64; // Default type (double) | |
double epsilon; | |
float datumId; | |
if (len > 1) | |
{ | |
hr = SimConnect_AddToDataDefinition(hSimConnect, definitionId, sDatumName, sUnitsName); | |
if (NT_ERROR(hr)) | |
{ | |
handle_Error(isolate, hr); | |
break; | |
} | |
} | |
if (len > 2) | |
{ | |
Local<Array> v2 = v8::Local<v8::Array>::Cast(value->Get(context, 2).ToLocalChecked()); | |
int t = v2->Int32Value(ctx).ToChecked(); | |
datumType = SIMCONNECT_DATATYPE(t); | |
hr = SimConnect_AddToDataDefinition(hSimConnect, definitionId, sDatumName, sUnitsName, datumType); | |
if (NT_ERROR(hr)) | |
{ | |
handle_Error(isolate, hr); | |
break; | |
} | |
} | |
if (len > 3) | |
{ | |
Local<Array> v3 = v8::Local<v8::Array>::Cast(value->Get(context, 3).ToLocalChecked()); | |
epsilon = v3->Int32Value(ctx).ToChecked(); | |
hr = SimConnect_AddToDataDefinition(hSimConnect, definitionId, sDatumName, sUnitsName, datumType, epsilon); | |
if (NT_ERROR(hr)) | |
{ | |
handle_Error(isolate, hr); | |
break; | |
} | |
} | |
if (len > 4) | |
{ | |
Local<Array> v4 = v8::Local<v8::Array>::Cast(value->Get(context, 4).ToLocalChecked()); | |
datumId = v4->Int32Value(ctx).ToChecked(); | |
hr = SimConnect_AddToDataDefinition(hSimConnect, definitionId, sDatumName, sUnitsName, datumType, epsilon, datumId); | |
if (NT_ERROR(hr)) | |
{ | |
handle_Error(isolate, hr); | |
break; | |
} | |
} | |
std::string datumNameStr(sDatumName); | |
datumNames.push_back(datumNameStr); | |
datumTypes.push_back(datumType); | |
} | |
} | |
} | |
return {definitionId, numValues, datumNames, datumTypes}; | |
} | |
// Custom useful functions //////////////////////////////////////////////////////////////////// | |
void SetAircraftInitialPosition(const v8::FunctionCallbackInfo<v8::Value> &args) | |
{ | |
if (ghSimConnect) | |
{ | |
Isolate *isolate = args.GetIsolate(); | |
v8::Local<v8::Context> ctx = Nan::GetCurrentContext(); | |
const v8::Local<v8::Context> context = isolate->GetCurrentContext(); | |
SIMCONNECT_DATA_INITPOSITION init; | |
Local<Object> json = args[0]->ToObject(ctx).ToLocalChecked(); | |
v8::Local<v8::String> altProp = Nan::New("altitude").ToLocalChecked(); | |
v8::Local<v8::String> latProp = Nan::New("latitude").ToLocalChecked(); | |
v8::Local<v8::String> lngProp = Nan::New("longitude").ToLocalChecked(); | |
v8::Local<v8::String> pitchProp = Nan::New("pitch").ToLocalChecked(); | |
v8::Local<v8::String> bankProp = Nan::New("bank").ToLocalChecked(); | |
v8::Local<v8::String> hdgProp = Nan::New("heading").ToLocalChecked(); | |
v8::Local<v8::String> gndProp = Nan::New("onGround").ToLocalChecked(); | |
v8::Local<v8::String> iasProp = Nan::New("airspeed").ToLocalChecked(); | |
Local<Object> Altitude = v8::Local<v8::Object>::Cast(json->Get(context, altProp).ToLocalChecked()); | |
Local<Object> Latitude = v8::Local<v8::Object>::Cast(json->Get(context, altProp).ToLocalChecked()); | |
Local<Object> Longitude = v8::Local<v8::Object>::Cast(json->Get(context, altProp).ToLocalChecked()); | |
Local<Object> Pitch = v8::Local<v8::Object>::Cast(json->Get(context, altProp).ToLocalChecked()); | |
Local<Object> Bank = v8::Local<v8::Object>::Cast(json->Get(context, altProp).ToLocalChecked()); | |
Local<Object> Heading = v8::Local<v8::Object>::Cast(json->Get(context, altProp).ToLocalChecked()); | |
Local<Object> OnGround = v8::Local<v8::Object>::Cast(json->Get(context, altProp).ToLocalChecked()); | |
Local<Object> Airspeed = v8::Local<v8::Object>::Cast(json->Get(context, altProp).ToLocalChecked()); | |
init.Altitude = json->HasRealNamedProperty(ctx, altProp).ToChecked() ? Altitude->NumberValue(ctx).ToChecked() : 0; | |
init.Latitude = json->HasRealNamedProperty(ctx, latProp).ToChecked() ? Latitude->NumberValue(ctx).ToChecked() : 0; | |
init.Longitude = json->HasRealNamedProperty(ctx, lngProp).ToChecked() ? Longitude->NumberValue(ctx).ToChecked() : 0; | |
init.Pitch = json->HasRealNamedProperty(ctx, pitchProp).ToChecked() ? Pitch->NumberValue(ctx).ToChecked() : 0; | |
init.Bank = json->HasRealNamedProperty(ctx, bankProp).ToChecked() ? Bank->NumberValue(ctx).ToChecked() : 0; | |
init.Heading = json->HasRealNamedProperty(ctx, hdgProp).ToChecked() ? Heading->NumberValue(ctx).ToChecked() : 0; | |
init.OnGround = json->HasRealNamedProperty(ctx, gndProp).ToChecked() ? OnGround->NumberValue(ctx).ToChecked() : 0; | |
init.Airspeed = json->HasRealNamedProperty(ctx, iasProp).ToChecked() ? Airspeed->NumberValue(ctx).ToChecked() : 0; | |
SIMCONNECT_DATA_DEFINITION_ID id = getUniqueDefineId(); | |
HRESULT hr = SimConnect_AddToDataDefinition(ghSimConnect, id, "Initial Position", NULL, SIMCONNECT_DATATYPE_INITPOSITION); | |
if (NT_ERROR(hr)) | |
{ | |
handle_Error(isolate, hr); | |
return; | |
} | |
hr = SimConnect_SetDataOnSimObject(ghSimConnect, id, SIMCONNECT_OBJECT_ID_USER, 0, 0, sizeof(init), &init); | |
if (NT_ERROR(hr)) | |
{ | |
handle_Error(isolate, hr); | |
return; | |
} | |
args.GetReturnValue().Set(v8::Boolean::New(isolate, SUCCEEDED(hr))); | |
} | |
} | |
void Initialize(v8::Local<v8::Object> exports) | |
{ | |
NODE_SET_METHOD(exports, "open", Open); | |
NODE_SET_METHOD(exports, "close", Close); | |
NODE_SET_METHOD(exports, "subscribeToSystemEvent", SubscribeToSystemEvent); | |
NODE_SET_METHOD(exports, "requestDataOnSimObject", RequestDataOnSimObject); | |
NODE_SET_METHOD(exports, "setDataOnSimObject", SetDataOnSimObject); | |
NODE_SET_METHOD(exports, "requestDataOnSimObjectType", RequestDataOnSimObjectType); | |
NODE_SET_METHOD(exports, "setAircraftInitialPosition", SetAircraftInitialPosition); | |
NODE_SET_METHOD(exports, "transmitClientEvent", TransmitClientEvent); | |
NODE_SET_METHOD(exports, "requestSystemState", RequestSystemState); | |
NODE_SET_METHOD(exports, "createDataDefinition", CreateDataDefinition); | |
NODE_SET_METHOD(exports, "flightLoad", FlightLoad); | |
NODE_SET_METHOD(exports, "isConnected", isConnected); | |
} | |
NODE_MODULE(addon, Initialize); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment