v1.3.1.0
Loading...
Searching...
No Matches
WASimClient.h
Go to the documentation of this file.
1/*
2This file is part of the WASimCommander project.
3https://github.com/mpaperno/WASimCommander
4
5COPYRIGHT: (c) Maxim Paperno; All Rights Reserved.
6
7This file may be used under the terms of either the GNU General Public License (GPL)
8or the GNU Lesser General Public License (LGPL), as published by the Free Software
9Foundation, either version 3 of the Licenses, or (at your option) any later version.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16Copies of the GNU GPL and LGPL are included with this project
17and are available at <http://www.gnu.org/licenses/>.
18*/
19
20#pragma once
21#include <cstdint>
22#include <functional>
23#include <map>
24#include <memory>
25#include <string>
26#include <vector>
27
28#define WIN32_LEAN_AND_MEAN
29#include <Windows.h>
30
31#include "WASimCommander.h"
32#include "client/exports.h"
33#include "client/enums.h"
34#include "client/structs.h"
35
36/// \file
37
38/// \def WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
39/// Define with value of 0 (zero) to invoke all callbacks consecutively, using a mutex lock. Default is (possible) concurrent invocation. Callbacks may still arrive from different threads. \relates WASimClient
40#ifndef WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
41 #define WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS 1
42#endif
43
44/// WASimCommander::Client namespace.
45/// Defines/declares everything needed to interact with the _WASimCommander Client API_, including the WASimClient class itself.
47{
48
49//----------------------------------------------------------------------------
50// Constants
51//----------------------------------------------------------------------------
52
53/// \name Return result values
54/// \{
55#ifndef E_NOT_CONNECTED
56static const HRESULT E_NOT_CONNECTED = /*ERROR_NOT_CONNECTED*/ 2250L | (/*FACILITY_WIN32*/ 7 << 16) | 0x80000000; ///< Error result: server not connected
57#endif
58#ifndef E_TIMEOUT
59static const HRESULT E_TIMEOUT = /*ERROR_TIMEOUT*/ 1460L | (/*FACILITY_WIN32*/ 7 << 16) | 0x80000000; ///< Error result: timeout communicating with server.
60#endif
61/// \}
62
63/// Starting ID range for "Custom Key Events" for use with `registerCustomKeyEvent()` generated IDs.
64/// (This corresponds to the value of `1 + THIRD_PARTY_EVENT_ID_MAX` constant from SimConnect SDK header file 'MSFS/Legacy/gauges.h'.)
65static const uint32_t CUSTOM_KEY_EVENT_ID_MIN = 0x00020000;
66
67
68//----------------------------------------------------------------------------
69// Callback Types
70//----------------------------------------------------------------------------
71
72 using clientEventCallback_t = std::function<void __stdcall(const ClientEvent &)>; ///< Callback function for Client events. \sa WASimClient::setClientEventCallback()
73 using listResultsCallback_t = std::function<void __stdcall(const ListResult &)>; ///< Callback function for delivering list results, eg. of local variables sent from Server. \sa WASimClient::setListResultsCallback()
74 using dataCallback_t = std::function<void __stdcall(const DataRequestRecord &)>; ///< Callback function for subscription result data. \sa WASimClient::setDataCallback()
75 using logCallback_t = std::function<void __stdcall(const LogRecord &, LogSource)>; ///< Callback function for log entries (from both Client and Server). \sa WASimClient::setLogCallback()
76 using commandCallback_t = std::function<void __stdcall(const Command &)>; ///< Callback function for commands sent from server. \sa WASimClient::setCommandResultCallback(), WASimClient::setResponseCallback()
77
78
79// -------------------------------------------------------------
80// WASimClient
81// -------------------------------------------------------------
82
83 /// WASimCommander Client implementation. Handles all aspects of communication with the WASimCommander Server WASM module.
84 class WSMCMND_API WASimClient
85 {
86 public:
87 /// Instantiate the client with a unique ID and optional configuration file path.
88 /// \param clientId This ID must not be shared with any other Client accessing the Server.
89 /// The ID is also used as the client name by converting it to a 8-character hexadecimal string. For example 3235839725 = "C0DEFEED". Get creative.
90 /// The client name is used as a key component of data exchange with the simulator engine.
91 /// The ID cannot be zero, and server connections will fail if it is. It can also be set/changed after class creation using the `setClientId()` method, but before connecting to the server.
92 /// \param configFile Optionally provide the path to a configuration file for reading initial startup settings. By default the client will look for a `client_conf.ini` file in the current working directory.
93 /// The parameter value may include the file name (with extension), or be only a file system path, in which case the default file name of "client_conf.ini" will be appended.
94 explicit WASimClient(uint32_t clientId, const std::string &configFile = std::string());
95 /// Any open network connections are automatically closed upon destruction, though it is better to close them yourself before deleting the client.
97
98 /// \name Network actions, status, and settings
99 /// \{
100
101 ClientStatus status() const; ///< Get current connection status of this client. \sa WASimCommander::Client::ClientStatus
102 bool isInitialized() const; ///< Check if simulator network link is established. \sa connectSimulator()
103 bool isConnected() const; ///< Check WASimCommander server connection status. \sa connectServer()
104 uint32_t clientVersion() const; ///< Return the current WASimClient version number. Version numbers are in "BCD" format: `MAJOR << 24 | MINOR << 16 | PATCH << 8 | BUILD`, eg: `1.23.45.67 = 0x01234567`
105 uint32_t serverVersion() const; ///< Return the version number of the last-connected, or successfully pinged, WASimModule (sever), or zero if unknown. See `clientVersion()` for numbering details.
106
107 /// Initialize the simulator network link and set up minimum necessary for WASimCommander server ping or connection. Uses default network SimConnect configuration ID.
108 /// \param timeout Maximum time to wait for response, in milliseconds. Zero (default) means to use the `defaultTimeout()` value.
109 /// \return `S_OK` (0) - Success;\n
110 /// `E_FAIL` (0x80004005) - General failure (most likely simulator is not running);\n
111 /// `E_TIMEOUT` (0x800705B4) - Connection attempt timed out (simulator/network issue);\n
112 /// `E_INVALIDARG` (0x80070057) - The Client ID set in constructor is invalid (zero) or the SimConnect.cfg file did not contain the default config index (see `networkConfigurationId()` ) ;
113 /// \note This method blocks until either the Simulator responds or the timeout has expired.
114 /// \sa defaultTimeout(), setDefaultTimeout(), networkConfigurationId() setNetworkConfigurationId(), connectSimulator(int, uint32_t)
115 HRESULT connectSimulator(uint32_t timeout = 0);
116 /// Initialize the simulator network link and set up minimum necessary for WASimCommander server ping or connection. This overload allows specifying a SimConnect configuration ID and optional timeout value.
117 /// \param networkConfigId SimConnect is used for the network layer. Specify the SimConnect.cfg index to use, or -1 (default) to force a local connection.
118 /// \param timeout Maximum time to wait for response, in milliseconds. Zero (default) means to use the `defaultTimeout()` value.
119 /// \return `S_OK` (0) - Success;\n
120 /// `E_FAIL` (0x80004005) - General failure (most likely simulator is not running);\n
121 /// `E_TIMEOUT` (0x800705B4) - Connection attempt timed out (simulator/network issue);\n
122 /// `E_INVALIDARG` (0x80070057) - The Client ID set in constructor is invalid (zero) or the SimConnect.cfg file did not contain the config index requested in the `simConnectConfigId` parameter;
123 /// \note This method blocks until either the Simulator responds or the timeout has expired. \sa connectSimulator(), defaultTimeout(), setDefaultTimeout()
124 HRESULT connectSimulator(int networkConfigId, uint32_t timeout = 0);
125 /// Shut down all network connections (and disconnect WASimCommander server if connected).
126 /// After calling this method, one must call `connectSimulator()`/`connectServer()`/`pingServer()` again before any other commands.
127 void disconnectSimulator();
128
129 /// Check if WASimCommander Server exists (Simulator running, the WASIM module is installed and working). Returns server version number, or zero if server did not respond.\n
130 /// This will implicitly call `connectSimulator()` first if it hasn't already been done, using the default network configuration settings. Zero will be returned if the connection could not be established.
131 /// \param timeout Maximum time to wait for response, in milliseconds. Zero (default) means to use the `defaultTimeout()` value.
132 /// \return Server version number, or zero (`0`) if server (or simulator) didn't respond within the timeout period.
133 /// \note This method blocks until either the Server responds or the timeout has expired. \sa defaultTimeout(), setDefaultTimeout()
134 uint32_t pingServer(uint32_t timeout = 0);
135
136 /// Connect to WASimCommander server.
137 /// This will implicitly call \c connectSimulator() first if it hasn't already been done, using the default network configuration setting.
138 /// \param timeout Maximum time to wait for response, in milliseconds. Zero (default) means to use the `defaultTimeout()` value.
139 /// \return `S_OK` (0) - Success.\n
140 /// `E_FAIL` (0x80004005) - General failure (most likely simulator is not running).\n
141 /// `E_TIMEOUT` (0x800705B4) - Connection attempt timed out (simulator/network issue or WASimCommander WASM module is not installed/running).\n
142 /// `E_INVALIDARG` (0x80070057) - The Client ID set in constructor is invalid (zero) or the SimConnect.cfg file did not contain the default config index.
143 /// \note This method blocks until either the Server responds or the timeout has expired. \sa defaultTimeout(), setDefaultTimeout()
144 HRESULT connectServer(uint32_t timeout = 0);
145 /// Disconnect from the WASimCommander server. This does _not_ close the Simulator network connection (use \c disconnectSimulator() to do that or both at once).
146 void disconnectServer();
147
148 /// Get the current default server response timeout value, which is used in all network requests.
149 /// The initial default setting is read from the `client_conf.ini` file or set to 1000ms if no config file was found. \sa setDefaultTimeout().
150 uint32_t defaultTimeout() const;
151 ///< Set the default timeout period for server responses. The default may be inadequate on slow network links or a very busy simulator. \sa defaultTimeout()
152 void setDefaultTimeout(uint32_t ms);
153
154 /// SimConnect is used for the network layer. This setting specifies the SimConnect.cfg index to use. The value of -1 forces a local connection.
155 /// The initial default setting is read from the `client_conf.ini` file or set to `-1` if no config file was found. \sa setNetworkConfigurationId().
156 int networkConfigurationId() const;
157 /// SimConnect is used for the network layer. This setting specifies the SimConnect.cfg index to use, or -1 to force a local connection.
158 /// Note that this must be called before `connectSimulator()` invocation in order to have any effect. \sa networkConfigurationId().
159 void setNetworkConfigurationId(int configId);
160
161 /// \}
162 /// \name RPN calculator code execution and reusable events
163 /// \{
164
165 /// Run a string of MSFS _Gauge API_ calculator code in RPN format, possibly with some kind of result expected.
166 /// \param code The text of the code to execute. See https://docs.flightsimulator.com/html/Additional_Information/Reverse_Polish_Notation.htm
167 /// \param resultType Expected result type, or `Enums::CalcResultType::None` (default) if no result is expected. If the type is `Enums::CalcResultType::Formatted` then the server
168 /// runs the code using `format_calculator_string()` _Gauge API_ function (see @ bottom of RPN docs for formatting options) and the result type is always a string.
169 /// Otherwise `execute_calculator_code()` is used and any of the result types can be returned (in fact `execute_calculator_code()` always returns any results in all 3 types at once, so even
170 /// if a numeric result is requested, the string result will also be populated).
171 /// \param pfResult A pointer to an initialized variable of `double` to store the result into if `resultType` is `Enums::CalcResultType::Double` or `Enums::CalcResultType::Integer`.
172 /// \param psResult A string pointer to store the string result into. The string version is typically populated even for numeric type requests, but definitely for `Enums::CalcResultType::String` or `Enums::CalcResultType::Formatted` type requests.
173 /// \return `S_OK` on success, `E_NOT_CONNECTED` if not connected to server; \n
174 /// If a result is expected, may also return `E_FAIL` if the server returned Nak response, or `E_TIMEOUT` on general server communication failure.
175 /// \note _If_ a result is expected (`resultType` != `Enums::CalcResultType::None`) then this method blocks until either the Server responds or the timeout has expired (see `defaultTimeout()`).
176 /// To request calculated results in a non-blocking fashion, use a data request instead.
177 ///
178 /// If you need to execute the same code multiple times, it would be more efficient to save the code as either a data request (for code returning values) or a registered event (for code not returning values).
179 /// The advantage is that in those cases the calculator string is pre-compiled to byte code and saved once, then each invocation of the _Gauge API_ calculator functions uses the more efficient byte code version.
180 /// (To prevent automatic data updates for data requests, just set the data request period to `Enums::UpdatePeriod::Never` or `Enums::UpdatePeriod::Once` and use the `updateDataRequest()` method to poll for value updates as needed.)
181 /// See `saveDataRequest()` and `registerEvent()` respectively for details.
182 /// \sa \refwce{CommandId::Exec}, defaultTimeout(), setDefaultTimeout()
183 HRESULT executeCalculatorCode(const std::string &code, WASimCommander::Enums::CalcResultType resultType = WASimCommander::Enums::CalcResultType::None, double *pfResult = nullptr, std::string *psResult = nullptr) const;
184
185 /// \}
186 /// \name Variables accessor methods
187 /// \{
188
189 /// Get a Variable value by name, with optional named unit type. This is primarily useful for local ('L') variables, SimVars ('A') and token variables ('T') which can be read via dedicated _Gauge API_ functions
190 /// (`get_named_variable_value()`/`get_named_variable_typed_value()`, `aircraft_varget()`, and `lookup_var()` respectively). \n
191 /// Other variables types can also be set this way ('C', 'E', 'M', etc) but such requests are simply **converted to a calculator string** and evaluated via the _Gauge API_ `execute_calculator_code()`. \n
192 /// Likewise, requesting string-type variables using this method also ends up running a calculator expression on the server side. \n
193 /// In both cases, using `WASimClient::executeCalculatorCode()` directly may be more efficient. Also, unlike `executeCalculatorCode()`, this method will not return a string representation of a numeric value.
194 /// \param variable See `VariableRequest` documentation for descriptions of the individual fields.
195 /// \param pfResult Pointer to a double precision variable to hold the numeric result.
196 /// \param psResult Pointer to a string type variable to hold a string-type result. See notes above regarding string types.
197 /// \return `S_OK` on success, `E_INVALIDARG` on parameter validation errors, `E_NOT_CONNECTED` if not connected to server, `E_TIMEOUT` on server communication failure, or `E_FAIL` if server returns a Nak response.
198 /// \note This method blocks until either the Server responds or the timeout has expired.
199 /// \sa \refwcc{VariableRequest}, \refwce{CommandId::Get}, defaultTimeout(), setDefaultTimeout()
200 HRESULT getVariable(const VariableRequest &variable, double *pfResult, std::string *psResult = nullptr);
201 /// A convenience version of `getVariable(VariableRequest(variableName, false, unitName), pfResult)`. See `getVariable()` and `VariableRequest` for details.
202 /// \param variableName Name of the local variable.
203 /// \param pfResult Pointer to a double precision variable to hold the result.
204 /// \param unitName Optional unit specifier to use. Most Local vars do not specify a unit and default to a generic "number" type.
205 /// \return `S_OK` on success, `E_INVALIDARG` on parameter validation errors, `E_NOT_CONNECTED` if not connected to server, `E_TIMEOUT` on server communication failure, or `E_FAIL` if server returns a Nak response.
206 /// \note This method blocks until either the Server responds or the timeout has expired. \sa \refwce{CommandId::Get}, defaultTimeout(), setDefaultTimeout()
207 HRESULT getLocalVariable(const std::string &variableName, double *pfResult, const std::string &unitName = std::string());
208 /// Gets the value of a local variable just like `getLocalVariable()` but will also create the variable on the simulator if it doesn't already exist.
209 /// \param variableName Name of the local variable.
210 /// \param pfResult Pointer to a double precision variable to hold the result.
211 /// \param defaultValue The L var will be created on the simulator if it doesn't exist yet using this initial value (and this same value will be returned in `pfResult`).
212 /// \param unitName Optional unit specifier to use. Most Local vars do not specify a unit and default to a generic "number" type.
213 /// \return `S_OK` on success, `E_INVALIDARG` on parameter validation errors, `E_NOT_CONNECTED` if not connected to server, `E_TIMEOUT` on server communication failure, or `E_FAIL` if server returns a Nak response.
214 /// \note This method blocks until either the Server responds or the timeout has expired. \sa \refwce{CommandId::GetCreate}, defaultTimeout(), setDefaultTimeout()
215 HRESULT getOrCreateLocalVariable(const std::string &variableName, double *pfResult, double defaultValue = 0.0, const std::string &unitName = std::string());
216
217 /// Set a Variable value by name, with optional named unit type. Although any settable variable type can set this way, it is primarily useful for local (`L`)
218 /// variables which can be set via dedicated _Gauge API_ functions (`set_named_variable_value()` and `set_named_variable_typed_value()`). \n\n
219 /// Other variables types can also be set this way but such requests are simply converted to a calculator string and
220 /// evaluated via the _Gauge API_ `execute_calculator_code()`. Using `WASimClient::executeCalculatorCode()` directly may be more efficient. \n
221 /// The following conditions must be observed:
222 /// - The variable type in `VariableRequest::variableType` must be "settable" ('A', 'B', 'C', 'H', 'K', 'L', or 'Z'), otherwise an `E_INVALIDARG` result is returned.
223 /// - Setting an 'A' type variable this way _requires_ the actual variable name in `VariableRequest::variableName` -- using just an ID returns `E_INVALIDARG`. \n
224 /// (Other settable variable types don't have any associated ID anyway, so this is not an issue.)
225 /// - For any variable type _other than_ 'L', a Unit can only be specified as a string (in `VariableRequest::unitName`), not an ID.
226 /// Using only an ID will not cause an error, but the unit will not be included in the generated RPN code. \n
227 /// For 'L' variable types, if both a name and ID are provided, the numeric ID is used insted of the name (this avoids a lookup on the server side).
228 ///
229 /// \param variable See `VariableRequest` documentation for descriptions of the individual fields.
230 /// \param value The numeric value to set.
231 /// \return `S_OK` on success, `E_INVALIDARG` on parameter validation errors, `E_NOT_CONNECTED` if not connected to server, or `E_FAIL` on general failure (unlikely).
232 /// \sa \refwce{CommandId::Set}
233 HRESULT setVariable(const VariableRequest &variable, const double value);
234 /// This is an overloaded method. This version allows setting an 'A' type (SimVar) variable to a string value. **Only 'A' type variables can be set this way.** \n
235 /// Since there is actually no direct way to set string-type values from WASM code, this is just a convenience method and simply invokes SimConnect to do the work.
236 /// On first use with a new variable name it will set up a mapping of the name to an internally-assigned ID (calling `SimConnect_AddToDataDefinition()`) and cache that mapping.
237 /// Then on subsequent invocations on the same variable the mapped ID will be used directly. The mappings are invalidated when disconnecting from the simulator.
238 HRESULT setVariable(const VariableRequest &variable, const std::string &stringValue);
239
240 /// A convenience version of `setVariable()` for Local variable types. Equivalent to `setVariable(VariableRequest(variableName, false, unitName), value)`. See `setVariable()` and `VariableRequest` for details.
241 /// \param variableName Name of the local variable.
242 /// \param value The value to set.
243 /// \param unitName Optional unit specifier to use. Most Local vars do not specify a unit and default to a generic "number" type.
244 /// \return `S_OK` on success, `E_INVALIDARG` on parameter validation errors, `E_NOT_CONNECTED` if not connected to server, or `E_FAIL` on general failure (unlikely).
245 /// \sa \refwce{CommandId::Set}
246 HRESULT setLocalVariable(const std::string &variableName, const double value, const std::string &unitName = std::string());
247 /// Set a Local Variable value by variable name, creating it first if it does not already exist. This first calls the `register_named_variable()` _Gauge API_ function to get the ID from the name,
248 /// which creates the variable if it doesn't exist. The returned ID (new or existing) is then used to set the value. Use the `lookup()` method to check for the existence of a variable name.
249 /// Equivalent to `setVariable(VariableRequest(variableName, true, unitName), value)`. See `setVariable()` and `VariableRequest` for details.
250 /// \param variableName Name of the local variable.
251 /// \param value The value to set. Becomes the initial value if the variable is created.
252 /// \param unitName Optional unit specifier to use. Most Local vars do not specify a unit and default to a generic "number" type.
253 /// \return `S_OK` on success, `E_INVALIDARG` on parameter validation errors, `E_NOT_CONNECTED` if not connected to server, or `E_FAIL` on general failure (unlikely).
254 /// \sa \refwce{CommandId::SetCreate}
255 HRESULT setOrCreateLocalVariable(const std::string &variableName, const double value, const std::string &unitName = std::string());
256
257 /// Sets a numeric value on an 'A' (aka "SimVar" / "Simulator Variable") type variable. \n
258 /// This is a convenience version of `setVariable()`, equivalent to `setVariable(VariableRequest(variableName, unitName), value)`. See `setVariable()` and `VariableRequest` for details.\n
259 /// Note that `variableName` can optionally contain an index after a colon (eg. `VAR NAME:1`), or the `setSimVarVariable(const string& name, uint8_t index, const string& unit, double value)`
260 /// overload could be used to provide the index separately. For MSFS 2024 Sim Vars which use component name suffixes, specify them in the variable name itself.
261 /// \since v1.3.0
262 inline HRESULT setSimVarVariable(const std::string &variableName, const std::string &unitName, double value) { return setVariable(VariableRequest(variableName, unitName), value); }
263 /// Sets a numeric value on an indexed 'A' (aka "SimVar" / "Simulator Variable") type variable. \n
264 /// This is a convenience version of `setVariable()`, equivalent to `setVariable(VariableRequest(variableName, unitName, index), value)`. See `setVariable()` and `VariableRequest` for details.
265 /// \since v1.3.0
266 inline HRESULT setSimVarVariable(const std::string &variableName, uint8_t index, const std::string &unitName, double value) { return setVariable(VariableRequest(variableName, unitName, index), value); }
267 /// Sets a string value on an 'A' (aka "SimVar" / "Simulator Variable") type variable. \n
268 /// This is a convenience version of `setVariable()`, equivalent to `setVariable(VariableRequest(`'A'`, variableName), stringValue)`. See `setVariable(const VariableRequest &, const std::string &)` for details.
269 /// \since v1.3.0
270 inline HRESULT setSimVarVariable(const std::string &variableName, const std::string &stringValue) { return setVariable(VariableRequest('A', variableName), stringValue); }
271 /// Sets a string value on an indexed 'A' (aka "SimVar" / "Simulator Variable") type variable. \n
272 /// This is a convenience version of `setVariable()`, equivalent to `setVariable(VariableRequest(variableName, {}, index), stringValue)`. See `setVariable(const VariableRequest &, const std::string &)` for details.
273 /// \since v1.3.0
274 inline HRESULT setSimVarVariable(const std::string &variableName, uint8_t index, const std::string &stringValue) { return setVariable(VariableRequest(variableName, {}, index), stringValue); }
275
276 /// \}
277 /// \name Data change subscriptions (variables and calculated results)
278 /// \{
279
280 /// Add a new `WASimCommander::DataRequest` for a variable or calculated result, or update an existing data request with the same `DataRequest::requestId`.
281 /// Data changes (updates) are delivered asynchronously via the callback function set with `setDataCallback()`, which then passes a `DataRequestRecord` structure as the callback argument
282 /// (this provides both a reference to the original `DataRequest` registered here, as well as result data).
283 /// \param request The `WASimCommander::DataRequest` structure to process. See `WASimCommander::DataRequest` documentation for details of the structure members.
284 /// \param async Set to `false` (default) to wait for an `Ack`/`Nak` response from the server before returning from this method, or `true` to return without waiting for a response. See return values and the Note below for more details.
285 /// \return `S_OK` on success, `E_INVALIDARG` if there is a problem with the `DataRequest` contents. \n
286 /// If currently connected to the server and `async` is `false`, may also return `E_FAIL` if the server returned `Nak` response, or `E_TIMEOUT` on general server communication failure.
287 /// \note If currently connected to the server and the `async` param is `false`, this method will block until either the Server responds or the timeout has expired (see `defaultTimeout()`).
288 /// If the client is _not_ currently connected to the server, the request is queued until the next connection is established (and this method is non-blocking regardless of `async` argument).
289 /// \par Tracking async calls
290 /// To track the status of an async request, set a callback function with `setCommandResultCallback()`. The server should respond with an \refwce{CommandId::Ack} or \refwce{CommandId::Nak}
291 /// \refwc{Command} where the `uData` value is \refwce{CommandId::Subscribe} and the \refwc{Command::token} will be the `requestId` value from the given `request` struct.
292 /// \sa \refwc{DataRequest} \refwce{CommandId::Subscribe}, removeDataRequest(), updateDataRequest(), setDataCallback(), DataRequestRecord
293 HRESULT saveDataRequest(const DataRequest &request, bool async = false);
294 /// Remove a previously-added `DataRequest`. This clears the subscription and any tracking/meta data from both server and client sides.
295 /// Using this method is effectively the same as calling `dataRequest()` with a `DataRequest` of type `RequestType::None`.
296 /// \param requestId ID of the request to remove.
297 /// \return `S_OK` on success, `E_FAIL` if the original request wasn't found.
298 /// \note If the subscription data may be needed again in the future, it would be more efficient to edit the request (using `saveDataRequest()`) and suspend updates by setting the `DataRequest::period` to `UpdatePeriod::Never`.
299 /// To resume updates change the period again.
300 /// \sa \refwc{DataRequest}, \refwce{CommandId::Subscribe}, saveDataRequest(), updateDataRequest()
301 HRESULT removeDataRequest(const uint32_t requestId);
302 /// Trigger a data update on a previously-added `DataRequest`. Designed to refresh data on subscriptions with update periods of `UpdatePeriod::Never` or `UpdatePeriod::Once`, though it can be used with any subscription.
303 /// Using this update method also skips any equality checks on the server side (though any delta epsilon value remains in effect on client side).
304 /// \param requestId The ID of a previously added `DataRequest`.
305 /// \return `S_OK` on success, `E_FAIL` if the original request wasn't found,.
306 /// \sa \refwce{CommandId::Update}, saveDataRequest(), removeDataRequest()
307 HRESULT updateDataRequest(uint32_t requestId);
308
309 /// Returns a copy of a `DataRequestRecord` which has been previously added. If the request with the given `requestId` doesn't exist, an invalid `DataRequestRecord` is returned which has
310 /// the members `DataRequest::requestId` set to `-1`, `DataRequest::valueSize` set to `0`, and `DataRequest::requestType` set to `RequestType::None`.
311 DataRequestRecord dataRequest(uint32_t requestId) const;
312 /// Returns a list of all data requests which have been added to the Client so far. (These are returned by copy operation, so for a long list it may get "expensive.")
313 std::vector<DataRequestRecord> dataRequests() const;
314 /// Returns a list of all `DataRequest::requestId`s which have been added to the Client so far.
315 std::vector<uint32_t> dataRequestIdsList() const;
316
317 /// Enables or disables all data request subscription updates at the same time. Use this to temporarily suspend value update checks when they are not needed, but may be again in the future.
318 /// This is a lot more efficient than disconnecting and re-connecting to the server, since all the data requests need to be re-created upon every new connection (similar to SimConnect itself).
319 /// \since{v1.2}
320 /// This method can be called while not connected to the server. In this case the setting is saved and sent to the server upon next connection, before sending any data request subscriptions.
321 /// This way updates could be suspended upon initial connection, then re-enabled when the data is actually needed.
322 /// \return `S_OK` on success; If currently connected to the server, may also return `E_TIMEOUT` on general server communication failure.
323 HRESULT setDataRequestsPaused(bool paused) const;
324
325 /// \}
326 /// \name RPN calculator code execution and reusable events
327 /// \{
328
329 /// Register a reusable event which executes a pre-set RPN calculator code string. The code is pre-compiled and stored on the server for quicker execution.
330 /// The event can have an optional custom name for direct use with any SimConnect client. Registered events can also be triggered by using the `transmitEvent()` method.
331 /// If the server is not currently connected, the event registration will be queued and sent next time a connection is established.
332 /// \return `S_OK` on success, `E_INVALIDARG` if the resulting code string is too long or if trying to change the name of an already registered event.
333 /// \sa RegisteredEvent, \refwce{CommandId::Register}, removeEvent(), transmitEvent()
334 HRESULT registerEvent(const RegisteredEvent &eventData);
335 /// Remove an event previously registered with `registerEvent()` method. This is effectively the same as calling `registerEvent()` with an empty `code` parameter.
336 /// If the server is not currently connected, the removal request will be queued and sent next time a connection is established.
337 /// \param eventId ID of the previously registered event.
338 /// \return `S_OK` on success, `E_INVALIDARG` if the eventId wasn't found.
339 /// \sa \refwce{CommandId::Register}, registerEvent(), transmitEvent()
340 HRESULT removeEvent(uint32_t eventId);
341 /// Trigger an event previously registered with `registerEvent()`. This is a more direct alternative to triggering events by name via SimConnect.
342 /// \param eventId ID of the previously registered event. If the event hasn't been registered, the server will log a warning but otherwise nothing will happen.
343 /// \return `S_OK` on success, `E_FAIL` on general failure (unlikely), `E_NOT_CONNECTED` if not connected to server.
344 /// \sa \refwce{CommandId::Transmit}, registerEvent(), removeEvent()
345 HRESULT transmitEvent(uint32_t eventId);
346
347 /// Returns a copy of a `RegisteredEvent` which has been previously added with `registerEvent()`. If the event with the given `eventId` doesn't exist, an invalid `RegisteredEvent` is returned which has
348 /// the members `RegisteredEvent::eventId` set to `-1`, and `RegisteredEvent::code` and `RegisteredEvent::name` both empty.
349 RegisteredEvent registeredEvent(uint32_t eventId);
350 /// Returns a list of all registered events which have been added to the Client with `registerEvent()`. The list members are created by copy.
351 std::vector<RegisteredEvent> registeredEvents() const;
352
353 /// \}
354 /// \name Simulator Key Events
355 /// \{
356
357 /// Can be used to trigger standard Simulator "Key Events" as well as "custom" _Gauge API/SimConnect_ events. Up to 5 optional values can be passed onto the event handler.
358 /// This provides functionality similar to the _Gauge API_ function `trigger_key_event_EX1()` and `SimConnect_TransmitClientEvent[_EX1()]`. \n\n
359 /// *Standard* Key Event IDs can be found in the SimConnect SDK header file 'MSFS/Legacy/gauges.h' in the form of `KEY_*` macro values, and event names can also be
360 /// resolved to IDs programmatically using the `lookup()` method. No preliminary setup is required to trigger these events, but a full connection to WASimModule ("server") is needed.
361 /// These are triggered on the simulator side using `trigger_key_event_EX1()` function calls. \n\n
362 /// *Custom Events* for which a numeric ID is already known (typically in the _Gauge API_ `THIRD_PARTY_EVENT_ID_MIN`/`THIRD_PARTY_EVENT_ID_MAX` ID range)
363 /// can also be triggered directly as with standard events. These types of events are also passed directly to `trigger_key_event_EX1()`. \n\n
364 /// *Named Custom Events*, for which an ID is not known or predefined, **must** first be registered with `registerCustomKeyEvent()`, which creates and maps (and optionally returns)
365 /// a unique ID corresponding to the custom event name. An active simulator (SimConnect) connection is required to trigger these types of events.
366 /// They are invoked via `SimConnect_TransmitClientEvent[_EX1()]` method directly from this client (this is actually just a convenience for the WASimClient user to avoid needing a separate SimConnect session).
367 /// Which actual SimConnect function is used depends on how the custom event was registered (default is to use the newer "_EX1" version which allows up to 5 event values). \n
368 /// See docs for `registerCustomKeyEvent()` for further details on using custom simulator events.
369 /// \param keyEventId Numeric ID of the Event to trigger.
370 /// \param v1,v2,v3,v4,v5 Optional values to pass to the event handler. Defaults are all zeros.
371 /// \return `S_OK` on success, `E_NOT_CONNECTED` if not connected (server or sim, see above), `E_TIMEOUT` on server communication failure, or `E_FAIL` on unexpected SimConnect error.
372 /// \note For Key Events triggered via `trigger_key_event_EX1()`, Server responds asynchronously with an Ack/Nak response to \refwce{CommandId::SendKey} command type;
373 /// A 'Nak' means the event ID is clearly not valid (eg. zero), but otherwise the simulator provides no feedback about event execution
374 /// (from [their docs](https://docs.flightsimulator.com/html/Programming_Tools/WASM/Gauge_API/trigger_key_event_EX1.htm#return_values): "If the event requested is not appropriate, it will simply not happen."). \n\n
375 /// For _custom named_ events, triggered via `SimConnect_TransmitClientEvent[_EX1()]`, SimConnect may asynchronously send EXCEPTION type response messages if the ID isn't valid
376 /// (likely because the event hasn't been successfully registered with `registerCustomKeyEvent()`). These messages are passed through to WASimClient's logging facilities at the `Warning` level.
377 /// But again there is no actual confirmation that the event is going to do anything.
378 /// \since v1.3.0 - Added ability to trigger custom named events.
379 HRESULT sendKeyEvent(uint32_t keyEventId, uint32_t v1 = 0, uint32_t v2 = 0, uint32_t v3 = 0, uint32_t v4 = 0, uint32_t v5 = 0) const;
380
381 /// This is an overloaded method. See `sendKeyEvent(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t) const` for main details.
382 /// This version allows triggering Key Events by name instead of an ID. \n\n
383 /// All simulator event names must be resolved, or mapped, to a numeric ID before they can be triggered (used).
384 /// The first time this method is invoked with a particular event name, it tries to associate that name with an ID (based on the event name, as described below).
385 /// If successful, the ID is cached, so subsequent calls to this method, with the same event name, will use the cached ID instead of trying to resolve the name again.
386 /// - For _standard_ Key Events, the name is resolved to an ID using the 'lookup()' method and the resulting ID (if valid) is cached for future uses. There **must** be an active server connection for this to work.
387 /// - For _custom_ events (names that contain a "." (period) or start with a "#"), the event is first registered using `registerCustomKeyEvent()` and the resulting ID is cached if that succeeds.
388 ///
389 /// \param keyEventName Name of the Event to trigger.
390 /// \param v1,v2,v3,v4,v5 Optional values to pass to the event handler. Defaults are all zeros.
391 /// \return `S_OK` on success, `E_INVALIDARG` if event name could not be resolved to an ID, `E_NOT_CONNECTED` if not connected (server or sim, depending on event type),
392 /// `E_TIMEOUT` on server communication failure, or `E_FAIL` on unexpected SimConnect error.
393 /// \note The name-to-ID cache is kept as a simple `std::unordered_map` type, so if you have a better way to save the event IDs from `lookup()` or `registerCustomKeyEvent()`,
394 /// use that instead, and call the more efficient `sendKeyEvent(eventId, ...)` overload directly.
395 /// \since v1.3.0 - Added ability to trigger custom named events.
396 HRESULT sendKeyEvent(const std::string &keyEventName, uint32_t v1 = 0, uint32_t v2 = 0, uint32_t v3 = 0, uint32_t v4 = 0, uint32_t v5 = 0);
397
398 /// Register a "Custom Simulator [Key] Event" by providing an event name. The method optionally returns the generated event ID, which can later be used with `sendKeyEvent()` method instead of the event name.
399 /// It can also be used to look up a previous registration's ID if the event name has already been registered. \n\n
400 /// Custom event names are mapped to internally-generated unique IDs using a standard SimConnect call to
401 /// [MapClientEventToSimEvent](https://docs.flightsimulator.com/html/Programming_Tools/SimConnect/API_Reference/Events_And_Data/SimConnect_MapClientEventToSimEvent.htm#parameters),
402 /// and the documentation briefly describes custom event usage and name syntax in the `EventName` parameter description. This method serves a similar purpose (and in fact eventually calls that same SimConnect function). \n\n
403 /// The mappings must be re-established every time a new connection with SimConnect is made, which WASimClient takes care of automatically. If currently connected to the simulator, the event is immediately mapped,
404 /// otherwise it will be mapped upon the next connection. An event registration can be removed with `removeCustomKeyEvent()` which will prevent any SimConnect mapping from being created upon the _next_ connection. \n\n
405 /// If you're not going to store the ID that will be generated anyway, `sendKeyEvent(customEventName, ...)` can be used directly, which will automatically call this method the first time the event name is used.
406 /// The ID can always be looked up later if needed (by calling this method). \n\n
407 /// Note that the custom event mapping/triggering feature is actually just a convenience for the WASimClient user and doesn't involve the usual server interactions (WASimModule) at all.
408 /// \param customEventName Name of the Event to register. The event name _must_ contain a "." (period) or start with a "#", otherwise an `E_INVALIDARG` result is returned. \n
409 /// If an event with the same name has already been registered, the method returns `S_OK` and no further actions are performed (besides setting the optional `puiCustomEventId` pointer value, see below).
410 /// \param puiCustomEventId Optional pointer to 32-bit unsigned integer variable to return the generated event ID. This ID can be used to trigger the event later using `sendKeyEvent()` (which is more efficient than using the event name each time).
411 /// The ID will always be unique per given event name, and is always equal to or greater than the `Client::CUSTOM_KEY_EVENT_ID_MIN` constant value. \n
412 /// The pointer's value will be populated even if the event name was already registered (with the result of the previously generated ID).
413 /// \param useLegacyTransmit Optional, default `false`. Boolean value indicating that the deprecated `SimConnect_TransmitClientEvent()` function should be used to trigger the event instead of the newer `SimConnect_TransmitClientEvent_EX1()`.
414 /// This may be necessary to support models which haven't updated to the newer version of the event handler. Note that the old `TransmitClientEvent()` function only supports sending 1 event value (vs. 5 for the "_EX1" version). \n
415 /// To re-register the same event name but with a different value for `useLegacyTransmit` parameter, first remove the initial registration with `removeCustomKeyEvent()` and then call this method again.
416 /// \return `S_OK` on success, `E_INVALIDARG` if the event name is invalid.
417 /// \since v1.3.0
418 HRESULT registerCustomKeyEvent(const std::string &customEventName, uint32_t *puiCustomEventId = nullptr, bool useLegacyTransmit = false);
419 /// Remove a Custom Event previously registered with `registerCustomEvent()` method using the event's name.
420 /// This will prevent the custom event from being mapped _next_ time the client connects to SimConnect.
421 /// \note SimConnect provides no way to remove a registered Custom event. Any active SimConnect mapping will remain in effect until SimConnect is disconnected (and can still be invoked with the corresponding ID, but not by name).
422 /// \param customEventName full name of the previously registered event. Must be the same name as used with `registerCustomKeyEvent()`.
423 /// \return `S_OK` on success, `E_INVALIDARG` if the eventId wasn't found.
424 /// \since v1.3.0
425 HRESULT removeCustomKeyEvent(const std::string &customEventName);
426 /// This is an overloaded method. Same as `removeCustomKeyEvent(const string &)` but the event can be specified using the associated numeric event ID (originally returned from `registerCustomKeyEvent()`) instead of the name.
427 /// \param eventId ID of the previously registered event.
428 /// \return `S_OK` on success, `E_INVALIDARG` if the eventId wasn't found.
429 /// \since v1.3.0
430 HRESULT removeCustomKeyEvent(uint32_t eventId);
431
432 /// \}
433 /// \name Metadata retrieval
434 /// \{
435
436 /// Send a request for a list update to the server. The results are delivered using the callback set in `setListResultsCallback()`.
437 /// \param itemsType The type of thing to list. Supported types are local variables (`Enums::LookupItemType::LocalVariable`, default), subscribed Data Requests (`Enums::LookupItemType::DataRequest`), and Registered Events (`Enums::LookupItemType::RegisteredEvent`).
438 /// \return `S_OK` on success, `E_INVALIDARG` if the item type is not supported, `E_NOT_CONNECTED` if not connected to server.
439 /// \note The list result callback is invoked from a new thread which delivers the results (\refwcc{ListResult} structure). Also check the `ListResult::result` HRESULT return code to be sure the list command completed successfully
440 /// (which may be `S_OK`, `E_FAIL` if server returned `Nak`, or `E_TIMEOUT` if the list request did not complete (results may be empty or partial)).
441 /// \sa \refwce{CommandId::List}
443
444 /// Request a lookup of a named item to find its corresponding numeric ID. \n
445 /// Most lookup types are done on the server side, so an active connection is required. The exception is looking up Key Event IDs (\refwce{LookupItemType::KeyEventId}), which are performed locally.
446 /// \param itemType The type of item to look up. A type of variable or a measurement unit. See the \refwce{LookupItemType} documentation for details.
447 /// \param itemName The name of the thing to check for.
448 /// \param piResult Pointer to 32-bit signed integer variable to hold the result.
449 /// \return `S_OK` on success, `E_FAIL` if server returns a Nak response (typically means the item name wasn't found), `E_NOT_CONNECTED` if not connected to server, `E_TIMEOUT` on server communication failure.
450 /// \note Except for Key Event ID type lookups, this method blocks until either the Server responds or the timeout has expired. \sa defaultTimeout(), setDefaultTimeout()
451 HRESULT lookup(WASimCommander::Enums::LookupItemType itemType, const std::string &itemName, int32_t *piResult);
452
453 /// \}
454 /// \name Low level API
455 /// \{
456
457 /// Sends a command, in the form of a `WASimCommander::Command` structure, to the server for processing. The various command types and the data requirements for each are described in the `WASimCommander::Enums::CommandId` documentation.
458 /// To receive command responses from the server, set a callback with `setCommandResultCallback()`, then check the results for a `Command::token` which matches the `token` set in the command you're sending here.
459 /// \return `S_OK` on success, `E_NOT_CONNECTED` if not connected to server, `E_TIMEOUT` on server communication failure.
460 HRESULT sendCommand(const Command &command) const;
461 /// Sends a command, in the form of a `WASimCommander::Command` structure, to the server for processing and waits for a reply (an `Ack/Nak` response `Command`).
462 /// The various command types and the data requirements for each are described in the `WASimCommander::Enums::CommandId` documentation.
463 /// \param command The `Command` struct defining the command and associated data to send.
464 /// \param response Pointer to an initialised `Command` structure for storing the resulting response (a Command with a `commandId` of `Ack` or `Nak`), if any.
465 /// \param timeout The maximum time to wait for a response, in milliseconds. If `0` (default) then the default network timeout value is used (`defaultTimeout()`, `setDefaultTimeout()`).
466 /// \return `S_OK` on success, `E_NOT_CONNECTED` if not connected to server, `E_TIMEOUT` on server communication failure, or possibly `E_FAIL` on unknown error (check log for details).
467 HRESULT sendCommandWithResponse(const Command &command, Command *response, uint32_t timeout = 0);
468
469 /// \}
470 /// \name Logging settings
471 /// \{
472
473 /// Get the current minimum logging severity level for the specified `facility` and `source`. \sa setLogLevel(), setLogCallback(), \refwce{CommandId::Log}
474 /// \param facility **One** of `WASimCommander::LogFacility` enum flag values. This must be only one of the available flags, not a combination. The `Remote` facility is the one delivered via the log callback handler.
475 /// \param source One of \refwcc{LogSource} enum values.
476 /// \return The current `WASimCommander::LogLevel` value, or `LogLevel::None` if the parameters were ivalid or the actual level is unknown (see Note below).
477 /// \note The remote server logging level for `File` and `Console` facilities is unknown at Client startup. The returned values are only going to be correct if they were set by this instance of the Client (using `setLogLevel()`).
478 WASimCommander::Enums::LogLevel logLevel(WASimCommander::Enums::LogFacility facility, LogSource source = LogSource::Client) const;
479 /// Set the current minimum logging severity level for the specified `facility` and `source` to `level`. \sa logLevel(), setLogCallback(), \refwce{CommandId::Log}
480 /// \param level The new minimum level. One of `WASimCommander::LogLevel` enum values. Use `LogLevel::None` to disable logging on the given facility/source.
481 /// \param facility One or more of `WASimCommander::LogFacility` enum flags. The `LogFacility::Remote` facility is the one delivered via the log callback handler.
482 /// \param source One of \refwcc{LogSource} enum values.
484
485 /// \}
486 /// \name Callbacks
487 /// \note In general, callbacks _may_ be invoked concurrently (and possibly from different threads).
488 /// The callback handler functions should at least be reentrant (if not thread-safe) since they could be called at any time.
489 /// Check the individual method documentation for more details about possible concurrency and threading. \n
490 /// The client can be built with \ref WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS macro/definition set to `0` to disable concurrency.
491 ///
492 /// <hr>
493 /// \{
494
495 /// Sets a callback for Client event updates which indicate status changes. Pass a `nullptr` value to remove a previously set callback.
496 /// \n Usage: \code client->setClientEventCallback(std::bind(&MyClass::onClientEvent, this, std::placeholders::_1)); \endcode
497 /// This callback may be invoked from the main thread (where WASimClient was created), the dedicated "dispatch" thread the client maintains, or a temporary thread in one specific case when SimConnect sends a "Quit" command.
498 /// \sa ClientEventType, ClientEvent, WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
499 void setClientEventCallback(clientEventCallback_t cb);
500 /// Same as `setClientEventCallback(clientEventCallback_t)`. Convenience for avoiding a std::bind expression.
501 /// \n Usage: \code client->setClientEventCallback(&MyClass::onClientEvent, this); \endcode \sa ClientEvent, WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
502 template<class Tcaller>
503 inline void setClientEventCallback(void(__stdcall Tcaller::* const member)(const ClientEvent &), Tcaller *const caller);
504
505 /// Sets a callback for list results arriving from the server. Pass a `nullptr` value to remove a previously set callback.
506 /// \n Usage: \code client->setListResultsCallback(std::bind(&MyClass::onListResult, this, std::placeholders::_1)); \endcode
507 /// This callback is invoked from temporary thread which accumulates incoming list results until the listing is complete (or times out).
508 /// Currently, only one pending list request can be active at any time, though this may change in the future.
509 /// \sa ListResult, list(), \refwce{CommandId::List}, WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
510 void setListResultsCallback(listResultsCallback_t cb);
511 /// Same as `setListResultsCallback(listResultsCallback_t)`. Convenience for avoiding a `std::bind` expression.
512 /// \n Usage: \code client->setListResultsCallback(&MyClass::onListResult, this); \endcode \sa list(), \refwce{CommandId::List}, WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
513 template<class Tcaller>
514 inline void setListResultsCallback(void(__stdcall Tcaller::* const member)(const ListResult &), Tcaller *const caller);
515
516 /// Sets a callback for value update data arriving from the server. Pass a `nullptr` value to remove a previously set callback.
517 /// \n Usage: \code client->setDataCallback(std::bind(&MyClass::onDataResult, this, std::placeholders::_1)); \endcode
518 /// This callback is invoked from the dedicated "dispatch" thread the client maintains.
519 /// \sa DataRequestRecord, saveDataRequest(), WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
520 void setDataCallback(dataCallback_t cb);
521 /// Same as `setDataCallback(dataCallback_t)`. Convenience overload template for avoiding a std::bind expression.
522 /// \n Usage: \code client->setDataCallback(&MyClass::onDataResult, this) \endcode \sa dataCallback_t, DataRequestRecord, saveDataRequest(), WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
523 template<class Tcaller>
524 inline void setDataCallback(void(__stdcall Tcaller::* const member)(const DataRequestRecord &), Tcaller *const caller);
525
526 /// Sets a callback for logging activity, both from the server and the client itself. Pass a `nullptr` value to remove a previously set callback.
527 /// \n Usage: \code client->setLogCallback(std::bind(&MyClass::onLogMessage, this, std::placeholders::_1)); \endcode
528 /// This callback may be invoked from either the main thread (where WASimClient was created), or the dedicated "dispatch" thread which the client maintains.
529 /// \sa setLogLevel(), WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
530 void setLogCallback(logCallback_t cb);
531 /// Same as `setLogCallback(logCallback_t)`. Convenience template for avoiding a std::bind expression.
532 /// \n Usage: \code client->setLogCallback(&MyClass::onLogMessage, this) \endcode \sa setLogLevel(), WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
533 template<class Tcaller>
534 inline void setLogCallback(void(__stdcall Tcaller::* const member)(const LogRecord &, LogSource), Tcaller *const caller);
535
536 /// Sets a callback for delivering command results returned by the server. Pass a `nullptr` value to remove a previously set callback.
537 /// While some of the high-level Client API calls do return an immediate status or result, many of the server commands are sent asynchronously, in a fire-and-forget fashion.
538 /// If you would like to be notified about _all_ command responses, set this callback. The `Command` type delivered will have the `Command::commandId` of type
539 /// `CommandId::Ack` or `CommandId::Nak`. The `Command::uData` member is set to the `commandId` of the original command being responded to.
540 /// Other `Command` struct members may have other meanings, depending on the actual command being responded to. See documentation for `WASimCommander::Enums::CommandId` for details.
541 /// \n Usage: \code client->setCommandResultCallback(std::bind(&MyClass::onCommandResult, this, std::placeholders::_1)); \endcode
542 /// This callback is invoked from the dedicated "dispatch" thread the client maintains.
543 /// \sa \refwce{CommandId}, WASimCommander::Command, WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
544 void setCommandResultCallback(commandCallback_t cb);
545 /// Same as `setCommandResultCallback(commandCallback_t)`. Convenience overload template for avoiding a std::bind expression.
546 /// \n Usage: \code client->setCommandResultCallback(&MyClass::onCommandResult, this); \endcode \sa setCommandResultCallback(commandCallback_t), WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
547 template<class Tcaller>
548 inline void setCommandResultCallback(void(__stdcall Tcaller::* const member)(const Command &), Tcaller *const caller);
549
550 /// Sets a callback for delivering response commands sent to this client by the server module. Note that the server may also initiate a few types of commands (not in response to client commands)
551 /// such as `Ping`, `List`, and `Disconnect`. In contrast to `setCommandResultCallback(commandCallback_t)`, this one will report on _all_ commands from the server, not just `Ack/Nak`.
552 /// This callback is meant for low-level API usage.
553 /// \n Usage: \code client->setResponseCallback(std::bind(&MyClass::onServerResponse, this, std::placeholders::_1)); \endcode
554 /// This callback is invoked from the dedicated "dispatch" thread the client maintains.
555 /// \sa sendServerCommand(), WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
556 void setResponseCallback(commandCallback_t cb);
557 /// Same as `setResponseCallback(commandCallback_t)`. Convenience overload template for avoiding a std::bind expression.
558 /// \n Usage: \code client->setResponseCallback(&MyClass::onServerResponse, this); \endcode \sa setResponseCallback(responseCallback_t), WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
559 template<class Tcaller>
560 inline void setResponseCallback(void(__stdcall Tcaller::* const member)(const Command &), Tcaller *const caller);
561
562 /// \}
563
564 private:
565 class Private;
566#pragma warning( suppress: 4251 ) // "warning C4251: ... needs to have dll-interface to be used by clients" -- no client access here, safe to suppress
567 const std::unique_ptr<Private> d;
568#ifndef DOXYGEN // insists on documenting this
569 friend class Private;
570#endif
571
572 // we do not share well with others
573 WASimClient(WASimClient&&) = delete;
574 WASimClient(const WASimClient &) = delete;
575 WASimClient &operator=(WASimClient&&) = delete;
576 WASimClient &operator=(const WASimClient &) = delete;
577
578 }; // WASimClient
579
580 template<class Tcaller>
581 inline void WASimClient::setClientEventCallback(void(__stdcall Tcaller::* const member)(const ClientEvent &), Tcaller * const caller)
582 {
583 setClientEventCallback(std::bind(member, caller, std::placeholders::_1));
584 }
585
586 template<class Tcaller>
587 inline void WASimClient::setListResultsCallback(void(__stdcall Tcaller::* const member)(const ListResult &), Tcaller * const caller)
588 {
589 setListResultsCallback(std::bind(member, caller, std::placeholders::_1));
590 }
591
592 template<class Tcaller>
593 inline void WASimClient::setDataCallback(void(__stdcall Tcaller::* const member)(const DataRequestRecord &), Tcaller * const caller)
594 {
595 setDataCallback(std::bind(member, caller, std::placeholders::_1));
596 }
597
598 template<class Tcaller>
599 inline void WASimClient::setLogCallback(void(__stdcall Tcaller::* const member)(const LogRecord &, LogSource), Tcaller * const caller)
600 {
601 setLogCallback(std::bind(member, caller, std::placeholders::_1, std::placeholders::_2));
602 }
603
604 template<class Tcaller>
605 inline void WASimClient::setCommandResultCallback(void(__stdcall Tcaller::* const member)(const Command &), Tcaller * const caller)
606 {
607 setCommandResultCallback(std::bind(member, caller, std::placeholders::_1));
608 }
609
610 template<class Tcaller>
611 inline void WASimClient::setResponseCallback(void(__stdcall Tcaller::* const member)(const Command &), Tcaller * const caller)
612 {
613 setResponseCallback(std::bind(member, caller, std::placeholders::_1));
614 }
615
616}; // namespace WASimCommander::Client
WASimCommander Client implementation. Handles all aspects of communication with the WASimCommander Se...
Definition WASimClient.h:85
void setDataCallback(dataCallback_t cb)
Sets a callback for value update data arriving from the server. Pass a nullptr value to remove a prev...
void setLogCallback(logCallback_t cb)
Sets a callback for logging activity, both from the server and the client itself. Pass a nullptr valu...
HRESULT setSimVarVariable(const std::string &variableName, const std::string &unitName, double value)
Sets a numeric value on an 'A' (aka "SimVar" / "Simulator Variable") type variable....
HRESULT setSimVarVariable(const std::string &variableName, const std::string &stringValue)
Sets a string value on an 'A' (aka "SimVar" / "Simulator Variable") type variable....
void setCommandResultCallback(commandCallback_t cb)
Sets a callback for delivering command results returned by the server. Pass a nullptr value to remove...
void setListResultsCallback(listResultsCallback_t cb)
Sets a callback for list results arriving from the server. Pass a nullptr value to remove a previousl...
HRESULT setSimVarVariable(const std::string &variableName, uint8_t index, const std::string &unitName, double value)
Sets a numeric value on an indexed 'A' (aka "SimVar" / "Simulator Variable") type variable....
void setResponseCallback(commandCallback_t cb)
Sets a callback for delivering response commands sent to this client by the server module....
HRESULT setSimVarVariable(const std::string &variableName, uint8_t index, const std::string &stringValue)
Sets a string value on an indexed 'A' (aka "SimVar" / "Simulator Variable") type variable....
void setClientEventCallback(clientEventCallback_t cb)
Sets a callback for Client event updates which indicate status changes. Pass a nullptr value to remov...
WASimCommander::Client namespace. Defines/declares everything needed to interact with the WASimComman...
Definition enums_impl.h:32
static const HRESULT E_TIMEOUT
Error result: timeout communicating with server.
Definition WASimClient.h:59
std::function< void __stdcall(const ListResult &)> listResultsCallback_t
Callback function for delivering list results, eg. of local variables sent from Server.
Definition WASimClient.h:73
LogSource
Log entry source, Client or Server.
Definition enums_impl.h:74
std::function< void __stdcall(const ClientEvent &)> clientEventCallback_t
Callback function for Client events.
Definition WASimClient.h:72
std::function< void __stdcall(const DataRequestRecord &)> dataCallback_t
Callback function for subscription result data.
Definition WASimClient.h:74
std::function< void __stdcall(const Command &)> commandCallback_t
Callback function for commands sent from server.
Definition WASimClient.h:76
static const HRESULT E_NOT_CONNECTED
Error result: server not connected.
Definition WASimClient.h:56
static const uint32_t CUSTOM_KEY_EVENT_ID_MIN
Starting ID range for "Custom Key Events" for use with registerCustomKeyEvent() generated IDs....
Definition WASimClient.h:65
std::function< void __stdcall(const LogRecord &, LogSource)> logCallback_t
Callback function for log entries (from both Client and Server).
Definition WASimClient.h:75
ClientStatus
Client status flags.
Definition enums_impl.h:36
Client Event data, delivered via callback.
Definition structs.h:32
Structure for delivering list results, eg. of local variables sent from Server.
Definition structs.h:42
LogLevel
Logging levels.
Definition enums_impl.h:155
LookupItemType
Types of things to look up or list.
Definition enums_impl.h:136
@ LocalVariable
LVar ('L') names and IDs. Available for List and Lookup commands.
CalcResultType
The type of result that calculator code is expected to produce.
Definition enums_impl.h:109
@ None
No result is expected (eg. triggering an event).
LogFacility
Logging destination type.
Definition enums_impl.h:171
@ Remote
Remote destination, eg. network transmission or a callback event.
DataRequestRecord inherits and extends WASimCommander::DataRequest with data pertinent for use by a d...
Definition structs.h:70
Structure to hold data for registered (reusable) calculator events. Used to submit events with WASimC...
Definition structs.h:150
Structure for using with WASimClient::getVariable() and WASimClient::setVariable() to specify informa...
Definition structs.h:110
Command data structure. The member contents depend on the command type as described in each command t...
Structure for variable value subscription requests.
Log record structure.