You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
SMX_PGE/smx-config/DiagnosticsWidgets.cs

403 lines
17 KiB

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Input;
namespace smx_config
{
public class DiagnosticsPanelButton: PanelSelectButton
{
public static readonly DependencyProperty PanelProperty = DependencyProperty.RegisterAttached("Panel",
typeof(int), typeof(PanelSelectButton), new FrameworkPropertyMetadata(0));
public int Panel {
get { return (int) this.GetValue(PanelProperty); }
set { this.SetValue(PanelProperty, value); }
}
// Which panel is currently selected.
public static readonly DependencyProperty SelectedPanelProperty = DependencyProperty.RegisterAttached("SelectedPanel",
typeof(int), typeof(PanelSelectButton), new FrameworkPropertyMetadata(0));
public int SelectedPanel {
get { return (int) this.GetValue(SelectedPanelProperty); }
set { this.SetValue(SelectedPanelProperty, value); }
}
// True if this panel is being pressed.
public static readonly DependencyProperty PressedProperty = DependencyProperty.Register("Pressed",
typeof(bool), typeof(DiagnosticsPanelButton), new FrameworkPropertyMetadata(false));
public bool Pressed {
get { return (bool) GetValue(PressedProperty); }
set { SetValue(PressedProperty, value); }
}
// True if a warning icon should be displayed for this panel.
public static readonly DependencyProperty WarningProperty = DependencyProperty.Register("Warning",
typeof(bool), typeof(DiagnosticsPanelButton), new FrameworkPropertyMetadata(false));
public bool Warning {
get { return (bool) GetValue(WarningProperty); }
set { SetValue(WarningProperty, value); }
}
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;
}
protected override void OnClick()
{
base.OnClick();
// Select this panel.
SelectedPanel = Panel;
CurrentSMXDevice.singleton.FireConfigurationChanged(this);
}
// Return true if this button should be displayed.
private bool ShouldBeDisplayed(SMX.SMXConfig config)
{
bool[] enabledPanels = config.GetEnabledPanels();
int PanelIndex = Panel % 9;
return enabledPanels[PanelIndex];
}
}
public class LevelBar: Control
{
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value",
typeof(double), typeof(LevelBar), new FrameworkPropertyMetadata(0.5, ValueChangedCallback));
public double Value {
get { return (double) GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public static readonly DependencyProperty ErrorProperty = DependencyProperty.Register("Error",
typeof(bool), typeof(LevelBar), new FrameworkPropertyMetadata(false, ValueChangedCallback));
public bool Error {
get { return (bool) GetValue(ErrorProperty); }
set { SetValue(ErrorProperty, value); }
}
private Rectangle Fill, Back;
private static void ValueChangedCallback(DependencyObject target, DependencyPropertyChangedEventArgs args)
{
LevelBar self = target as LevelBar;
self.Refresh();
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
Fill = Template.FindName("Fill", this) as Rectangle;
Back = Template.FindName("Back", this) as Rectangle;
Refresh();
}
private void Refresh()
{
// If Error is true, fill the bar red.
double FillHeight = Error? 1:Value;
Fill.Height = Math.Round(Math.Max(FillHeight, 0) * (Back.Height - 2));
if(Error)
{
Fill.Fill = new SolidColorBrush(Color.FromRgb(255,0,0));
}
else
{
// Scale from green (#FF0000) to yellow (#FFFF00) as we go from 0 to .4.
double ColorValue = Value / 0.4;
Byte Yellow = (Byte) (Math.Max(0, Math.Min(255, ColorValue * 255)) );
Fill.Fill = new SolidColorBrush(Color.FromRgb(255,Yellow,0));
}
}
}
public class DiagnosticsControl: Control
{
// Which panel is currently selected:
public static readonly DependencyProperty SelectedPanelProperty = DependencyProperty.Register("SelectedPanel",
typeof(int), typeof(DiagnosticsControl), new FrameworkPropertyMetadata(0));
public int SelectedPanel {
get { return (int) this.GetValue(SelectedPanelProperty); }
set { this.SetValue(SelectedPanelProperty, value); }
}
private LevelBar[] LevelBars;
private Label[] LevelBarText;
private ComboBox DiagnosticMode;
private Panel CurrentDIPGroup;
private FrameImage CurrentDIP;
private FrameImage ExpectedDIP;
private FrameworkElement NoResponseFromPanel;
private FrameworkElement NoResponseFromSensors;
private FrameworkElement BadSensorDIPSwitches;
private FrameworkElement P1Diagnostics, P2Diagnostics;
private FrameworkElement DIPLabelLeft, DIPLabelRight;
public delegate void ShowAllLightsEvent(bool on);
public event ShowAllLightsEvent SetShowAllLights;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
LevelBars = new LevelBar[4];
LevelBars[0] = Template.FindName("SensorBar1", this) as LevelBar;
LevelBars[1] = Template.FindName("SensorBar2", this) as LevelBar;
LevelBars[2] = Template.FindName("SensorBar3", this) as LevelBar;
LevelBars[3] = Template.FindName("SensorBar4", this) as LevelBar;
LevelBarText = new Label[4];
LevelBarText[0] = Template.FindName("SensorBarLevel1", this) as Label;
LevelBarText[1] = Template.FindName("SensorBarLevel2", this) as Label;
LevelBarText[2] = Template.FindName("SensorBarLevel3", this) as Label;
LevelBarText[3] = Template.FindName("SensorBarLevel4", this) as Label;
DiagnosticMode = Template.FindName("DiagnosticMode", this) as ComboBox;
CurrentDIPGroup = Template.FindName("CurrentDIPGroup", this) as Panel;
CurrentDIP = Template.FindName("CurrentDIP", this) as FrameImage;
ExpectedDIP = Template.FindName("ExpectedDIP", this) as FrameImage;
NoResponseFromPanel = Template.FindName("NoResponseFromPanel", this) as FrameworkElement;
NoResponseFromSensors = Template.FindName("NoResponseFromSensors", this) as FrameworkElement;
BadSensorDIPSwitches = Template.FindName("BadSensorDIPSwitches", this) as FrameworkElement;
P1Diagnostics = Template.FindName("P1Diagnostics", this) as FrameworkElement;
P2Diagnostics = Template.FindName("P2Diagnostics", this) as FrameworkElement;
DIPLabelRight = Template.FindName("DIPLabelRight", this) as FrameworkElement;
DIPLabelLeft = Template.FindName("DIPLabelLeft", this) as FrameworkElement;
Button Recalibrate = Template.FindName("Recalibrate", this) as Button;
Recalibrate.Click += delegate(object sender, RoutedEventArgs e)
{
for(int pad = 0; pad < 2; ++pad)
SMX.SMX.ForceRecalibration(pad);
};
// Note that we won't get a MouseUp if the display is hidden due to a controller
// disconnection while the mouse is held. We handle this in Refresh().
Button LightAll = Template.FindName("LightAll", this) as Button;
LightAll.PreviewMouseDown += delegate(object sender, MouseButtonEventArgs e)
{
SetShowAllLights?.Invoke(true);
};
LightAll.PreviewMouseUp += delegate(object sender, MouseButtonEventArgs e)
{
SetShowAllLights?.Invoke(false);
};
// Update the test mode when the dropdown is changed.
DiagnosticMode.AddHandler(ComboBox.SelectionChangedEvent, new RoutedEventHandler(delegate(object sender, RoutedEventArgs e)
{
for(int pad = 0; pad < 2; ++pad)
SMX.SMX.SetSensorTestMode(pad, GetTestMode());
}));
OnConfigChange onConfigChange;
onConfigChange = new OnConfigChange(this, delegate (LoadFromConfigDelegateArgs args) {
Refresh(args);
});
onConfigChange.RefreshOnTestDataChange = true;
Loaded += delegate(object sender, RoutedEventArgs e)
{
for(int pad = 0; pad < 2; ++pad)
SMX.SMX.SetSensorTestMode(pad, GetTestMode());
};
Unloaded += delegate(object sender, RoutedEventArgs e)
{
for(int pad = 0; pad < 2; ++pad)
SMX.SMX.SetSensorTestMode(pad, SMX.SMX.SensorTestMode.Off);
};
}
private SMX.SMX.SensorTestMode GetTestMode()
{
switch(DiagnosticMode.SelectedIndex)
{
case 0: return SMX.SMX.SensorTestMode.CalibratedValues;
case 1: return SMX.SMX.SensorTestMode.UncalibratedValues;
case 2: return SMX.SMX.SensorTestMode.Noise;
case 3:
default: return SMX.SMX.SensorTestMode.Tare;
}
}
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;
}
}
// If the selected panel isn't enabled for input, select another one.
private void SelectValidPanel(SMX.SMXConfig config)
{
bool[] enabledPanels = config.GetEnabledPanels();
int SelectedPanelIndex = SelectedPanel % 9;
// If we're not selected, or this button is visible, we don't need to do anything.
if(!enabledPanels[SelectedPanelIndex])
SelectedPanel = config.GetFirstEnabledPanel();
}
// Update the selected diagnostics button based on the value of selectedButton.
private void RefreshSelectedPanel()
{
LoadFromConfigDelegateArgs args = CurrentSMXDevice.singleton.GetState();
DiagnosticsPanelButton[] buttons = getPanelSelectionButtons();
// Tell the buttons which one is selected.
foreach(DiagnosticsPanelButton button in buttons)
button.IsSelected = button.Panel == SelectedPanel;
}
// Return all panel selection buttons.
DiagnosticsPanelButton[] getPanelSelectionButtons()
{
DiagnosticsPanelButton[] result = new DiagnosticsPanelButton[9];
for(int i = 0; i < 9; ++i)
{
result[i] = Template.FindName("Panel" + i, this) as DiagnosticsPanelButton;
}
return result;
}
}
public class PanelTestModeCheckbox: CheckBox
{
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
}
protected override void OnClick()
{
base.OnClick();
SMX.SMX.SetPanelTestMode((bool) IsChecked? SMX.SMX.PanelTestMode.PressureTest:SMX.SMX.PanelTestMode.Off);
}
}
}