Skip to content

Commit

Permalink
Add Jetbridge for LVAR access
Browse files Browse the repository at this point in the history
  • Loading branch information
Scott Vincent committed Mar 25, 2021
1 parent 0bd7076 commit 3007851
Show file tree
Hide file tree
Showing 13 changed files with 295 additions and 17 deletions.
6 changes: 6 additions & 0 deletions Redist/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Copy a32nx-jetbridge folder to your FS2020 Community folder.

This allows Data Link to read and write local (lvar) values as these are not
currently supported by the SimConnect SDK.

Specifically, it is used for the A32NX Airbus A320 Neo to access the APU.
9 changes: 9 additions & 0 deletions Redist/a32nx-jetbridge/layout.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"content": [
{
"path": "modules/jetbridge.wasm",
"size": 441349,
"date": 132603215301079537
}
]
}
15 changes: 15 additions & 0 deletions Redist/a32nx-jetbridge/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"dependencies": [],
"content_type": "MISC",
"title": "Jetbridge",
"manufacturer": "",
"creator": "A32NX",
"package_version": "0.1.0",
"minimum_game_version": "1.14.5",
"release_notes": {
"neutral": {
"LastUpdate": "",
"OlderHistory": ""
}
}
}
Binary file added Redist/a32nx-jetbridge/modules/jetbridge.wasm
Binary file not shown.
159 changes: 146 additions & 13 deletions instrument-data-link/instrument-data-link.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,31 @@ bool vJoyInitialised = false;
int vJoyConfiguredButtons;
#endif

// SimConnect doesn't currently support readuing local (lvar)
// variables (params for 3rd party aircraft) but Jetbridge
// allows us to do this.
// To use jetbridge you must copy the Redist\a32nx-jetbridge
// package to your FS2020 Community folder. Source available from:
// https://github.com/theomessin/jetbridge
//
// Comment the following line out if you don't want to use Jetbridge.
#define jetbridgeFallback

#ifdef jetbridgeFallback
#include "jetbridge\client.h"

const char* JETBRIDGE_APU_MASTER_SW = "L:A32NX_OVHD_APU_MASTER_SW_PB_IS_ON, bool";
const int JETBRIDGE_APU_MASTER_SW_LEN = 41;
const char* JETBRIDGE_APU_START = "L:A32NX_OVHD_APU_START_PB_IS_ON, bool";
const int JETBRIDGE_APU_START_LEN = 37;
const char* JETBRIDGE_APU_START_AVAIL = "L:A32NX_OVHD_APU_START_PB_IS_AVAILABLE, bool";
const int JETBRIDGE_APU_START_AVAIL_LEN = 44;
const char* JETBRIDGE_APU_BLEED = "L:A32NX_OVHD_PNEU_APU_BLEED_PB_IS_ON, bool";
const int JETBRIDGE_APU_BLEED_LEN = 42;

jetbridge::Client* jetbridgeClient = 0;
#endif

bool quit = false;
HANDLE hSimConnect = NULL;
extern const char* versionString;
Expand All @@ -50,7 +75,7 @@ long writeDataSize = sizeof(WriteData);
long instrumentsDataSize = sizeof(SimVars);
long autopilotDataSize = (long)((LONG_PTR)(&simVars.autothrottleActive) + (long)sizeof(double) - (LONG_PTR)&simVars);
long radioDataSize = (long)((LONG_PTR)(&simVars.transponderCode) + (long)sizeof(double) - (LONG_PTR)&simVars);
long lightsDataSize = (long)((LONG_PTR)(&simVars.apuBleed) + (long)sizeof(double) - (LONG_PTR)&simVars);
long lightsDataSize = (long)((LONG_PTR)(&simVars.apuPercentRpm) + (long)sizeof(double) - (LONG_PTR)&simVars);

int active = -1;
int bytes;
Expand Down Expand Up @@ -78,7 +103,73 @@ enum REQUEST_ID {
REQ_ID
};

void CALLBACK MyDispatchProcRD(SIMCONNECT_RECV* pData, DWORD cbData, void* pContext)
#ifdef jetbridgeFallback
void readJetbridgeVar(const char *var)
{
char rpnCode[128];
sprintf_s(rpnCode, "(%s)", var);
jetbridgeClient->request(rpnCode);
}

void writeJetbridgeVar(const char* var, double val)
{
// FS2020 uses RPN (Reverse Polish Notation).
char rpnCode[128];
sprintf_s(rpnCode, "%f (>%s)", val, var);
jetbridgeClient->request(rpnCode);
}

void updateVarFromJetbridge(const char* data)
{
if (strncmp(&data[1], JETBRIDGE_APU_MASTER_SW, JETBRIDGE_APU_MASTER_SW_LEN) == 0) {
simVars.apuMasterSw = atof(&data[JETBRIDGE_APU_MASTER_SW_LEN] + 2);
}
else if (strncmp(&data[1], JETBRIDGE_APU_START, JETBRIDGE_APU_START_LEN) == 0) {
simVars.apuStart = atof(&data[JETBRIDGE_APU_START_LEN] + 2);
}
else if (strncmp(&data[1], JETBRIDGE_APU_START_AVAIL, JETBRIDGE_APU_START_AVAIL_LEN) == 0) {
simVars.apuStartAvail = atof(&data[JETBRIDGE_APU_START_AVAIL_LEN] + 2);
}
else if (strncmp(&data[1], JETBRIDGE_APU_BLEED, JETBRIDGE_APU_BLEED_LEN) == 0) {
simVars.apuBleed = atof(&data[JETBRIDGE_APU_BLEED_LEN] + 2);
}
}

void jetbridgeButtonPress(int eventId, double value)
{
switch (eventId) {
case KEY_APU_OFF_SWITCH:
writeJetbridgeVar(JETBRIDGE_APU_MASTER_SW, value);
break;
case KEY_APU_STARTER:
writeJetbridgeVar(JETBRIDGE_APU_START, value);
break;
case KEY_BLEED_AIR_SOURCE_CONTROL_SET:
writeJetbridgeVar(JETBRIDGE_APU_BLEED, value);
break;
}
}

void pollJetbridge()
{
// Use low frequency for jetbridge as vars not critical
int loopMillis = 500;

while (!quit)
{
if (simVars.connected) {
readJetbridgeVar(JETBRIDGE_APU_MASTER_SW);
readJetbridgeVar(JETBRIDGE_APU_START);
readJetbridgeVar(JETBRIDGE_APU_START_AVAIL);
readJetbridgeVar(JETBRIDGE_APU_BLEED);
}

Sleep(loopMillis);
}
}
#endif

void CALLBACK MyDispatchProc(SIMCONNECT_RECV* pData, DWORD cbData, void* pContext)
{
static int displayDelay = 0;

Expand Down Expand Up @@ -112,7 +203,7 @@ void CALLBACK MyDispatchProcRD(SIMCONNECT_RECV* pData, DWORD cbData, void* pCont

case SIMCONNECT_RECV_ID_SIMOBJECT_DATA:
{
SIMCONNECT_RECV_SIMOBJECT_DATA* pObjData = (SIMCONNECT_RECV_SIMOBJECT_DATA*)pData;
auto pObjData = static_cast<SIMCONNECT_RECV_SIMOBJECT_DATA*>(pData);

switch (pObjData->dwRequestID)
{
Expand All @@ -123,7 +214,7 @@ void CALLBACK MyDispatchProcRD(SIMCONNECT_RECV* pData, DWORD cbData, void* pCont
// displayDelay--;
//}
//else {
// printf("Aircraft: %s Cruise Speed: %f\n", simVars.aircraft, simVars.cruiseSpeed);
// //printf("Aircraft: %s Cruise Speed: %f\n", simVars.aircraft, simVars.cruiseSpeed);
//}
break;
}
Expand All @@ -137,6 +228,18 @@ void CALLBACK MyDispatchProcRD(SIMCONNECT_RECV* pData, DWORD cbData, void* pCont
break;
}

#ifdef jetbridgeFallback
case SIMCONNECT_RECV_ID_CLIENT_DATA: {
auto pClientData = static_cast<SIMCONNECT_RECV_CLIENT_DATA*>(pData);

if (pClientData->dwRequestID == jetbridge::kDownlinkRequest) {
auto packet = static_cast<jetbridge::Packet*>((jetbridge::Packet*)&pClientData->dwData);
updateVarFromJetbridge(packet->data);
}
break;
}
#endif

case SIMCONNECT_RECV_ID_QUIT:
{
// Comment out next line to stay running when FS2020 quits
Expand Down Expand Up @@ -178,6 +281,10 @@ void addReadDefs()
varSize += dataLen;
}
}
else if (_stricmp(SimVarDefs[i][1], "jetbridge") == 0) {
// SimConnect variables start after all Jetbridge variables
varStart++;
}
else {
// Add double (float64)
if (SimConnect_AddToDataDefinition(hSimConnect, DEF_READ_ALL, SimVarDefs[i][0], SimVarDefs[i][1]) < 0) {
Expand All @@ -203,6 +310,24 @@ void mapEvents()
}
}

void init()
{
addReadDefs();
mapEvents();

// Start requesting data
if (SimConnect_RequestDataOnSimObject(hSimConnect, REQ_ID, DEF_READ_ALL, SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_PERIOD_VISUAL_FRAME, 0, 0, 0, 0) < 0) {
printf("Failed to start requesting data\n");
}

#ifdef jetbridgeFallback
if (jetbridgeClient != 0) {
delete jetbridgeClient;
}
jetbridgeClient = new jetbridge::Client(hSimConnect);
#endif
}

void cleanUp()
{
if (hSimConnect) {
Expand Down Expand Up @@ -236,13 +361,18 @@ int __cdecl _tmain(int argc, _TCHAR* argv[])
printf("Searching for local MS FS2020...\n");
simVars.connected = 0;

#ifdef jetbridgeFallback
std::thread jetbridgeThread(pollJetbridge);
#endif

HRESULT result;

int loopMillis = 10;
int retryDelay = 0;
while (!quit)
{
if (simVars.connected) {
result = SimConnect_CallDispatch(hSimConnect, MyDispatchProcRD, NULL);
result = SimConnect_CallDispatch(hSimConnect, MyDispatchProc, NULL);
if (result != 0) {
printf("Disconnected from MS FS2020\n");
simVars.connected = 0;
Expand All @@ -256,23 +386,22 @@ int __cdecl _tmain(int argc, _TCHAR* argv[])
result = SimConnect_Open(&hSimConnect, "Instrument Data Link", NULL, 0, 0, 0);
if (result == 0) {
printf("Connected to MS FS2020\n");
addReadDefs();
mapEvents();
init();
simVars.connected = 1;

// Start requesting data
if (SimConnect_RequestDataOnSimObject(hSimConnect, REQ_ID, DEF_READ_ALL, SIMCONNECT_OBJECT_ID_USER, SIMCONNECT_PERIOD_VISUAL_FRAME, 0, 0, 0, 0) < 0) {
printf("Failed to start requesting data\n");
}
}
else {
retryDelay = 200;
}
}

Sleep(10);
Sleep(loopMillis);
}

#ifdef jetbridgeFallback
// Wait for thread to exit
jetbridgeThread.join();
#endif

cleanUp();
return 0;
}
Expand All @@ -297,6 +426,10 @@ void processRequest()
return;
}

#ifdef jetbridgeFallback
jetbridgeButtonPress(recvBuffer.writeData.eventId, recvBuffer.writeData.value);
#endif

if (SimConnect_TransmitClientEvent(hSimConnect, 0, recvBuffer.writeData.eventId, (DWORD)recvBuffer.writeData.value, SIMCONNECT_GROUP_PRIORITY_HIGHEST, SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY) != 0) {
printf("Failed to transmit event: %d\n", recvBuffer.writeData.eventId);
}
Expand Down
4 changes: 4 additions & 0 deletions instrument-data-link/instrument-data-link.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,13 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="instrument-data-link.cpp" />
<ClCompile Include="jetbridge\Client.cpp" />
<ClCompile Include="jetbridge\Protocol.cpp" />
<ClCompile Include="simvarDefs.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="jetbridge\Client.h" />
<ClInclude Include="jetbridge\Protocol.h" />
<ClInclude Include="simvarDefs.h" />
</ItemGroup>
<ItemGroup>
Expand Down
17 changes: 17 additions & 0 deletions instrument-data-link/instrument-data-link.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,28 @@
<ItemGroup>
<ClCompile Include="instrument-data-link.cpp" />
<ClCompile Include="simvarDefs.cpp" />
<ClCompile Include="jetbridge\Client.cpp">
<Filter>Jetbridge</Filter>
</ClCompile>
<ClCompile Include="jetbridge\Protocol.cpp">
<Filter>Jetbridge</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="simvarDefs.h" />
<ClInclude Include="jetbridge\Client.h">
<Filter>Jetbridge</Filter>
</ClInclude>
<ClInclude Include="jetbridge\Protocol.h">
<Filter>Jetbridge</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="C:\MSFS SDK\SimConnect SDK\VS\SimConnectClient-static.props" />
</ItemGroup>
<ItemGroup>
<Filter Include="Jetbridge">
<UniqueIdentifier>{43b308f5-d220-472b-8b64-866564fadcc3}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>
23 changes: 23 additions & 0 deletions instrument-data-link/jetbridge/Client.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include "Client.h"
#include "SimConnect.h"

jetbridge::Client::Client(void* simconnect) {
this->simconnect = simconnect;
srand(time(0));

SimConnect_AddToClientDataDefinition(simconnect, kPacketDefinition, 0, sizeof(Packet));
SimConnect_MapClientDataNameToID(simconnect, kPublicDownlinkChannel, kPublicDownlinkArea);
SimConnect_MapClientDataNameToID(simconnect, kPublicUplinkChannel, kPublicUplinkArea);

// We'll listen to downlink client data events.
SimConnect_RequestClientData(simconnect, kPublicDownlinkArea, kDownlinkRequest, kPacketDefinition,
SIMCONNECT_CLIENT_DATA_PERIOD_ON_SET, SIMCONNECT_CLIENT_DATA_REQUEST_FLAG_CHANGED);
}

void jetbridge::Client::request(const char data[]) {
// Prepare the outgoing packet
Packet* packet = new Packet(data);

// Transmit the request packet
SimConnect_SetClientData(simconnect, kPublicUplinkArea, kPacketDefinition, 0, 0, sizeof(Packet), packet);
}
21 changes: 21 additions & 0 deletions instrument-data-link/jetbridge/Client.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

#include <Windows.h>

#include <future>
#include <map>

#include "Protocol.h"

namespace jetbridge {

class Client {
private:
void* simconnect = 0;

public:
Client(void* simconnect);
void request(const char data[]);
};

} // namespace jetbridge
12 changes: 12 additions & 0 deletions instrument-data-link/jetbridge/Protocol.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include "Protocol.h"

#include <cstdlib>
#include <cstring>
#include <ctime>

jetbridge::Packet::Packet(const char data[]) {
this->id = rand();

// Copy the passed data to the packet data
std::memcpy(this->data, data, sizeof(this->data));
}
Loading

0 comments on commit 3007851

Please sign in to comment.