agrobot_base/AgroBase/OperationControl/Controls/AttitudeIndicator.xaml.cs

200 lines
6.9 KiB
C#

using System.ComponentModel;
using System.Windows;
using System.Windows.Media;
using UserControl = System.Windows.Controls.UserControl;
using Color = System.Windows.Media.Color;
namespace OperationControl.Controls
{
/// <summary>
/// Interaction logic for AttitudeIndicator.xaml
/// </summary>
public partial class AttitudeIndicator : UserControl, INotifyPropertyChanged
{
public AttitudeIndicator()
{
InitializeComponent();
DataContext = this;
UpdateChrome();
}
// ===== Dependency Properties =====
public static readonly DependencyProperty RollDegProperty =
DependencyProperty.Register(nameof(RollDeg), typeof(double), typeof(AttitudeIndicator),
new PropertyMetadata(0.0, OnAngleChanged));
public static readonly DependencyProperty PitchDegProperty =
DependencyProperty.Register(nameof(PitchDeg), typeof(double), typeof(AttitudeIndicator),
new PropertyMetadata(0.0, OnAngleChanged));
public static readonly DependencyProperty PxPerDegProperty =
DependencyProperty.Register(nameof(PxPerDeg), typeof(double), typeof(AttitudeIndicator),
new PropertyMetadata(2.0, OnAngleChanged));
public static readonly DependencyProperty WarningLimitProperty =
DependencyProperty.Register(nameof(WarningLimit), typeof(double), typeof(AttitudeIndicator),
new PropertyMetadata(10.0, OnAngleChanged));
public static readonly DependencyProperty CriticalLimitProperty =
DependencyProperty.Register(nameof(CriticalLimit), typeof(double), typeof(AttitudeIndicator),
new PropertyMetadata(18.0, OnAngleChanged));
private static readonly DependencyPropertyKey PitchOffsetPxPropertyKey =
DependencyProperty.RegisterReadOnly(
nameof(PitchOffsetPx),
typeof(double),
typeof(AttitudeIndicator),
new PropertyMetadata(0.0));
public static readonly DependencyProperty PitchOffsetPxProperty = PitchOffsetPxPropertyKey.DependencyProperty;
public double PitchOffsetPx
{
get => (double)GetValue(PitchOffsetPxProperty);
private set => SetValue(PitchOffsetPxPropertyKey, value);
}
public double RollDeg
{
get => (double)GetValue(RollDegProperty);
set => SetValue(RollDegProperty, value);
}
public double PitchDeg
{
get => (double)GetValue(PitchDegProperty);
set => SetValue(PitchDegProperty, value);
}
public double PxPerDeg
{
get => (double)GetValue(PxPerDegProperty);
set => SetValue(PxPerDegProperty, value);
}
public double WarningLimit
{
get => (double)GetValue(WarningLimitProperty);
set => SetValue(WarningLimitProperty, value);
}
public double CriticalLimit
{
get => (double)GetValue(CriticalLimitProperty);
set => SetValue(CriticalLimitProperty, value);
}
// === LATERAL ERROR ===
public static readonly DependencyProperty LateralErrorProperty =
DependencyProperty.Register(
nameof(LateralError),
typeof(double),
typeof(AttitudeIndicator),
new PropertyMetadata(0.0, OnLateralChanged));
public static readonly DependencyProperty LateralMaxAbsProperty =
DependencyProperty.Register(
nameof(LateralMaxAbs),
typeof(double),
typeof(AttitudeIndicator),
new PropertyMetadata(50.0, OnLateralChanged)); // 0,5 m por padrão
private static readonly DependencyPropertyKey LateralNormalizedPropertyKey =
DependencyProperty.RegisterReadOnly(
nameof(LateralNormalized),
typeof(double),
typeof(AttitudeIndicator),
new PropertyMetadata(0.0));
public static readonly DependencyProperty LateralNormalizedProperty = LateralNormalizedPropertyKey.DependencyProperty;
public double LateralError
{
get => (double)GetValue(LateralErrorProperty);
set => SetValue(LateralErrorProperty, value);
}
public double LateralMaxAbs
{
get => (double)GetValue(LateralMaxAbsProperty);
set => SetValue(LateralMaxAbsProperty, value);
}
/// <summary>
/// Erro lateral normalizado em [-1, 1]
/// -1 = limite à esquerda, 0 = centro, +1 = limite à direita
/// </summary>
public double LateralNormalized
{
get => (double)GetValue(LateralNormalizedProperty);
private set => SetValue(LateralNormalizedPropertyKey, value);
}
// Derivados
public double MaxAbsAngle => Math.Max(Math.Abs(RollDeg), Math.Abs(PitchDeg));
public string RollTexto => $"FRT {FormatSignedDeg(RollDeg)}";
public string PitchTexto => $"LAT {FormatSignedDeg(PitchDeg)}";
private static string FormatSignedDeg(double value)
{
if (double.IsNaN(value) || double.IsInfinity(value))
return "--.-°";
return value.ToString("+0.0;-0.0;0.0") + "°";
}
public event PropertyChangedEventHandler PropertyChanged;
void Raise(string n) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(n));
static void OnAngleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var c = (AttitudeIndicator)d;
c.PitchOffsetPx = -c.PitchDeg * c.PxPerDeg;
c.Raise(nameof(PitchOffsetPx));
c.Raise(nameof(MaxAbsAngle));
c.Raise(nameof(RollTexto));
c.Raise(nameof(PitchTexto));
c.UpdateChrome();
}
void UpdateChrome()
{
if (Chrome == null) return;
var a = MaxAbsAngle;
if (a >= CriticalLimit)
Chrome.BorderBrush = new SolidColorBrush(Color.FromRgb(0xE7, 0x4C, 0x3C)); // vermelho
else if (a >= WarningLimit)
Chrome.BorderBrush = new SolidColorBrush(Color.FromRgb(0xF1, 0xC4, 0x0F)); // amarelo
else
Chrome.BorderBrush = new SolidColorBrush(Color.FromRgb(0x2E, 0xCC, 0x71)); // verde
}
static void OnLateralChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var c = (AttitudeIndicator)d;
c.UpdateLateral();
c.Raise(nameof(LateralNormalized));
}
void UpdateLateral()
{
var max = Math.Max(0.0001, LateralMaxAbs);
// normaliza para [-1, 1] e faz clamp
var n = LateralError / max;
if (n > 1) n = 1;
if (n < -1) n = -1;
LateralNormalized = n;
}
}
}