Пример #1
0
        private static async Task <TimingResult> TestTaskInner(CancellationToken token)
        {
            Stopwatch s         = Stopwatch.StartNew();
            TimeSpan  prevValue = TimeSpan.Zero;
            int       i         = 0;

            while (true)
            {
                Console.WriteLine(s.Elapsed.TotalMilliseconds);
                await MultimediaTimer.Delay(1, token);

                if (Console.KeyAvailable)
                {
                    break;
                }

                i++;
            }

            return(new TimingResult()
            {
                Elapsed = s.Elapsed,
                Iterations = i
            });
        }
Пример #2
0
        public Core_Interception()
        {
            _providerDescriptor = new ProviderDescriptor
            {
                ProviderName = ProviderName
            };
            ProcessSettingsFile();

            _deviceLibrary = new IceptDeviceLibrary(_providerDescriptor, _blockingEnabled);
            if (_deviceLibrary.GetInputList().Devices?.Count > 0)
            {
                _isLive       = true;
                _errorMessage = string.Empty;
            }
            else
            {
                _isLive       = false;
                _errorMessage = "No Interception devices found, driver assumed to not be installed";
            }

            _deviceContext = ManagedWrapper.CreateContext();

            StartPollingIfNeeded();
            //_pollThreadDesired = true;
            _timer = new MultimediaTimer()
            {
                Interval = _pollRate
            };
            _timer.Elapsed += DoPoll;
        }
Пример #3
0
 protected TimerState(PlayerStateController playerStateController, VideoSource videoSource, IFrameDisplay frameDisplay)
     : base(playerStateController, videoSource, frameDisplay)
 {
     _timer = new MultimediaTimer {
         Mode = TimerMode.Periodic
     };
     _timeProc = new MultimediaTimer.TimeProc(Tick);
 }
Пример #4
0
 public Manager()
 {
     _deviceContext = ManagedWrapper.CreateContext();
     _timer         = new MultimediaTimer()
     {
         Interval = _pollRate
     };
     _timer.Elapsed += DoPoll;
 }
Пример #5
0
 /// <summary>
 /// Default base constructor
 /// </summary>
 public AFFBManager(int refreshPeriod_ms)
 {
     RefreshPeriod_ms = refreshPeriod_ms;
     Tick_per_s       = 1000.0 / (double)RefreshPeriod_ms;
     Timer            = new MultimediaTimer(refreshPeriod_ms);
     for (int i = 0; i < RunningEffects.Length; i++)
     {
         RunningEffects[i] = new Effect();
         RunningEffects[i].Reset();
     }
     NewEffect.Reset();
 }
    private static void TestMultimediaTimer()
    {
        Stopwatch s = new Stopwatch();

        using (var timer = new MultimediaTimer()
        {
            Interval = 1
        })
        {
            timer.Elapsed += (o, e) => Console.WriteLine(s.ElapsedMilliseconds);
            s.Start();
            timer.Start();
            Console.ReadKey();
            timer.Stop();
        }
    }
Пример #7
0
        public void Subscribe(int vid, int pid, dynamic callback)
        {
            _callback       = callback;
            _filteredDevice = HelperFunctions.GetDeviceId(_deviceContext, false, vid, pid, 1);
            if (_filteredDevice == 0)
            {
                throw new Exception($"Could not find device with VID {vid}, PID {pid}");
            }

            ManagedWrapper.SetFilter(_deviceContext, IsMonitoredDevice, ManagedWrapper.Filter.All);
            _timer = new MultimediaTimer()
            {
                Interval = 10
            };
            _timer.Elapsed += DoPoll;
            _timer.Start();
        }
Пример #8
0
        public PatternPlayer(IEnumerable <Instrument> instruments, int bpm, MultimediaTimer timer, bool loop)
        {
            _masteringVoice = new MasteringVoice(_xAudio2);

            _bpm  = bpm;
            _loop = loop;
            foreach (Instrument instrument in instruments)
            {
                _instrumentStates.Add(instrument, new InstrumentState(new Sound(instrument, _xAudio2), instrument.Beats.Count));
            }

            double interval = (60d / _bpm) * 1000d;

            _timer          = timer ?? throw new ArgumentNullException(nameof(timer));
            _timer.Interval = (int)interval;
            _timer.Elapsed += OnElapsed;
        }
Пример #9
0
        /// <summary>
        /// Sets up calibration procedure and the tracking client
        /// and wires the events. Then reads settings from file.
        /// </summary>
        protected override sealed void Initialize()
        {
            this.counter               = 0;
            this.trackingTimer         = new Timer();
            this.stopWatch             = new Stopwatch();
            this.multimediaTimer       = new MultimediaTimer();
            this.multimediaTimer.Tick += this.TrackingTimerTick;

            if (File.Exists(this.SettingsFile))
            {
                this.mouseOnlySettings = this.DeserializeSettings(this.SettingsFile);
            }
            else
            {
                this.mouseOnlySettings = new MouseOnlySetting();
                this.SerializeSettings(this.mouseOnlySettings, this.SettingsFile);
            }

            this.UpdateSettings();
        }
Пример #10
0
        public Core_Interception()
        {
            _providerDescriptor = new ProviderDescriptor
            {
                ProviderName = ProviderName
            };
            _deviceLibrary = new IceptDeviceLibrary(_providerDescriptor);

            ProcessSettingsFile();

            _deviceContext = ManagedWrapper.CreateContext();

            StartPollingIfNeeded();
            //_pollThreadDesired = true;
            _timer = new MultimediaTimer()
            {
                Interval = _pollRate
            };
            _timer.Elapsed += DoPoll;
        }
        private DataReader()
        {
            _scudTimer          = new MultimediaTimer(1000);
            _scudTimer.Elapsed += _scudTimerElapsed;

            _iptTimer          = new MultimediaTimer(250);
            _iptTimer.Elapsed += _iptTimer_Elapsed;

            MbCliWrapper.Connected += (s, e) =>
            {
                _isScudConnected = true;
            };
            MbCliWrapper.Disconnected += (s, e) =>
            {
                _isScudConnected = false;
            };
            MbCliWrapper.Error +=
                (s, e) =>
            {
                OnScudError(
                    new DataReaderErrorEventArgs(e.ErrorCode, string.Format("Ошибка СКУД.\n{0}", e.ErrorText)));
            };
        }
Пример #12
0
        private static TimingResult TestMultimediaTimer()
        {
            TimeSpan  total      = TimeSpan.Zero;
            int       iterations = 0;
            Stopwatch s          = new Stopwatch();

            using (var timer = new MultimediaTimer()
            {
                Interval = 1
            })
            {
                timer.Elapsed += (o, e) =>
                {
                    var ts = s.Elapsed;
                    lock (s)
                    {
                        total += ts;
                        iterations++;
                    }
                    Console.WriteLine(ts.TotalMilliseconds);
                };
                s.Start();
                timer.Start();
                Console.ReadKey(true);
                timer.Stop();
            }

            lock (s)
            {
                return(new TimingResult()
                {
                    Elapsed = s.Elapsed,
                    Iterations = iterations
                });
            }
        }
        /// <summary>
        /// Starts the
        /// </summary>
        public void StartPdoExchangeTask()
        {
            /* Declare multimedia Timer with 20 ms interval */
            MmTimer = new MultimediaTimer {
                Interval = 20
            };

            /* Timer event in charge of the exchange of PDO data and for the calling of Scope SDOs */
            MmTimer.Elapsed += /*async*/ (o, e) =>
            {
                /* Call an exchange of PDO */
                wkc_pdo = EcSendRecieveProcessdataExtern();

                for (int i = 0; i < Devices.Count; i++)
                {
                    if (Devices[i] is PCS device)
                    {
                        device.pdo_output_map.controlword = device.Controlword;

                        device.pdo_output_map.target_position = device.TargetPosition;
                        device.pdo_output_map.target_velocity = device.TargetVelocity;
                        device.pdo_output_map.modes_of_oper   = device.TargetMode;

                        /* From low-level soem code copy the pdo data */
                        EcCopyPdos(device.SlaveNumber, ref device.pdo_input_map, device.pdo_output_map);

                        device.stateMachineDsp402.StateWord = device.pdo_input_map.statusword;
                        //device.CurrentMode = device.pdo_input_map.modes_of_oper_disp;
                        //device.ActualPos = device.pdo_input_map.actual_position;
                        //device.ActualVel = device.pdo_input_map.actual_velocity;
                    }
                }

                //wkc_wathchdog = (wkc_pdo < 0) ? wkc_wathchdog + 1 : 0; /* check for worker counter, increase watchdog if worker counter is <=0 */
                Console.WriteLine(wkc_pdo);

                if (wkc_pdo < 3)
                {
                    wkc_wathchdog += 1;
                }
                else
                {
                    wkc_wathchdog = 0;
                }

                if (wkc_wathchdog > 1000)
                {
                    Disconnect();

                    //MW.Dispatcher.Invoke(() =>
                    //{
                    //await MW.ShowMessageAsync("Client Disconnected", "Lost contact to device.");+
                    //MessageBox.Show("Client Disconnected. Lost contact to device.");
                    //});
                }
            };

            /* start multimedia timer */
            MmTimer.Start();

            StartScopeTask();

            timerUpdatePdoProperties.Elapsed  += new ElapsedEventHandler(DoTimeEventUpdateProperties); //Non realtime relevant stuff
            timerUpdatePdoProperties.Interval  = 50;                                                   // in ms
            timerUpdatePdoProperties.Enabled   = true;
            timerUpdatePdoProperties.AutoReset = true;
        }
Пример #14
0
 public TimerRegister(int StartAddress, int Length) : base(StartAddress, Length)
 {
     hiresTimer          = new MultimediaTimer(1000);
     hiresTimer.Elapsed += new MultimediaElapsedEventHandler(Timer_Tick);
 }
Пример #15
0
        public CommunicationUDP(UdpCommControl udpctl, CheckBox checkboxEoeRw)
        {
            MW = udpctl.MW;
            Devices.Clear();
            Devices.Add(new PCS(this, 1, "Intec PCS", MW.ObjectDictionary));
            (Devices[0] as PCS).EcStateMachine = EC_SM.EC_SM_NA;
            IpAddress = udpctl.Ipaddress;

            //create a new client
            client    = UdpUser.ConnectTo(IpAddress, _port);
            commType  = CommType.COMM_UDP;
            Connected = true;

            WriteFlag = checkboxEoeRw.IsChecked.Value;

            MmTimer = new MultimediaTimer {
                Interval = 100
            };

            Task <byte[]> ret;

            byte[] send_client_msg = new byte[182];

            ts_cycle = new CancellationTokenSource();
            CancellationToken ct = ts_cycle.Token;

            Task.Factory.StartNew(async() =>
            {
                while (true)
                {
                    if (ct.IsCancellationRequested) /* TRUE when ts_cyle.Cancel() is called */
                    {
                        break;                      /* Get out of loop to end thread*/
                    }

                    send_client_msg = new byte[182];

                    if (Connected && Devices[0] is PCS device)
                    {
                        InsertPdoWriteControlword();    /* PDO 1 */
                        InsertPdoReadStatusword();      /* PDO 2 */
                        InsertPdoWriteTargetPosition(); /* PDO 3 */
                        InsertPdoReadActualPosition();  /* PDO 4 */
                        InsertPdoWriteTargetVelocity(); /* PDO 5 */
                        InsertPdoReadActualVelocity();  /* PDO 6 */
                        InsertPdoWriteTargetMode();     /* PDO 7 */
                        InsertPdoReadModeDisp();        /* PDO 8 */

                        InsertScopeObjects();

                        PutBufferInSendArray(ref send_client_msg);

                        ret = SendUdp(send_client_msg); // This must be only thread to call SendUDP

                        GetObjectsFromBuffer(ret.Result);
                    }

                    await Task.Delay(1);
                }
            }, ct);
        }
Пример #16
0
 protected TimerState(PlayerStateController playerStateController, VideoSource videoSource, IFrameDisplay frameDisplay)
     : base(playerStateController, videoSource, frameDisplay)
 {
     _timer = new MultimediaTimer {Mode = TimerMode.Periodic};
     _timeProc = new MultimediaTimer.TimeProc(Tick);
 }
Пример #17
0
 private static void Delay(int ms)
 {
     MultimediaTimer.Delay(ms).Wait();
 }
Пример #18
0
        //
        // Tablet View Constructor
        //
        public WindowTabletView(Configuration config, TabletDriver driver)
        {
            if (config.TabletView.Borderless)
            {
                WindowStyle = WindowStyle.None;
            }
            InitializeComponent();

            this.config = config;
            this.driver = driver;

            // Tablet renderer
            tabletRenderer = new TabletRenderer(config);
            canvasTabletView.Children.Add(tabletRenderer);

            // Offset texts
            Canvas.SetLeft(textTabletInfo, Canvas.GetLeft(textTabletInfo) + config.TabletView.OffsetText.X);
            Canvas.SetTop(textTabletInfo, Canvas.GetTop(textTabletInfo) + config.TabletView.OffsetText.Y);
            Canvas.SetLeft(textInput, Canvas.GetLeft(textInput) + config.TabletView.OffsetText.X);
            Canvas.SetTop(textInput, Canvas.GetTop(textInput) + config.TabletView.OffsetText.Y);
            Canvas.SetLeft(textOutput, Canvas.GetLeft(textOutput) + config.TabletView.OffsetText.X);
            Canvas.SetTop(textOutput, Canvas.GetTop(textOutput) + config.TabletView.OffsetText.Y);
            Canvas.SetLeft(textLatency, Canvas.GetLeft(textLatency) + config.TabletView.OffsetText.X);
            Canvas.SetTop(textLatency, Canvas.GetTop(textLatency) + config.TabletView.OffsetText.Y);

            // Background color
            Brush brush;

            try { brush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(config.TabletView.BackgroundColor)); }
            catch (Exception) { brush = Brushes.White; }
            canvasTabletView.Background = brush;
            Background = brush;

            // Text colors
            try { brush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(config.TabletView.InfoColor)); }
            catch (Exception) { brush = Brushes.Black; }
            textTabletInfo.Foreground = brush;
            try { brush = new SolidColorBrush((Color)ColorConverter.ConvertFromString(config.TabletView.LatencyColor)); }
            catch (Exception) { brush = Brushes.Black; }
            textLatency.Foreground = brush;
            textInput.Foreground   = tabletRenderer.brushInput;
            textOutput.Foreground  = tabletRenderer.brushOutput;

            // Text font
            try
            {
                FontFamilyConverter fontConverter = new FontFamilyConverter();
                FontFamily          fontFamily    = (FontFamily)fontConverter.ConvertFromString(config.TabletView.Font);
                textTabletInfo.FontFamily = fontFamily;
                textInput.FontFamily      = fontFamily;
                textOutput.FontFamily     = fontFamily;
                textLatency.FontFamily    = fontFamily;
            }
            catch (Exception) { }

            // Font size
            textTabletInfo.FontSize = config.TabletView.FontSize;
            textInput.FontSize      = config.TabletView.FontSize;
            textOutput.FontSize     = config.TabletView.FontSize;
            textLatency.FontSize    = config.TabletView.FontSize;

            // Info text
            textTabletInfo.Text = config.TabletName + " - " +
                                  Utils.GetNumberString(config.TabletAreas[0].Width) + " x " +
                                  Utils.GetNumberString(config.TabletAreas[0].Height) + " mm → " +
                                  Utils.GetNumberString(config.ScreenAreas[0].Width, "0") + " x " +
                                  Utils.GetNumberString(config.ScreenAreas[0].Height, "0") + " px";


            //
            // Update/draw timer
            //
            timer = new MultimediaTimer {
                Interval = 2
            };
            timer.Tick += UpdateTimer_Tick;

            // Last values
            lastPosition = new Vector(0, 0);
            lastUpdate   = DateTime.Now;
            lastPressure = 0;

            // Average values
            velocity = 0;
            latency  = 0;


            // Input loss
            hadInputLoss       = true;
            lastInputStartTime = DateTime.Now;

            // Window events
            Loaded  += WindowTabletView_Loaded;
            Closing += WindowTabletView_Closing;
            KeyDown += WindowTabletView_KeyDown;

            MouseDown += WindowTabletView_MouseDown;
            MouseUp   += WindowTabletView_MouseUp;


            // Set GC mode to low latency
            GCSettings.LatencyMode = GCLatencyMode.SustainedLowLatency;
        }
Пример #19
0
        protected void ManagerThreadMethod()
        {
__restart:

            Log("Program configured for " + Config.TranslatingModes, LogLevels.IMPORTANT);

            var boards = USBSerialIO.ScanAllCOMPortsForIOBoards();

            if (boards.Length > 0)
            {
                IOboard = boards[0];
                Log("Found io board on " + IOboard.COMPortName + " version=" + IOboard.BoardVersion + " type=" + IOboard.BoardDescription, LogLevels.IMPORTANT);
            }
            else
            {
                IOboard = null;
                if (Config.RunWithoutIOBoard)
                {
                    Log("No boards found! Continue without real hardware", LogLevels.ERROR);
                }
                else
                {
                    Log("No boards found! Thread will terminate", LogLevels.ERROR);
                    Running = false;
                    //Console.ReadKey(true);
                    return;
                }
            }

            // Output system : lamps
            Outputs = new MAMEOutputWinAgent();
            Outputs.Start();

            switch (Config.TranslatingModes)
            {
            case FFBTranslatingModes.PWM_CENTERED:
            case FFBTranslatingModes.PWM_DIR: {
                FFB = new FFBManagerTorque(GlobalRefreshPeriod_ms);
            }
            break;

            case FFBTranslatingModes.MODEL3_UNKNOWN_DRVBD: {
                // Default to Scud/Daytona2
                FFB = new FFBManagerModel3Scud(GlobalRefreshPeriod_ms);
            }
            break;

            case FFBTranslatingModes.MODEL3_LEMANS_DRVBD: {
                FFB = new FFBManagerModel3Lemans(GlobalRefreshPeriod_ms);
            }
            break;

            case FFBTranslatingModes.MODEL3_SCUD_DRVBD: {
                FFB = new FFBManagerModel3Scud(GlobalRefreshPeriod_ms);
            }
            break;

            default:
                throw new NotImplementedException("Unsupported FFB mode " + Config.TranslatingModes.ToString());
            }


            // Use this to allow 1ms sleep granularity (else default is 16ms!!!)
            // This consumes more CPU cycles in the OS, but does improve
            // a lot reactivity when soft real-time work needs to be done.
            MultimediaTimer.SetTickGranularityOnWindows();

            vJoy.EnablevJoy();             // Create joystick interface
            vJoy.Acquire(1);               // Use first enumerated vJoy device
            vJoy.StartAndRegisterFFB(FFB); // Start FFB callback mechanism in vJoy

            // In case we want to use XInput/DInput devices to gather multiple inputs?
            //XInput();
            //DirectInput();

            if (IOboard != null)
            {
                Log("Initializing IO board", LogLevels.IMPORTANT);
                // Initialize board
                IOboard.PerformInit();
                // Enable safety watchdog
                IOboard.EnableWD();
                // Enable auto-streaming
                IOboard.StartStreaming();
            }

            if (Config.VerbosevJoyManager)
            {
                Log("Start feeding...");
            }

            // Start FFB manager
            FFB.Start();

            // Internal values for special operation
            double prev_angle = 0.0;

            UInt32 autofire_mode_on = 0;

            uint   error_counter = 0;
            UInt64 nextRun_ms    = (UInt64)(MultimediaTimer.RefTimer.ElapsedMilliseconds);

            while (Running)
            {
                TickCount++;

                nextRun_ms += GlobalRefreshPeriod_ms;
                UInt64 now      = (UInt64)(MultimediaTimer.RefTimer.ElapsedMilliseconds);
                int    delay_ms = (int)(nextRun_ms - now);
                if (delay_ms >= 0)
                {
                    // Sleep until next tick
                    Thread.Sleep(delay_ms);
                }
                else
                {
                    if (Config.VerbosevJoyManager)
                    {
                        Log("One period missed by " + (-delay_ms) + "ms", LogLevels.DEBUG);
                    }
                }
                if (IOboard != null)
                {
                    try {
                        if (IOboard.IsOpen)
                        {
                            // Empty serial buffer
                            if (delay_ms < 0)
                            {
                                IOboard.UpdateOnStreaming((-delay_ms) / GlobalRefreshPeriod_ms);
                            }
                            // Shift tick to synch with IOboard
                            var before = MultimediaTimer.RefTimer.ElapsedMilliseconds;
                            // Update status on received packets
                            var nbproc = IOboard.UpdateOnStreaming();
                            var after  = MultimediaTimer.RefTimer.ElapsedMilliseconds;
                            // Delay is expected to be 1-2ms for processing in stream
                            delay_ms = (int)(after - before);
                            // Accept up to 2ms of delay (jitter), else consider we have
                            if (delay_ms > 2 && nbproc == 1)
                            {
                                var add_delay = Math.Min(GlobalRefreshPeriod_ms - 1, delay_ms - 1);
                                add_delay   = 1;
                                nextRun_ms += (ulong)add_delay;
                                if (Config.VerbosevJoyManager)
                                {
                                    Log("Read took " + delay_ms + "ms delay, adding " + add_delay + "ms to sync with IO board serial port", LogLevels.DEBUG);
                                }
                            }

                            if (Config.VerbosevJoyManager)
                            {
                                if (nbproc > 1)
                                {
                                    Log("Processed " + nbproc + " msg instead of 1", LogLevels.DEBUG);
                                }
                            }
                            // Refresh wheel angle (between -1...1)
                            if (IOboard.AnalogInputs.Length > 0)
                            {
                                // Scale analog input between 0..0xFFF, then map it to -1/+1, 0 being center
                                var angle_u = ((double)IOboard.AnalogInputs[0]) * (2.0 / (double)0xFFF) - 1.0;
                                // Refresh values in FFB manager
                                if (IOboard.WheelStates.Length > 0)
                                {
                                    // If full state given by IO board (should be in unit_per_s!)
                                    FFB.RefreshCurrentState(angle_u, IOboard.WheelStates[0], IOboard.WheelStates[1]);
                                }
                                else
                                {
                                    // If only periodic position
                                    FFB.RefreshCurrentPosition(angle_u);
                                }
                                prev_angle = angle_u;
                            }

                            // For debugging purpose, add a 4th axis to display torque output
                            uint[] axesXYRZplusSL0ForTrq = new uint[5];
                            IOboard.AnalogInputs.CopyTo(axesXYRZplusSL0ForTrq, 0);
                            axesXYRZplusSL0ForTrq[4] = (uint)(FFB.OutputTorqueLevel * 0x7FF + 0x800);

                            // Set values into vJoy report:
                            // - axes
                            vJoy.UpdateAxes12(axesXYRZplusSL0ForTrq);

                            // - buttons (only32 supported for now)
                            if (IOboard.DigitalInputs8.Length > 0)
                            {
                                UInt32 rawinput_states = 0;
                                int    rawidx          = 0;
                                // For each single input, process mapping, autofire and toggle
                                for (int i = 0; i < Math.Min(4, IOboard.DigitalInputs8.Length); i++)
                                {
                                    // Scan 8bit input block
                                    for (int j = 0; j < 8; j++)
                                    {
                                        // Default input value is current logic (false if not inverted)
                                        bool newrawval = Config.RawInputTovJoyMap[rawidx].IsInvertedLogic;

                                        // Check if input is "on" and invert default value
                                        if ((IOboard.DigitalInputs8[i] & (1 << j)) != 0)
                                        {
                                            // If was false, then set true
                                            newrawval = !newrawval;
                                        }
                                        // Now newrawval is the raw state of the input taking into account inv.logic

                                        // Bit corresponding to this input
                                        var rawbit = (UInt32)(1 << rawidx);
                                        // Store new state of raw input
                                        if (newrawval)
                                        {
                                            rawinput_states |= rawbit;
                                        }

                                        // Previous state of this input (for transition detection)
                                        var prev_state = (RawInputsStates & rawbit) != 0;

                                        // Check if we toggle the bit (or autofire mode)
                                        if (Config.RawInputTovJoyMap[rawidx].IsToggle)
                                        {
                                            // Toggle only if we detect a false->true transition in raw value
                                            if (newrawval && (!prev_state))
                                            {
                                                // Toggle = xor on every vJoy buttons
                                                vJoy.ToggleButtons(Config.RawInputTovJoyMap[rawidx].vJoyBtns);
                                            }
                                        }
                                        else if (Config.RawInputTovJoyMap[rawidx].IsAutoFire)
                                        {
                                            // Autofire set, if false->true transition, then toggle autofire state
                                            if (newrawval && (!prev_state))
                                            {
                                                // Enable/disable autofire
                                                autofire_mode_on ^= rawbit;
                                            }
                                            // No perform autofire toggle if autofire enabled
                                            if ((autofire_mode_on & rawbit) != 0)
                                            {
                                                // Toggle = xor every 20 periods
                                                if ((TickCount % 20) == 0)
                                                {
                                                    vJoy.ToggleButtons(Config.RawInputTovJoyMap[rawidx].vJoyBtns);
                                                }
                                            }
                                        }
                                        else
                                        {
                                            // No toggle, no autofire : perform simple mask
                                            if (newrawval)
                                            {
                                                vJoy.SetButtons(Config.RawInputTovJoyMap[rawidx].vJoyBtns);
                                            }
                                            else
                                            {
                                                vJoy.ClearButtons(Config.RawInputTovJoyMap[rawidx].vJoyBtns);
                                            }
                                        }

                                        // Next input
                                        rawidx++;
                                    }
                                }

                                // Save raw input state for next run
                                RawInputsStates = rawinput_states;
                            }

                            // - 360deg POV to view for wheel angle
                            //vJoy.UpodateContinuousPOV((uint)((IOboard.AnalogInputs[0] / (double)0xFFF) * 35900.0) + 18000);

                            // Update vJoy and send to driver every n ticks to limit workload on driver
                            if ((TickCount % vJoyUpdate) == 0)
                            {
                                vJoy.PublishiReport();
                            }

                            // Outputs (Lamps)
                            if (Outputs != null)
                            {
                                // First 2 bits are unused for lamps (used by PWM Fwd/Rev)
                                IOboard.DigitalOutputs8[0] = (byte)(Outputs.LampsValue);
                            }

                            // Now output torque to Pwm+Dir or drive board command
                            switch (Config.TranslatingModes)
                            {
                            // PWM centered mode (50% = 0 torque)
                            case FFBTranslatingModes.PWM_CENTERED: {
                                // Latch a copy
                                var outlevel = FFB.OutputTorqueLevel;
                                // Enforce range again to be [-1; 1]
                                outlevel = Math.Min(1.0, Math.Max(outlevel, -1.0));
                                UInt16 analogOut = (UInt16)(outlevel * 0x7FF + 0x800);
                                IOboard.AnalogOutputs[0] = analogOut;
                            }
                            break;

                            // PWM+dir mode (0% = 0 torque, direction given by first output)
                            case FFBTranslatingModes.PWM_DIR: {
                                // Latch a copy
                                var outlevel = FFB.OutputTorqueLevel;
                                if (outlevel >= 0.0)
                                {
                                    UInt16 analogOut = (UInt16)(outlevel * 0xFFF);
                                    // Save into IOboard
                                    IOboard.AnalogOutputs[0]    = analogOut;
                                    IOboard.DigitalOutputs8[0] |= 1 << 0;       // set FwdCmd bit 0
                                    IOboard.DigitalOutputs8[0] &= 0xFD;         // clear RevCmd bit 1
                                }
                                else
                                {
                                    UInt16 analogOut = (UInt16)(-outlevel * 0xFFF);
                                    // Save into IOboard
                                    IOboard.AnalogOutputs[0]    = analogOut;
                                    IOboard.DigitalOutputs8[0] |= 1 << 1;       // set RevCmd bit 1
                                    IOboard.DigitalOutputs8[0] &= 0xFE;         // clear FwdCmd bit 0
                                }
                            }
                            break;

                            // Driveboard translation mode
                            case FFBTranslatingModes.MODEL3_UNKNOWN_DRVBD:
                            case FFBTranslatingModes.MODEL3_LEMANS_DRVBD:
                            case FFBTranslatingModes.MODEL3_SCUD_DRVBD: {
                                // Latch a copy
                                var outlevel = FFB.OutputEffectCommand;
                                if (IOboard.DigitalOutputs8.Length > 1)
                                {
                                    IOboard.DigitalOutputs8[1] = (byte)(outlevel & 0xFF);
                                }
                            }
                            break;
                            }

                            // Save output state
                            RawOutputsStates = 0;
                            for (int i = 0; i < IOboard.DigitalOutputs8.Length; i++)
                            {
                                var shift = (i << 3);
                                RawOutputsStates = (UInt32)(IOboard.DigitalOutputs8[i] << shift);
                            }

                            // Send all outputs - this will revive the watchdog!
                            IOboard.SendOutputs();
                        }
                        else
                        {
                            Log("Re-connecting to same IO board on port " + IOboard.COMPortName, LogLevels.IMPORTANT);
                            IOboard.OpenComm();
                            // Enable safety watchdog
                            IOboard.EnableWD();
                            // Enable auto-streaming
                            IOboard.StartStreaming();
                            error_counter = 0;
                        }
                    } catch (Exception ex) {
                        Log("IO board Failing with " + ex.Message, LogLevels.ERROR);
                        try {
                            if (IOboard.IsOpen)
                            {
                                IOboard.CloseComm();
                            }
                        } catch (Exception ex2) {
                            Log("Unable to close communication " + ex2.Message, LogLevels.ERROR);
                        }
                        error_counter++;
                        if (error_counter > 10)
                        {
                            // Serious problem here, try complete restart with scanning
                            FFB.Stop();
                            goto __restart;
                        }
                        System.Threading.Thread.Sleep(500);
                    }
                }
            }
            ;

            MultimediaTimer.RestoreTickGranularityOnWindows();

            if (Outputs != null)
            {
                Outputs.Stop();
            }
            FFB.Stop();
            if (IOboard != null)
            {
                IOboard.CloseComm();
            }
            vJoy.Release();
        }