Example #1
0
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            slider           = GetTemplateChild("Slider") as DoubleSlider;
            LowerLabel       = GetTemplateChild("LowerValue") as Label;
            UpperLabel       = GetTemplateChild("UpperValue") as Label;
            ThresholdWarning = GetTemplateChild("ThresholdWarning") as Image;
            SensorDisplay    = GetTemplateChild("PlatformSensorDisplay") as PlatformSensorDisplay;

            slider.ValueChanged += delegate(DoubleSlider slider) { SaveToConfig(); };

            // Show the edit button for the custom-sensors slider.
            Button EditCustomSensorsButton = GetTemplateChild("EditCustomSensorsButton") as Button;

            EditCustomSensorsButton.Visibility = Type == "custom-sensors"? Visibility.Visible:Visibility.Hidden;
            EditCustomSensorsButton.Click     += delegate(object sender, RoutedEventArgs e)
            {
                SetCustomSensors dialog = new SetCustomSensors();
                dialog.Owner = Window.GetWindow(this);
                dialog.ShowDialog();
            };

            onConfigChange = new OnConfigChange(this, delegate(LoadFromConfigDelegateArgs args) {
                LoadUIFromConfig(ActivePad.GetFirstActivePadConfig(args));
            });
        }
Example #2
0
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            int[] PanelToIndex = new int[] {
                7, 8, 9,
                4, 5, 6,
                1, 2, 3,
            };

            EnabledPanelButtons = new PanelButton[9];
            for (int i = 0; i < 9; ++i)
            {
                EnabledPanelButtons[i] = GetTemplateChild("EnablePanel" + PanelToIndex[i]) as PanelButton;
            }

            foreach (PanelButton button in EnabledPanelButtons)
            {
                button.Click += EnabledPanelButtonClicked;
            }

            onConfigChange = new OnConfigChange(this, delegate(LoadFromConfigDelegateArgs args) {
                LoadUIFromConfig(ActivePad.GetFirstActivePadConfig(args));
            });
        }
Example #3
0
        protected override void OnClick()
        {
            if (AdvancedModeEnabled)
            {
                // Stop forcing advanced mode on, and sync the thresholds so we exit advanced mode.
                ForcedOn = false;

                foreach (Tuple <int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
                {
                    int           pad    = activePad.Item1;
                    SMX.SMXConfig config = activePad.Item2;
                    ConfigPresets.SyncUnifiedThresholds(ref config);
                    SMX.SMX.SetConfig(pad, config);
                }
                CurrentSMXDevice.singleton.FireConfigurationChanged(this);
            }
            else
            {
                // Enable advanced mode.
                ForcedOn = true;
            }

            // Refresh the UI.
            LoadUIFromConfig(ActivePad.GetFirstActivePadConfig());
        }
        private void ImportSettings(object sender, RoutedEventArgs e)
        {
            // Prompt for a file to read.
            Microsoft.Win32.OpenFileDialog dialog = new Microsoft.Win32.OpenFileDialog();
            dialog.FileName   = "StepManiaX settings";
            dialog.DefaultExt = ".smxcfg";
            dialog.Filter     = "StepManiaX settings (.smxcfg)|*.smxcfg";
            bool?result = dialog.ShowDialog();

            if (result == null || !(bool)result)
            {
                return;
            }

            string json = Helpers.ReadFile(dialog.FileName);

            // Apply settings from the file to all active pads.
            foreach (Tuple <int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
            {
                int           pad    = activePad.Item1;
                SMX.SMXConfig config = activePad.Item2;

                SMXHelpers.ImportSettingsFromJSON(json, ref config);
                SMX.SMX.SetConfig(pad, config);
            }

            CurrentSMXDevice.singleton.FireConfigurationChanged(null);
        }
Example #5
0
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            onConfigChange = new OnConfigChange(this, delegate(LoadFromConfigDelegateArgs args) {
                LoadUIFromConfig(ActivePad.GetFirstActivePadConfig(args));
            });
        }
 private void FactoryReset_Click(object sender, RoutedEventArgs e)
 {
     foreach (Tuple <int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
     {
         int pad = activePad.Item1;
         SMX.SMX.FactoryReset(pad);
     }
     CurrentSMXDevice.singleton.FireConfigurationChanged(null);
 }
        // Update which of the "Leave this application running", etc. blocks to display.
        private void RefreshUploadPadText(LoadFromConfigDelegateArgs args)
        {
            foreach (Tuple <int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
            {
                SMX.SMXConfig config = activePad.Item2;

                bool uploadsSupported = config.masterVersion >= 4;
                LeaveRunning.Visibility = uploadsSupported ? Visibility.Collapsed : Visibility.Visible;
                break;
            }
        }
Example #8
0
        public static void SyncSliderThresholds()
        {
            foreach (Tuple <int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
            {
                SMX.SMXConfig config = activePad.Item2;
                SyncSliderThresholdsForConfig(ref config);
                SMX.SMX.SetConfig(activePad.Item1, config);
            }

            CurrentSMXDevice.singleton.FireConfigurationChanged(null);
        }
Example #9
0
        private void Select()
        {
            foreach (Tuple <int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
            {
                int           pad    = activePad.Item1;
                SMX.SMXConfig config = activePad.Item2;

                ConfigPresets.SetPreset(Type, ref config);
                SMX.SMX.SetConfig(pad, config);
            }
            CurrentSMXDevice.singleton.FireConfigurationChanged(this);
        }
Example #10
0
        protected override void OnClick()
        {
            Properties.Settings.Default.AdvancedMode = !Properties.Settings.Default.AdvancedMode;
            if (!Properties.Settings.Default.AdvancedMode)
            {
                // Sync thresholds when we exit advanced mode.  XXX: not needed since MainWindow is recreating
                // sliders anyway
                ThresholdSettings.SyncSliderThresholds();
            }

            // Refresh the UI.
            LoadUIFromConfig(ActivePad.GetFirstActivePadConfig());
        }
Example #11
0
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            slider     = GetTemplateChild("Slider") as DoubleSlider;
            LowerLabel = GetTemplateChild("LowerValue") as Label;
            UpperLabel = GetTemplateChild("UpperValue") as Label;

            slider.ValueChanged += delegate(DoubleSlider slider) { SaveToConfig(); };

            onConfigChange = new OnConfigChange(this, delegate(LoadFromConfigDelegateArgs args) {
                LoadUIFromConfig(ActivePad.GetFirstActivePadConfig(args));
            });
        }
Example #12
0
        private void Select()
        {
            foreach (Tuple <int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
            {
                int           pad    = activePad.Item1;
                SMX.SMXConfig config = activePad.Item2;

                ConfigPresets.SetPreset(Type, ref config);
                Console.WriteLine("PresetButton::Select (" + Type + "): " +
                                  config.panelThreshold1Low + ", " + config.panelThreshold4Low + ", " + config.panelThreshold7Low + ", " + config.panelThreshold2Low);
                SMX.SMX.SetConfig(pad, config);
            }
            CurrentSMXDevice.singleton.FireConfigurationChanged(this);
        }
Example #13
0
        private void EnabledPanelButtonClicked(object sender, EventArgs e)
        {
            // One of the panel buttons on the panel toggle UI was clicked.  Toggle the
            // panel.
            int button = GetIndexFromButton(sender);

            Console.WriteLine("Clicked " + button);

            // Set the enabled sensor mask on both pads to the state of the UI.
            foreach (Tuple <int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
            {
                int           pad    = activePad.Item1;
                SMX.SMXConfig config = activePad.Item2;

                // This could be done algorithmically, but this is clearer.
                int[] PanelButtonToSensorIndex =
                {
                    0, 0, 1, 1, 2, 2, 3, 3, 4
                };
                byte[] PanelButtonToSensorMask =
                {
                    0xF0, 0x0F,
                    0xF0, 0x0F,
                    0xF0, 0x0F,
                    0xF0, 0x0F,
                    0xF0,
                };
                for (int i = 0; i < 5; ++i)
                {
                    config.enabledSensors[i] = 0;
                }

                for (int Panel = 0; Panel < 9; ++Panel)
                {
                    int  index = PanelButtonToSensorIndex[Panel];
                    byte mask  = PanelButtonToSensorMask[Panel];
                    if (EnabledPanelButtons[Panel].IsChecked == true)
                    {
                        config.enabledSensors[index] |= (byte)mask;
                    }
                }

                // If we're not in "light all panels" mode, sync up autoLightPanelMask
                // with the new enabledSensors.
                config.refreshAutoLightPanelMask();

                SMX.SMX.SetConfig(pad, config);
            }
        }
Example #14
0
        protected override void OnClick()
        {
            //SMX.SMXConfig firstConfig = ActivePad.GetFirstActivePadConfig();
            foreach (Tuple <int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
            {
                int           pad    = activePad.Item1;
                SMX.SMXConfig config = activePad.Item2;
                config.setLightAllPanelsMode(!LightAllPanels);
                SMX.SMX.SetConfig(pad, config);
            }
            CurrentSMXDevice.singleton.FireConfigurationChanged(this);

            // Refresh the UI.
            //LoadUIFromConfig(firstConfig);
        }
Example #15
0
        private void SaveToConfig()
        {
            if (UpdatingUI)
            {
                return;
            }

            // Apply the change and save it to the devices.
            foreach (Tuple <int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
            {
                int           pad    = activePad.Item1;
                SMX.SMXConfig config = activePad.Item2;

                SetValueToConfig(ref config);
                SMX.SMX.SetConfig(pad, config);
                CurrentSMXDevice.singleton.FireConfigurationChanged(this);
            }
        }
Example #16
0
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            Button button = GetTemplateChild("PART_Button") as Button;

            button.Click += delegate(object sender, RoutedEventArgs e) { Select(); };

            onConfigChange = new OnConfigChange(this, delegate(LoadFromConfigDelegateArgs args) {
                foreach (Tuple <int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
                {
                    SMX.SMXConfig config = activePad.Item2;
                    string CurrentPreset = ConfigPresets.GetPreset(config);
                    Selected             = CurrentPreset == Type;
                    break;
                }
            });
        }
        private void UploadLatestGIF()
        {
            // Create a progress window.  Center it on top of the main window.
            ProgressWindow dialog = new ProgressWindow();

            dialog.Left  = (Left + Width / 2) - (dialog.Width / 2);
            dialog.Top   = (Top + Height / 2) - (dialog.Height / 2);
            dialog.Title = "Storing animations on pad...";

            int[] CurrentProgress = new int[] { 0, 0 };

            // Upload graphics for all connected pads.  If two pads are connected
            // we can start both of these simultaneously, and they'll be sent in
            // parallel.
            int total = 0;

            foreach (Tuple <int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
            {
                int pad = activePad.Item1;
                SMX.SMX.LightsUpload_BeginUpload(pad, delegate(int progress) {
                    // This is called from a thread, so dispatch back to the main thread.
                    Dispatcher.Invoke(delegate() {
                        // Store progress, so we can sum both pads.
                        CurrentProgress[pad] = progress;

                        dialog.SetProgress(CurrentProgress[0] + CurrentProgress[1]);
                        if (progress == 100)
                        {
                            dialog.Close();
                        }
                    });
                });

                // Each pad that we start uploading to is 100 units of progress.
                total += 100;
                dialog.SetTotal(total);
            }

            // Show the progress window as a modal dialog.  This function won't return
            // until we call dialog.Close above.
            dialog.ShowDialog();
        }
Example #18
0
        protected override void OnClick()
        {
            foreach (Tuple <int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
            {
                int           pad    = activePad.Item1;
                SMX.SMXConfig config = activePad.Item2;

                // Disable the sensor by setting its high threshold to 255, and enable it by syncing it up
                // with the other thresholds.
                if (!EnableSensor)
                {
                    config.panelSettings[4].fsrHighThreshold[2] = config.panelSettings[4].fsrHighThreshold[0];
                }
                else
                {
                    config.panelSettings[4].fsrHighThreshold[2] = 255;
                }
                SMX.SMX.SetConfig(pad, config);
            }
            CurrentSMXDevice.singleton.FireConfigurationChanged(this);
        }
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            OnConfigChange onConfigChange;

            onConfigChange = new OnConfigChange(this, delegate(LoadFromConfigDelegateArgs args) {
                int SelectedPad = Panel < 9? 0:1;
                int PanelIndex  = Panel % 9;
                Pressed         = args.controller[SelectedPad].inputs[PanelIndex];

                Warning = !args.controller[SelectedPad].test_data.bHaveDataFromPanel[PanelIndex] ||
                          args.controller[SelectedPad].test_data.AnySensorsOnPanelNotResponding(PanelIndex) ||
                          args.controller[SelectedPad].test_data.AnyBadJumpersOnPanel(PanelIndex);

                // Only show this panel button if the panel's input is enabled.
                SMX.SMXConfig config = ActivePad.GetFirstActivePadConfig(args);
                Visibility           = ShouldBeDisplayed(config)? Visibility.Visible:Visibility.Collapsed;
            });
            onConfigChange.RefreshOnInputChange    = true;
            onConfigChange.RefreshOnTestDataChange = true;
        }
        private void ExportSettings(object sender, RoutedEventArgs e)
        {
            // Save the current thresholds on the first available pad as a preset.
            foreach (Tuple <int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
            {
                int           pad    = activePad.Item1;
                SMX.SMXConfig config = activePad.Item2;
                string        json   = SMXHelpers.ExportSettingsToJSON(config);

                Microsoft.Win32.SaveFileDialog dialog = new Microsoft.Win32.SaveFileDialog();
                dialog.FileName   = "StepManiaX settings";
                dialog.DefaultExt = ".smxcfg";
                dialog.Filter     = "StepManiaX settings (.smxcfg)|*.smxcfg";
                bool?result = dialog.ShowDialog();
                if (result == null || !(bool)result)
                {
                    return;
                }

                System.IO.File.WriteAllText(dialog.FileName, json);
                return;
            }
        }
        // We have two main modes: color mode and GIF animation mode.  These behave differently
        // on gen1-3 pads and gen4 pads.
        //
        // On gen4 pads, this determines whether we show animations on press, or show a solid color.
        // This makes it easy to pick a solid color if that's all you want to change, instead of having
        // to make a GIF with the color.  We always show animations on release in both modes.
        //
        // On gen1-3 pads, panels are black in color mode, so they behave the same as they did originally.
        // Animations are only shown in GIF animation mode.  These animations are enabled with
        // LightsAnimation_SetAuto.
        //
        // The gen1-3 firmware ignores the AutoLightingUsePressedAnimations flag, but we still use it to
        // store which mode the user has selected.
        private void PressedColorModeButton(object sender, RoutedEventArgs e)
        {
            // The user pressed either the "panel colors" or "GIF animations" button.
            bool pressedPanelColors = sender == PanelColorsButton;

            foreach (Tuple <int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
            {
                SMX.SMXConfig config = activePad.Item2;

                // If we're in panel colors mode, clear the AutoLightingUsePressedAnimations flag.
                // Otherwise, set it.
                if (pressedPanelColors)
                {
                    config.configFlags &= ~SMX.SMXConfigFlags.AutoLightingUsePressedAnimations;
                }
                else
                {
                    config.configFlags |= SMX.SMXConfigFlags.AutoLightingUsePressedAnimations;
                }
                SMX.SMX.SetConfig(activePad.Item1, config);
            }

            CurrentSMXDevice.singleton.FireConfigurationChanged(null);
        }
        private void LoadUIFromConfig(LoadFromConfigDelegateArgs args)
        {
            // Refresh whether LightsAnimation_SetAuto should be enabled.
            SMX.SMXConfig firstConfig = ActivePad.GetFirstActivePadConfig();
            bool          usePressedAnimationsEnabled = (firstConfig.configFlags & SMX.SMXConfigFlags.AutoLightingUsePressedAnimations) != 0;

            SMX.SMX.LightsAnimation_SetAuto(usePressedAnimationsEnabled);

            bool EitherControllerConnected = args.controller[0].info.connected || args.controller[1].info.connected;

            Main.Visibility          = EitherControllerConnected ? Visibility.Visible : Visibility.Hidden;
            Searching.Visibility     = EitherControllerConnected ? Visibility.Hidden : Visibility.Visible;
            ConnectedPads.Visibility = EitherControllerConnected ? Visibility.Visible : Visibility.Hidden;
            PanelColorP1.Visibility  = args.controller[0].info.connected ? Visibility.Visible : Visibility.Collapsed;
            PanelColorP2.Visibility  = args.controller[1].info.connected ? Visibility.Visible : Visibility.Collapsed;
            EnableCenterTopSensorCheckbox.Visibility =
                P1_Floor.Visibility     =
                    P2_Floor.Visibility =
                        args.firmwareVersion() >= 5 ? Visibility.Visible : Visibility.Collapsed;

            // Show the color slider or GIF UI depending on which one is set in flags.
            // If both pads are turned on, just use the first one.
            foreach (Tuple <int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
            {
                SMX.SMXConfig config = activePad.Item2;

                // If SMXConfigFlags_AutoLightingUsePressedAnimations is set, show the GIF UI.
                // If it's not set, show the color slider UI.
                SMX.SMXConfigFlags flags  = config.configFlags;
                bool usePressedAnimations = (flags & SMX.SMXConfigFlags.AutoLightingUsePressedAnimations) != 0;
                ColorPickerGroup.Visibility = usePressedAnimations ? Visibility.Collapsed : Visibility.Visible;
                GIFGroup.Visibility         = usePressedAnimations ? Visibility.Visible : Visibility.Collapsed;

                // Tell the color mode buttons which one is selected, to set the button highlight.
                PanelColorsButton.Selected   = !usePressedAnimations;
                GIFAnimationsButton.Selected = usePressedAnimations;

                break;
            }

            RefreshConnectedPadList(args);
            RefreshUploadPadText(args);
            RefreshSelectedColorPicker();

            // If a device has connected or disconnected, refresh the displayed threshold
            // sliders.  Don't do this otherwise, or we'll do this when the sliders are
            // dragged.
            if (args.ConnectionsChanged)
            {
                CreateThresholdSliders();
            }

            // Show the threshold warning explanation if any panels are showing the threshold warning icon.
            bool ShowThresholdWarningText = false;

            foreach (Tuple <int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
            {
                SMX.SMXConfig config = activePad.Item2;
                for (int panelIdx = 0; panelIdx < 9; ++panelIdx)
                {
                    for (int sensor = 0; sensor < 4; ++sensor)
                    {
                        if (config.ShowThresholdWarning(panelIdx, sensor))
                        {
                            ShowThresholdWarningText = true;
                        }
                    }
                }
            }
            ThresholdWarningText.Visibility = ShowThresholdWarningText ? Visibility.Visible : Visibility.Hidden;

            // If a second controller has connected and we're on Both, see if we need to prompt
            // to sync configs.  We only actually need to do this if a controller just connected.
            if (args.ConfigurationChanged)
            {
                CheckConfiguringBothPads(args);
            }
        }
        private void LoadGIF(object sender, RoutedEventArgs e)
        {
            // If the "load idle GIF" button was pressed, load the released animation.
            // Otherwise, load the pressed animation.
            bool pressed = sender == this.LoadPressed;

            // Prompt for a file to read.
            Microsoft.Win32.OpenFileDialog dialog = new Microsoft.Win32.OpenFileDialog();
            dialog.FileName   = "Select an animated GIF";
            dialog.DefaultExt = ".gif";
            dialog.Filter     = "Animated GIF (.gif)|*.gif";
            bool?result = dialog.ShowDialog();

            if (result == null || !(bool)result)
            {
                return;
            }

            byte[]             buf  = Helpers.ReadBinaryFile(dialog.FileName);
            SMX.SMX.LightsType type = pressed ? SMX.SMX.LightsType.LightsType_Pressed : SMX.SMX.LightsType.LightsType_Released;

            foreach (Tuple <int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
            {
                int pad = activePad.Item1;

                // Load the animation.
                string error;
                if (!SMX.SMX.LightsAnimation_Load(buf, pad, type, out error))
                {
                    // Any errors here are problems with the GIF, so there's no point trying
                    // to load it for the second pad if the first returns an error.  Just show the
                    // error and stop.
                    MessageBox.Show(error, "Error", MessageBoxButton.OK, MessageBoxImage.Warning);

                    // Return without saving to settings on error.
                    return;
                }

                // Save the GIF to disk so we can load it quickly later.
                Helpers.SaveAnimationToDisk(pad, type, buf);

                // Refresh after loading a GIF to update the "Leave this application running" text.
                CurrentSMXDevice.singleton.FireConfigurationChanged(null);
            }

            // For firmwares that support it, upload the animation to the pad now.  Otherwise,
            // we'll run the animation directly.
            foreach (Tuple <int, SMX.SMXConfig> activePad in ActivePad.ActivePads())
            {
                int pad = activePad.Item1;

                SMX.SMXConfig config;
                if (!SMX.SMX.GetConfig(pad, out config))
                {
                    continue;
                }

                if (config.masterVersion >= 4)
                {
                    UploadLatestGIF();
                }

                break;
            }
        }
Example #24
0
 void RefreshVisibility()
 {
     SMX.SMXConfig config = ActivePad.GetFirstActivePadConfig();
     this.Visibility = ShouldBeDisplayed(config)? Visibility.Visible:Visibility.Collapsed;
 }
        bool IsThresholdSliderShown(string type)
        {
            bool AdvancedModeEnabled = Properties.Settings.Default.AdvancedMode;

            SMX.SMXConfig config        = ActivePad.GetFirstActivePadConfig();
            bool[]        enabledPanels = config.GetEnabledPanels();

            // Check the list of sensors this slider controls.  If the list is empty, don't show it.
            // For example, if the user adds all four sensors on the up panel to custom-sensors, the
            // up button has nothing left to control, so we'll hide it.
            //
            // Don't do this for custom, inner-sensors or outer-sensors.  Those are always shown in
            // advanced mode.
            List <ThresholdSettings.PanelAndSensor> panelAndSensors = ThresholdSettings.GetControlledSensorsForSliderType(type, AdvancedModeEnabled, false);

            if (type == "custom-sensors" || type == "inner-sensors" || type == "outer-sensors")
            {
                if (!AdvancedModeEnabled || !config.fsr())
                {
                    return(false);
                }
            }
            else
            {
                if (panelAndSensors.Count == 0)
                {
                    return(false);
                }
            }

            // Hide thresholds that only affect panels that are disabled, so we don't show
            // corner panel sliders in advanced mode if the corner panels are disabled.  We
            // don't handle this in GetControlledSensorsForSliderType, since we do want cardinal
            // and corner to write thresholds to disabled panels, so they're in sync if they're
            // turned back on.
            switch (type)
            {
            case "up-left": return(enabledPanels[0]);

            case "up": return(enabledPanels[1]);

            case "up-right": return(enabledPanels[2]);

            case "left": return(enabledPanels[3]);

            case "center": return(enabledPanels[4]);

            case "right": return(enabledPanels[5]);

            case "down-left": return(enabledPanels[6]);

            case "down": return(enabledPanels[7]);

            case "down-right": return(enabledPanels[8]);

            // Show cardinal and corner if at least one panel they affect is enabled.
            case "cardinal": return(enabledPanels[3] || enabledPanels[5] || enabledPanels[8]);

            case "corner": return(enabledPanels[0] || enabledPanels[2] || enabledPanels[6] || enabledPanels[8]);

            default: return(true);
            }
        }
        private void Refresh(LoadFromConfigDelegateArgs args)
        {
            // First, make sure a valid panel is selected.
            SMX.SMXConfig config = ActivePad.GetFirstActivePadConfig(args);
            SelectValidPanel(config);

            RefreshSelectedPanel();

            // Make sure SetShowAllLights is disabled if the controller is disconnected, since
            // we can miss mouse up events.
            bool EitherControllerConnected = args.controller[0].info.connected || args.controller[1].info.connected;

            if (!EitherControllerConnected)
            {
                SetShowAllLights?.Invoke(false);
            }

            P1Diagnostics.Visibility = args.controller[0].info.connected? Visibility.Visible:Visibility.Collapsed;
            P2Diagnostics.Visibility = args.controller[1].info.connected? Visibility.Visible:Visibility.Collapsed;

            // Update the displayed DIP switch icons.
            int SelectedPad = SelectedPanel < 9? 0:1;
            int PanelIndex  = SelectedPanel % 9;
            int dip         = args.controller[SelectedPad].test_data.iDIPSwitchPerPanel[PanelIndex];

            CurrentDIP.Frame  = dip;
            ExpectedDIP.Frame = PanelIndex;

            // Show or hide the sensor error text.
            bool AnySensorsNotResponding = false, HaveIncorrectSensorDIP = false;

            if (args.controller[SelectedPad].test_data.bHaveDataFromPanel[PanelIndex])
            {
                AnySensorsNotResponding = args.controller[SelectedPad].test_data.AnySensorsOnPanelNotResponding(PanelIndex);

                // Don't show both warnings.
                HaveIncorrectSensorDIP = !AnySensorsNotResponding && args.controller[SelectedPad].test_data.AnyBadJumpersOnPanel(PanelIndex);
            }
            NoResponseFromSensors.Visibility = AnySensorsNotResponding? Visibility.Visible:Visibility.Collapsed;
            BadSensorDIPSwitches.Visibility  = HaveIncorrectSensorDIP? Visibility.Visible:Visibility.Collapsed;

            // Adjust the DIP labels to match the PCB.
            bool DIPLabelsOnLeft = config.masterVersion < 4;

            DIPLabelRight.Visibility = DIPLabelsOnLeft? Visibility.Collapsed:Visibility.Visible;
            DIPLabelLeft.Visibility  = DIPLabelsOnLeft? Visibility.Visible:Visibility.Collapsed;

            // Update the level bar from the test mode data for the selected panel.
            for (int sensor = 0; sensor < 4; ++sensor)
            {
                var   controllerData = args.controller[SelectedPad];
                Int16 value          = controllerData.test_data.sensorLevel[PanelIndex * 4 + sensor];

                if (GetTestMode() == SMX.SMX.SensorTestMode.Noise)
                {
                    // In noise mode, we receive standard deviation values squared.  Display the square
                    // root, since the panels don't do this for us.  This makes the numbers different
                    // than the configured value (square it to convert back), but without this we display
                    // a bunch of 4 and 5-digit numbers that are too hard to read.
                    value = (Int16)Math.Sqrt(value);
                }

                LevelBarText[sensor].Visibility = Visibility.Visible;
                if (!args.controller[SelectedPad].test_data.bHaveDataFromPanel[PanelIndex])
                {
                    LevelBars[sensor].Value         = 0;
                    LevelBarText[sensor].Visibility = Visibility.Hidden;
                    LevelBarText[sensor].Content    = "-";
                    LevelBars[sensor].Error         = false;
                }
                else if (args.controller[SelectedPad].test_data.bBadSensorInput[PanelIndex * 4 + sensor])
                {
                    LevelBars[sensor].Value      = 0;
                    LevelBarText[sensor].Content = "!";
                    LevelBars[sensor].Error      = true;
                }
                else
                {
                    // Very slightly negative values happen due to noise.  They don't indicate a
                    // problem, but they're confusing in the UI, so clamp them away.
                    if (value < 0 && value >= -10)
                    {
                        value = 0;
                    }

                    // Scale differently depending on if this is an FSR panel or a load cell panel.
                    bool isFSR = controllerData.config.masterVersion >= 4 && (controllerData.config.configFlags & SMX.SMXConfigFlags.PlatformFlags_FSR) != 0;
                    if (isFSR)
                    {
                        value >>= 2;
                    }
                    float maxValue = isFSR? 250:500;
                    LevelBars[sensor].Value      = value / maxValue;
                    LevelBarText[sensor].Content = value;
                    LevelBars[sensor].Error      = false;
                }
            }

            NoResponseFromPanel.Visibility = Visibility.Collapsed;
            CurrentDIPGroup.Visibility     = Visibility.Visible;
            if (!args.controller[SelectedPad].test_data.bHaveDataFromPanel[PanelIndex])
            {
                NoResponseFromPanel.Visibility   = Visibility.Visible;
                NoResponseFromSensors.Visibility = Visibility.Collapsed;
                CurrentDIPGroup.Visibility       = Visibility.Hidden;
                return;
            }
        }