diff --git a/dist/libs/simconnect.node b/dist/libs/simconnect.node index f11d998..acb2cec 100644 Binary files a/dist/libs/simconnect.node and b/dist/libs/simconnect.node differ diff --git a/package.json b/package.json index affa050..148ec94 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@flybywiresim/msfs-nodejs", - "version": "0.2.0", + "version": "0.3.0", "description": "Wrapper around the MSFS SDK for Node.JS applications", "main": "dist/index.js", "module": "dist/index.esm.js", diff --git a/src/bindings/simconnect/binding.gyp b/src/bindings/simconnect/binding.gyp index c6ef401..26e9049 100644 --- a/src/bindings/simconnect/binding.gyp +++ b/src/bindings/simconnect/binding.gyp @@ -8,7 +8,8 @@ "dispatcher.cc", "helper.cc", "simconnect.cc", - "simulatordataarea.cc" + "simulatordataarea.cc", + "systemevent.cc" ], "cflags!": [ "-fno-exceptions" ], "cflags_cc!": [ "-fno-exceptions" ], diff --git a/src/bindings/simconnect/connection.cc b/src/bindings/simconnect/connection.cc index 3175959..30d00e4 100644 --- a/src/bindings/simconnect/connection.cc +++ b/src/bindings/simconnect/connection.cc @@ -12,7 +12,8 @@ Connection::Connection(const Napi::CallbackInfo& info) : _isConnected(false), _lastError(), _clientDataIds(), - _simulatorDataIds() { } + _simulatorDataIds(), + _systemEventIds() { } Connection::~Connection() { if (this->_simConnect != 0) { @@ -58,6 +59,19 @@ void Connection::addSimulatorDataId(SIMCONNECT_DATA_DEFINITION_ID simulatorDataI this->_simulatorDataIds.push_back(simulatorDataId); } +bool Connection::systemEventIdExists(std::uint32_t systemEventId) const { + for (const auto& id : std::as_const(this->_systemEventIds)) { + if (id == systemEventId) + return true; + } + + return false; +} + +void Connection::addSystemEventId(std::uint32_t systemEventId) { + this->_systemEventIds.push_back(systemEventId); +} + void Connection::connectionEstablished(bool established) { this->_isConnected = established; } diff --git a/src/bindings/simconnect/connection.h b/src/bindings/simconnect/connection.h index ef8e639..cb735b1 100644 --- a/src/bindings/simconnect/connection.h +++ b/src/bindings/simconnect/connection.h @@ -1,100 +1,112 @@ -#pragma once - -#include -#include - -#include -#include -#include - -namespace msfs { -namespace simconnect { - class ClientDataArea; - class Dispatcher; - - class Connection : public Napi::ObjectWrap { - private: - HANDLE _simConnect; - bool _isConnected; - std::string _lastError; - std::list _clientDataIds; - std::list _simulatorDataIds; - - void close(); - - public: - Connection(const Napi::CallbackInfo& info); - ~Connection(); - - /** - * @brief Returns the current HANDLE for the connection - * @return The handle for the connection - */ - HANDLE simConnect() const; - /** - * @brief Checks if the connection is established to the server - * @return True if the connection is established, else false - */ - bool isConnected() const; - /** - * @brief Checks if a client data ID exists - * @param clientDataId The client data ID - * @return True if the ID is already registered, else false - */ - bool clientDataIdExists(SIMCONNECT_CLIENT_DATA_ID clientDataId) const; - /** - * @brief Adds the client data ID to the managed list - * @param clientDataId The client data ID - */ - void addClientDataId(SIMCONNECT_CLIENT_DATA_ID clientDataId); - /** - * @brief Checks if a simulator data ID exists - * @param clientDataId The simulator data ID - * @return True if the ID is already registered, else false - */ - bool simulatorDataIdExists(SIMCONNECT_DATA_DEFINITION_ID simulatorDataId) const; - /** - * @brief Adds the simulator data ID to the managed list - * @param simulatorDataId The simulator data ID - */ - void addSimulatorDataId(SIMCONNECT_DATA_DEFINITION_ID simulatorDataId); - /** - * @brief Marks if the connection with the server response is established - * @param established True if the server response was received, else false - */ - void connectionEstablished(bool established); - /** - * @brief Opens a SimConnect connection to the server - * @param info The callback block where the first element needs to be the client's name - * @return Returns a Napi::Boolean and sets the last error, if the function returned false - * @throw Excpetions if the arguments do not match - */ - Napi::Value open(const Napi::CallbackInfo& info); - /** - * @brief Closes a SimConnect connection - * @param info The parameter block without additional parameters - */ - void close(const Napi::CallbackInfo& info); - /** - * @brief Checks if the SimConnect connection is actove - * @param info The parameter block without additional parameters - * @return True if the connection is active, else false - */ - Napi::Value isConnected(const Napi::CallbackInfo& info); - /** - * @brief Creates anew client data area on the server - * @param info The info block with the paramaters clientDataId, size and readOnly - * @return True if the creation was successful, else false with the last error set - */ - Napi::Value createClientDataArea(const Napi::CallbackInfo& info); - /** - * @brief Returns the last error of an other call - * @param info The parameter block without additional parameters - * @return Returns Napi::String with the last error - */ - Napi::Value lastError(const Napi::CallbackInfo& info); - - static Napi::Object initialize(Napi::Env env, Napi::Object exports); - }; -} -} +#pragma once + +#include +#include + +#include +#include +#include + +namespace msfs { +namespace simconnect { + class ClientDataArea; + class Dispatcher; + + class Connection : public Napi::ObjectWrap { + private: + HANDLE _simConnect; + bool _isConnected; + std::string _lastError; + std::list _clientDataIds; + std::list _simulatorDataIds; + std::list _systemEventIds; + + void close(); + + public: + Connection(const Napi::CallbackInfo& info); + ~Connection(); + + /** + * @brief Returns the current HANDLE for the connection + * @return The handle for the connection + */ + HANDLE simConnect() const; + /** + * @brief Checks if the connection is established to the server + * @return True if the connection is established, else false + */ + bool isConnected() const; + /** + * @brief Checks if a client data ID exists + * @param clientDataId The client data ID + * @return True if the ID is already registered, else false + */ + bool clientDataIdExists(SIMCONNECT_CLIENT_DATA_ID clientDataId) const; + /** + * @brief Adds the client data ID to the managed list + * @param clientDataId The client data ID + */ + void addClientDataId(SIMCONNECT_CLIENT_DATA_ID clientDataId); + /** + * @brief Checks if a simulator data ID exists + * @param simulatorDataId The simulator data ID + * @return True if the ID is already registered, else false + */ + bool simulatorDataIdExists(SIMCONNECT_DATA_DEFINITION_ID simulatorDataId) const; + /** + * @brief Adds the simulator data ID to the managed list + * @param simulatorDataId The simulator data ID + */ + void addSimulatorDataId(SIMCONNECT_DATA_DEFINITION_ID simulatorDataId); + /** + * @brief Checks if a system event ID exists + * @param systemEventId The system event ID + * @return True if the ID is already registered, else false + */ + bool systemEventIdExists(std::uint32_t systemEventId) const; + /** + * @brief Adds the system event ID to the managed list + * @param systemEventId The system event ID + */ + void addSystemEventId(std::uint32_t systemEventId); + /** + * @brief Marks if the connection with the server response is established + * @param established True if the server response was received, else false + */ + void connectionEstablished(bool established); + /** + * @brief Opens a SimConnect connection to the server + * @param info The callback block where the first element needs to be the client's name + * @return Returns a Napi::Boolean and sets the last error, if the function returned false + * @throw Excpetions if the arguments do not match + */ + Napi::Value open(const Napi::CallbackInfo& info); + /** + * @brief Closes a SimConnect connection + * @param info The parameter block without additional parameters + */ + void close(const Napi::CallbackInfo& info); + /** + * @brief Checks if the SimConnect connection is actove + * @param info The parameter block without additional parameters + * @return True if the connection is active, else false + */ + Napi::Value isConnected(const Napi::CallbackInfo& info); + /** + * @brief Creates anew client data area on the server + * @param info The info block with the paramaters clientDataId, size and readOnly + * @return True if the creation was successful, else false with the last error set + */ + Napi::Value createClientDataArea(const Napi::CallbackInfo& info); + /** + * @brief Returns the last error of an other call + * @param info The parameter block without additional parameters + * @return Returns Napi::String with the last error + */ + Napi::Value lastError(const Napi::CallbackInfo& info); + + static Napi::Object initialize(Napi::Env env, Napi::Object exports); + }; +} +} diff --git a/src/bindings/simconnect/dispatcher.cc b/src/bindings/simconnect/dispatcher.cc index e1f517b..57b9520 100644 --- a/src/bindings/simconnect/dispatcher.cc +++ b/src/bindings/simconnect/dispatcher.cc @@ -9,6 +9,7 @@ Dispatcher::Dispatcher(const Napi::CallbackInfo& info) : _connection(nullptr), _requestedClientAreas(), _requestedSimulatorDataArea(), + _subscribedSystemEvents(), _lastError() { Napi::Env env = info.Env(); @@ -161,6 +162,71 @@ Napi::Value Dispatcher::requestSimulatorData(const Napi::CallbackInfo& info) { return Napi::Boolean::New(env, retval); } +Napi::Value Dispatcher::subscribeSystemEvent(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (this->_connection->isConnected() == false) { + Napi::Error::New(env, "Not connected to the server").ThrowAsJavaScriptException(); + return env.Null(); + } + + if (info.Length() != 1) { + Napi::TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException(); + return env.Null(); + } + if (!info[0].IsObject()) { + Napi::TypeError::New(env, "Invalid argument type. 'systemEvent' must be an object of type SystemEvent").ThrowAsJavaScriptException(); + return env.Null(); + } + + auto systemEvent = Napi::ObjectWrap::Unwrap(info[0].As()); + + for (auto area : this->_subscribedSystemEvents) { + if (area->id() == systemEvent->id()) { + return Napi::Boolean::New(env, true); + } + } + + auto retval = systemEvent->subscribe(); + if (retval) { + this->_subscribedSystemEvents.push_back(systemEvent); + } + + return Napi::Boolean::New(env, retval); +} + +Napi::Value Dispatcher::unsubscribeSystemEvent(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (this->_connection->isConnected() == false) { + Napi::Error::New(env, "Not connected to the server").ThrowAsJavaScriptException(); + return env.Null(); + } + + if (info.Length() != 1) { + Napi::TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException(); + return env.Null(); + } + if (!info[0].IsObject()) { + Napi::TypeError::New(env, "Invalid argument type. 'systemEvent' must be an object of type SystemEvent").ThrowAsJavaScriptException(); + return env.Null(); + } + + auto systemEvent = Napi::ObjectWrap::Unwrap(info[0].As()); + + for (auto it = this->_subscribedSystemEvents.begin(); it != this->_subscribedSystemEvents.end(); ++it) { + if ((*it)->id() == systemEvent->id()) { + auto retval = systemEvent->unsubscribe(); + if (true == retval) { + this->_subscribedSystemEvents.erase(it); + } + return Napi::Boolean::New(env, retval); + } + } + + return Napi::Boolean::New(env, false); +} + Napi::Object Dispatcher::convertClientDataAreaMessage(Napi::Env env, SIMCONNECT_RECV_CLIENT_DATA* message, SIMCONNECT_CLIENT_DATA_ID clientDataId, const ClientDataArea::ClientDataDefinition& definition) { @@ -368,12 +434,52 @@ Napi::Object Dispatcher::convertSimulatorDataArea(Napi::Env env, SIMCONNECT_RECV return object; } +bool Dispatcher::processFilenameSystemEvents(Napi::Env env, SIMCONNECT_RECV* receivedData, const SystemEvent* event, Napi::Object& returnObject) { + static const std::vector filenameEvents{ + "AircraftLoaded", + "FlightLoaded", + "FlightSaved", + "FlightPlanActivated", + }; + + auto foundEventName = std::find(std::cbegin(filenameEvents), std::cend(filenameEvents), event->eventName()) != std::cend(filenameEvents); + if (foundEventName == true) { + SIMCONNECT_RECV_EVENT_FILENAME* data = static_cast(receivedData); + Napi::Object filenameObject = Napi::Object::New(env); + filenameObject.Set(Napi::String::New(env, "filename"), Napi::String::New(env, data->szFileName)); + returnObject.Set(Napi::String::New(env, "content"), filenameObject); + } + + return foundEventName; +} + +bool Dispatcher::processObjectSystemEvents(Napi::Env env, SIMCONNECT_RECV* receivedData, const SystemEvent* event, Napi::Object& returnObject) { + static const std::vector objectEvents{ + "ObjectAdded", + "ObjectRemoved", + }; + + auto foundEventName = std::find(std::cbegin(objectEvents), std::cend(objectEvents), event->eventName()) != std::cend(objectEvents); + if (foundEventName == true) { + SIMCONNECT_RECV_EVENT_OBJECT_ADDREMOVE* data = static_cast(receivedData); + Napi::Object aiObject = Napi::Object::New(env); + + aiObject.Set(Napi::String::New(env, "type"), Napi::Number::New(env, data->eObjType)); + aiObject.Set(Napi::String::New(env, "id"), Napi::Number::New(env, data->dwData)); + returnObject.Set(Napi::String::New(env, "content"), aiObject); + } + + return foundEventName; +} + Napi::Value Dispatcher::nextDispatch(const Napi::CallbackInfo& info) { Napi::Env env = info.Env(); if (this->_connection->simConnect() == 0) { Napi::Error::New(env, "Not connected to the server").ThrowAsJavaScriptException(); this->_requestedClientAreas.clear(); + this->_requestedSimulatorDataArea.clear(); + this->_subscribedSystemEvents.clear(); return env.Null(); } @@ -459,6 +565,43 @@ Napi::Value Dispatcher::nextDispatch(const Napi::CallbackInfo& info) { break; } + // all system event messages + case SIMCONNECT_RECV_ID_EVENT: + case SIMCONNECT_RECV_ID_EVENT_FILENAME: + case SIMCONNECT_RECV_ID_EVENT_OBJECT_ADDREMOVE: { + SIMCONNECT_RECV_EVENT* data = static_cast(receiveData); + for (const auto& event : std::as_const(this->_subscribedSystemEvents)) { + if (event->id() == data->uEventID) { + Napi::Object eventObject = Napi::Object::New(env); + eventObject.Set(Napi::String::New(env, "eventId"), Napi::Number::New(env, event->id())); + + if (event->eventName() == "Pause" || event->eventName() == "Pause_EX1") { + Napi::Object pauseObject = Napi::Object::New(env); + pauseObject.Set(Napi::String::New(env, "type"), Napi::Number::New(env, data->dwData)); + eventObject.Set(Napi::String::New(env, "content"), pauseObject); + } else if (event->eventName() == "Sim") { + Napi::Object simObject = Napi::Object::New(env); + simObject.Set(Napi::String::New(env, "running"), Napi::Boolean::New(env, data->dwData == 1)); + eventObject.Set(Napi::String::New(env, "content"), simObject); + } else if (event->eventName() == "Sound") { + Napi::Object soundObject = Napi::Object::New(env); + soundObject.Set(Napi::String::New(env, "soundSwitchOn"), Napi::Boolean::New(env, data->dwData != 0)); + eventObject.Set(Napi::String::New(env, "content"), soundObject); + } else if (event->eventName() == "View") { + Napi::Object viewObject = Napi::Object::New(env); + viewObject.Set(Napi::String::New(env, "view"), Napi::Number::New(env, data->dwData)); + eventObject.Set(Napi::String::New(env, "content"), viewObject); + } else if (false == Dispatcher::processFilenameSystemEvents(env, receiveData, event, eventObject)) { + Dispatcher::processObjectSystemEvents(env, receiveData, event, eventObject); + } + + object.Set(Napi::String::New(env, "type"), Napi::String::New(env, "systemEvent")); + object.Set(Napi::String::New(env, "data"), eventObject); + } + } + + break; + } default: object.Set(Napi::String::New(env, "data"), Dispatcher::convertUnknownMessage(env, receiveData)); object.Set(Napi::String::New(env, "type"), Napi::String::New(env, "error")); @@ -483,6 +626,8 @@ Napi::Object Dispatcher::initialize(Napi::Env env, Napi::Object exports) { Napi::Function func = DefineClass(env, "DispatcherBindings", { InstanceMethod<&Dispatcher::requestClientData>("requestClientData", static_cast(napi_writable | napi_configurable)), InstanceMethod<&Dispatcher::requestSimulatorData>("requestSimulatorData", static_cast(napi_writable | napi_configurable)), + InstanceMethod<&Dispatcher::subscribeSystemEvent>("subscribeSystemEvent", static_cast(napi_writable | napi_configurable)), + InstanceMethod<&Dispatcher::unsubscribeSystemEvent>("unsubscribeSystemEvent", static_cast(napi_writable | napi_configurable)), InstanceMethod<&Dispatcher::nextDispatch>("nextDispatch", static_cast(napi_writable | napi_configurable)), InstanceMethod<&Dispatcher::lastError>("lastError", static_cast(napi_writable | napi_configurable)), }); diff --git a/src/bindings/simconnect/dispatcher.h b/src/bindings/simconnect/dispatcher.h index e0a6c02..32f86bf 100644 --- a/src/bindings/simconnect/dispatcher.h +++ b/src/bindings/simconnect/dispatcher.h @@ -5,6 +5,7 @@ #include "clientdataarea.h" #include "connection.h" #include "simulatordataarea.h" +#include "systemevent.h" namespace msfs { namespace simconnect { @@ -14,6 +15,7 @@ namespace simconnect { SIMCONNECT_DATA_REQUEST_ID _requestId; std::list _requestedClientAreas; std::list _requestedSimulatorDataArea; + std::list _subscribedSystemEvents; std::string _lastError; static Napi::Object convertOpenMessage(Napi::Env env, SIMCONNECT_RECV_OPEN* message); @@ -29,6 +31,8 @@ namespace simconnect { static Napi::Object convertSimulatorDataXYZ(Napi::Env env, std::uint8_t* data); Napi::Object convertSimulatorDataArea(Napi::Env env, SIMCONNECT_RECV* receivedData, DWORD sizeReceivedData, std::uint8_t* data, const std::list& definition); + static bool processFilenameSystemEvents(Napi::Env env, SIMCONNECT_RECV* receivedData, const SystemEvent* event, Napi::Object& returnObject); + static bool processObjectSystemEvents(Napi::Env env, SIMCONNECT_RECV* receivedData, const SystemEvent* event, Napi::Object& returnObject); public: Dispatcher(const Napi::CallbackInfo& info); @@ -40,11 +44,23 @@ namespace simconnect { */ Napi::Value requestClientData(const Napi::CallbackInfo& info); /** - * @brief Requests all elements of a ClientDataArea + * @brief Requests all elements of a SimulatorDataArea * @param info The information block with the SimulatorDataArea, the period and the flag * @return True if the entries are requested, else false */ Napi::Value requestSimulatorData(const Napi::CallbackInfo& info); + /** + * @brief Subscribes to a system event of SystemEvent + * @param info The information block with the SystemEvent + * @return True if the subscribtion was successful, else false + */ + Napi::Value subscribeSystemEvent(const Napi::CallbackInfo& info); + /** + * @brief Unsubscribes to a system event of SystemEvent + * @param info The information block with the SystemEvent + * @return True if the unsubscribtion was successful, else false + */ + Napi::Value unsubscribeSystemEvent(const Napi::CallbackInfo& info); /** * @brief Processes the next dispatch and triggers the corresponding events * @param info The information block without additional parameters diff --git a/src/bindings/simconnect/simconnect.cc b/src/bindings/simconnect/simconnect.cc index a42e675..48f2560 100644 --- a/src/bindings/simconnect/simconnect.cc +++ b/src/bindings/simconnect/simconnect.cc @@ -5,6 +5,7 @@ #include "dispatcher.h" #include "instancedata.h" #include "simulatordataarea.h" +#include "systemevent.h" using namespace msfs::simconnect; @@ -17,6 +18,7 @@ Napi::Object initialize(Napi::Env env, Napi::Object exports) { exports = ClientDataArea::initialize(env, exports); exports = Dispatcher::initialize(env, exports); exports = SimulatorDataArea::initialize(env, exports); + exports = SystemEvent::initialize(env, exports); return exports; } diff --git a/src/bindings/simconnect/systemevent.cc b/src/bindings/simconnect/systemevent.cc new file mode 100644 index 0000000..993f667 --- /dev/null +++ b/src/bindings/simconnect/systemevent.cc @@ -0,0 +1,120 @@ +#include +#include + +#include "connection.h" +#include "helper.h" +#include "instancedata.h" +#include "systemevent.h" + +using namespace msfs::simconnect; + +static const std::array _systemEventNames = { + "1sec", + "4sec", + "6Hz", + "AircraftLoaded", + "Crashed", + "CrashReset", + "FlightLoaded", + "FlightSaved", + "FlightPlanActivated", + "FlightPlanDeactivated", + "Frame", + "ObjectAdded", + "ObjectRemoved", + "Pause", + "Pause_EX1", + "Paused", + "PauseFrame", + "PositionChanged", + "Sim", + "SimStart", + "SimStop", + "Sound", + "Unpaused", + "View" +}; + +SystemEvent::SystemEvent(const Napi::CallbackInfo& info) : + Napi::ObjectWrap(info), + _connection(nullptr), + _event(), + _id(0), + _lastError() { + Napi::Env env = info.Env(); + + if (info.Length() != 3) { + Napi::TypeError::New(env, "Wrong number of arguments").ThrowAsJavaScriptException(); + return; + } + if (!info[0].IsObject()) { + Napi::TypeError::New(env, "Invalid argument type. 'connection' must be an object of type Connection").ThrowAsJavaScriptException(); + return; + } + if (!info[1].IsNumber()) { + Napi::TypeError::New(env, "Invalid argument type. 'systemEventId' must be a number").ThrowAsJavaScriptException(); + return; + } + if (!info[2].IsNumber()) { + Napi::TypeError::New(env, "Invalid argument type. 'eventType' must be a SystemEventType").ThrowAsJavaScriptException(); + return; + } + + this->_connection = Napi::ObjectWrap::Unwrap(info[0].As()); + this->_id = static_cast(info[1].As().Uint32Value()); + + if (info[2].As().Uint32Value() >= _systemEventNames.size()) { + Napi::TypeError::New(env, "Invalid argument. 'eventType' exceedes SystemEventType").ThrowAsJavaScriptException(); + return; + } + this->_event = _systemEventNames[info[2].As().Uint32Value()]; + + if (this->_connection->systemEventIdExists(this->_id)) { + Napi::TypeError::New(env, "The system event ID already exists").ThrowAsJavaScriptException(); + return; + } + + this->_connection->addSystemEventId(this->_id); +} + +std::uint32_t SystemEvent::id() const { + return this->_id; +} + +const std::string& SystemEvent::eventName() const { + return this->_event; +} + +bool SystemEvent::subscribe() { + HRESULT result = SimConnect_SubscribeToSystemEvent(this->_connection->simConnect(), this->_id, this->_event.c_str()); + return SUCCEEDED(result); +} + +bool SystemEvent::unsubscribe() { + HRESULT result = SimConnect_UnsubscribeFromSystemEvent(this->_connection->simConnect(), this->_id); + return SUCCEEDED(result); +} + +Napi::Value SystemEvent::lastError(const Napi::CallbackInfo& info) { + Napi::Env env = info.Env(); + + if (info.Length() != 0) { + Napi::TypeError::New(env, "Parameters are not allowed").ThrowAsJavaScriptException(); + return env.Null(); + } + + return Napi::String::New(env, this->_lastError); +} + +Napi::Object SystemEvent::initialize(Napi::Env env, Napi::Object exports) { + Napi::Function func = DefineClass(env, "SystemEventBindings", { + InstanceMethod<&SystemEvent::lastError>("lastError", static_cast(napi_writable | napi_configurable)), + }); + + Napi::FunctionReference* constructor = new Napi::FunctionReference(); + *constructor = Napi::Persistent(func); + env.GetInstanceData()->clientDataAreaConstructor = constructor; + + exports.Set("SystemEventBindings", func); + return exports; +} diff --git a/src/bindings/simconnect/systemevent.h b/src/bindings/simconnect/systemevent.h new file mode 100644 index 0000000..15c54ad --- /dev/null +++ b/src/bindings/simconnect/systemevent.h @@ -0,0 +1,49 @@ +#pragma once + +#include + +#include "connection.h" + +namespace msfs { +namespace simconnect { + class SystemEvent : public Napi::ObjectWrap { + private: + Connection* _connection; + std::string _event; + std::uint32_t _id; + std::string _lastError; + + public: + SystemEvent(const Napi::CallbackInfo& info); + + /** + * @brief Returns the unique event ID + * @return Returns the unique event ID as std::uint32_t + */ + std::uint32_t id() const; + /** + * @brief Returns the event name + * @return Returns the event name as std::string + */ + const std::string& eventName() const; + /** + * @brief Subscribes the event to the SimConnect connection + * @return Return true if the event is subscribed, else false + */ + bool subscribe(); + /** + * @brief Unubscribes the event from the SimConnect connection + * @return Return true if the event is unsubscribed, else false + */ + bool unsubscribe(); + /** + * @brief Returns the last error of an other call + * @param info The parameter block without additional parameters + * @return Returns Napi::String with the last error + */ + Napi::Value lastError(const Napi::CallbackInfo& info); + + static Napi::Object initialize(Napi::Env env, Napi::Object exports); + }; +} +} diff --git a/src/simconnect/constants/index.ts b/src/simconnect/constants/index.ts index 75bd77c..a7feeb2 100644 --- a/src/simconnect/constants/index.ts +++ b/src/simconnect/constants/index.ts @@ -1,2 +1,3 @@ export * from './clientdata'; export * from './simulatordata'; +export * from './systemevent'; diff --git a/src/simconnect/constants/systemevent.ts b/src/simconnect/constants/systemevent.ts new file mode 100644 index 0000000..e2eee39 --- /dev/null +++ b/src/simconnect/constants/systemevent.ts @@ -0,0 +1,26 @@ +export enum SystemEventType { + OneSecond = 0, + FourSeconds = 1, + SixHertz = 2, + AircraftLoaded = 3, + Crashed = 4, + CrashReset = 5, + FlightLoaded = 6, + FlightSaved = 7, + FlightPlanActivated = 8, + FlightPlanDeactivated = 9, + Frame = 10, + ObjectAdded = 11, + ObjectRemoved = 12, + Pause = 13, + PauseEX1 = 14, + Paused = 15, + PauseFrame = 16, + PositionChanged = 17, + Sim = 18, + SimStart = 19, + SimStop = 20, + Sound = 21, + Unpaused = 22, + View = 23, +} diff --git a/src/simconnect/dispatcher.ts b/src/simconnect/dispatcher.ts index 134947c..7e460f4 100644 --- a/src/simconnect/dispatcher.ts +++ b/src/simconnect/dispatcher.ts @@ -1,6 +1,7 @@ import { Connection } from './connection'; import { ClientDataPeriod, ClientDataRequest, SimulatorDataPeriod } from './constants'; import { ClientDataArea, SimulatorDataArea } from './dataareas'; +import { SystemEvent } from './events'; import { DispatcherResponse } from './types'; const simconnect = require('./libs/simconnect'); @@ -8,6 +9,8 @@ const simconnect = require('./libs/simconnect'); export interface Dispatcher { requestClientData(clientData: ClientDataArea, period: ClientDataPeriod, request: ClientDataRequest): boolean; requestSimulatorData(simulatorData: SimulatorDataArea, period: SimulatorDataPeriod): boolean; + subscribeSystemEvent(systemEvent: SystemEvent): boolean; + unsubscribeSystemEvent(systemEvent: SystemEvent): boolean; nextDispatch(): DispatcherResponse; lastError(): string; } diff --git a/src/simconnect/events/index.ts b/src/simconnect/events/index.ts new file mode 100644 index 0000000..2bfc301 --- /dev/null +++ b/src/simconnect/events/index.ts @@ -0,0 +1 @@ +export * from './systemevent'; diff --git a/src/simconnect/events/systemevent.ts b/src/simconnect/events/systemevent.ts new file mode 100644 index 0000000..754e6a3 --- /dev/null +++ b/src/simconnect/events/systemevent.ts @@ -0,0 +1,12 @@ +import { Connection } from '../connection'; +import { SystemEventType } from '../constants'; + +const simconnect = require('./libs/simconnect'); + +export interface SystemEvent { + lastError(): string; +} + +export const SystemEvent: { + new(connection: Connection, systemEventId: number, eventType: SystemEventType): SystemEvent, +} = simconnect.SystemEventBindings; diff --git a/src/simconnect/index.ts b/src/simconnect/index.ts index 85c5a8c..8374147 100644 --- a/src/simconnect/index.ts +++ b/src/simconnect/index.ts @@ -1,5 +1,6 @@ export * from './connection'; export * from './constants'; +export * from './events'; export * from './dataareas'; export * from './dispatcher'; export * from './receiver'; diff --git a/src/simconnect/receiver.ts b/src/simconnect/receiver.ts index 0f3b8f8..199d47d 100644 --- a/src/simconnect/receiver.ts +++ b/src/simconnect/receiver.ts @@ -2,12 +2,14 @@ import { Connection } from './connection'; import { ClientDataPeriod, ClientDataRequest, SimulatorDataPeriod } from './constants'; import { ClientDataArea, SimulatorDataArea } from './dataareas'; import { Dispatcher } from './dispatcher'; +import { SystemEvent } from './events'; import { ClientDataRequestMessage, ErrorMessage, ExceptionMessage, OpenMessage, SimulatorDataRequestMessage, + SystemEventMessage, } from './types'; export type ReceiverCallbacks = { @@ -15,6 +17,7 @@ export type ReceiverCallbacks = { quit: () => void; clientData: (message: ClientDataRequestMessage) => void; simulatorData: (message: SimulatorDataRequestMessage) => void; + systemEvent: (message: SystemEventMessage) => void; exception: (message: ExceptionMessage) => void; error: (message: ErrorMessage) => void; } @@ -29,6 +32,7 @@ export class Receiver { quit: null, clientData: null, simulatorData: null, + systemEvent: null, exception: null, error: null, } @@ -54,6 +58,9 @@ export class Receiver { case 'simulatorData': if (this.callbacks.simulatorData !== null) this.callbacks.simulatorData(response.data as SimulatorDataRequestMessage); break; + case 'systemEvent': + if (this.callbacks.systemEvent !== null) this.callbacks.systemEvent(response.data as SystemEventMessage); + break; case 'exception': if (this.callbacks.exception !== null) this.callbacks.exception(response.data as ExceptionMessage); break; @@ -89,4 +96,12 @@ export class Receiver { public requestSimulatorData(simulatorData: SimulatorDataArea, period: SimulatorDataPeriod): boolean { return this.dispatcher.requestSimulatorData(simulatorData, period); } + + public subscribeSystemEvent(systemEvent: SystemEvent): boolean { + return this.dispatcher.subscribeSystemEvent(systemEvent); + } + + public unsubscribeSystemEvent(systemEvent: SystemEvent): boolean { + return this.dispatcher.unsubscribeSystemEvent(systemEvent); + } } diff --git a/src/simconnect/types/index.ts b/src/simconnect/types/index.ts index 72954b2..ae13141 100644 --- a/src/simconnect/types/index.ts +++ b/src/simconnect/types/index.ts @@ -1,5 +1,5 @@ export interface DispatcherResponse { - type: 'open' | 'quit' | 'clientData' | 'simulatorData' | 'exception' | 'error'; + type: 'open' | 'quit' | 'clientData' | 'simulatorData' | 'systemEvent' | 'exception' | 'error'; data: object; } @@ -35,3 +35,4 @@ export interface ErrorMessage { export * from './clientdata'; export * from './simulatordata'; +export * from './systemevent'; diff --git a/src/simconnect/types/systemevent.ts b/src/simconnect/types/systemevent.ts new file mode 100644 index 0000000..54b5ae6 --- /dev/null +++ b/src/simconnect/types/systemevent.ts @@ -0,0 +1,52 @@ +export enum SystemEventObjectType { + User = 0, + All = 1, + Aircraft = 2, + Helicopter = 3, + Boat = 4, + Ground = 5, +} + +export enum SystemEventPauseType { + Unpaused = 0, + FullPause = 1, + PauseWithSound = 2, + ActivePause = 4, + SimPause = 8, +} + +export enum SystemEventViewType { + Cockpit2D = 1, + CockpitVirtual = 2, + Orthogonal = 4, +} + +export interface SystemEventFilename { + filename: string; +} + +export interface SystemEventAiObject { + id: number; + type: SystemEventObjectType; +} + +export interface SystemEventPause { + type: SystemEventPauseType; +} + +export interface SystemEventSim { + running: boolean; +} + +export interface SystemEventSound { + soundSwitchOn: boolean; +} + +export interface SystemEventView { + view: SystemEventViewType; +} + +export interface SystemEventMessage { + eventId: number; + content?: SystemEventFilename | SystemEventAiObject | SystemEventPause | SystemEventSim | SystemEventSound | SystemEventView; +}