Andy B. Posted July 8 Report Posted July 8 I have the following class of which other classes inherit from. I am trying to make a title property for the aircraft's name. Unfortunately, title, model, manufacturer, and the others don't work. They keep giving me String.Empty or something similar. On the other hand, the PFD and ComsRadios sections work. Just confused as to why things don't work. I am also having the same problem with the PMDG737 offsets always returning 0. I have a class 'PMDG737' that inherits the following airplane class. Even though the PMDG737 offsets are created after the connection is open, they still report as 0. Only reason I mention it is because the problems are similar. My connection manager is in a different class and works fine. Here is the airplane class. using System.Windows.Threading; using FSUIPC; using NHotkey; using NHotkey.WindowsForms; using NLog; using tfm.Airplanes.Shared; using tfm.Utilities; namespace tfm.Airplanes { public abstract class Airplane { // Offsets (kept private unless required to be made public). #region // Aircraft identifiers. #region private Offset<string> _ICAOOffset = new Offset<string>(0x0618, 16); private Offset<string> _airlineOffset = new Offset<string>(0x062C, 4); private Offset<string> _manufacturerOffset = new Offset<string>(0x09D2, 16); private Offset<string> _modelOffset = new Offset<string>(0x0B26, 32); protected Offset<string> _titleOffset; #endregion #endregion // Fields #region private readonly Logger logger = LogManager.GetCurrentClassLogger(); private PFD _pfd = new PFD(); private FsFuelTanksCollection _fuelTanks; private List<FsFuelTank> _activeFuelTanks = new List<FsFuelTank>(); protected bool _isControlKeysEnabled = false; protected bool _isFirstRun = true; private CommRadios _comRadios = new CommRadios(); protected double _lastReportedASLAltitude; protected DispatcherTimer _timer; private string _title = string.Empty; #endregion // Event flags. #region protected bool _aslChangedEventEnabled = false; #endregion // Properties. #region // The primary flight display. public virtual PFD PFD { get => _pfd; } // The coms radio. public virtual CommRadios CommRadios { get => _comRadios; } // The active fuel tanks on the airplane. public virtual List<FsFuelTank> ActiveFuelTanks { get => _activeFuelTanks; } // The aircraft's current location. public PlayerLocationInfo CurrentLocation { get => FSUIPCConnection.AirportsDatabase.Airports.GetPlayerLocation(AirportComponents.All); } // Airline that owns the aircraft. public string Airline { get => _airlineOffset.Value; } // The aircraft model. public string Model { get => _modelOffset.Value; } // The manufacturer (could be software developer). public string Manufacturer { get => _manufacturerOffset.Value; } // Aircraft ICAO code. public string ICAO { get => _ICAOOffset.Value; } // The aircraft title (all identifiers in a single string). public virtual string Title { get => _titleOffset.Value; } #endregion // Events #region public EventHandler<ControlKeysRegistrationChangedEventArgs> ControlKeyRegistrationChanged; #endregion public Airplane() { _titleOffset = new Offset<string>(0x3d00, 255); // Detect fuel tanks. DetectFuelTanks(); // Register features RegisterPrimaryControlKeyFeatures(); this.PFD.AslAltitudeChanged += AnnounceAltitudeChanges; this.CommRadios.Com1FrequencyChanged += AnnounceCom1FrequencyChanges; this.CommRadios.Com2FrequencyChanged += AnnounceCom2FrequencyChanges; this.CommRadios.Com1StandbyFrequencyChanged += AnnounceCom1StandbyFrequencyChanges; this.CommRadios.Com2StandbyFrequencyChanged += AnnounceCom2StandbyFrequencyChanges; // Enable the aircraft components depending on the first run state. this.PFD.Enable(); this.CommRadios.Enable(); // Delay setting first run to fals until everything loads. Task.Delay(500).ContinueWith(t => { _isFirstRun = false; }); _timer = new() { Interval = TimeSpan.FromMilliseconds(50), }; _timer.Tick += (s, e) => { try { FSUIPCConnection.Process(); } catch(Exception ex) { logger.Error(ex.Message); } }; _timer.Start(); } // Register/unregister hotkeys. #region protected virtual void RegisterPrimaryControlKeyFeatures() { HotkeyManager.Current.AddOrReplace("Primary control key", Keys.Oem6, (s, e) => { /* * Register the hotkeys for the primary control key. Each one passes through two layers of control. * (1) The OnPrimaryControlKeyPressed event where a secondary method is called. This first control layer allows the hotkey to call * the features code, and also allowing the trapping code to release the key to Windows. * (2) The features code, usually represented as a method in the form of On{featurename}KeyPressed where the feature's code lives. * * Most of the features following the above format are speech requests with the primary and secondary control keys. Anything else is usually represented in the UI. */ // Play the PFD sound whenever the key is pressed. AudioManager.Mixer.AddMixerInput(AudioManager.PFDSound); // Register the keys in the primary control section. HotkeyManager.Current.AddOrReplace("ASL altitude", Keys.A, PrimaryControlKeyPressed); HotkeyManager.Current.AddOrReplace("Current location", Keys.C, PrimaryControlKeyPressed); HotkeyManager.Current.AddOrReplace("Com radios", Keys.C | Keys.Shift, PrimaryControlKeyPressed); HotkeyManager.Current.AddOrReplace("Heading", Keys.H, PrimaryControlKeyPressed); HotkeyManager.Current.AddOrReplace("AGL altitude", Keys.G, PrimaryControlKeyPressed); HotkeyManager.Current.AddOrReplace("Indicated airspeed", Keys.S, PrimaryControlKeyPressed); HotkeyManager.Current.AddOrReplace("Mach speed", Keys.M, PrimaryControlKeyPressed); HotkeyManager.Current.AddOrReplace("Ground speed", Keys.U, PrimaryControlKeyPressed); HotkeyManager.Current.AddOrReplace("Vertical speed", Keys.V, PrimaryControlKeyPressed); HotkeyManager.Current.AddOrReplace("Remaining fuel", Keys.F, PrimaryControlKeyPressed); HotkeyManager.Current.AddOrReplace("Altimeter", Keys.B, PrimaryControlKeyPressed); }); _isControlKeysEnabled = true; ControlKeyRegistrationChanged?.Invoke(this, new ControlKeysRegistrationChangedEventArgs(_isControlKeysEnabled)); } protected virtual void UnregisterPrimaryControlKeyFeatures() { HotkeyManager.Current.Remove("ASL altitude"); HotkeyManager.Current.Remove("Current location"); HotkeyManager.Current.Remove("Com radios"); HotkeyManager.Current.Remove("Heading"); HotkeyManager.Current.Remove("AGL altitude"); HotkeyManager.Current.Remove("Indicated airspeed"); HotkeyManager.Current.Remove("Mach speed"); HotkeyManager.Current.Remove("Ground speed"); HotkeyManager.Current.Remove("Vertical speed"); HotkeyManager.Current.Remove("Remaining fuel"); HotkeyManager.Current.Remove("Altimeter"); } public virtual void ToggleControlKeys() { _isControlKeysEnabled = !_isControlKeysEnabled; if (_isControlKeysEnabled) { RegisterPrimaryControlKeyFeatures(); } else { UnregisterPrimaryControlKeyFeatures(); HotkeyManager.Current.Remove("Primary control key"); ControlKeyRegistrationChanged?.Invoke(this, new ControlKeysRegistrationChangedEventArgs(_isControlKeysEnabled)); } } #endregion // Hotkey event handlers. #region protected virtual async void PrimaryControlKeyPressed(object sender, HotkeyEventArgs e) { switch (e.Name) { case "ASL altitude": OnASLAltitudeKeyPress(); break; case "AGL altitude": OnAGLAltitudeKeyPress(); break; case "Current location": OnCurrentLocationKeyPressed(); break; case "Com radios": OnComRadioKeyPress(); break; case "Heading": OnHeadingKeyPress(); break; case "Altimeter": OnAltimeterKeyPress(); break; case "Ground speed": OnGroundSpeedKeyPress(); break; case "Indicated airspeed": OnIndicatedAirspeedKeyPress(); break; case "Mach speed": OnMachSpeedKeyPress(); break; case "Remaining fuel": OnRemainingFuelKeyPress(); break; case "Vertical speed": OnVerticalSpeedKeyPress(); break; } // Unregister primary control features so they don't interfere with normal Windows operation. UnregisterPrimaryControlKeyFeatures(); } protected virtual void OnASLAltitudeKeyPress() { string units = tfm.Properties.Settings.Default.DisplayAltitudesInMeters ? "meters" : "feet"; AudioManager.Output( tfm.Properties.Settings.Default.DisplayAltitudesInMeters ? $"{Math.Truncate(PFD.AslAltitude)} {units}" : $"{Math.Truncate(MathServices.MetersToFeet(PFD.AslAltitude))} {units}" ); // Remove the hotkey so it doesn't interfere with typing. HotkeyManager.Current.Remove("ASL altitude"); } protected virtual void OnAGLAltitudeKeyPress() { string units = tfm.Properties.Settings.Default.DisplayAltitudesInMeters ? "meters" : "feet"; AudioManager.Output( tfm.Properties.Settings.Default.DisplayAltitudesInMeters ? $"{PFD.AglAltitude} {units}" : $"{Math.Truncate(MathServices.MetersToFeet(PFD.AglAltitude))} {units}" ); HotkeyManager.Current.Remove("AGL altitude"); } protected virtual void OnHeadingKeyPress() { AudioManager.Output(PFD.TrueHeading.ToString()); HotkeyManager.Current.Remove("Heading"); } protected virtual void OnIndicatedAirspeedKeyPress() { AudioManager.Output($"{PFD.IndicatedAirSpeed} knots"); HotkeyManager.Current.Remove("Indicated airspeed"); } protected virtual void OnMachSpeedKeyPress() { AudioManager.Output($"Mach {PFD.MachSpeed}"); HotkeyManager.Current.Remove("Mach speed"); } protected virtual void OnGroundSpeedKeyPress() { AudioManager.Output($"{PFD.GroundSpeed} knots"); HotkeyManager.Current.Remove("Ground speed"); } protected virtual void OnVerticalSpeedKeyPress() { string units = tfm.Properties.Settings.Default.DisplayAltitudesInMeters ? "MPS" : "FPM"; AudioManager.Output( tfm.Properties.Settings.Default.DisplayAltitudesInMeters ? $"{PFD.VerticalSpeed} {units}" : $"{Math.Truncate(MathServices.MetersPerSecondToFeetPerMinute(PFD.VerticalSpeed))} {units}" ); HotkeyManager.Current.Remove("Vertical speed"); } protected virtual void OnRemainingFuelKeyPress() { FSUIPCConnection.PayloadServices.RefreshData(); double lbs = Math.Truncate(FSUIPCConnection.PayloadServices.FuelWeightLbs); double gal = Math.Truncate(FSUIPCConnection.PayloadServices.FuelLevelUSGallons); AudioManager.Output($"{lbs} pounds ({gal} Gallons"); HotkeyManager.Current.Remove("Remaining fuel"); } protected virtual void OnAltimeterKeyPress() { string units = tfm.Properties.Settings.Default.DisplayAltimeterInches ? "inches" : "millibars"; AudioManager.Output( tfm.Properties.Settings.Default.DisplayAltimeterInches ? $"{MathServices.MillibarsToInches(PFD.Altimeter)} {units}" : $"{PFD.Altimeter} {units}" ); HotkeyManager.Current.Remove("Altimeter"); } protected virtual void OnComRadioKeyPress() { AudioManager.Output($"Com1: {this.CommRadios.Comm1Frequency}"); AudioManager.Output($"Com2: {this.CommRadios.Com2Frequency}"); AudioManager.Output($"Com 1 standby: {this.CommRadios.Com1StandbyFrequency}"); AudioManager.Output($"Com 2 standby: {this.CommRadios.Com2StandbyFrequency}"); } protected virtual async Task OnCurrentLocationKeyPressed() { try { var location = await LocationService.GetLocationInfoAsync(); AudioManager.Output(location); } catch(Exception ex) { logger.Error("Error in getting current location."); } } #endregion // Event handlers for aircraft changes. #region protected virtual void AnnounceAltitudeChanges(object sender, AltitudeChangedEventArgs e) { if (_isFirstRun) return; double currentAltitude = tfm.Properties.Settings.Default.DisplayAltitudesInMeters ? e.Meters : e.Feet; string units = tfm.Properties.Settings.Default.DisplayAltitudesInMeters ? "meters" : "feet"; bool announceAltitudes = tfm.Properties.Settings.Default.AltitudeAnnouncements; double altitudeInterval = double.Parse(tfm.Properties.Settings.Default.AnnounceAltitudeInterval); if (announceAltitudes) { double startingAltitude = Math.Ceiling(currentAltitude / altitudeInterval) * altitudeInterval; for (double i = startingAltitude; i < 65000; i += altitudeInterval) { if (currentAltitude >= i - 10 && currentAltitude <= i + 10) { if (_lastReportedASLAltitude != i) { AudioManager.Output($"{i} {units}"); _lastReportedASLAltitude = i; } break; } } } } protected virtual void AnnounceCom1FrequencyChanges(object sender, ComFrequencyChangedEventArgs e) { if (!_isFirstRun) { AudioManager.Output($"Com1: {e.Frequency}"); } } protected virtual void AnnounceCom2FrequencyChanges(object sender, ComFrequencyChangedEventArgs e) { if (!_isFirstRun) { AudioManager.Output($"Com2: {this.CommRadios.Com2Frequency}"); } } protected virtual void AnnounceCom1StandbyFrequencyChanges(object sender, ComFrequencyChangedEventArgs e) { if (!_isFirstRun) { AudioManager.Output($"Com1 standby: {this.CommRadios.Com1StandbyFrequency}"); } } protected virtual void AnnounceCom2StandbyFrequencyChanges(object sender, ComFrequencyChangedEventArgs e) { if (!_isFirstRun) { AudioManager.Output($"Com2 standby: {this.CommRadios.Com2StandbyFrequency}"); } } #endregion // Detect the active fuel tanks on the airplane and get their values. protected virtual void DetectFuelTanks() { if (FSUIPCConnection.IsOpen) { FSUIPCConnection.PayloadServices.RefreshData(); _activeFuelTanks.Clear(); // Assign the fuel tanks to our class level variable for easier access _fuelTanks = FSUIPCConnection.PayloadServices.FuelTanks; foreach (FsFuelTank tank in _fuelTanks) { if (tank.IsPresent) { _activeFuelTanks.Add(tank); logger.Info("found " + tank.Tank.ToString()); } } } } } }
Paul Henty Posted July 10 Report Posted July 10 I suggest you make a very plain, simple application so you can test out offsets that don't appear to work. Just a form that opens a connection, processes the offsets and writes them to the form. If they don't work in your simple project then you need to ask John Dowson in the main MSFS (FSUIPC7) support forum. Maybe some of the offsets don't work in FSUIPC7. If they do work in your simple project then it's something to do with the logic in your application. You'll need to use the tools available to find out what the problem is (break points, logging etc.). Paul
John Dowson Posted September 10 Report Posted September 10 On 7/10/2024 at 11:47 AM, Paul Henty said: Maybe some of the offsets don't work in FSUIPC7. Note that you can always use FSUIPC's offset logging facilities to check/verify the contents of an offset. The aircraft title offset 0x3D00 is certainly populated/available, but may not when MSFS is in the main menu. John
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now