v1.3.0.0
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', '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 conveneince 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 intial 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.
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, simVarIndex), value)`. See `setVariable()` and `VariableRequest` for details.
265 /// \since v1.3.0
266 inline HRESULT setSimVarVariable(const std::string &variableName, uint8_t simVarIndex, const std::string &unitName, double value) { return setVariable(VariableRequest(variableName, unitName, simVarIndex), 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 registerd, 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 /// which 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 /// 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. \n
406 /// \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
407 /// 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).
408 /// \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).
409 /// 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
410 /// The pointer's value will be populated even if the event name was already registered (with the result of the previously generated ID).
411 /// \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()`.
412 /// 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
413 /// 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.
414 /// \return `S_OK` on success, `E_INVALIDARG` if the event name is invalid.
415 /// \since v1.3.0
416 HRESULT registerCustomKeyEvent(const std::string &customEventName, uint32_t *puiCustomEventId = nullptr, bool useLegacyTransmit = false);
417 /// Remove a Custom Event previously registered with `registerCustomEvent()` method using the event's name.
418 /// This will prevent the custom event from being mapped _next_ time the client connects to SimConnect.
419 /// \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).
420 /// \param customEventName full name of the previously registered event. Must be the same name as used with `registerCustomKeyEvent()`.
421 /// \return `S_OK` on success, `E_INVALIDARG` if the eventId wasn't found.
422 /// \since v1.3.0
423 HRESULT removeCustomKeyEvent(const std::string &customEventName);
424 /// 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.
425 /// \param eventId ID of the previously registered event.
426 /// \return `S_OK` on success, `E_INVALIDARG` if the eventId wasn't found.
427 /// \since v1.3.0
428 HRESULT removeCustomKeyEvent(uint32_t eventId);
429
430 /// \}
431 /// \name Metadata retrieval
432 /// \{
433
434 /// Send a request for a list update to the server. The results are delivered using the callback set in `setListResultsCallback()`.
435 /// \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`).
436 /// \return `S_OK` on success, `E_INVALIDARG` if the item type is not supported, `E_NOT_CONNECTED` if not connected to server.
437 /// \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
438 /// (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)).
439 /// \sa \refwce{CommandId::List}
441
442 /// Request server-side lookup of an named item to find the corresponding numeric ID.
443 /// \param itemType The type of item to look up. A type of variable or a measurement unit. See the `WASimCommander::LookupItemType` documentation for details.
444 /// \param itemName The name of the thing to check for.
445 /// \param piResult Pointer to 32-bit signed integer variable to hold the result.
446 /// \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.
447 /// \note This method blocks until either the Server responds or the timeout has expired. \sa defaultTimeout(), setDefaultTimeout()
448 HRESULT lookup(WASimCommander::Enums::LookupItemType itemType, const std::string &itemName, int32_t *piResult);
449
450 /// \}
451 /// \name Low level API
452 /// \{
453
454 /// 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.
455 /// 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.
456 /// \return `S_OK` on success, `E_NOT_CONNECTED` if not connected to server, `E_TIMEOUT` on server communication failure.
457 HRESULT sendCommand(const Command &command) const;
458 /// 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`).
459 /// The various command types and the data requirements for each are described in the `WASimCommander::Enums::CommandId` documentation.
460 /// \param command The `Command` struct defining the command and associated data to send.
461 /// \param response Pointer to an initialised `Command` structure for storing the resulting response (a Command with a `commandId` of `Ack` or `Nak`), if any.
462 /// \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()`).
463 /// \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).
464 HRESULT sendCommandWithResponse(const Command &command, Command *response, uint32_t timeout = 0);
465
466 /// \}
467 /// \name Logging settings
468 /// \{
469
470 /// Get the current minimum logging severity level for the specified `facility` and `source`. \sa setLogLevel(), setLogCallback(), \refwce{CommandId::Log}
471 /// \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.
472 /// \param source One of \refwcc{LogSource} enum values.
473 /// \return The current `WASimCommander::LogLevel` value, or `LogLevel::None` if the parameters were ivalid or the actual level is unknown (see Note below).
474 /// \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()`).
476 /// Set the current minimum logging severity level for the specified `facility` and `source` to `level`. \sa logLevel(), setLogCallback(), \refwce{CommandId::Log}
477 /// \param level The new minimum level. One of `WASimCommander::LogLevel` enum values. Use `LogLevel::None` to disable logging on the given faciliity/source.
478 /// \param facility One or more of `WASimCommander::LogFacility` enum flags. The `LogFacility::Remote` facility is the one delivered via the log callback handler.
479 /// \param source One of \refwcc{LogSource} enum values.
481
482 /// \}
483 /// \name Callbacks
484 /// \note In general, callbacks _may_ be invoked concurrently (and possibly from different threads).
485 /// The callback handler functions should at least be reentrant (if not thread-safe) since they could be called at any time.
486 /// Check the individual method documentation for more details about possible concurrency and threading. \n
487 /// The client can be built with \ref WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS macro/definition set to `0` to disable concurrency.
488 ///
489 /// <hr>
490 /// \{
491
492 /// Sets a callback for Client event updates which indicate status changes. Pass a `nullptr` value to remove a previously set callback.
493 /// \n Usage: \code client->setClientEventCallback(std::bind(&MyClass::onClientEvent, this, std::placeholders::_1)); \endcode
494 /// 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.
495 /// \sa ClientEventType, ClientEvent, WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
496 void setClientEventCallback(clientEventCallback_t cb);
497 /// Same as `setClientEventCallback(clientEventCallback_t)`. Convenience for avoiding a std::bind expression.
498 /// \n Usage: \code client->setClientEventCallback(&MyClass::onClientEvent, this); \endcode \sa ClientEvent, WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
499 template<class Tcaller>
500 inline void setClientEventCallback(void(__stdcall Tcaller::* const member)(const ClientEvent &), Tcaller *const caller);
501
502 /// Sets a callback for list results arriving from the server. Pass a `nullptr` value to remove a previously set callback.
503 /// \n Usage: \code client->setListResultsCallback(std::bind(&MyClass::onListResult, this, std::placeholders::_1)); \endcode
504 /// This callback is invoked from temporary thread which accumulates incoming list results until the listing is complete (or times out).
505 /// Currently, only one pending list request can be active at any time, though this may change in the future.
506 /// \sa ListResult, list(), \refwce{CommandId::List}, WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
507 void setListResultsCallback(listResultsCallback_t cb);
508 /// Same as `setListResultsCallback(listResultsCallback_t)`. Convenience for avoiding a `std::bind` expression.
509 /// \n Usage: \code client->setListResultsCallback(&MyClass::onListResult, this); \endcode \sa list(), \refwce{CommandId::List}, WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
510 template<class Tcaller>
511 inline void setListResultsCallback(void(__stdcall Tcaller::* const member)(const ListResult &), Tcaller *const caller);
512
513 /// Sets a callback for value update data arriving from the server. Pass a `nullptr` value to remove a previously set callback.
514 /// \n Usage: \code client->setDataCallback(std::bind(&MyClass::onDataResult, this, std::placeholders::_1)); \endcode
515 /// This callback is invoked from the dedicated "dispatch" thread the client maintains.
516 /// \sa DataRequestRecord, saveDataRequest(), WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
517 void setDataCallback(dataCallback_t cb);
518 /// Same as `setDataCallback(dataCallback_t)`. Convenience overload template for avoiding a std::bind expression.
519 /// \n Usage: \code client->setDataCallback(&MyClass::onDataResult, this) \endcode \sa dataCallback_t, DataRequestRecord, saveDataRequest(), WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
520 template<class Tcaller>
521 inline void setDataCallback(void(__stdcall Tcaller::* const member)(const DataRequestRecord &), Tcaller *const caller);
522
523 /// Sets a callback for logging activity, both from the server and the client itself. Pass a `nullptr` value to remove a previously set callback.
524 /// \n Usage: \code client->setLogCallback(std::bind(&MyClass::onLogMessage, this, std::placeholders::_1)); \endcode
525 /// This callback may be invoked from either the main thread (where WASimClient was created), or the dedicated "dispatch" thread which the client maintains.
526 /// \sa setLogLevel(), WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
527 void setLogCallback(logCallback_t cb);
528 /// Same as `setLogCallback(logCallback_t)`. Convenience template for avoiding a std::bind expression.
529 /// \n Usage: \code client->setLogCallback(&MyClass::onLogMessage, this) \endcode \sa setLogLevel(), WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
530 template<class Tcaller>
531 inline void setLogCallback(void(__stdcall Tcaller::* const member)(const LogRecord &, LogSource), Tcaller *const caller);
532
533 /// Sets a callback for delivering command results returned by the server. Pass a `nullptr` value to remove a previously set callback.
534 /// 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.
535 /// 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
536 /// `CommandId::Ack` or `CommandId::Nak`. The `Command::uData` member is set to the `commandId` of the original command being responded to.
537 /// Other `Command` struct members may have other meanings, depending on the actual command being responded to. See documentation for `WASimCommander::Enums::CommandId` for details.
538 /// \n Usage: \code client->setCommandResultCallback(std::bind(&MyClass::onCommandResult, this, std::placeholders::_1)); \endcode
539 /// This callback is invoked from the dedicated "dispatch" thread the client maintains.
540 /// \sa \refwce{CommandId}, WASimCommander::Command, WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
541 void setCommandResultCallback(commandCallback_t cb);
542 /// Same as `setCommandResultCallback(commandCallback_t)`. Convenience overload template for avoiding a std::bind expression.
543 /// \n Usage: \code client->setCommandResultCallback(&MyClass::onCommandResult, this); \endcode \sa setCommandResultCallback(commandCallback_t), WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
544 template<class Tcaller>
545 inline void setCommandResultCallback(void(__stdcall Tcaller::* const member)(const Command &), Tcaller *const caller);
546
547 /// 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)
548 /// 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`.
549 /// This callback is meant for low-level API usage.
550 /// \n Usage: \code client->setResponseCallback(std::bind(&MyClass::onServerResponse, this, std::placeholders::_1)); \endcode
551 /// This callback is invoked from the dedicated "dispatch" thread the client maintains.
552 /// \sa sendServerCommand(), WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
553 void setResponseCallback(commandCallback_t cb);
554 /// Same as `setResponseCallback(commandCallback_t)`. Convenience overload template for avoiding a std::bind expression.
555 /// \n Usage: \code client->setResponseCallback(&MyClass::onServerResponse, this); \endcode \sa setResponseCallback(responseCallback_t), WSMCMND_CLIENT_USE_CONCURRENT_CALLBACKS
556 template<class Tcaller>
557 inline void setResponseCallback(void(__stdcall Tcaller::* const member)(const Command &), Tcaller *const caller);
558
559 /// \}
560
561 private:
562 class Private;
563#pragma warning( suppress: 4251 ) // "warning C4251: ... needs to have dll-interface to be used by clients" -- no client access here, safe to suppress
564 const std::unique_ptr<Private> d;
565#ifndef DOXYGEN // insists on documenting this
566 friend class Private;
567#endif
568
569 // we do not share well with others
570 WASimClient(WASimClient&&) = delete;
571 WASimClient(const WASimClient &) = delete;
572 WASimClient &operator=(WASimClient&&) = delete;
573 WASimClient &operator=(const WASimClient &) = delete;
574
575 }; // WASimClient
576
577 template<class Tcaller>
578 inline void WASimClient::setClientEventCallback(void(__stdcall Tcaller::* const member)(const ClientEvent &), Tcaller * const caller)
579 {
580 setClientEventCallback(std::bind(member, caller, std::placeholders::_1));
581 }
582
583 template<class Tcaller>
584 inline void WASimClient::setListResultsCallback(void(__stdcall Tcaller::* const member)(const ListResult &), Tcaller * const caller)
585 {
586 setListResultsCallback(std::bind(member, caller, std::placeholders::_1));
587 }
588
589 template<class Tcaller>
590 inline void WASimClient::setDataCallback(void(__stdcall Tcaller::* const member)(const DataRequestRecord &), Tcaller * const caller)
591 {
592 setDataCallback(std::bind(member, caller, std::placeholders::_1));
593 }
594
595 template<class Tcaller>
596 inline void WASimClient::setLogCallback(void(__stdcall Tcaller::* const member)(const LogRecord &, LogSource), Tcaller * const caller)
597 {
598 setLogCallback(std::bind(member, caller, std::placeholders::_1, std::placeholders::_2));
599 }
600
601 template<class Tcaller>
602 inline void WASimClient::setCommandResultCallback(void(__stdcall Tcaller::* const member)(const Command &), Tcaller * const caller)
603 {
604 setCommandResultCallback(std::bind(member, caller, std::placeholders::_1));
605 }
606
607 template<class Tcaller>
608 inline void WASimClient::setResponseCallback(void(__stdcall Tcaller::* const member)(const Command &), Tcaller * const caller)
609 {
610 setResponseCallback(std::bind(member, caller, std::placeholders::_1));
611 }
612
613}; // 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....
Definition: WASimClient.h:262
HRESULT setSimVarVariable(const std::string &variableName, const std::string &stringValue)
Sets a string value on an 'A' (aka "SimVar" / "Simulator Variable") type variable....
Definition: WASimClient.h:270
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...
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 simVarIndex, const std::string &unitName, double value)
Sets a numeric value on an indexed 'A' (aka "SimVar" / "Simulator Variable") type variable....
Definition: WASimClient.h:266
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....
Definition: WASimClient.h:274
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 LogRecord &, LogSource)> logCallback_t
Callback function for log entries (from both Client and Server).
Definition: WASimClient.h:75
std::function< void __stdcall(const DataRequestRecord &)> dataCallback_t
Callback function for subscription result data.
Definition: WASimClient.h:74
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
std::function< void __stdcall(const Command &)> commandCallback_t
Callback function for commands sent from server.
Definition: WASimClient.h:76
LogSource
Log entry source, Client or Server.
Definition: enums_impl.h:74
@ Client
Log record from WASimClient.
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 ClientEvent &)> clientEventCallback_t
Callback function for Client events.
Definition: WASimClient.h:72
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.