
Andy B.
Members-
Posts
82 -
Joined
-
Last visited
-
Days Won
3
Content Type
Profiles
Forums
Events
Gallery
Downloads
Everything posted by Andy B.
-
Offsets constantly updating, even when they shouldn't
Andy B. replied to Andy B.'s topic in FSUIPC Client DLL for .NET
What aircraft are you using? I tried the PMDG 737, but never started its offset refresh methods. -
Hi, I am creating a PFD class for our app. In the class below, I am trying to figure out how to use the timer to update the properties, then trigger an event. The problem is when the properties are updated, I either get 0 for the value, or a constantly rotating value. The AslAltitude property is one good example. In the current implementation, I keep getting the AslAltitudeChanged event triggering, even when there shouldn't be a new change in value. With the plane on the ground and with the parking brake on, the asl altitude property gives me numbers like 1100, 600, 899, 711, 1258... All you really have to do is put this in a project with a connection/process routine, subscribe to the AslAltitude changed event, then assing the value to a textbox. The AircraftValueChangedEventArgs class is nothing more than a class that takes a double. Here is the code. public class AircraftValueChangedEventArgs: EventArgs { public double NewValue {get; set;} public AircraftValueChangedEventArgs(double value) { this.NewValue = value; } } Now, on to the more important code.. using FSUIPC; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Threading; namespace tfm.Airplanes.Shared { public class PFD { // Events. #region public event EventHandler<AircraftValueChangedEventArgs> AslAltitudeChanged; public event EventHandler<AircraftValueChangedEventArgs> AglAltitudeChanged; public event EventHandler<AircraftValueChangedEventArgs> TrueHeadingChanged; public event EventHandler<AircraftValueChangedEventArgs> IndicatedAirspeedChanged; public event EventHandler<AircraftValueChangedEventArgs> GroundSpeedChanged; public event EventHandler<AircraftValueChangedEventArgs> MachSpeedChanged; public event EventHandler<AircraftValueChangedEventArgs> VerticalSpeedChanged; public event EventHandler<AircraftValueChangedEventArgs> PitchChanged; public event EventHandler<AircraftValueChangedEventArgs> BankChanged; public event EventHandler<AircraftValueChangedEventArgs> AltimeterChanged; #endregion // FSUIPC offsets. #region private Offset<int> _aslAltitudeOffset = new Offset<int>(0x3324); // Feet. private Offset<int> _aglAltitudeOffset = new Offset<int>(0x0020); // In meters (* 256). private Offset<uint> _indicatedAirSpeedOffset = new Offset<uint>(0x02bc); // Knots (*128). private Offset<uint> _groundSpeedOffset = new Offset<uint>(0x02b4); private Offset<short> _machSpeedOffset = new Offset<short>(0x11c6); // Mach speed (*20480). private Offset<double> _trueHeadingOffset= new Offset<double>(0x6038); private Offset<short> _verticalSpeedOffset = new Offset<short>(0x02C8); // FPM = value *60*3.28084/256 private Offset<ushort> _altimeterOffset = new Offset<ushort>(0x0330); private Offset<double> _fdPitchOffset = new Offset<double>(0x2ee8); // In degrees (+ for up, - for down). private Offset<double> _fdBankOffset = new Offset<double>(0x2ef0); // In degrees (- left, + right). private Offset<int> _pitchOffset = new Offset<int>(0x0578); // In degrees (- = down, + = up). private Offset<int> _bankOffset = new Offset<int>(0x057c); // In degrees (+ for up, - for down). #endregion // Private fields. #region private DispatcherTimer _timer; private double _aslAltitude; private double _aglAltitude; private double _groundSpeed; private double _indicatedAirSpeed; private double _machSpeed; private double _trueHeading; private double _fdPitch; private double _fdBank; private double _pitch; private double _bank; private double _verticalSpeed; private double _altimeter; // Enter ILS if appropriate. #endregion // public properties. #region public double AslAltitude { // No change needed, so return the original value. get { return _aslAltitudeOffset.Value; } set { _aslAltitude = _aslAltitudeOffset.Value; } } public double AglAltitude { get { double groundAlt = (double)_aglAltitude / 256d * 3.28084d; double agl = (double)this.AslAltitude - groundAlt; agl = double.Truncate(agl); return agl; } set { var newAltitude = value; double groundAlt = (double)newAltitude / 256d * 3.28084d; double agl = (double)this.AslAltitude - groundAlt; agl = double.Truncate(agl); if (newAltitude != _aglAltitude) { _aglAltitude = newAltitude; AglAltitudeChanged?.Invoke(this, new AircraftValueChangedEventArgs(agl)); } } } public double GroundSpeed { get { double speed = ((double)_groundSpeed * 3600d) / (65536d * 1852d); speed = double.Truncate(speed); return speed; } set { double newSpeed = value; if(_groundSpeed != newSpeed) { _groundSpeed = newSpeed; double speed = ((double)newSpeed * 3600d) / (65536d * 1852d); speed = double.Truncate(speed); GroundSpeedChanged?.Invoke(this, new AircraftValueChangedEventArgs(speed)); } } } public double IndicatedAirSpeed { get { double speed = (double)_indicatedAirSpeed / 128d; speed = double.Truncate(speed); return speed; } set { double newSpeed = value; if(_indicatedAirSpeed != newSpeed) { _indicatedAirSpeed = newSpeed; double speed = (double)_indicatedAirSpeed / 128d; speed = double.Truncate(speed); IndicatedAirspeedChanged?.Invoke(this, new AircraftValueChangedEventArgs(speed)); } } } public double MachSpeed { get { double speed = Math.Truncate(_machSpeed / 20480 * 1000) / 1000; return speed; } set { double newSpeed = value; if (_machSpeed != newSpeed) { _machSpeed = newSpeed; double speed = Math.Truncate(newSpeed / 20480 * 1000) / 1000; MachSpeedChanged?.Invoke(this, new AircraftValueChangedEventArgs(speed)); } } } public double TrueHeading { get { var heading = Utilities.MathServices.ConvertRadiansToDegrees(_trueHeading); heading = double.Truncate(heading); return heading; } set { double newHeading = value; if(_trueHeading != newHeading) { _trueHeading = newHeading; var heading = Utilities.MathServices.ConvertRadiansToDegrees(newHeading); heading = double.Truncate(heading); TrueHeadingChanged?.Invoke(this, new AircraftValueChangedEventArgs(heading)); } } } public double FdPitch { get { return _fdPitch; } set { _fdPitch = value; } } public double FdBank { get { return _fdBank; } set { _fdBank = value; } } public double Pitch { get { double pitch = (double)_pitch * 360d / (65536d * 65536d); pitch = Math.Truncate(pitch); return pitch; } set { double newPitch = value; if(_pitch != newPitch) { _pitch = newPitch; double pitch = (double)newPitch * 360d / (65536d * 65536d); pitch = Math.Truncate(pitch); PitchChanged?.Invoke(this, new AircraftValueChangedEventArgs(pitch)); } } } public double Bank { get { double bank = (double)_bank * 360d / (65536d * 65536d); bank = Math.Truncate(bank); return bank; } set { double newBank = value; if(_bank != newBank) { _bank = newBank; double bank = (double)newBank * 360d / (65536d * 65536d); bank = Math.Truncate(bank); BankChanged?.Invoke(this, new AircraftValueChangedEventArgs(bank)); } } } public double VerticalSpeed { get { double speed = _verticalSpeed * 60 * 3.28084 / 256; speed = double.Truncate(speed); return speed; } set { double newSpeed = value; if(_verticalSpeed != newSpeed) { _verticalSpeed = newSpeed; double speed = newSpeed * 60 * 3.28084 / 256; speed = double.Truncate(speed); VerticalSpeedChanged?.Invoke(this, new AircraftValueChangedEventArgs(speed)); } } } public double Altimeter { get { double AltQNH = (double)_altimeterOffset.Value / 16d; double altimeterInches = Math.Floor(((100 * AltQNH * 29.92) / 1013.2) + 0.5); altimeterInches = altimeterInches / 100; return altimeterInches; } set { double qnh = value * 33.864; qnh = Math.Round(qnh, 1) * 16; _altimeterOffset.Value = (ushort)qnh; AltimeterChanged?.Invoke(this, new AircraftValueChangedEventArgs(this.Altimeter)); } } #endregion // Constructor public PFD() { _timer = new() { Interval = TimeSpan.FromMilliseconds(300), }; _timer.Tick += (s, e) => { if (_aslAltitudeOffset.ValueChanged) { this.AslAltitude = _aslAltitudeOffset.Value; AslAltitudeChanged?.Invoke(this, new AircraftValueChangedEventArgs(this.AslAltitude)); } if(_aglAltitudeOffset.ValueChanged) { this.AglAltitude = _aglAltitudeOffset.Value; } if(_trueHeadingOffset.ValueChanged) { this.TrueHeading= _trueHeadingOffset.Value; } if(_indicatedAirSpeedOffset.ValueChanged) { this.IndicatedAirSpeed = _indicatedAirSpeedOffset.Value; } if(_groundSpeedOffset.ValueChanged) { this.GroundSpeed = _groundSpeedOffset.Value; } if(_machSpeedOffset.ValueChanged) { this.MachSpeed = _machSpeedOffset.Value; } if(_verticalSpeedOffset.ValueChanged) { this.VerticalSpeed = _verticalSpeedOffset.Value; } if(_pitchOffset.ValueChanged) { this.Pitch = _pitchOffset.Value; } if(_bankOffset.ValueChanged) { this.Bank = _bankOffset.Value; } if(_altimeterOffset.ValueChanged) { this.Altimeter = _altimeterOffset.Value; } }; // Initial readouts. this.AslAltitude = _aslAltitudeOffset.Value; //this.AglAltitude = _aglAltitudeOffset.Value; //this.TrueHeading= _trueHeadingOffset.Value; //this.IndicatedAirSpeed = _indicatedAirSpeedOffset.Value; //this.GroundSpeed = _groundSpeedOffset.Value; //this.MachSpeed = _machSpeedOffset.Value; //this.VerticalSpeed = _verticalSpeedOffset.Value; //this.Pitch = _pitchOffset.Value; //this.Bank = _bankOffset.Value; //this.Altimeter = _altimeterOffset.Value; _timer.Start(); } } } Again, I want the AslAltitude to be correct when the event fires, but only fire the event when the AslAltitude actually changes.
-
FSUIPC is connected, but can't get values from offsets
Andy B. replied to Andy B.'s topic in FSUIPC Client DLL for .NET
Ok, just tried your idea and it works... You are a life saver! I will work with this, but will come back if I have any other problems. -
FSUIPC is connected, but can't get values from offsets
Andy B. replied to Andy B.'s topic in FSUIPC Client DLL for .NET
I will try it and let you know how it works out. Thought I tried that before, but tried lots of different things. -
FSUIPC is connected, but can't get values from offsets
Andy B. replied to Andy B.'s topic in FSUIPC Client DLL for .NET
Here is my ConnectionManager class. Relevent code is in the AutoConnect method. Follows it is a main window that attempts to display the aircraft name in the title. using DavyKager; using FSUIPC; using NAudio.SoundFont; using NLog; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using System.Windows.Threading; using tfm.Properties.Data.Navdata; namespace tfm.Utilities { /* * Manages the connection to FSUIPC and MSFS. All other processing of offsets * or data goes elsewhere. NOTE: only put connection related * code in this class. */ public static class ConnectionManager { // Timer to manage the connection to MSFS. private static DispatcherTimer connectionTimer; // Timer to process FSUIPC and MSFS data. private static DispatcherTimer processTimer; private static bool connectedAnnounced = false; private static bool disconnectedAnnounced = false; // Logger facility to log connection status. private static readonly Logger logger = LogManager.GetCurrentClassLogger(); public static PMDG_737_NGX_Offsets offsets = new PMDG_737_NGX_Offsets(); public static void AutoConnect() { // Start looking for MSFS. #region connectionTimer = new() { Interval = TimeSpan.FromMilliseconds(50), }; connectionTimer.Tick += async (s, e) => { // Open the connection. try { FSUIPCConnection.Open(); logger.Info("Connected to flight simulator (auto connect)."); // Automatically announce connected state. if (tfm.Properties.Settings.Default.AnnounceConnectedState && !connectedAnnounced) { AudioManager.Output(useSAPI: tfm.Properties.Settings.Default.UseSAPIForAutoConnect, output: "Connected."); connectedAnnounced = true; disconnectedAnnounced = false; } // Stop trying to connect so the connection status is properly updated. connectionTimer.Stop(); // Start processing data from MSFS. processTimer.Start(); var test = offsets.MCP_Heading.Value; } catch { if (tfm.Properties.Settings.Default.AnnounceConnectedState && !disconnectedAnnounced) { AudioManager.Output(useSAPI: tfm.Properties.Settings.Default.UseSAPIForAutoConnect, output: "Disconnected."); disconnectedAnnounced = true; connectedAnnounced = false; } } }; connectionTimer.Start(); #endregion // Start processing data. #region processTimer = new() { Interval = TimeSpan.FromMilliseconds(50), }; processTimer.Tick += (s, e) => { try { FSUIPCConnection.Process(); if (tfm.Airplanes.Aircraft.AircraftName.Value.Contains("PMDG")) { offsets.RefreshData(); } } catch (FSUIPCException ex) { /* * Since the connection to MSFS failed, stop the process timer and restart * the connection timer. We don't want to continue processing if * the connection is closed. We also don't want the connection timer running at * the same time as the process timer. It could cause connection problems, and needlessly fill the logs with endless loops of * errors. */ processTimer.Stop(); connectionTimer.Start(); logger.Warn($"Connection to MSFS closed: ({ex.FSUIPCErrorCode} - {ex.Message})."); } }; #endregion } public static void Connect() { processTimer = new() { Interval = TimeSpan.FromMilliseconds(50), }; processTimer.Tick += async (s, e) => { try { FSUIPCConnection.Process(); } catch (FSUIPCException ex) { logger.Warn($"Failed to connect - {ex.Message}."); } }; try { if (!FSUIPCConnection.IsOpen) { FSUIPCConnection.Open(); logger.Info("Connected to flight simulator (manual connection."); // Automatically announce connected state. if (tfm.Properties.Settings.Default.AnnounceConnectedState && !connectedAnnounced) { AudioManager.Output(useSAPI: tfm.Properties.Settings.Default.UseSAPIForAutoConnect, output: "Connected."); connectedAnnounced = true; disconnectedAnnounced = false; } // Start processing data from MSFS. processTimer.Start(); } else { AudioManager.Output(useSAPI: tfm.Properties.Settings.Default.UseSAPIForAutoConnect, output: "Already connected."); } } catch { if (tfm.Properties.Settings.Default.AnnounceConnectedState && !connectedAnnounced) { AudioManager.Output(useSAPI: tfm.Properties.Settings.Default.UseSAPIForAutoConnect, output: "Not able to connect."); disconnectedAnnounced = true; connectedAnnounced = false; } } } public static void Disconnect() { try { if (FSUIPCConnection.IsOpen) { FSUIPCConnection.Close(); if(tfm.Properties.Settings.Default.AnnounceConnectedState && !disconnectedAnnounced) { AudioManager.Output(useSAPI: tfm.Properties.Settings.Default.UseSAPIForAutoConnect, output: "Disconnected."); processTimer.Stop(); } } else { AudioManager.Output(useSAPI: tfm.Properties.Settings.Default.UseSAPIForAutoConnect, output: "Already disconnected."); } } catch { AudioManager.Output(useSAPI: tfm.Properties.Settings.Default.UseSAPIForAutoConnect, output: "Failed to disconnect."); } } } } Now, the main window class... using tfm.UI; using DavyKager; using FSUIPC; using NLog; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Windows.Threading; using tfm.Utilities; namespace tfm { public partial class MainWindow : Window { Offset<uint> activityCounter = new Offset<uint>(0x337E); private readonly Logger logger = LogManager.GetCurrentClassLogger(); private DispatcherTimer timer; private Offset<string> AircraftName = new Offset<string>(0x3d00, 255); public MainWindow() { InitializeComponent(); SetupTFMMenu(); timer = new() { Interval = TimeSpan.FromMilliseconds(300), }; timer.Tick += async (s, e) => { if (AircraftName.ValueChanged) { Title = $"Talking flight monitor {AircraftName.Value}"; } }; timer.Start(); } private async void Window_Loaded(object sender, RoutedEventArgs e) { // Load tolk. Tolk.Load(); // If required, install default Navigraph database. #region using (var _dbContext = new tfm.Properties.Data.Navigraph.NavigraphContext()) { _dbContext.InstallDefaultDatabase(); } #endregion // If required, upgrade settings. #region "Upgrade settings" if (tfm.Properties.Settings.Default.SettingsRequiresUpgrade) { tfm.Properties.Settings.Default.Upgrade(); tfm.Properties.pmdg737_offsets.Default.Upgrade(); tfm.Properties.pmdg747_offsets.Default.Upgrade(); tfm.Properties.Weather.Default.Upgrade(); tfm.Properties.NavlogColumns.Default.Upgrade(); tfm.Properties.Settings.Default.SettingsRequiresUpgrade = false; // Disable vatsim mode for now. tfm.Properties.Settings.Default.VatsimMode = false; tfm.Properties.Settings.Default.Save(); tfm.Properties.pmdg737_offsets.Default.Save(); tfm.Properties.pmdg747_offsets.Default.Save(); tfm.Properties.Weather.Default.Save(); } #endregion // Start looking for MSFS. if (tfm.Properties.Settings.Default.AutoConnect) { connectMenuItem.IsEnabled = false; disconnectMenuItem.IsEnabled = false; ConnectionManager.AutoConnect(); } // Start audio services. AudioManager.SetupAudio(); // load airports database. AirportServices.LoadAirportsDatabase(); LoadHomeScreen(); } public void SetupTFMMenu() { // Connection status. connectionStatusMenuItem.Click += (s, e) => { contentArea.Content = null; contentArea.Content = new ConnectionStatusUserControl(); }; // Runtime environment logs. runtimeEnvironmentMenuItem.Click += (s, e) => { contentArea.Content = null; contentArea.Content = new UI.Logs.RuntimeEnvironmentUserControl(); }; //Settings. settingsMenuItem.Click += (s, e) => { contentArea.Content = null; contentArea.Content = new UI.Settings.SettingsUserControl(); }; // Airports database. airportsDatabaseMenuItem.Click += (s, e) => { contentArea.Content = null; contentArea.Content = new UI.AirportsDatabaseUserControl(); }; } private void LoadHomeScreen() { switch (tfm.Properties.Settings.Default.HomeScreen) { case "Airports database": contentArea.Content = new UI.AirportsDatabaseUserControl(); break; case "Connection status": contentArea.Content = new UI.ConnectionStatusUserControl(); break; case "Runtime environment": contentArea.Content = new UI.Logs.RuntimeEnvironmentUserControl(); break; case "Settings": contentArea.Content = new UI.Settings.SettingsUserControl(); break; case "Empty": contentArea.Content = null; break; } } private async void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { // If in the airports database window. if (contentArea.Content is UI.AirportsDatabaseUserControl) { if (!AirportServices.MakeRunwaysProcess.HasExited) { e.Cancel = true; var result = await Task.Run(() => { return System.Windows.MessageBox.Show("Are you sure? TFM is still building the airports database. If you leave now, the build process will terminate. You will have to start the build process from the beginning the next time.", "Cancel airports database build process?", System.Windows.MessageBoxButton.YesNo); }); if (result == System.Windows.MessageBoxResult.Yes) { await Task.Run(() => { AirportServices.MakeRunwaysProcess.Kill(); }); e.Cancel = false; App.Current.Shutdown(); } else { e.Cancel = true; } } } } private void connectMenuItem_Click(object sender, RoutedEventArgs e) { ConnectionManager.Connect(); } private void disconnectMenuItem_Click(object sender, RoutedEventArgs e) { ConnectionManager.Disconnect(); } private void documentationMenuItem_Click(object sender, RoutedEventArgs e) { UIHelper.OpenURL("https://docs.talkingflightmonitor.com"); } private void supportMenuItem_Click(object sender, RoutedEventArgs e) { UIHelper.OpenURL("https://www.talkingflightmonitor.com/contact-us/"); } private void bugTrackerMenuItem_Click(object sender, RoutedEventArgs e) { UIHelper.OpenURL("https://bugtracker.talkingflightmonitor.com"); } private void websiteMenuItem_Click(object sender, RoutedEventArgs e) { UIHelper.OpenURL("https://www.talkingflightmonitor.com"); } } } Again, in the AutoConnect method, if I remove any reference to the offsets instance, the line var test = offsets.MCP_HEADING.Value, and the offsets.RefreshData() lines, everything works as expected. -
FSUIPC is connected, but can't get values from offsets
Andy B. replied to Andy B.'s topic in FSUIPC Client DLL for .NET
I didn't know if you have seen this yet, but still haven't been able to figure out why fsuipc throws a connection not open when trying to get PMDG info. If anyone has any ideas, it would be greatly appreciated. -
FSUIPC is connected, but can't get values from offsets
Andy B. replied to Andy B.'s topic in FSUIPC Client DLL for .NET
Ok, it seems that taking out everything except the absolute requirements gives me the activity counter and the increasing count as time goes on :). Now my main problem is getting PMDG data. When I put an instance of the pmdg offsets.refresh method in the process timer, I always get a connection not open error. Remove the PMDG stuff and everything works as long as it isn't pulling pmdg data. For example, requesting the aircraft name fails because it is provided by PMDG. I do have the SDK section in the options file, and followed the preveious version of my connection code. Strange... -
FSUIPC is connected, but can't get values from offsets
Andy B. replied to Andy B.'s topic in FSUIPC Client DLL for .NET
Here is my window that I am trying to test the connection in. We are now back to the activity counter being 0 all the time and PMDG requests crash with fsuipc connection not open... strange, because we duplicated the workflow exactly from the previous edition of talking flight monitor. So, confused why it doesn't work now. Anyways, here is the window I am trying to use. At this point, it puts the value of the activity counter in the titlebar. using tfm.UI; using DavyKager; using FSUIPC; using NLog; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Windows.Threading; using tfm.Utilities; namespace tfm { public partial class MainWindow : Window { Offset<uint> activityCounter = new Offset<uint>(0x337E); private Offset<ushort> Com1Freq = new Offset<ushort>(0x034e); private readonly Logger logger = LogManager.GetCurrentClassLogger(); public MainWindow() { InitializeComponent(); SetupTFMMenu(); } private void Window_Loaded(object sender, RoutedEventArgs e) { // Timer for reading offsets. #region var timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(300), }; timer.Tick += async (s, e) => { Dispatcher.BeginInvoke(() => { if (activityCounter.ValueChanged) { //Title = $" - Talking flight monitor {activityCounter.Value}"; } }); }; #endregion // If required, install default Navigraph database. #region using (var _dbContext = new tfm.Properties.Data.Navigraph.NavigraphContext()) { _dbContext.InstallDefaultDatabase(); } #endregion // If required, upgrade settings. #region "Upgrade settings" if (tfm.Properties.Settings.Default.SettingsRequiresUpgrade) { tfm.Properties.Settings.Default.Upgrade(); tfm.Properties.pmdg737_offsets.Default.Upgrade(); tfm.Properties.pmdg747_offsets.Default.Upgrade(); tfm.Properties.Weather.Default.Upgrade(); tfm.Properties.NavlogColumns.Default.Upgrade(); tfm.Properties.Settings.Default.SettingsRequiresUpgrade = false; // Disable vatsim mode for now. tfm.Properties.Settings.Default.VatsimMode = false; tfm.Properties.Settings.Default.Save(); tfm.Properties.pmdg737_offsets.Default.Save(); tfm.Properties.pmdg747_offsets.Default.Save(); tfm.Properties.Weather.Default.Save(); } #endregion // Start looking for MSFS. if (tfm.Properties.Settings.Default.AutoConnect) { connectMenuItem.IsEnabled = false; disconnectMenuItem.IsEnabled = false; ConnectionManager.AutoConnect(); } // Start audio services. AudioManager.SetupAudio(); // load airports database. AirportServices.LoadAirportsDatabase(); timer.Start(); Title = $"Talking flight monitor - {activityCounter.Value}"; LoadHomeScreen(); } public void SetupTFMMenu() { // Connection status. connectionStatusMenuItem.Click += (s, e) => { contentArea.Content = null; contentArea.Content = new ConnectionStatusUserControl(); }; // Runtime environment logs. runtimeEnvironmentMenuItem.Click += (s, e) => { contentArea.Content = null; contentArea.Content = new UI.Logs.RuntimeEnvironmentUserControl(); }; //Settings. settingsMenuItem.Click += (s, e) => { contentArea.Content = null; contentArea.Content = new UI.Settings.SettingsUserControl(); }; // Airports database. airportsDatabaseMenuItem.Click += (s, e) => { contentArea.Content = null; contentArea.Content = new UI.AirportsDatabaseUserControl(); }; } private void LoadHomeScreen() { switch (tfm.Properties.Settings.Default.HomeScreen) { case "Airports database": contentArea.Content = new UI.AirportsDatabaseUserControl(); break; case "Connection status": contentArea.Content = new UI.ConnectionStatusUserControl(); break; case "Runtime environment": contentArea.Content = new UI.Logs.RuntimeEnvironmentUserControl(); break; case "Settings": contentArea.Content = new UI.Settings.SettingsUserControl(); break; case "Empty": contentArea.Content = null; break; } } private async void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { // If in the airports database window. if (contentArea.Content is UI.AirportsDatabaseUserControl) { if (!AirportServices.MakeRunwaysProcess.HasExited) { e.Cancel = true; var result = await Task.Run(() => { return System.Windows.MessageBox.Show("Are you sure? TFM is still building the airports database. If you leave now, the build process will terminate. You will have to start the build process from the beginning the next time.", "Cancel airports database build process?", System.Windows.MessageBoxButton.YesNo); }); if (result == System.Windows.MessageBoxResult.Yes) { await Task.Run(() => { AirportServices.MakeRunwaysProcess.Kill(); }); e.Cancel = false; App.Current.Shutdown(); } else { e.Cancel = true; } } } } private void connectMenuItem_Click(object sender, RoutedEventArgs e) { ConnectionManager.Connect(); } private void disconnectMenuItem_Click(object sender, RoutedEventArgs e) { ConnectionManager.Disconnect(); } private void documentationMenuItem_Click(object sender, RoutedEventArgs e) { UIHelper.OpenURL("https://docs.talkingflightmonitor.com"); } private void supportMenuItem_Click(object sender, RoutedEventArgs e) { UIHelper.OpenURL("https://www.talkingflightmonitor.com/contact-us/"); } private void bugTrackerMenuItem_Click(object sender, RoutedEventArgs e) { UIHelper.OpenURL("https://bugtracker.talkingflightmonitor.com"); } private void websiteMenuItem_Click(object sender, RoutedEventArgs e) { UIHelper.OpenURL("https://www.talkingflightmonitor.com"); } } } -
FSUIPC is connected, but can't get values from offsets
Andy B. replied to Andy B.'s topic in FSUIPC Client DLL for .NET
Hi, I seem to be getting something by putting another timer in the window I wanted to show values in. Now, I have the PMDG 737 loaded with the PMDG offsets refresh data in the process timer. When trying to get the name of the aircraft, it tells me the connection isn't open, then crashes. On the other hand, pulling the activity counter at least gives me a static number, even though it should increas as the timer ticks. Who knows, back to the drawing board. -
FSUIPC is connected, but can't get values from offsets
Andy B. replied to Andy B.'s topic in FSUIPC Client DLL for .NET
Hi, Here is the method I am concerned with. I don't see any potential problems. What might be going wrong here? I am connected to flight simulator, but everything is returned as a 0. I don't see in this method where processTimer.start() is out of place. I have been trying to figure it out for a while though. public static void AutoConnect() { // Start looking for MSFS. #region connectionTimer = new() { Interval = TimeSpan.FromMilliseconds(50), }; connectionTimer.Tick += async (s, e) => { // Open the connection. try { FSUIPCConnection.Open(); logger.Info("Connected to flight simulator (auto connect)."); // Automatically announce connected state. if (tfm.Properties.Settings.Default.AnnounceConnectedState && !connectedAnnounced) { AudioManager.Output(useSAPI: tfm.Properties.Settings.Default.UseSAPIForAutoConnect, output: "Connected."); connectedAnnounced = true; disconnectedAnnounced = false; } // Stop trying to connect so the connection status is properly updated. connectionTimer.Stop(); // Start processing data from MSFS. processTimer.Start(); } catch { if (tfm.Properties.Settings.Default.AnnounceConnectedState && !disconnectedAnnounced) { AudioManager.Output(useSAPI: tfm.Properties.Settings.Default.UseSAPIForAutoConnect, output: "Disconnected."); disconnectedAnnounced = true; connectedAnnounced = false; } } }; connectionTimer.Start(); #endregion // Start processing data. #region processTimer = new() { Interval = TimeSpan.FromMilliseconds(50), }; processTimer.Tick += (s, e) => { try { FSUIPCConnection.Process(); } catch (FSUIPCException ex) { /* * Since the connection to MSFS failed, stop the process timer and restart * the connection timer. We don't want to continue processing if * the connection is closed. We also don't want the connection timer running at * the same time as the process timer. It could cause connection problems, and needlessly fill the logs with endless loops of * errors. */ processTimer.Stop(); connectionTimer.Start(); logger.Warn($"Connection to MSFS closed: ({ex.FSUIPCErrorCode} - {ex.Message})."); } }; #endregion } -
Hi, I am having problems getting values from offsets. When checking for the connected state, FSUIPC says I am connected to flight simulator when trying to get a value from an offset. I wonder if it is my connection code, but not sure. Here is my ConnectionManager class. The main concern right now is the AutoConnect method. Any help is welcome. using DavyKager; using FSUIPC; using NAudio.SoundFont; using NLog; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using System.Windows.Threading; using tfm.Properties.Data.Navdata; namespace tfm.Utilities { /* * Manages the connection to FSUIPC and MSFS. All other processing of offsets * or data goes elsewhere. NOTE: only put connection related * code in this class. */ public static class ConnectionManager { private static Offset<uint> activityCounter = new Offset<uint>(0x337E); // Timer to manage the connection to MSFS. private static DispatcherTimer connectionTimer; // Timer to process FSUIPC and MSFS data. private static DispatcherTimer processTimer; private static bool connectedAnnounced = false; private static bool disconnectedAnnounced = false; // Logger facility to log connection status. private static readonly Logger logger = LogManager.GetCurrentClassLogger(); public static void AutoConnect() { // Start looking for MSFS. #region connectionTimer = new() { Interval = TimeSpan.FromMilliseconds(50), }; processTimer = new() { Interval = TimeSpan.FromMilliseconds(50), }; connectionTimer.Tick += async (s, e) => { // Open the connection. try { FSUIPCConnection.Open(); logger.Info("Connected to flight simulator (auto connect)."); // Automatically announce connected state. if (tfm.Properties.Settings.Default.AnnounceConnectedState && !connectedAnnounced) { AudioManager.Output(useSAPI: tfm.Properties.Settings.Default.UseSAPIForAutoConnect, output: "Connected."); connectedAnnounced = true; disconnectedAnnounced = false; } // Start processing data from MSFS. processTimer.Start(); // Stop trying to connect so the connection status is properly updated. connectionTimer.Stop(); } catch { if (tfm.Properties.Settings.Default.AnnounceConnectedState && !disconnectedAnnounced) { AudioManager.Output(useSAPI: tfm.Properties.Settings.Default.UseSAPIForAutoConnect, output: "Disconnected."); disconnectedAnnounced = true; connectedAnnounced = false; } } }; connectionTimer.Start(); #endregion // Start processing data. #region processTimer.Tick += (s, e) => { processTimer.Start(); try { FSUIPCConnection.Process(); var counter = activityCounter.Value; } catch (FSUIPCException ex) { /* * Since the connection to MSFS failed, stop the process timer and restart * the connection timer. We don't want to continue processing if * the connection is closed. We also don't want the connection timer running at * the same time as the process timer. It could cause connection problems, and needlessly fill the logs with endless loops of * errors. */ processTimer.Stop(); connectionTimer.Start(); logger.Warn($"Connection to MSFS closed: ({ex.FSUIPCErrorCode} - {ex.Message})."); } processTimer.Stop(); }; #endregion } public static void Connect() { processTimer = new() { Interval = TimeSpan.FromMilliseconds(50), }; processTimer.Tick += async (s, e) => { try { FSUIPCConnection.Process(); } catch (FSUIPCException ex) { logger.Warn($"Failed to connect - {ex.Message}."); } }; try { if (!FSUIPCConnection.IsOpen) { FSUIPCConnection.Open(); logger.Info("Connected to flight simulator (manual connection."); // Automatically announce connected state. if (tfm.Properties.Settings.Default.AnnounceConnectedState && !connectedAnnounced) { AudioManager.Output(useSAPI: tfm.Properties.Settings.Default.UseSAPIForAutoConnect, output: "Connected."); connectedAnnounced = true; disconnectedAnnounced = false; } // Start processing data from MSFS. processTimer.Start(); } else { AudioManager.Output(useSAPI: tfm.Properties.Settings.Default.UseSAPIForAutoConnect, output: "Already connected."); } } catch { if (tfm.Properties.Settings.Default.AnnounceConnectedState && !connectedAnnounced) { AudioManager.Output(useSAPI: tfm.Properties.Settings.Default.UseSAPIForAutoConnect, output: "Not able to connect."); disconnectedAnnounced = true; connectedAnnounced = false; } } } public static void Disconnect() { try { if (FSUIPCConnection.IsOpen) { FSUIPCConnection.Close(); if(tfm.Properties.Settings.Default.AnnounceConnectedState && !disconnectedAnnounced) { AudioManager.Output(useSAPI: tfm.Properties.Settings.Default.UseSAPIForAutoConnect, output: "Disconnected."); processTimer.Stop(); } } else { AudioManager.Output(useSAPI: tfm.Properties.Settings.Default.UseSAPIForAutoConnect, output: "Already disconnected."); } } catch { AudioManager.Output(useSAPI: tfm.Properties.Settings.Default.UseSAPIForAutoConnect, output: "Failed to disconnect."); } } } }
-
I figured it out. The timer that does the processing for the offsets was missing. Had to stop the connection timer when connection opens and start the process timer. If the process timer throws an exception, restart the connection timer. It all works now. 🙂
-
Just did a test where all the timer did is report the activity count. When I started my app before MSFS was loaded, the total count is 0. After getting to the welcome screen, it still reported 0, even though IsOpen reported true. After restarting my app, I now have a total count of 219. Now, when I close MSFS, the display in my app still shows 219/connected for the current status. I don't understand, because this method works for other parts like aircraft panels. Not sure why it doesn't work here.
-
I tried this offset. I have the same result as when checking IsOpen. The app shows not connected until MSFS is active, then changes to connected as expected. However, closing MSFS while the app is open still results in the connected message staying on screen, even when MSFS has completely unloaded. Restarting my app then displays not connected. Wonder why it's doing this...strange.
-
So I understand, FSUIPCConnection.IsOpen only checks to see if FSUIPC itself is actively running. It knows nothing about MSFS or its current state. When MSFS closes, my app will always return the last known state of FSUIPC, which was open at the end of the test. To deal with this, I should check the activity counter offset since it can determine if MSFS is currently loaded. I can use the offset for now, but is there a way you can add this as a property to FSUIPCConnection?
-
I have a C# WPF app with a main window that controls startup routines. In the main window, I have the following. The user control displays connection status. Unfortunately, the connection status shows 'not connected' when the window loads if MSFS is closed. It will also display 'connected' when MSFS is loaded. When the app is running and MSFS closes, the status remains at 'connected'. It should toggle between not connected and connected based on the status of MSFS/FSUIPC. Any ideas how I can fix it? connectionTimer = new() { AutoReset = true, Enabled = true, Interval = 300, }; // Connection elapsed event. #region connectionTimer.Elapsed += (s, e) => { // Try to open the connection. Otherwise connection timer will do nothing. try { FSUIPCConnection.Open(); FSUIPCConnection.Process(); } catch(Exception ex) { } }; #endregion connectionTimer.Start(); #endregion In the user control embedded into the main window, I have the following. var timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(300), IsEnabled = true, }; timer.Tick += async (s, args) => await UpdatePanelControlsAsync(); timer.Start(); private async Task UpdatePanelControlsAsync() { await Task.Run(() => { string status = FSUIPCConnection.IsOpen? "Connected" : "Not connected"; Dispatcher.Invoke(() => { connectionStatusTextBox.Text = status.ToString(); }); });
-
PMDG 737 in MSFS missing probe heat switches
Andy B. replied to Andy B.'s topic in FSUIPC Client DLL for .NET
Oh, nevermind. I didn't see your line that said the test switches mapped to the probe heat switches. Guess I should use the test switches then 🙂 -
PMDG 737 in MSFS missing probe heat switches
Andy B. replied to Andy B.'s topic in FSUIPC Client DLL for .NET
So, what should I use for the probe heat switches, the test probe heat switches, or something else? -
Hi, I am working on Talking flight monitor and the PMDG 737. I noticed that the anti-ice probe heat switches are missing. Is it possible to add them to the library?
-
This fix worked. I also threw the MakeRwys.exe process and the BuildAirportsDatabase methods on different tasks.
-
After running BuildAirportsDatabaseAsync through the debugger, I found this stack trace. I don't understand where it came from, or why it is causing problems. I have 229GB left on my internal drive and over 3TB on my external. I also have 16GB of memory and 8GB of dedicated video memory. In the event this process is blowing out my memory, is there another way that isn't as resource intence? System.ComponentModel.Win32Exception HResult=0x80004005 Message=Not enough quota is available to process this command. Source=WindowsBase StackTrace: at MS.Win32.UnsafeNativeMethods.PostMessage(HandleRef hwnd, WindowMessage msg, IntPtr wparam, IntPtr lparam) at System.Windows.Interop.HwndTarget.UpdateWindowSettings(Boolean enableRenderTarget, Nullable`1 channelSet) at System.Windows.Interop.HwndTarget.UpdateWindowPos(IntPtr lParam) at System.Windows.Interop.HwndTarget.HandleMessage(WindowMessage msg, IntPtr wparam, IntPtr lparam) at System.Windows.Interop.HwndSource.HwndTargetFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o) at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs) at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler) at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs) at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
-
Here is the log. BuildAirportsDatabaseAsync never makes it to the build part for some reason. Last log entry was just before the db.MakeRunwaysFiles check. Here is the log. [Info]: Loading screen reader driver.: [tfm.App.Application_Startup]: [2023-09-18 18:16] [Debug]: Deleting existing MSFS make runways output folder.: [tfm.App.RunMakeRunways]: [2023-09-18 18:16] [Debug]: Creating MSFS make runways output folder.: [tfm.App.RunMakeRunways]: [2023-09-18 18:16] [Debug]: Copying C:\Users\a_bor\Documents\GitHub\talking-flight-monitor\source\bin\Debug\net7.0-windows\data\MakeRwys\MakeRwys.exe to C:\Users\a_bor\AppData\Local\tfm\Make runways\MSFS\MakeRwys.exe.: [tfm.App.RunMakeRunways]: [2023-09-18 18:16] [Debug]: Waiting for make runways to finish.: [tfm.App.RunMakeRunways]: [2023-09-18 18:16] [Debug]: Getting a copy of the current airports database.: [tfm.App.BuildAirportsDatabaseAsync]: [2023-09-18 18:40] [Debug]: Assigned MSFS make runways output folder: C:\Users\a_bor\AppData\Local\tfm\Make runways\MSFS.: [tfm.App.BuildAirportsDatabaseAsync]: [2023-09-18 18:40] [Debug]: Assigned MSFS airports database folder: C:\Users\a_bor\AppData\Local\tfm\Airports database\MSFS: [tfm.App.BuildAirportsDatabaseAsync]: [2023-09-18 18:40]
-
Some additional info. I tried running the feature again. Everything works as expected with the BuildAirportsDatabaseAsync method until it creates the airports database location (for the binary files). After that, the app dies with no notice to the user.
-
I have two methods: RunMakeRunways that takes a parameter from a WPF dialog asking the user to choose a simulator to build the database for. After choosing, then browsing to the P3D install folder if required, pressing the OK button launches a process that runs MakeRwys.exe in the proper location. In case of MSFS, %localappdata%\tfm\Make runways\MSFS. In P3D's case, the install folder with the working path for the process set to the P3D install folder. Once the RunMakeRunways method is complete (it finishes the MakeRwys.exe process), it bases the original simulator choice to BuildAirportsDatabaseAsync (the method in the previous post). Now, in the case of if(simulator == "P3D") block, everything works as expected. As for the if(simulator == "MSFS") block, I have done the following: * Put logging statements after every line of code. * Ran it through the debugger, which strangely works as expected. * Ran it outside of the debugger and get this strange behavior. * Consulted AI models with no luck. At the end of the entire process, the message box is supposed to show how many airports were loaded. Unfortunately, when passing "MSFS" to BuildAirportsDatabaseAsync, execution apparently stops at the line: var db = FSUIPCConnection.AirportsDatabase; The next log statement never appears, and Talking flight monitor is no longer running. No errors or exceptions.
-
Hi, I tried building and loading the database offline. It works for P3D, but not MSFS. Here is my BuildAirportsAsync method where the "P3D" condition suceeds and the "MSFS" condition fails. In fact, when "MSFS" is passed into this method, db = FSUIPCConnection.AirportsDatabase fails, and the method stops working with no errors or exceptions. What might be wrong? public static async void BuildAirportsDatabaseAsync(string simulator) { // In case a simulator is running, clear the database before rebuilding. if (FSUIPCConnection.IsOpen) { if (FSUIPCConnection.AirportsDatabase.IsLoaded) { FSUIPCConnection.AirportsDatabase.Clear(); } } try { var db = FSUIPCConnection.AirportsDatabase; if (simulator == "MSFS") { db.MakeRunwaysFolder = msfsMakeRunwaysOutputPath; db.DatabaseFolder = msfsAirportsDatabaseFolder; } if(simulator == "P3D") { db.DatabaseFolder = p3dAirportsDatabaseFolder; db.MakeRunwaysFolder = p3dMakeRunwaysOutputPath; } if (db.MakeRunwaysFilesExist) { await db.RebuildDatabaseAsync(); db.Load(); System.Windows.MessageBox.Show($"{simulator} airports build successfully. Total {db.Airports.Count}"); logger.Info($"{simulator} airports build successfully. Total {db.Airports.Count}"); } } catch(FSUIPCException x) { System.Windows.MessageBox.Show($"{x.Message}"); } }