Jump to content
The simFlight Network Forums

Paul Henty

Members
  • Posts

    1,729
  • Joined

  • Days Won

    78

Posts posted by Paul Henty

  1. Hi Pete,

     

    I'm trying to send key presses to FSX using the 1070 control sent via offset 0x3110. (FSUIPC Version 4.938f).

     

    In my test I'm using the 'L' key to toggle the lights. According to the logging the key seems to be getting through and is sent, but the lights do not toggle.

     

    Here's me pressing 'L' on the keyboard: - the lights get toggled.

      2202204 KEYDOWN: VK=76, Waiting=0, Repeat=N, Shifts=0
      2202204 .. Key not programmed -- passed on to FS
      2202204 *** EVENT: Cntrl= 65574 (0x00010026), Param= 0 (0x00000000) ALL_LIGHTS_TOGGLE
      2202266 KEYUP: VK=76, Waiting=0

    This is my program sending the 1070 control via 3110. All seems okay and the log is reporting the [L] key press being made, but the lights control is not triggered.

      2205360 WRITE repeated 1 times
      2205360 WRITE0[2788]  3114,   4 bytes: 4C 00 00 00                                      L...
      2205360 WRITE0[2788]  3110,   4 bytes: 2E 04 00 00                                      ....
      2205360 FSUIPC Control Action: Ctrl=1070, Param=76
      2205360 SendKeyToFS(0000004C=[L], KEYDOWN) ctr=0
      2205360 Sending WM_KEYDOWN, Key=76 (Scan code 38), Ctr=1
      2205500 SendKeyToFS(0000004C=[L], KEYUP) ctr=0
      2205500 Sending WM_KEYUP, Key=76 (Scan code 38), Ctr=1
    

    Have I got something wrong here or have I misunderstood what 1070 is meant to do?

     

    Thanks,

     

    Paul

  2. Looking through the control list document there is this control:

     

    FULL_WINDOW_TOGGLE  65553

     

    There seems to be some confusion about if this is P3D only but it's worth a try for FSX.

     

    A dedicated control is much better than sending a key press. If that doesn't work or does something different than the full screen toggle then I'll have a look at the key press route.

     

    Paul

  3. Hi Motus,

     

    I've highlighted where your mistakes are. It looks like you've copied and pasted from the one above, but not changed all the variable names.

     

     

           private Offset<byte[]> fsLocalDateTime = new Offset<byte[]>(0x0238, 10);

            private Offset<byte[]> fsZuluDateTime = new Offset<byte[]>(0x023B, 10);
            private Offset<byte> zmin = new Offset<byte>(0x23c);
            private Offset<byte> zhour = new Offset<byte>(0x23b);
            private Offset<byte> second = new Offset<byte>(0x23a);

     

     private void timer1_Tick(object sender, EventArgs e)
            {
                // Process the default group
                try
                {
                    FSUIPCConnection.Process();

     

                // get local Time

                    short year = BitConverter.ToInt16(fsLocalDateTime.Value, :cool:;
                    DateTime fsTime = new DateTime(year, 1, 1, fsLocalDateTime.Value[0], fsLocalDateTime.Value[1], fsLocalDateTime.Value[2]);
                    short dayNo = BitConverter.ToInt16(fsLocalDateTime.Value, 6);
                    fsTime = fsTime.Add(new TimeSpan(dayNo - 1, 0, 0, 0));
                    this.textBox1.Text = fsTime.ToString("dddd, MMMM dd yyyy hh:mm:ss");

     

     

                 // get Zulu Time
                    year = BitConverter.ToInt16(fsZuluDateTime.Value, :cool:;

                    DateTime fsZuluTime = new DateTime(year, 1, 1, fsZuluDateTime.Value[0], fsZuluDateTime.Value[1], fsZuluDateTime.Value[2]);
                    short zuludayNo = BitConverter.ToInt16(fsZuluDateTime.Value, 6);
                    fsZuluTime = fsTime.Add(new TimeSpan(dayNo - 1, 0, 0, 0)); // Should be fsZuluTime and zuludayNo
                    this.textBox2.Text = fsTime.ToString("dddd, MMMM dd yyyy hh:mm:ss"); // Should be fsZuluTime

               

               // or like this

                    year = BitConverter.ToInt16(fsZuluDateTime.Value, :cool:;
                    DateTime fsZuluTimeBis = new DateTime(year, 1, 1, zhour.Value, zmin.Value, second.Value);

                    this.textBox3.Text = fsZuluTimeBis.ToString("dddd, MMMM dd yyyy hh:mm:ss");

                }

           }

     

     

     

    Paul

  4. Two things to check:

     

    1. The FSUIPCClient.dll is in the same folder as your .exe. All supporting DLLs must be placed alongside your .exe.

     

    2. If you've used the 'release' build profile, make sure it's also set to compile to a 32bit (x86) .exe. Otherwise it will run as a 64bit .exe and won't be able to load the FSUIPCClient.dll.

     

    for #2:

     

    go to the properties of your project

    go to the 'build' tab on the left,  

    select the 'release' profile from the 'configuration' dropdown near the top.

    Make sure the 'platform target' is set to 'x86'.

     

    Paul

  5. The usual way to handle this is to attempt the connection and catch the error thrown if FSUIPC is not running. This could be because the user doesn't have it installed or because FSX isn't loaded yet. Either way, you can display a message or a status in your application to let the user know that your application cannot connect to FSUIPC.

     

    If you have a look at my C# Example Application there is code in there that attempts a connection and if it doesn't find one it will keep trying. Meanwhile, the connection status is displayed on the form. If the connection is ever lost it goes back to the looking state and the main functionality is suspended until the connection comes back.

     

    Paul 

  6. I've reread your post where you did the logging. I think I misread it earlier. It seems to me now that line I quoted was not sent by PMDG but by your code.

     

    What you need to do is turn on the event logging in FSUIPC and press the keys on the CDU with your mouse and see what controls and parameters PMDG is sending. That will help us replicate the correct messages.

     

    Paul

  7.  

    From the FSUIPC debugger I can see that

    #define MOUSE_FLAG_LEFTSINGLE    0x20000000

     

    0x20000000 is the left mouse click event being fired.

     

    But how does one use that in this:

                // Press MENU button

                sendControl.Value = THIRD_PARTY_EVENT_ID_MIN + 551;
                FSUIPCConnection.Process("SendControl");
     

     

    I've really no idea how you would use the mouse flags.

     

    Controls do have parameters, so my first thought was that the mouse flags could be send as the parameter to the control. However, looking at your log from FSUIPC it clearly shows the menu control being sent by PMDG without any parameter (it's 0).

     

    2119648 *** EVENT: Cntrl= 70183 (0x00011227), Param= 0 (0x00000000)  <70183> MENU Button press only

     

     

    If you want to try sending the mouse flag you can but I don't have much expectation that it will work:

    To do this you need another offset. This one MUST be declared before the sendControl offset, or it won't work properly (you can set the values in any order you like).

    // Order is important!
    private Offset<int> controlParameter = new Offset<int>("SendControl", 0x3114, true); // Both in the SendControl group, not a typo!
    private Offset<int> sendControl = new Offset<int>("SendControl", 0x3110, true);
    private readonly int MOUSE_FLAG_LEFTSINGLE  = 0x20000000;
    
                // Press MENU button            
                sendControl.Value = THIRD_PARTY_EVENT_ID_MIN + 551;
                controlParameter.Value = MOUSE_FLAG_LEFTSINGLE;
                FSUIPCConnection.Process("SendControl");
    
    If that doesn't work then you really need to ask PMDG directly or on their support forums how these CDU controls are meant to be used.
     
    Paul
  8. Hi Ralph,

     

    Here is the updated version of the DLL (Version 3.0 beta) containing the sub menu functionality.

     

    To update, either overwrite your existing DLL and XML file with the new ones, or you can put them in a new folder and redo the reference in the C# project.

     

    The sub menu functionality is the same as the main menu except you have to specify the parent menu:

     

    Here's some sample code which should be self-explanatory. In all other respects (e.g. responding to the user clicking them) the sub menus are exactly the same as the normal menu. I refer you back to the main 2.4 thread.

                ui = FSUIPCConnection.UserInputServices;
                // Add our main menu items
                // The first parameter is the key and can be whatever you want.
                // Second is the text to display
                // Third is whether to pause FS on selection
                ui.AddMenuItem("MenuA", "Menu A", false);
                ui.AddMenuItem("MenuB", "Menu B", false);
                ui.AddMenuItem("MenuC", "Menu C", false);
                // Adding sub menus to menu B
                // First is the ID of this sub menu
                // Second is the ID of the parent menu
                // Third is the test to display
                ui.AddSubMenuItem("SubMenuB1", "MenuB", "Sub B1");
                ui.AddSubMenuItem("SubMenuB2", "MenuB", "Sub B2");
                

    As an aside, I've improved the way the payload stations and fuel tanks work as you can now hold on to a reference to a particular tank or payload station between calls to RefreshData(). Before it was recreating new tank and payload objects forcing users to get new copies from PayloadServices after RefreshData(). It is still the case that you need to call RefreshData() at least once before any fuel tanks are available. I couldn't see an elegant way around that.

     

    Paul

    FSUIPCClient3.0_BETA.zip

  9. Thanks very much for the donation, it's much appreciated.

     

    I can't see anything wrong with your code. I can't test it properly because I don't have the PMDG 737.

     

    I've tested sending a normal 'fsx' control (parking brakes toggle) and it's working okay. Maybe you can adding this to the end of your controls and see if this one reacts.

               sendControl.Value = 65752; // parking brakes
                FSUIPCConnection.Process("SendControl");

    If this doesn't work on the 737NGX then try on a stock FS aircraft, just to prove the code/dll/fsuipc is working okay.

     

    If it's just the 737NGX CDU events that aren't working, try enabling the event (non axis) logging in FSUIPC. You can show the log in a separate console window to get real time feedback. Then press the buttons on the CDU and see what events are being triggered. Make sure they are the ones you are expecting. You can also run your code and compare the events fired manually with the ones your code it firing.

     

    Paul

  10. Thanks for that Pete, I didn't know about those control for sending Key Presses.

     

    Ralph - here's an example of sending the key presses for your sequence:

     

    First you need to declare the offset to send the control to (0x3110). Here i've put it in it's own group and made it write only. Also I've declared the constant for the THIRD_PARTY_EVENT_ID_MIN value to make things easier.

    private Offset<int> sendControl = new Offset<int>("SendControl", 0x3110, true);
    private readonly int THIRD_PARTY_EVENT_ID_MIN = 0x00011000; // equal to 69632
    

    Then to send the sequence you need this:

    // Press MENU button
    sendControl.Value = THIRD_PARTY_EVENT_ID_MIN + 551;
    FSUIPCConnection.Process("SendControl");
    
    // Press FS ACTIONS> LSK5R
    sendControl.Value = THIRD_PARTY_EVENT_ID_MIN + 544;
    FSUIPCConnection.Process("SendControl");
    
    // Press <FUEL LSK1L
    sendControl.Value = THIRD_PARTY_EVENT_ID_MIN + 534;
    FSUIPCConnection.Process("SendControl");
    
    // Press digit 3
    sendControl.Value = THIRD_PARTY_EVENT_ID_MIN + 563;
    FSUIPCConnection.Process("SendControl");
    
    // Press digit 4
    sendControl.Value = THIRD_PARTY_EVENT_ID_MIN + 564;
    FSUIPCConnection.Process("SendControl");
    
    Etc...

    You could go to the trouble of declaring all the constants as well as the 'min' constant. You can then use those variable names instead of using the '+' syntax all the time. e.g.

    private readonly int EVT_CDU_L_4  = THIRD_PARTY_EVENT_ID_MIN + 564;
    

    then you can just use this to set the control value. It will make the code much more readable.

    sendControl.Value = EVT_CDU_L_4;
    

    Paul

  11. The DLL has nothing specific, but FSUIPC has a key send facility at offset 0x3200. It seems to be a 'front' for a normal Win32 SendMessage() however, so unless you're familiar with the Win32 API and working with bits and bytes it might be a bit of a challenge. The documentation for the key down message is here: (You'll need this to know what to set as the Iparam and Wparam).

     

    http://msdn.microsoft.com/en-us/library/windows/desktop/ms646280%28v=vs.85%29.aspx

     

    You'll also need to manage each key up as well.

     

    I wonder if the PMDG interface has a more direct way of simulating key presses for the CDU through a flight sim 'control' (aka 'events')? It's worth looking in the PMDG SDK for the control list (I think it's in the form of a C header file). This would be much easier than sending actual keyboard presses into the Flight Sim windows process.. If it does exist then report back and we'll go from there.

     

    If they don't have such a control then I could put a feature in the DLL to send real key presses to FSX so that you don't have to deal directly with the Win32 API parameters. It will make it much simpler.

     

    Paul

  12. new Offset<string>("info", 0x3D00, 24);
    

    Yes, the 24 is the number of bytes to read. This is only required for certain offset types where the length cannot be determined from the type itself. So with 'int' for example you don't need to specify the length because an 'int' is always 4 bytes. For strings and arrays the dll needs some extra info as strings don't have a fixed length.

     

    The "info" part is an example of the grouping feature. You don't have to use grouping, but it can make sense when you don't want to read some offsets as often as others. Typically in applications there is data that needs to be updated frequently (e.g. aircraft position, airspeed etc) and other data that may only be required infrequently or just once. To avoid reading more data than is required you can place offsets in named groups. The names can be anything you like.

     

    You can then choose when to Process() the different groups by passing the name to the Process() method as you've seen. You can also process multiple groups at once, see the user guide for how to do that.

     

    You could declare the offset like this:

    private Offset<string> aircraftName = new Offset<string>(0x3D00, 24);

    and then it will work with the normal Process().

     

    It's really up to you how, or if, you use the grouping.

     

    Paul

  13. Thanks for the information.

     

    I've just run your code here and the reason you're not reading back the new fuel level is that the refresh is too soon. It seems that FSX or FSUIPC needs a bit of time to adjust to the new values in the fuel tank. If you put a pause between them then it works as expected.

                // Get a reference to save so much typing later...
                PayloadServices ps = FSUIPCConnection.PayloadServices;
                // Get the latest data from FSUIPC
                ps.RefreshData();
                // Display centre tank percentage
                string str1 = ps.GetFuelTank(FSFuelTanks.Centre_Main).WeightLbs.ToString("F0");
                ps.GetFuelTank(FSFuelTanks.Centre_Main).WeightLbs = 16000;
                ps.WriteChanges();
                Thread.Sleep(2000); // Wait 2 seconds for everything to catch up
                ps.RefreshData();
                string str2 = ps.GetFuelTank(FSFuelTanks.Centre_Main).WeightLbs.ToString("F0");
                this.Text = str1 + " " + str2;
    

    You probably don't need to wait a full 2 seconds, that's just what I used to test this.

     

    And BTW, this line:

                ps.GetFuelTank(FSFuelTanks.Left_Main).WeightLbs = 8000;

    This is a Get method, but we use it to also Set a value?

     

     

     

    The 'get' part is getting the fuel tank object. Once you have got the fuel tank object you set the WeightLbs property on that object. If I break it into two steps you can see it makes more sense.

    FsFuelTank leftTank = ps.GetFuelTank(FSFuelTanks.Left_Main);
    leftTank.WeightLbs = 8000;
    

    Putting it all in one line just saves typing and creating a variable. You might want to break it out though if you need to do more work on the single tank. This will save running the GetFuelTank method over and over. So imagine some code like this:

    FsFuelTank leftTank = ps.GetFuelTank(FSFuelTanks.Left_Main);
    if (leftTank.WeightLbs < 1000 && automaticRefuelActive)
    {
       leftTank.LevelPercentage = 100;
       ps.WriteChanges();
    }
    

    This makes more sense than calling GetFuelTank twice and also makes the code cleaner and easier to read.

     

     

    So now to the main problem of the PDMG737 not respecting the fuel level changes. What's probably happening is that the PMDG code is constantly updating the fuel levels in the FSX tanks, So when you write your level it's soon overwritten again by the PMDG code. The reason for this is so they can more accurately simulate fuel consumption and feeding fuel betweens tanks.

     

    There are special offsets in FSUIPC for the PMDG 737ngx which allow access to areas of the aircraft that are modelled outside of FSX. These would normally not be available to FSUIPC but Pete has integrated FSUIPC with PDMG's SDK. For details of how to enable this interface and the extra offsets available see the document called "Offset Mapping for PMDG 737NGX.pdf" in the FSUIPC Documents folder under your FSX modules folder.

     

    This will not help with your fuel however as they are all read-only. The document does however mention that certain aspects of the 737NGX can be changed by sending 'controls' via FSUIPC. These are listed in the PMDG 737 SDK documentation. I don't have this aircraft so I can't look myself, but there might be controls to set fuel levels.

     

    Another place you can look for help is the 'User Contributions' forum under Pete's main support forum. Someone may have already solved this problem. Also if PMDG have forums then you could also try asking there.

     

    Paul

  14. If you write fuel tank levels with WriteChanges() then these should show up instantly in the sim.

     

    To really make sure the new level is being written, do another ps.RefreshData() after the WriteChanges() and then get the value again. At the moment your check is just reading the local (pending) value you've set as it's before the WriteChanges() call.

     

    If that comes back okay then here are some other thoughts:

     

    1. If you just read the centre tank without changing it, does it match the gauge on the 737?

     

    2. Is the change from your program reflected in the FSX Fuel dialog from the Aircraft menu?

     

    3. Have you tried the code with the default 737? It may be that PDMG don't use the FSX fuel tanks at all, but use their own code. This sort of thing is common with complex add-on aircraft.

     

    4. Maybe they are using one of the other centre tanks - try Centre_2 and Centre_3.

     

    I'm not sure about the nested menus - I'll look in to that tomorrow.

     

    Paul

  15. Firstly you're declaring an offset in the method. This is not good practice as you run the risk of creating duplicate offsets. I recommend you always create the offsets at class (form) level. See the C# sample application (the top of the main form code) to see the correct place to put all the offset declarations.

     

    Turning to the main problem.... the centerFuelTankLevel is a variable representing the Offset<int> class. That's why ToString() is giving you that class name back.

     

    To get the value from the offset you need to use the Value property:

    this.Text = centerFuelTankLevel.Value.ToString();
    

    Now, this will give you a large number which won't mean much. In the documentation Pete says the value here is scaled like this:
     

     

    % * 128 * 65536

     

     

     

    So you need to reverse this scaling to get a value that's useful to you.

    double centerFuelPercent = (double)centerFuelTankLevel.Value / 128d / 65536d * 100d;
    this.Text = centerFuel.ToString("F0") + "%";

    The (double) is a cast to convert the integer to a double so we can do floating point maths on it.

    The 'd' after the literal numbers is to define these as doubles instead of integers.

    The "F0" in the ToString() method is to format the number with 0 decimal places.

     

    Having said all of that, there is much easier way of working with payloads and fuel in my DLL. You need version 2.4 if you've not already got that. You can get it from the link below. At the bottom of the same thread is example code in C# of reading the fuel tanks with the "PayloadServices". With this method everything is converted to the various units for you (lbs, kg, percentages etc) and you don't need to worry about any conversions or scaling.

     

    http://forum.simflight.com/topic/74848-fsuipc-client-dll-for-net-version-24/

     

    You code would become:

                try
                {
                    FSUIPCConnection.Open();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "FSUIPC Connetion Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                // Get a reference to save so much typing later...
                PayloadServices ps = FSUIPCConnection.PayloadServices;
                // Get the latest data from FSUIPC
                ps.RefreshData();
                // Display centre tank percentage
                this.Text = ps.GetFuelTank(FSFuelTanks.Centre_Main).LevelPercentage.ToString("F0") + "%";
    
    

    If you need any more help with this let me know.

     

    Paul

  16. Hi Ralph,

     

    1) FSUIPC should be loaded by FSX. If the FSUIPC menu is in the 'Addons' menu in FSX then it's running and the DLL should connect. If you continue to have problems connecting then the text in the message box might help diagnose the problem.

     

    2) Yes, FSX (or WideClient.exe) needs to be running on the same machine.

     

    3) If your PC/Laptop is fast enough and has enough memory it shouldn't be a problem running VS and FSX at the same time. My development laptop is an i7 running at 1.6 ghz and has 8gb of memory and runs both at the same time with no problem (although I have set FSX to low settings - see next paragraph). If your computer doesn't have much memory or has a slow processor then that could be the cause of your problems.

     

    You can try to improve things by turning down all the scenery options in FSX to very low settings and avoiding third-party scenery and aircraft. Also running in a very small sized window might help. I see you've got the PMDG 737 in your signature - that will not be helping matters if you have that loaded. Of course if this is essential for your application then there's not much you can do.

     

    If you have another PC on your home network that can run Visual Studio then you could invest in Pete's WideFS program. You'll need to buy a licence for WideFS (make sure it's version 7) and register it on the FSX PC. Then on the Visual Studio PC you can run WideClient.exe which is tiny in comparison to FSX. This will act like FSUIPC on the VS pc and will talk to the FSX PC over the network to get all your values, but it hardly uses any resources.

     

    So, yes, it sounds like you're doing everything right, it's just that your PC doesn't sound like it's up to the job of running two large programs like FSX and VS2013.

     

    Paul

×
×
  • 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.