Skip to content
This repository has been archived by the owner on Dec 26, 2020. It is now read-only.

Commit

Permalink
Add ability for SimVars to be set.
Browse files Browse the repository at this point in the history
  • Loading branch information
Oceanswave committed Sep 7, 2020
1 parent 8481cb6 commit bfa49b5
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 47 deletions.
3 changes: 3 additions & 0 deletions FSMosquitoTopic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ public static class FSMosquitoTopic
// SimConnect Status Messages (Egress)
public const string SimConnectStatus = "fsm/client/{0}/simconnect/status";

// Set Simconnect topic value (Ingress)
public const string SetSimConnectTopicValue = "fsm/client/{0}/v/set/"; //Wildcard added by the subscription

// Simconnect topic value (Egress)
public const string SimConnectTopicValue = "fsm/client/{0}/v/{1}";
}
Expand Down
18 changes: 14 additions & 4 deletions Forms/MainForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public MainForm(IFsMqtt fsMqtt, IFsSimConnect fsSimConnect, ILogger<MainForm> lo
FsMqtt.MqttConnectionClosed += FsMqtt_MqttConnectionClosed;
FsMqtt.ReportSimConnectStatusRequestRecieved += FsMqtt_ReportSimConnectStatusRequestRecieved;
FsMqtt.SubscribeRequestRecieved += FsMqtt_SubscribeRequestRecieved;
FsMqtt.SetSimVarRequestRecieved += FsMqtt_SetSimVarRequestRecieved;
FsMqtt.MqttMessageRecieved += FsMqtt_MqttMessageRecieved;
FsMqtt.MqttMessageTransmitted += FsMqtt_MqttMessageTransmitted;

Expand All @@ -47,6 +48,7 @@ public MainForm(IFsMqtt fsMqtt, IFsSimConnect fsSimConnect, ILogger<MainForm> lo
_pulseSimConnectStatusTimer.Elapsed += _pulseSimConnectStatusTimer_Elapsed;
InitializeControls();
}

public IFsMqtt FsMqtt
{
get;
Expand Down Expand Up @@ -139,11 +141,11 @@ private void SimConnect_SimConnectClosed(object sender, EventArgs e)
_pulseSimConnectStatusTimer.Stop();
}

private void SimConnect_TopicValueChanged(object sender, (SimConnectTopic topic, object value) topicValue)
private void SimConnect_TopicValueChanged(object sender, (SimConnectTopic topic, uint objectId, object value) topicValue)
{
if (FsMqtt.IsConnected)
{
FsMqtt.PublishTopicValue(topicValue.topic, topicValue.value);
FsMqtt.PublishTopicValue(topicValue.topic, topicValue.objectId, topicValue.value);
}
}

Expand Down Expand Up @@ -200,6 +202,14 @@ private void FsMqtt_SubscribeRequestRecieved(object _, SimConnectTopic[] topics)
}
}
}

private void FsMqtt_SetSimVarRequestRecieved(object sender, (string datumName, uint? objectId, object value) request)
{
if (FsSimConnect.IsConnected)
{
FsSimConnect.Set(request.datumName, request.objectId, request.value);
}
}
#endregion

#region Initialize Controls
Expand Down Expand Up @@ -243,7 +253,7 @@ private void InitializeControls()
Dock = DockStyle.Top,
};

var label = new Label
var lblMosquito = new Label
{
Height = 140,
Text = "FSMosquito",
Expand All @@ -252,7 +262,7 @@ private void InitializeControls()
Dock = DockStyle.Top,
};

Controls.Add(label);
Controls.Add(lblMosquito);
Controls.Add(pb1);
Controls.Add(statusPanel);
}
Expand Down
27 changes: 22 additions & 5 deletions FsMqtt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class FsMqtt : IFsMqtt
public event EventHandler MqttConnectionClosed;
public event EventHandler ReportSimConnectStatusRequestRecieved;
public event EventHandler<SimConnectTopic[]> SubscribeRequestRecieved;
public event EventHandler<(string datumName, uint? objectId, object value)> SetSimVarRequestRecieved;
public event EventHandler MqttMessageRecieved;
public event EventHandler MqttMessageTransmitted;

Expand Down Expand Up @@ -114,8 +115,9 @@ public async Task PublishSimConnectStatus(string simConnectStatus)
await Publish(FSMosquitoTopic.SimConnectStatus, simConnectStatus);
}

public async Task PublishTopicValue(SimConnectTopic topic, object value)
public async Task PublishTopicValue(SimConnectTopic topic, uint objectId, object value)
{
//TODO: Include objectId in topic.
var normalizedTopicName = Regex.Replace(topic.DatumName.ToLower(), "\\s", "_");
await Publish(FSMosquitoTopic.SimConnectTopicValue, value, true, new string[] { _clientId, normalizedTopicName });
}
Expand Down Expand Up @@ -176,7 +178,8 @@ await MqttClient.SubscribeAsync(
// SimConnect Events Subscription
new MqttTopicFilterBuilder().WithTopic(string.Format(FSMosquitoTopic.ReportSimConnectStatus, _clientId)).Build(),
new MqttTopicFilterBuilder().WithTopic(string.Format(FSMosquitoTopic.SubscribeToSimConnect, _clientId)).Build(),
new MqttTopicFilterBuilder().WithTopic(string.Format(FSMosquitoTopic.InvokeSimConnectFunction, _clientId)).Build()
new MqttTopicFilterBuilder().WithTopic(string.Format(FSMosquitoTopic.InvokeSimConnectFunction, _clientId)).Build(),
new MqttTopicFilterBuilder().WithTopic(string.Format(FSMosquitoTopic.SetSimConnectTopicValue, _clientId) + "+").Build()
);

// Report that we've connected.
Expand Down Expand Up @@ -214,7 +217,7 @@ private Task OnApplicationMessageReceived(MqttApplicationMessageReceivedEventArg
try
{
var payload = string.Empty;

if (e.ApplicationMessage.Payload != null && e.ApplicationMessage.Payload.Length > 0)
payload = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);

Expand All @@ -229,11 +232,17 @@ private Task OnApplicationMessageReceived(MqttApplicationMessageReceivedEventArg
var typedPayload = JsonConvert.DeserializeObject<SimConnectTopic[]>(payload);
OnSubscribeRequestRecieved(typedPayload);
break;
case var setSimVar when setSimVar.StartsWith(string.Format(FSMosquitoTopic.SetSimConnectTopicValue, _clientId)):
var datumName = setSimVar.Substring(setSimVar.LastIndexOf('/') + 1, setSimVar.Length - setSimVar.LastIndexOf('/') - 1);
var deNormalizedTopicName = Regex.Replace(datumName.ToUpper(), "_", " ");
var setSimConnectVarRequestPayload = JsonConvert.DeserializeObject<SetSimConnectVarRequest>(payload);
OnSetSimVarRequestRecieved(deNormalizedTopicName, setSimConnectVarRequestPayload.ObjectId, setSimConnectVarRequestPayload.Value);
break;
}

OnMqttMessageRecieved();
}
catch(Exception ex)
catch (Exception ex)
{
_logger.LogError($"Error deserializing Application Message for topic {e.ApplicationMessage.Topic}: {ex.Message}", ex);
}
Expand All @@ -251,7 +260,7 @@ private async Task ProcessMessageQueue()
return;
}

while(_mqttMessageQueue.Count > 0)
while (_mqttMessageQueue.Count > 0)
{
if (_mqttMessageQueue.TryDequeue(out MqttApplicationMessage message))
{
Expand Down Expand Up @@ -303,6 +312,14 @@ private void OnSubscribeRequestRecieved(SimConnectTopic[] topics)
}
}

private void OnSetSimVarRequestRecieved(string datumName, uint? objectId, object value)
{
if (SetSimVarRequestRecieved != null)
{
SetSimVarRequestRecieved.Invoke(this, (datumName, objectId, value));
}
}

private void OnMqttMessageRecieved()
{
if (MqttMessageRecieved != null)
Expand Down
80 changes: 48 additions & 32 deletions FsSimConnect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public sealed class FsSimConnect : IFsSimConnect

public event EventHandler SimConnectOpened;
public event EventHandler SimConnectClosed;
public event EventHandler<(SimConnectTopic, object)> TopicValueChanged;
public event EventHandler<(SimConnectTopic, uint, object)> TopicValueChanged;
public event EventHandler SimConnectDataReceived;
public event EventHandler SimConnectDataRequested;

Expand Down Expand Up @@ -145,6 +145,27 @@ public void SignalReceiveSimConnectMessage()
}
}

public void Set(string datumName, uint? objectId, object value)
{
if (objectId.HasValue == false)
{
objectId = 0;
}

if (!_subscriptions.ContainsKey(datumName))
{
_logger.LogInformation($"Skipping setting value of {datumName} to {value} on object {objectId} as the topic has not been previously registered.");
}

var subscription = _subscriptions[datumName];

var def = (Definition)Enum.ToObject(typeof(Definition), subscription.Id);
_simConnect.SetDataOnSimObject(def, objectId.Value, SIMCONNECT_DATA_SET_FLAG.DEFAULT, value);

// Cause the updated value to be re-transmitted next pulse.
subscription.LastValue = null;
}

public void Subscribe(SimConnectTopic topic)
{
if (_subscriptions.ContainsKey(topic.DatumName))
Expand Down Expand Up @@ -283,38 +304,33 @@ private void SimConnect_OnRecvSimObjectDataByType(SimConnect sender, SIMCONNECT_
uint objectId = data.dwObjectID;

// ObjectID == 1 is the user object data (I think)
switch(objectId)
object currentValue;
if (_pendingSubscriptions.TryRemove((int)requestId, out SimConnectSubscription subscription))
{
case 1:
object currentValue;
if (_pendingSubscriptions.TryRemove((int)requestId, out SimConnectSubscription subscription))
switch (subscription.Topic.Units)
{
case Consts.SimConnectBool:
currentValue = (bool)data.dwData[0];
break;
case Consts.SimConnectStringV:
currentValue = ((StringStruct)data.dwData[0]).value;
break;
default:
currentValue = (double)data.dwData[0];
break;
}

subscription.PendingRequestId = null;
subscription.PendingRequestStartTimeStamp = null;

if (subscription.LastValue == null || subscription.LastValue.Equals(currentValue) == false)
{
if (subscription.LastValue != currentValue)
{
switch (subscription.Topic.Units)
{
case Consts.SimConnectBool:
currentValue = (bool)data.dwData[0];
break;
case Consts.SimConnectStringV:
currentValue = ((StringStruct)data.dwData[0]).value;
break;
default:
currentValue = (double)data.dwData[0];
break;
}

subscription.PendingRequestId = null;
subscription.PendingRequestStartTimeStamp = null;

if (subscription.LastValue == null || subscription.LastValue.Equals(currentValue) == false)
{
if (subscription.LastValue != currentValue)
{
subscription.LastValue = currentValue;
OnTopicValue_Changed(subscription.Topic, currentValue);
}
}
subscription.LastValue = currentValue;
OnTopicValue_Changed(subscription.Topic, objectId, currentValue);
}
break;
}
}

OnSimConnectDataRecieved();
Expand Down Expand Up @@ -351,11 +367,11 @@ private void OnSimConnect_Closed()
}
}

private void OnTopicValue_Changed(SimConnectTopic topic, object value)
private void OnTopicValue_Changed(SimConnectTopic topic, uint objectId, object value)
{
if (TopicValueChanged != null)
{
TopicValueChanged.Invoke(this, (topic, value));
TopicValueChanged.Invoke(this, (topic, objectId, value));
}
}

Expand Down
36 changes: 36 additions & 0 deletions Helpers/SettingsHelpers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
namespace FSMosquitoClient.Helpers
{
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.IO;

public static class SettingsHelpers
{
public static bool AddOrUpdateAppSetting<T>(string sectionPathKey, T value)
{
try
{
var filePath = Path.Combine(AppContext.BaseDirectory, "appsettings.json");
var json = File.ReadAllText(filePath);
var obj = JObject.Parse(json);

var token = obj.SelectToken(sectionPathKey);
if (token == null)
{
return false;
}
token.Replace(JToken.FromObject(value));

var output = obj.ToString(Formatting.Indented);
File.WriteAllText(filePath, output);
return true;
}
catch (Exception ex)
{
Console.WriteLine("Error writing app settings | {0}", ex.Message);
}
return false;
}
}
}
12 changes: 9 additions & 3 deletions IFsMqtt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,20 @@ public interface IFsMqtt : IDisposable
public event EventHandler MqttMessageTransmitted;

/// <summary>
/// Event that is raised when a report SimConnect status Request is received.
/// Event that is raised when a report SimConnect status request is received.
/// </summary>
public event EventHandler ReportSimConnectStatusRequestRecieved;

/// <summary>
/// Event that is raised when a SimConnect Topic Subscription Request is received.
/// Event that is raised when a SimConnect Topic Subscription request is received.
/// </summary>
public event EventHandler<SimConnectTopic[]> SubscribeRequestRecieved;

/// <summary>
/// Event that is raised when a SimConnect Set SimVar request is recieved.
/// </summary>
public event EventHandler<(string datumName, uint? objectId, object value)> SetSimVarRequestRecieved;

/// <summary>
/// Gets a value that indicates if the current instance is connected to MQTT
/// </summary>
Expand Down Expand Up @@ -66,8 +71,9 @@ public interface IFsMqtt : IDisposable
/// Publishes the specified topic value to the MQTT broker
/// </summary>
/// <param name="topic"></param>
/// <param name="objectId"></param>
/// <param name="value"></param>
/// <returns></returns>
Task PublishTopicValue(SimConnectTopic topic, object value);
Task PublishTopicValue(SimConnectTopic topic, uint objectId, object value);
}
}
10 changes: 9 additions & 1 deletion IFsSimConnect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public interface IFsSimConnect : IDisposable
/// <summary>
/// Event that is raised when a previously subscribed topic value has changed.
/// </summary>
public event EventHandler<(SimConnectTopic, object)> TopicValueChanged;
public event EventHandler<(SimConnectTopic, uint, object)> TopicValueChanged;

/// <summary>
/// Event that is raised when a SimConnect data object is received.
Expand Down Expand Up @@ -53,6 +53,14 @@ public interface IFsSimConnect : IDisposable
/// </summary>
void Disconnect();

/// <summary>
/// Sets a SimConnect Datum value on the specified object id with the indicated value
/// </summary>
/// <param name="datumName"></param>
/// <param name="objectId"></param>
/// <param name="value"></param>
void Set(string datumName, uint? objectId, object value);

/// <summary>
/// Subscribes to a SimConnect Datum Topic
/// </summary>
Expand Down
21 changes: 21 additions & 0 deletions Models/SetSimConnectVar.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace FSMosquitoClient
{
using Newtonsoft.Json;

class SetSimConnectVarRequest
{
[JsonProperty("objectId")]
public uint? ObjectId
{
get;
set;
}

[JsonProperty("value")]
public object Value
{
get;
set;
}
}
}
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,11 @@ There are three main classes involved:

---

TODO: Right now just the 'SimVars' and 'System Events' described in the FS2020 SimConnect SDK are registered and only in a one-way fashion.
This should be expanded to allow values to be set from the FSMosquito server, as well as SimConnect functions to be called.
The client currently also accepts SimVars to be set via MQTT messages as well, this opens up some interesting possibilities for automation.

TODO: System variables, invoking various SimConnect functions
TODO: Add ability to change MQTT broker in UI
TODO: Add ability to specify PAT (Pilot Access Token) and change topics accordingly
### Logs

Currently the FSMosquitoClient creates log files located in the ./logs folder relative to where the FSMosquitoClient.exe is located.
Expand Down

0 comments on commit bfa49b5

Please sign in to comment.