AndersMalmgren / FreePIE

Programmable Input Emulator
644 stars 144 forks source link

Losing connection with vJoy #164

Open qrkyckling opened 6 years ago

qrkyckling commented 6 years ago

I have a .py script that reads my thrustmaster joystick buttons and creates new buttons in a vJoy device from combinations of those. This because the TM buttons have ON or OFF position and when in OFF I can't bind them in simulators as I would like. So I create a vJoy button only device which I feed to enable buttons when the joystick button is not detected. And then I can bind the vJoy buttons inside the simulator.

It all works nice for a while but FreePIE losing connection to vJoy requires alt+tab out of simulation to stop script (shift+f5) and restart it again (f5), which makes it work again.

Sometimes connection lost in 10 minutes sometimes it lasts up to two hours or more.

It appears to me that the connection between FreePIE and vJoy gets broken.

image

qrkyckling commented 6 years ago

This is the vJoy driver information. It is the one I get when I install "latest". image

qrkyckling commented 6 years ago

I have now disabled power management of vJoy device and will see if that resolves problem.

EDIT: It did not.

MarijnS95 commented 6 years ago

Would it be possible to share your script so that we can have a look at what's going on?

If anything, FreePIE is just sending axis/button states to the vjoy library that communicates with the driver. Sounds as if FreePIE is not pushing updates anymore.

qrkyckling commented 6 years ago

Github does not support .py files. Do you want me to zip it up or a screenshot good enough ?

image

qrkyckling commented 6 years ago

I have FreePIE running right now with the vJoy unresponsive. No owner of vJoy device. image

MarijnS95 commented 6 years ago

You can paste the script in a code block (see the github markdown documentation), but if this is all there is to it then nothing looks out of the ordinary. Good to know that diagnostics.watch keeps going (so your script is properly updating), and that it stops in the monitor, which means the problem is somewhere between passing the data on to vJoy, and the data coming out of the driver as DirectInput controller.

All I can think of right now is adding a watch/log when the status of the joystick changes, though I do not see a reason for the device to suddenly get unacquired. However, the example feeder does handle this case by reacquiring the device if updating fails: https://github.com/shauleiz/vJoy/blob/2c9a6f14967083d29f5a294b8f5ac65d3d42ac87/apps/vJoyClient/vJoyClient.cpp#L164-L169

FreePIE does not do this, though it should be trivial to add that. Are you in a position to make this change and recompile FreePIE, or should I send you an updated binary (+ send a pull-request once this is confirmed to work)?

qrkyckling commented 6 years ago

I'm trying to befriend the FreePIE code and Visual Studio environment. I managed to build and run and I'm trying out this code which I call from SetButton() for now. I do not see any obvious place (other than inside every function) to place this checkalive() call. For now I throw exception but will change to reconnect if this get caught.

Do you think this will be acceptable ?

    public void CheckAlive()
    {
        var status = joystick.GetVJDStatus(Index);
        if (status != VjdStat.VJD_STAT_OWN)
        {
            throw new Exception(string.Format("We no longer own the vJoy device"));
        }
    }
AndersMalmgren commented 6 years ago

Can't you check it in the BeforeNextExecute

qrkyckling commented 6 years ago

Seems like a better place. Yes, I missed that one. I'll see if it catches the problem from there.

MarijnS95 commented 6 years ago

You could do that in DoBeforeNextExecute, though I'd recommend printing it to the log instead of raising an exception.

At the same time you could add a conditional to all joystick.Set* calls; if it returns false, log it and reacquire the device, similar to the vJoy client linked above.

qrkyckling commented 6 years ago

I'll change to System.Console.WriteLine or to be quiet when I reaquire the device. If that is the problem. I hope that is the problem. If that is not the problem then we have a problem :-)

qrkyckling commented 6 years ago

Well I'm having problems recreating the detach. I disconnected the joystick on purpose to make sure there is no glitching. When disconnected the monitoring stops working as well so now I know that is not the issue.

Will continue trying to recreate detach.

qrkyckling commented 6 years ago

I've finally managed to lose connection and verified FreePIE automatically connects back again. Printing a line on console about "lost connection and attempting to reacquire."

I'll try to have a PR ready by tomorrow. Busy weekend.

MarijnS95 commented 6 years ago

Cool :). Was it the joystick.Set* calls returning false after some time, or is it detected using joystick.GetVJDStatus(Index)?

qrkyckling commented 6 years ago

I check joystick.GetVJDStatus(Index) in CheckAlive() which is called from the DoBeforeNextExecute loop.

It might be less expensive to just check retval from joystick.Set* calls but I'm thinking at that time it is already too late and you will miss one event? So for now I poll at every loop.

Still no idea why it disconnects tho.. While trying to fix this there has been times when it went 8h+ without disconnect (I gave up and shut down) and there are times when disconnects happen within 15 minutes.

MarijnS95 commented 6 years ago

I agree it is a weird situation, especially since no-one has ever faced this before. However, this looks like expected behaviour thanks to the boolean returns from the Set* functions so you shouldn't worry too much about it.

Using the boolean will be faster because it's readily available (unlikeGetVJDStatus which has to call into the C library, which in turn does an IoControl call to the driver to retrieve the result). The difference will be negligible though, just like every python call to setButton, setAxis and others do exactly the same. (If anyone wants to tackle this: store the values in JoystickState and call UpdateVJD with it from DoBeforeNextExecute).

If false is returned and the event is missed, it can easily be sent again after reacquiring the device, in the same if block. Such an edgecase for every set method adds duplication which can be hidden with some lambda functions ;). I'll let @AndersMalmgren decide on that.

private void UpdateJoystickField(Action updater)
{
    if (!updater())
    {
        joystick.Acquire();
        if (!updater())
            throw new Exception("Failed to update joystick after .Acquire()!");
    }
}
qrkyckling commented 6 years ago

PR created.

165