Jump to content
The simFlight Network Forums

Andy B.

Members
  • Posts

    82
  • Joined

  • Last visited

  • Days Won

    3

Everything posted by Andy B.

  1. I did, and it didn't work. I tried with a button click to increase it by 1 or 2, then I tried hooking up a keydown event in a textbox to see if that would work. Still doesn't work. These aren't the only 'knobs' I am having problems with either. The cargo temp panel in overhead maint has temp knobs. They are giving me the same problem. Sending any type of value fails unless the 777 directly changes it with a state load.
  2. Hi, I am trying to set the position of the lighting knobs in the PMDG777 for MSFS2020. Unfortunately, nothing I do seems to work. I know it has something to do with the parameters I am sending, because the values change in our app when the 777 changes the values itself. EG: when loading a panel state. Here is an example from the master brightness knob. The other ones like circiut breaker, glare shield, overhead, and dome light are probably the same. The SDK states that the valid values are 0-100 for all of them. However, sending a left click, right click, mouse wheel up, mouse wheel down, left drag, or right drag don't change the values. What do I send for the parameters? I figured I would come here and ask because I am using your library. Here is what I have (C#) in dotnet 9.0. FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_OH_MASTER_BRIGHT_ROTATE, ...);
  3. Can you let me know when you are done?
  4. Yes, it appears to be stable. I tried it around 20 times in the debugger, and none of them threw the protobuf error. Out of interest, is the entire airports database thread safe, or only parts of it? Just wondering, because we use it in a multi threaded environment quite often.
  5. I don't think this has anything to do with make runways, but this is what I get now. I never modified my code, so it is the same as before. System.InvalidOperationException HResult=0x80131509 Message=Collection was modified; enumeration operation may not execute. Source=System.Private.CoreLib StackTrace: at System.ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion() at System.Collections.Generic.List`1.ForEach(Action`1 action) at FSUIPC.FsAirport.LoadComponents(Byte ClassInstance, AirportComponents Components) at FSUIPC.FsAirportCollection.GetPlayerLocation(Byte ClassInstance, AirportComponents Components) at FSUIPC.FsAirportCollection.GetPlayerLocation(AirportComponents Components) at tfm.Airplanes.Airplane.get_CurrentLocation() in C:\Users\a_bor\Documents\GitHub\talking-flight-monitor\source\Airplanes\Airplane.cs:line 155 at tfm.Airplanes.Airplane.<DetectILS>d__191.MoveNext() in C:\Users\a_bor\Documents\GitHub\talking-flight-monitor\source\Airplanes\Airplane.cs:line 1403
  6. Hi, It happens with most airports, especially in the United States. A prime example that gives me this problem is KSFO, KLAX, or anything on the west coast. Sometimes KJFK and KBOS do it, but it is almost nonexistent. I also noticed that when I pull out if the player is on the runway, and what runway they are on, I get unpredictable results. A good example is runways 24 and 25 at KSFO. Going into KRNO last night to runway 17R, my PMDG738 had CMDA and CMDB on, landed on the runway, but the scenery extract from the airports database was telling me that I wasn't on a runway at all. Visually, the landing was perfect. I am running the newest library version. This is MSFS2020. We aren't using 3rd party scenery, only the MSFS2020 default.
  7. Hi, I get the following exception when doing the following: Public PlayerInfo CurrentLocation { get => FSUIPCConnection.AirportsDatabase.GetPlayerLocation(AirportComponents.All); } Here is the error I get. Strangely enough, the airports database built without problems. How would I fix this? The only way I get the below is to run it through the debugger in Visual Studio 2022. ProtoBuf.ProtoException HResult=0x80131500 Message=Invalid wire-type (6); this usually means you have over-written a file without truncating or setting the length; see https://stackoverflow.com/q/2152978/23354 Source=protobuf-net.Core StackTrace: at ProtoBuf.ProtoReader.State.ThrowProtoException(String message) at ProtoBuf.ProtoReader.State.ThrowWireTypeException() at ProtoBuf.Internal.Serializers.SimpleCompiledSerializer`1.ProtoBuf.Serializers.ISerializer<T>.Read(State& state, T value) at ProtoBuf.ProtoReader.State.ReadAsRoot[T](T value, ISerializer`1 serializer) at ProtoBuf.Internal.DynamicStub.ConcreteStub`1.TryDeserialize(ObjectScope scope, TypeModel model, State& state, Object& value) at ProtoBuf.Internal.DynamicStub.TryDeserialize(ObjectScope scope, Type type, TypeModel model, State& state, Object& value) at ProtoBuf.Meta.TypeModel.Deserialize(ObjectScope scope, State& state, Type type, Object value) at ProtoBuf.Meta.TypeModel.DeserializeWithLengthPrefix(Stream source, Object value, Type type, PrefixStyle style, Int32 expectedField, TypeResolver resolver, Int64& bytesRead, Boolean& haveObject, SerializationContext context) at ProtoBuf.Meta.TypeModel.DeserializeWithLengthPrefix(Stream source, Object value, Type type, PrefixStyle style, Int32 expectedField, TypeResolver resolver, Int64& bytesRead) at ProtoBuf.Meta.TypeModel.DeserializeWithLengthPrefix(Stream source, Object value, Type type, PrefixStyle style, Int32 fieldNumber) at ProtoBuf.Serializer.DeserializeWithLengthPrefix[T](Stream source, PrefixStyle style, Int32 fieldNumber) at FSUIPC.DataStore.ReadSelection[T](String fileName, List`1 collection, String ICAO) at FSUIPC.FsAirport.LoadComponents(Byte ClassInstance, AirportComponents Components) at FSUIPC.FsAirportCollection.GetPlayerLocation(Byte ClassInstance, AirportComponents Components) at FSUIPC.FsAirportCollection.GetPlayerLocation(AirportComponents Components) at tfm.Airplanes.Airplane.get_CurrentLocation() in C:\Users\a_bor\Documents\GitHub\talking-flight-monitor\source\Airplanes\Airplane.cs:line 155 at tfm.Airplanes.Airplane.<DetectILS>d__191.MoveNext() in C:\Users\a_bor\Documents\GitHub\talking-flight-monitor\source\Airplanes\Airplane.cs:line 1403 The relevant line of code is: if (tfm.Properties.Settings.Default.ReadILS && !this.CurrentLocation.OnGround) Found in the method below: protected async virtual void DetectILS() { if (_isFirstRun) return; { if (tfm.Properties.Settings.Default.ReadILS && !this.CurrentLocation.OnGround) { // Detect the glide slope. if (_nav1GSOffset.Value == 1 && _glideSlopeDetected == false) { AudioManager.Output("Glide slope is alive"); _glideSlopeDetected = true; } // Detect the localizer. if (NavRadios.RadioFlags[7] && _hasLocalizer == false) { AudioManager.Output("Nav1 has localizer"); _hasLocalizer = true; } // The localizer is alive. if (NavRadios.Nav1Signal == 256 && NavRadios.RadioFlags[7] && _localizerDetected == false) { //double magvar = (double)_magneticVariationOffset.Value * 360d / 65536d; //double rwyHeading = (double)NavRadios.InverseRunwayHeading * 360d / 65536d + 180d - magvar; //rwyHeading = Math.Truncate(rwyHeading); if (FlightPlanning.FlightPlan.DestinationRunway != null) { var course = NavRadios.Nav1Course; AudioManager.Output($"Localizer is alive: Course {course}"); _localizerDetected = true; _ilsTimer.AutoReset = true; _ilsTimer.Enabled = true; } else { AudioManager.Output("Warning! Destination runway not set."); } } // Has the glide slope. if (NavRadios.AutoPilotRadioFlags[6] && _hasGlideSlope == false) { AudioManager.Output("Nav1 has glide slope"); _hasGlideSlope = true; } } else { _ilsTimer.Enabled = false; _hasGlideSlope = false; _hasLocalizer = false; _localizerDetected = false; _glideSlopeDetected = false; } } }
  8. Yes, it works just fine in my setup. My aircraft heading and the runway heading might be off by a degree or so, but I never ran off the runway like that. One of my staff members reported some time ago that he used our app to reposition to the runway, attempted o takeoff, and crashed into something. When he reloaded and used GSX or another app to reposition to the runway, he didn't crash. Nobody can confirm if it is because make runways data is bad, because our app rounds headings to the nearest degree, or both, but I guess it is a problem. I can't even confirm if our app is to blame for his crash or many of our users getting placed off the centerline.
  9. Hi, When using the built-in move aircraft method for a runway, users are reporting that it is putting the aircraft to the left or right of the centerline, not directly on the centerline. This is also reported by Vatsim ATC as well. Here is a little sample of how we use the MoveAircraftHere method for a runway. Is there a way to get i to put us on the centerline of the runway? Users are also reporting that it puts them in a strange location on the runway such as a taxiway or not on the lineup or holding point. if(row is RunwayDataGridRow runwayData) { var airport = FSUIPCConnection.AirportsDatabase.Airports[airportIcaoTextBox.Text.ToUpper()]; airport.LoadComponents(AirportComponents.Runways); var runway = airport.Runways[runwayData.ID.ToString()]; runway.MoveAircraftHere(false); this.Close(); }
  10. Got this to work. All I had to do is add a firstRun flag. Set it to true by default, then at the end of the constructor, set it to false after a 500ms delay. Then, in each event handler implementation, put this at the top of the event handler. Private Void AnnounceSomething(Object sender, PMDG777ValueChangedEventArgs e) { if(isFirstRun) return; // Rest of the code. } Works perfectly, and it doesn't interfere with the rest of the working code. I still am going to create public methods for attaching/detaching different groups of events in case I need to use them.
  11. So, for example, in my main window, I have a menu item that when clicked, creates an instance of this class and assigns it to a property in another class. Once that is done, the menu item's click event can attach the event handlers. This should stop the spam when the 777 loads?
  12. I have event handlers that handle the announcement of offset values when they change. So far, everything is working perfectly. However, when starting our app, we get announcements for different 777 controls. Some of them include MCP heading, MCP speed, MCP altitude, flight director status, and auto throttle left/right status. Given the class partials below, is there a way to stop these announcements while the class instance is created? We need it to be silent during startup. I tried setting a loaded or initialized flag, but that doesn't really work. It seems that when the timer starts, it triggers the event handlers in mass for the first time - this is what we need to deal with I think. using tfm.Utilities; using tfm.Airplanes.PMDG777.Shared; using System.Windows.Threading; using FSUIPC; using tfm.Airplanes; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using NLog; namespace tfm.Airplanes.PMDG777 { public partial class PMDG777: Airplane { private bool isInitialized = false; // Parking brake. public KeyValuePair<object, string> ParkingBrake { get { KeyValuePair<object, string> item = new KeyValuePair<object, string>(); if(_offsets.BRAKES_ParkingBrakeLeverOn.Value == 0) { item = new KeyValuePair<object, string>(0, "off"); } else if(_offsets.BRAKES_ParkingBrakeLeverOn.Value == 1) { item = new KeyValuePair<object, string>(1, "on"); } return item; } } // Flaps public KeyValuePair<object, string> Flaps { get { KeyValuePair<object, string> item = new KeyValuePair<object, string>(); if (_offsets.FCTL_Flaps_Lever.Value == 0) item = new KeyValuePair<object, string>(0, "up"); else if (_offsets.FCTL_Flaps_Lever.Value == 1) item = new KeyValuePair<object, string>(1, "1"); else if (_offsets.FCTL_Flaps_Lever.Value == 2) item = new KeyValuePair<object, string>(2, "5"); else if (_offsets.FCTL_Flaps_Lever.Value == 3) item = new KeyValuePair<object, string>(3, "15"); else if (_offsets.FCTL_Flaps_Lever.Value == 4) item = new KeyValuePair<object, string>(4, "20"); else if (_offsets.FCTL_Flaps_Lever.Value == 5) item = new KeyValuePair<object, string>(5, "25"); else if (_offsets.FCTL_Flaps_Lever.Value == 6) item = new KeyValuePair<object, string>(6, "30"); return item; } } public CDU1 CDU1 { get => _cdu1; } public CDU2 CDU2 { get => _cdu2; } // Events. #region public EventHandler<PMDG777ValueChangedEventArgs> ParkingBrakeChanged; public EventHandler<PMDG777ValueChangedEventArgs> FlapsChanged; #endregion public PMDG777() { logger.Info($"Starting support for {this.Title}"); // Start timer. logger.Info("Starting services."); _timer = new DispatcherTimer() { Interval = TimeSpan.FromMilliseconds(500), }; _timer.Tick += ProcessOffsets; _timer.Start(); AttachEventHandlers(); Task.Delay(500).ContinueWith(t => { isInitialized = true; }); } // Timer implementation. private void ProcessOffsets(object sender, EventArgs e) { _offsets.RefreshData(); // parking brake. if (_offsets.BRAKES_ParkingBrakeLeverOn.ValueChanged) { ParkingBrakeChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(ParkingBrake)); } // Flaps lever. if (_offsets.FCTL_Flaps_Lever.ValueChanged) { FlapsChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(this.Flaps)); } // MCP speed (indicated/mach). if (_offsets.MCP_IASMach.ValueChanged) { MCPSpeedChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(MCPSpeed)); } // MCP speed intervene. if (_offsets.MCP_IASBlan.ValueChanged) { MCPSpeedInterveneChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(MCPSpeedIntervene)); } // MCP heading if (_offsets.MCP_Heading.ValueChanged) MCPHeadingChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(MCPHeading)); // MCP altitude. if (_offsets.MCP_Altitude.ValueChanged) MCPAltitudeChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(MCPAltitude)); // MCP vertical speed. if (_offsets.MCP_VertSpeed.ValueChanged) MCPVerticalSpeedChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(MCPVerticalSpeed)); // MCP FPA. if (_offsets.MCP_FPA.ValueChanged) MCPFPAChanged.Invoke(this, new PMDG777ValueChangedEventArgs(MCPFPA)); // MCP vertical speed intervene if (_offsets.MCP_VertSpeedBlank.ValueChanged) MCPVerticalSpeedInterveneChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(MCPVerticalSpeedIntervene)); // Flight director switches. if (_offsets.MCP_FD_Sw_On[0].ValueChanged) LeftFlightDirectorSwitchChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(LeftFlightDirectorSwitch)); if (_offsets.MCP_FD_Sw_On[1].ValueChanged) RightFlightDirectorSwitchChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(RightFlightDirectorSwitch)); // Auto throttle switches. if (_offsets.MCP_ATArm_Sw_On[0].ValueChanged) LeftAutoThrottleSwitchChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(LeftAutoThrottleSwitch)); if (_offsets.MCP_ATArm_Sw_On[1].ValueChanged) RightAutoThrottleSwitchChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(RightAutoThrottleSwitch)); // Bank limit selector. if (_offsets.MCP_BankLimitSel.ValueChanged) BankLimitSelectorChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(BankLimitSelector)); // Disengage bar. if (_offsets.MCP_DisengageBar.ValueChanged) DisengageBarChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(DisengageBar)); // MCP heading mode (HDG/TRK). if (_offsets.MCP_HDGDial_Mode.ValueChanged) MCPHeadingModeChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(MCPHeadingMode)); // MCP vertical speed mode. if (_offsets.MCP_VSDial_Mode.ValueChanged) MCPVerticalSpeedModeChanged(this, new PMDG777ValueChangedEventArgs(MCPVerticalSpeedMode)); // Auto pilot annunciators. if (_offsets.MCP_annunAP[0].ValueChanged) LeftAutoPilotAnnunciatorChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(LeftAutoPilotAnnunciator)); if (_offsets.MCP_annunAP[1].ValueChanged) RightAutoPilotAnnunciatorChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(RightAutoPilotAnnunciator)); // Auto throttle annunciator. if (_offsets.MCP_annunAT.ValueChanged) AutoThrottleAnnunciatorChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(AutoThrottleAnnunciator)); // LNav annunciator. if (_offsets.MCP_annunLNAV.ValueChanged) LNavAnnunciatorChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(LNavAnnunciator)); // VNav annunciator. if (_offsets.MCP_annunVNAV.ValueChanged) VNavAnnunciatorChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(VNavAnnunciator)); // Level change annunciator. if (_offsets.MCP_annunFLCH.ValueChanged) LevelChangeAnnunciatorChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(LevelChangeAnnunciator)); // Heading hold annunciator. if (_offsets.MCP_annunHDG_HOLD.ValueChanged) HeadingHoldAnnunciatorChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(HeadingHoldAnnunciator)); // Vertical speed annunciator. if (_offsets.MCP_annunVS_FPA.ValueChanged) VerticalSpeedAnnunciatorChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(VerticalSpeedAnnunciator)); // Altitude hold annunciator. if (_offsets.MCP_annunALT_HOLD.ValueChanged) AltitudeHoldAnnunciatorChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(AltitudeHoldAnnunciator)); // Localizer hold annunciator. if (_offsets.MCP_annunLOC.ValueChanged) LocalizerAnnunciatorChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(LocalizerAnnunciator)); // Approach mode annunciator. if (_offsets.MCP_annunAPP.ValueChanged) ApproachModeAnnunciatorChanged?.Invoke(this, new PMDG777ValueChangedEventArgs(ApproachModeAnnunciator)); } // Event implementations. #region private void AnnounceParkingBrake(object sender, PMDG777ValueChangedEventArgs e) { Task.Run(() => AudioManager.Output($"Parking brake {e.NewValue.Value}")); } private void AnnounceFlaps(object sender, PMDG777ValueChangedEventArgs e) { Task.Run(() => AudioManager.Output($"Flaps {e.NewValue.Value}")); } #endregion private void AttachEventHandlers() { #region ParkingBrakeChanged += AnnounceParkingBrake; FlapsChanged += AnnounceFlaps; MCPSpeedChanged += AnnounceMCPSpeed; MCPSpeedInterveneChanged += AnnounceMCPSpeedIntervene; MCPHeadingChanged += AnnounceMCPHeading; MCPAltitudeChanged += AnnounceMCPAltitude; MCPVerticalSpeedChanged += AnnounceMCPVerticalSpeed; MCPFPAChanged += AnnounceMCPFPA; MCPVerticalSpeedInterveneChanged += AnnounceMCPVerticalSpeedIntervene; LeftFlightDirectorSwitchChanged += AnnounceLeftFlightDirectorSwitch; RightFlightDirectorSwitchChanged += AnnounceRightFlightDirectorSwitch; LeftAutoThrottleSwitchChanged += AnnounceLeftAutothrottleSwitch; RightAutoThrottleSwitchChanged += AnnounceRightAutoThrottleSwitch; BankLimitSelectorChanged += AnnounceBankLimitSelector; DisengageBarChanged += AnnounceDisengageBar; MCPHeadingModeChanged += AnnounceMCPHeadingMode; MCPVerticalSpeedModeChanged += AnnounceMCPVerticalSpeedMode; LeftAutoPilotAnnunciatorChanged += AnnounceLeftAutoPilotAnnunciator; RightAutoPilotAnnunciatorChanged += AnnounceRightAutoPilotAnnunciator; AutoThrottleAnnunciatorChanged += AnnounceAutoThrottleAnnunciator; LNavAnnunciatorChanged += AnnounceLNavAnnunciator; VNavAnnunciatorChanged += AnnounceVNavAnnunciator; LevelChangeAnnunciatorChanged += AnnounceLevelChangeAnnunciator; HeadingHoldAnnunciatorChanged += AnnounceHeadingHoldAnnunciator; VerticalSpeedAnnunciatorChanged += AnnounceVerticalSpeedAnnunciator; AltitudeHoldAnnunciatorChanged += AnnounceAltitudeHoldAnnunciator; LocalizerAnnunciatorChanged += AnnounceLocalizerAnnunciator; ApproachModeAnnunciatorChanged += AnnounceApproachModeAnnunciator; #endregion } } } using tfm.Airplanes.Shared; using FSUIPC; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using tfm.Airplanes.PMDG777.Shared; using tfm.Utilities; namespace tfm.Airplanes.PMDG777 { public partial class PMDG777: Airplane { // Properties #region // MCP speed. public KeyValuePair<object, string> MCPSpeed { get { KeyValuePair<object, string> item = new KeyValuePair<object, string>(); if (_offsets.MCP_IASMach.Value < 10) item = new KeyValuePair<object, string>(_offsets.MCP_IASMach.Value, "mach"); else item = new KeyValuePair<object, string>(_offsets.MCP_IASMach.Value, "indicated"); return item; } } // Speed intervene. public KeyValuePair<object, string> MCPSpeedIntervene { get { KeyValuePair<object, string> item = new KeyValuePair<object, string>(); item = _offsets.MCP_IASBlan.Value == 0 ? new KeyValuePair<object, string>(0, "on") : new KeyValuePair<object, string>(1, "off"); return item; } } // MCP heading. public KeyValuePair<object, string> MCPHeading { get => new KeyValuePair<object, string>(_offsets.MCP_Heading.Value, _offsets.MCP_Heading.Value.ToString()); } // MCP altitude public KeyValuePair<object, string> MCPAltitude { get => new KeyValuePair<object, string>(_offsets.MCP_Altitude.Value, _offsets.MCP_Altitude.Value.ToString()); } // MCP vertical speed. public KeyValuePair<object, string> MCPVerticalSpeed { get => new KeyValuePair<object, string>(_offsets.MCP_VertSpeed.Value, _offsets.MCP_VertSpeed.Value.ToString()); } // MCP FPA. public KeyValuePair<object, string> MCPFPA { get => new KeyValuePair<object, string>(_offsets.MCP_FPA.Value, _offsets.MCP_FPA.Value.ToString()); } // MCP vertical speed intervene public KeyValuePair<object, string> MCPVerticalSpeedIntervene { get { KeyValuePair<object, string> item = new KeyValuePair<object, string>(); if (_offsets.MCP_VertSpeedBlank.Value == 0) item = new KeyValuePair<object, string>(0, "on"); else item = new KeyValuePair<object, string>(1, "off"); return item; } } // Left flight director switch. public KeyValuePair<object, string> LeftFlightDirectorSwitch { get { KeyValuePair<object, string> item = new KeyValuePair<object, string>(); if (_offsets.MCP_FD_Sw_On[0].Value == 0) item = new KeyValuePair<object, string>(0, "off"); else item = new KeyValuePair<object, string>(1, "on"); return item; } } // Right flight director switch. public KeyValuePair<object, string> RightFlightDirectorSwitch { get { KeyValuePair<object, string> item = new KeyValuePair<object, string>(); if (_offsets.MCP_FD_Sw_On[1].Value == 0) item = new KeyValuePair<object, string>(0, "off"); else item = new KeyValuePair<object, string>(1, "on"); return item; } } // Left auto throttle switch. public KeyValuePair<object, string> LeftAutoThrottleSwitch { get => _offsets.MCP_ATArm_Sw_On[0].Value == 0 ? new KeyValuePair<object, string>(0, "disarmed") : new KeyValuePair<object, string>(1, "armed"); } // Right auto throttle switch. public KeyValuePair<object, string> RightAutoThrottleSwitch { get => _offsets.MCP_ATArm_Sw_On[1].Value == 0 ? new KeyValuePair<object, string>(0, "disarmed") : new KeyValuePair<object, string>(1, "armed"); } // Bank limit selector. public KeyValuePair<object, string> BankLimitSelector { get { KeyValuePair<object, string> item = new KeyValuePair<object, string>(); if (_offsets.MCP_BankLimitSel.Value == 0) item = new KeyValuePair<object, string>(0, "auto"); else if (_offsets.MCP_BankLimitSel.Value == 1) item = new KeyValuePair<object, string>(1, "5"); else if (_offsets.MCP_BankLimitSel.Value == 2) item = new KeyValuePair<object, string>(2, "10"); else if (_offsets.MCP_BankLimitSel.Value == 3) item = new KeyValuePair<object, string>(3, "15"); else if (_offsets.MCP_BankLimitSel.Value == 4) item = new KeyValuePair<object, string>(4, "20"); else if (_offsets.MCP_BankLimitSel.Value == 5) item = new KeyValuePair<object, string>(5, "25"); return item; } } // Disengage bar. public KeyValuePair<object, string> DisengageBar { get => _offsets.MCP_DisengageBar.Value == 0 ? new KeyValuePair<object, string>(0, "off") : new KeyValuePair<object, string>(1, "on"); } // MCP heading mode (HDG/TRK). public KeyValuePair<object, string> MCPHeadingMode { get => _offsets.MCP_HDGDial_Mode.Value == 0 ? new KeyValuePair<object, string>(0, "HDG") : new KeyValuePair<object, string>(1, "TRK"); } // MCP vertical speed mode. public KeyValuePair<object, string> MCPVerticalSpeedMode { get => _offsets.MCP_VSDial_Mode.Value == 0 ? new KeyValuePair<object, string>(0, "VS") : new KeyValuePair<object, string>(1, "FPA"); } // Left Auto pilot annunciator public KeyValuePair<object, string> LeftAutoPilotAnnunciator { get => _offsets.MCP_annunAP[0].Value == 0 ? new KeyValuePair<object, string>(0, "off") : new KeyValuePair<object, string>(1, "on"); } // Right auto pilot annunciator public KeyValuePair<object, string> RightAutoPilotAnnunciator { get => _offsets.MCP_annunAP[1].Value == 0 ? new KeyValuePair<object, string>(0, "off") : new KeyValuePair<object, string>(1, "on"); } // Auto throttle annunciator. public KeyValuePair<object, string> AutoThrottleAnnunciator { get => _offsets.MCP_annunAT.Value == 0 ? new KeyValuePair<object, string>(0, "off") : new KeyValuePair<object, string>(1, "on"); } // Lnav public KeyValuePair<object, string> LNavAnnunciator { get => _offsets.MCP_annunLNAV.Value == 0 ? new KeyValuePair<object, string>(0, "off") : new KeyValuePair<object, string>(1, "on"); } // VNav annunciator. public KeyValuePair<object, string> VNavAnnunciator { get => _offsets.MCP_annunVNAV.Value == 0 ? new KeyValuePair<object, string>(0, "off") : new KeyValuePair<object, string>(1, "on"); } // Level change annunciator. public KeyValuePair<object, string> LevelChangeAnnunciator { get => _offsets.MCP_annunFLCH.Value == 0 ? new KeyValuePair<object, string>(0, "off") : new KeyValuePair<object, string>(1, "on"); } // Heading hold annunciator. public KeyValuePair<object, string> HeadingHoldAnnunciator { get => _offsets.MCP_annunHDG_HOLD.Value == 0 ? new KeyValuePair<object, string>(0, "off") : new KeyValuePair<object, string>(1, "on"); } // VS/FPA annunciator. public KeyValuePair<object, string> VerticalSpeedAnnunciator { get => _offsets.MCP_annunVS_FPA.Value == 0 ? new KeyValuePair<object, string>(0, "off") : new KeyValuePair<object, string>(1, "on"); } // Altitude hold annunciator. public KeyValuePair<object, string> AltitudeHoldAnnunciator { get => _offsets.MCP_annunALT_HOLD.Value == 0 ? new KeyValuePair<object, string>(0, "off") : new KeyValuePair<object, string>(1, "on"); } // Localizer annunciator. public KeyValuePair<object, string> LocalizerAnnunciator { get => _offsets.MCP_annunLOC.Value == 0 ? new KeyValuePair<object, string>(0, "off") : new KeyValuePair<object, string>(1, "on"); } // Approach mode annunciator. public KeyValuePair<object, string> ApproachModeAnnunciator { get => _offsets.MCP_annunAPP.Value == 0 ? new KeyValuePair<object, string>(0, "off") : new KeyValuePair<object, string>(1, "on"); } #endregion // Event definitions #region public EventHandler<PMDG777ValueChangedEventArgs> MCPSpeedChanged; public EventHandler<PMDG777ValueChangedEventArgs> MCPSpeedInterveneChanged; public EventHandler<PMDG777ValueChangedEventArgs> MCPHeadingChanged; public EventHandler<PMDG777ValueChangedEventArgs> MCPAltitudeChanged; public EventHandler<PMDG777ValueChangedEventArgs> MCPVerticalSpeedChanged; public EventHandler<PMDG777ValueChangedEventArgs> MCPFPAChanged; public EventHandler<PMDG777ValueChangedEventArgs> MCPVerticalSpeedInterveneChanged; public EventHandler<PMDG777ValueChangedEventArgs> LeftFlightDirectorSwitchChanged; public EventHandler<PMDG777ValueChangedEventArgs> RightFlightDirectorSwitchChanged; public EventHandler<PMDG777ValueChangedEventArgs> LeftAutoThrottleSwitchChanged; public EventHandler<PMDG777ValueChangedEventArgs> RightAutoThrottleSwitchChanged; public EventHandler<PMDG777ValueChangedEventArgs> BankLimitSelectorChanged; public EventHandler<PMDG777ValueChangedEventArgs> DisengageBarChanged; public EventHandler<PMDG777ValueChangedEventArgs> MCPHeadingModeChanged; public EventHandler<PMDG777ValueChangedEventArgs> MCPVerticalSpeedModeChanged; public EventHandler<PMDG777ValueChangedEventArgs> LeftAutoPilotAnnunciatorChanged; public EventHandler<PMDG777ValueChangedEventArgs> RightAutoPilotAnnunciatorChanged; public EventHandler<PMDG777ValueChangedEventArgs> AutoThrottleAnnunciatorChanged; public EventHandler<PMDG777ValueChangedEventArgs> LNavAnnunciatorChanged; public EventHandler<PMDG777ValueChangedEventArgs> VNavAnnunciatorChanged; public EventHandler<PMDG777ValueChangedEventArgs> LevelChangeAnnunciatorChanged; public EventHandler<PMDG777ValueChangedEventArgs> HeadingHoldAnnunciatorChanged; public EventHandler<PMDG777ValueChangedEventArgs> VerticalSpeedAnnunciatorChanged; public EventHandler<PMDG777ValueChangedEventArgs> AltitudeHoldAnnunciatorChanged; public EventHandler<PMDG777ValueChangedEventArgs> LocalizerAnnunciatorChanged; public EventHandler<PMDG777ValueChangedEventArgs> ApproachModeAnnunciatorChanged; #endregion // Event implementations. #region private async void AnnounceMCPSpeed(object sender, PMDG777ValueChangedEventArgs e) { if (isInitialized) { await Task.Run(() => { float speed = (float)e.NewValue.Key; string announcement = string.Empty; announcement = speed < 10 ? $"{e.NewValue.Value} {speed}" : $"{speed} {e.NewValue.Value}"; AudioManager.Output(announcement); }); } } private void AnnounceMCPSpeedIntervene(object sender, PMDG777ValueChangedEventArgs e) { Task.Run(() => AudioManager.Output($"Speed intervene {e.NewValue.Value}")); } private void AnnounceMCPHeading(object sender, PMDG777ValueChangedEventArgs e) { Task.Run(() => AudioManager.Output(e.NewValue.Value)); } private async void AnnounceMCPAltitude(object sender, PMDG777ValueChangedEventArgs e) { await Task.Run(() => AudioManager.Output(MCPAltitude.Value)); } private async void AnnounceMCPVerticalSpeed(object sender, PMDG777ValueChangedEventArgs e) { await Task.Run(() => AudioManager.Output(MCPVerticalSpeed.Value)); } private async void AnnounceMCPFPA(object sender, PMDG777ValueChangedEventArgs e) { await Task.Run(() => AudioManager.Output(e.NewValue.Value)); } private async void AnnounceMCPVerticalSpeedIntervene(object sender, PMDG777ValueChangedEventArgs e) { await Task.Run(() => AudioManager.Output($"Vertical speed intervene {e.NewValue.Value}")); } private async void AnnounceLeftFlightDirectorSwitch(object sender, PMDG777ValueChangedEventArgs e) { await Task.Run(() => AudioManager.Output($"Left flight director {e.NewValue.Value}")); } private async void AnnounceRightFlightDirectorSwitch(object sender, PMDG777ValueChangedEventArgs e) { await Task.Run(() => AudioManager.Output($"Right flight director {e.NewValue.Value}")); } private async void AnnounceLeftAutothrottleSwitch(object sender, PMDG777ValueChangedEventArgs e) { await Task.Run(() => AudioManager.Output($"Left auto throttle {e.NewValue.Value}")); } private async void AnnounceRightAutoThrottleSwitch(object sender, PMDG777ValueChangedEventArgs e) { await Task.Run(() => AudioManager.Output($"Right auto throttle {e.NewValue.Value}")); } private async void AnnounceBankLimitSelector(object sender, PMDG777ValueChangedEventArgs e) { await Task.Run(() => AudioManager.Output($"Bank limit {e.NewValue}")); } private async void AnnounceDisengageBar(object sender, PMDG777ValueChangedEventArgs e) { await Task.Run(() => AudioManager.Output($"Disengage bar {e.NewValue.Value}")); } private async void AnnounceMCPHeadingMode(object sender, PMDG777ValueChangedEventArgs e) { await Task.Run(() => AudioManager.Output($"MCP heading mode {e.NewValue.Value}")); } private async void AnnounceMCPVerticalSpeedMode(object sender, PMDG777ValueChangedEventArgs e) { await Task.Run(() => AudioManager.Output($"Vertical speed mode {e.NewValue.Value}")); } private async void AnnounceLeftAutoPilotAnnunciator(object sender, PMDG777ValueChangedEventArgs e) { if (tfm.Properties.pmdg777_offsets.Default.MCP_annunAP1) { await Task.Run(() => AudioManager.Output($"Left auto pilot annunciator {e.NewValue.Value}")); } } private async void AnnounceRightAutoPilotAnnunciator(object sender, PMDG777ValueChangedEventArgs e) { if (tfm.Properties.pmdg777_offsets.Default.MCP_annunAP2) { await Task.Run(() => AudioManager.Output($"Right auto pilot annunciator {e.NewValue.Value}")); } } private async void AnnounceAutoThrottleAnnunciator(object sender, PMDG777ValueChangedEventArgs e) { if (tfm.Properties.pmdg777_offsets.Default.MCP_annunAT) { await Task.Run(() => AudioManager.Output($"Auto throttle annunciator {e.NewValue.Value}")); } } private async void AnnounceLNavAnnunciator(object sender, PMDG777ValueChangedEventArgs e) { if (tfm.Properties.pmdg777_offsets.Default.MCP_annunLNAV) { await Task.Run(() => AudioManager.Output($"LNav {e.NewValue.Value}")); } } private async void AnnounceVNavAnnunciator(object sender, PMDG777ValueChangedEventArgs e) { if (tfm.Properties.pmdg777_offsets.Default.MCP_annunVNAV) { await Task.Run(() => AudioManager.Output($"VNav {e.NewValue.Value}")); } } private async void AnnounceLevelChangeAnnunciator(object sender, PMDG777ValueChangedEventArgs e) { if (tfm.Properties.pmdg777_offsets.Default.MCP_annunFLCH) { await Task.Run(() => AudioManager.Output($"Level change {e.NewValue.Value}")); } } private async void AnnounceHeadingHoldAnnunciator(object sender, PMDG777ValueChangedEventArgs e) { if (tfm.Properties.pmdg777_offsets.Default.MCP_annunHDG_HOLD) await Task.Run(() => AudioManager.Output($"Heading hold {e.NewValue.Value}")); } private async void AnnounceVerticalSpeedAnnunciator(object sender, PMDG777ValueChangedEventArgs e) { if (tfm.Properties.pmdg777_offsets.Default.MCP_annunVS_FPA) await Task.Run(() => AudioManager.Output($"Vertical speed {e.NewValue.Value}")); } private async void AnnounceAltitudeHoldAnnunciator(object sender, PMDG777ValueChangedEventArgs e) { if (tfm.Properties.pmdg777_offsets.Default.MCP_annunALT_HOLD) await Task.Run(() => AudioManager.Output($"Altitude hold {e.NewValue.Value}")); } private async void AnnounceLocalizerAnnunciator(object sender, PMDG777ValueChangedEventArgs e) { if (tfm.Properties.pmdg777_offsets.Default.MCP_annunLOC) await Task.Run(() => AudioManager.Output($"Localizer hold {e.NewValue.Value}")); } private async void AnnounceApproachModeAnnunciator(object sender, PMDG777ValueChangedEventArgs e) { if (tfm.Properties.pmdg777_offsets.Default.MCP_annunAPP) await Task.Run(() => AudioManager.Output($"Approach mode {e.NewValue.Value}")); } #endregion // Methods for MCP buttons and switches. #region public void SetMCPIndicatedAirspeed(string speedText) { Task.Run(() => { bool isParsed = int.TryParse(speedText, out int speed); if (isParsed) { FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_IAS_SET, speed); } else { throw new InvalidSpeedException("Invalid speed because it is the wrong format or out of range.", speedText); } }); } public void SetMCPMachSpeed(double speed) { Task.Run(() => { int parameter = (int)(speed / 0.001d); FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_MACH_SET, parameter); }); } public void AltitudeIntervene() { Task.Run(() => FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_ALTITUDE_PUSH_SWITCH, ClkL)); } public void VerticalSpeedIntervene() { Task.Run(() => FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_VS_SWITCH, ClkL)); } public void ToggleVNav() { Task.Run(() => { FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_VNAV_SWITCH, ClkL); }); } public void ToggleAltitudeHold() { Task.Run(() => { FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_ALT_HOLD_SWITCH, ClkL); }); } public void ToggleLevelChange() { Task.Run(() => { FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_LVL_CHG_SWITCH, ClkL); }); } public void SetMCPAltitude(string altitudeText) { bool isAltitudeParsed = ushort.TryParse(altitudeText, out ushort altitude); if (isAltitudeParsed) { FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_ALT_SET, altitude); } else { throw new InvalidAltitudeException("Altitude is incorrect format or out of range.", altitudeText); } } public void ToggleVerticalSpeedMode() { Task.Run(() => { FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_VS_FPA_SWITCH, ClkL); }); } public void SetMCPVerticalSpeed(string verticalSpeedText) { Task.Run(() => { bool isParsed = ushort.TryParse(verticalSpeedText, out ushort verticalSpeed); if (isParsed) { FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_VS_SET, (verticalSpeed + 10000)); } else { throw new InvalidAltitudeException("Vertical speed is an invalid format or out of range.", verticalSpeedText); } }); } public void SetMCPFPA(string fpaText) { Task.Run(() => { bool isParsed = double.TryParse(fpaText, out double FPA); if (isParsed) { FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_FPA_SET, ((int)((FPA + 10) / 0.1))); } else { throw new InvalidAltitudeException("Invalid FPA because it is an invalid format or out of range.", fpaText); } }); } public void SetMCPHeading(string headingText) { Task.Run(() => { bool isParsed = ushort.TryParse(headingText, out ushort heading); if (isParsed) { FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_HDGTRK_SET, heading); } else { throw new InvalidHeadingException("Invalid heading because of wrong format or is out of range.", headingText); } }); } public void HeadingIntervene() { Task.Run(() => FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_HEADING_PUSH_SWITCH, ClkL)); } public void ToggleHeadingHold() { Task.Run(() => { FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_HDG_HOLD_SWITCH, ClkL); }); } public void ToggleLNav() { Task.Run(() => { FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_LNAV_SWITCH, ClkL); }); } public void ToggleMCPHeadingMode() { Task.Run(() => { FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_HDG_TRK_SWITCH, ClkL); }); } public void ToggleLeftFlightDirector() { Task.Run(() => { var action = LeftFlightDirectorSwitch.Value == "off" ? ClkL : ClkR; FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_FD_SWITCH_L, action); }); } public void ToggleRightFlightDirector() { Task.Run(() => { var action = RightFlightDirectorSwitch.Value == "off" ? ClkL : ClkR; FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_FD_SWITCH_R, action); }); } public void ToggleLeftAutoPilot() { Task.Run(() => { FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_AP_L_SWITCH, ClkL); }); } public void ToggleRightAutoPilot() { Task.Run(() => { FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_AP_R_SWITCH, ClkL); }); } public void ToggleLeftAutoThrottle() { Task.Run(() => { var action = LeftAutoThrottleSwitch.Value == "disarmed" ? ClkL : ClkR; FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_AT_ARM_SWITCH_L, action); }); } public void ToggleRightAutoThrottle() { Task.Run(() => { var action = RightAutoThrottleSwitch.Value == "disarmed" ? ClkL : ClkR; FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_AT_ARM_SWITCH_R, action); }); } public void ToggleAutoThrottle() { Task.Run(() => { FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_AT_SWITCH, ClkL); }); } public void ToggleContinuousThrust() { Task.Run(() => FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_CLB_CON_SWITCH, ClkL)); } public void ToggleIASMach() { Task.Run(() => FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_IAS_MACH_SWITCH, ClkL)); } public void ToggleSpeedIntervene() { Task.Run(() => FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_SPEED_PUSH_SWITCH, ClkL)); } public void ToggleDisengageBar() { Task.Run(() => { var action = DisengageBar.Value == "off" ? ClkL : ClkR; FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_DISENGAGE_BAR, action); }); } public void ToggleHDGTRK() { Task.Run(() => FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_HDG_TRK_SWITCH, ClkL)); } public void ToggleLocalizerHold() { Task.Run(() => FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_LOC_SWITCH, ClkL)); } public void ToggleApproachMode() { Task.Run(() => FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_APP_SWITCH, ClkL)); } public void CycleBankLimiter() { Task.Run(() => { var counter = (byte)BankLimitSelector.Key; var parameter = counter == 4 ? 0 : counter + 1; FSUIPCConnection.SendControlToFS(PMDG_777X_Control.EVT_MCP_BANK_ANGLE_SELECTOR, parameter); }); } #endregion } }
  13. I am using version 3.12 of your library.
  14. Hi, Here is what I find with 'testing'. * Offsets work in a simple test app (WPF). * Offsets work in the previous version of our app. * In the new design (wpf/dotnet 8.0), offsets work. However, it seems as though they don't receive an initial value until the timer can tick at least once. When the process timer ticks, the initial value doesn't change, so nothing happens. To demonstrate, I will explain what happens. The example is the aircraft title (0x3d00). When the base Aircraft instance is created through inherited classes (the PMDG737/777). The class creates an instance of Offset<string>(0x3d00, 256) and assigns it to a variable _title. In the constructor of base Airplane class, I start a timer that checks for _title.ValueChanged. If it has, perform some logging to track the value. In this example, the _title offset is always empty, so nothing ever happens. Even though process is called elsewhere (connection manager), the offset's initial values are not loaded. On the other hand, calling Process outside a timer while in the constructor seems to load the initial data into the offsets. I'm confused with the entire issue. I am not using DispatcherTimer in any of my non UI classes. Could this be causing the problem? * An instance of Airplane is created. * Start a process timer. * In the timer event, check for changes to the title offset. * Do an action if it changes. * The title offset is empty because it never arrives at its MSFS provided value.
  15. This still didn't work. I can send you the entire app for a lookover if it would help.
  16. I don't think I understand how things work here. In the previous version of our app, we used static classes in a WPF app. Process() was called in the Application class where the rest of the code lived. Now, we are trying to convert it from a procedural based codebase to an object oriented one. In previous posts, I had problems with connections and getting values from offsets. It still happens to be that way, but now with the PMDG aircraft for MSFS. I think it is more with my understanding of things than the FSUIPC connection. * I have a static class as my connection manager class. It connects to MSFS and runs FSUIPCConnection.Process(). Is this global for the entire application, or only the connection manager class? * Do I have to open the connection and run FSUIPCConnection.Process for each non static class? * At what point would I call an event after the offset value changes? Simple example follows: ConnectionManager.connect() has already been called in the MainWindow, and the connection is verified. FSUIPCConnection.Process() is already running. In a non static class, I have the following: using tfm.Utilities; using FSUIPC; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace tfm.Airplanes { public class PMDG737: Airplane { private PMDG_737_NGX_Offsets _Offsets = new PMDG_737_NGX_Offsets(); public PMDG737() { FSUIPCConnection.Process(); System.Timers.Timer _timer = new() { Interval = 300, AutoReset = true, }; _timer.Elapsed += (s, e) => { try { AudioManager.Output(_Offsets.MAIN_TEFlapsNeedle[0].Value.ToString()); FSUIPCConnection.Process(); _Offsets.RefreshData(); } catch (FSUIPCException ex) { } }; _timer.Start(); } } } No matter what I try, the value is always 0. Changing the controls in MSFS doesn't give me the right value in this class either. An instance of PMDG737 is created inside a menuitem.click event in the main window, then assigned to a static property in another class. What am I missing?
  17. 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()); } } } } } }
  18. Since the PMDG777 for MSFS is out, how long it will take to add the offsets?
  19. To be clear, the connection check only allows me to run TFM at this point. In normal operation, it would detect fuel tanks, setup fuel services in TFM, then write to the log. Since there isn't a TFM log entry about fuel tanks, and removing the check for the connection throws a connection not open error, I assume the problem still exists.
  20. Ok, removed the FSUIPC.Open call and tried again with an open connection and the new library build. I am still not able to detect fuel tanks from payload services. When the check for the open connection is in place, an entry is written to the log about the fuel tanks found. There is nothing in the log about fuel tanks. I even went so far as to delete the entire log file and start from a new one. Same code as above with the following log. [Info]: Creating audio output devices.: [tfm.Utilities.AudioManager.SetupAudio]: [2024-06-22 08:02] [Info]: Creating sound mixer.: [tfm.Utilities.AudioManager.SetupAudio]: [2024-06-22 08:02] [Info]: Starting audio services.: [tfm.Utilities.AudioManager.SetupAudio]: [2024-06-22 08:02] [Info]: Loaded 41552 airports.: [tfm.Utilities.AirportServices.LoadAirportsDatabase]: [2024-06-22 08:02] [Info]: Connected to flight simulator (auto connect).: [tfm.Utilities.ConnectionManager.AutoConnect]: [2024-06-22 08:02] When I remove the connection check, I get an fsuipc connection isn't open error.
  21. I am trying to get the active fuel tanks from payload services. However, it is throwing an exception saying that the fsuipc connection isn't open, when in fact it already is open. I can be sure of it because I am getting data from other offsets, including payload services itself. Any idea what might be going on? Below is the code in question. I put a connection check around it just to get the app to start. I tried putting a new open before it to see if that worked. It does, but the connection manager gets somewhat confused about connection start. private void DetectFuelTanks() { FSUIPCConnection.Open(); 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()); } } } }
  22. I just tried reading offset<double>(0x6020) and got 255.664........ This is in meters at KMSP. When I convert it to feet (*3.281), I get 836.55......... in feet. Which is the correct answer. This particular offset will sit and wait until the value changes before updating itself, and it allows me to pull its initial value. I wonder if 0x3324 is broken in MSFS for some strange reason. When I tried 3324 in P3D, it works as expected. Unfortunately, in MSFS, it is busted. I wonder of the rest of my PFD values are doing the same for that reason? That the offsets are busted... It is weird because older versions of TFM are producing the same behavior (rotating random numbers with the older FSX/P3D offsets).
  23. I did that and the asl, agl, IAS, and altimeter cycle between random values again. If I remove the process call and the group name, everything returns to the original behavior (inital values are all 0s).
  24. Would it make sense in the timer for the PFD class to introduce the process method with a group name of PFD, then assign the PFD groupname to all of the offsets in the pfd class? Or, will I have to do it differently?
  25. So, everything works as expected once the aircraft starts moving. Until then, the PFD reports 0s for everything. Is there a way to get the actual values before it moves?
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use. Guidelines Privacy Policy We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.