using System.Windows; using System.Windows.Controls; using System.Windows.Media; using UserControl = System.Windows.Controls.UserControl; using Brush = System.Windows.Media.Brush; using Point = System.Windows.Point; using System.ComponentModel; using System.Runtime.CompilerServices; namespace OperationControl.Controls.Pulverizador { /// /// Interaction logic for BicoControl.xaml /// public partial class BicoLequeIndicator : UserControl { public BicoLequeIndicator() { InitializeComponent(); Loaded += (s, e) => UpdateGeometry(); } #region DependencyProperties básicas // Comando enviado para a solenoide public bool IsCommandOn { get => (bool)GetValue(IsCommandOnProperty); set => SetValue(IsCommandOnProperty, value); } public static readonly DependencyProperty IsCommandOnProperty = DependencyProperty.Register(nameof(IsCommandOn), typeof(bool), typeof(BicoLequeIndicator), new PropertyMetadata(false, OnVisualPropertyChanged)); // Leitura de ligado (se está realmente pulverizando) public bool IsSpraying { get => (bool)GetValue(IsSprayingProperty); set => SetValue(IsSprayingProperty, value); } public static readonly DependencyProperty IsSprayingProperty = DependencyProperty.Register(nameof(IsSpraying), typeof(bool), typeof(BicoLequeIndicator), new PropertyMetadata(false, OnVisualPropertyChanged)); // Ângulo central do jato (0 = vertical; positivo gira p/ esquerda, negativo p/ direita) public double AnguloControle { get => (double)GetValue(AnguloControleProperty); set => SetValue(AnguloControleProperty, value); } public static readonly DependencyProperty AnguloControleProperty = DependencyProperty.Register(nameof(AnguloControle), typeof(double), typeof(BicoLequeIndicator), new PropertyMetadata(0.0, OnGeometryPropertyChanged)); // Abertura total do leque (em graus) – ex: 110° public double OpeningAngle { get => (double)GetValue(OpeningAngleProperty); set => SetValue(OpeningAngleProperty, value); } public static readonly DependencyProperty OpeningAngleProperty = DependencyProperty.Register(nameof(OpeningAngle), typeof(double), typeof(BicoLequeIndicator), new PropertyMetadata(110.0, OnGeometryPropertyChanged)); // Vazao instantanea (mL/s) public double Vazao { get => (double)GetValue(VazaoProperty); set => SetValue(VazaoProperty, value); } public static readonly DependencyProperty VazaoProperty = DependencyProperty.Register(nameof(Vazao), typeof(double), typeof(BicoLequeIndicator), new PropertyMetadata(0.0, OnGeometryPropertyChanged)); // Tempo atuado (s) public double TempoAtuado { get => (double)GetValue(TempoAtuadoProperty); set => SetValue(TempoAtuadoProperty, value); } public static readonly DependencyProperty TempoAtuadoProperty = DependencyProperty.Register(nameof(TempoAtuado), typeof(double), typeof(BicoLequeIndicator), new PropertyMetadata(0.0, OnGeometryPropertyChanged)); // Atuacoes public int Atuacoes { get => (int)GetValue(AtuacoesProperty); set => SetValue(AtuacoesProperty, value); } public static readonly DependencyProperty AtuacoesProperty = DependencyProperty.Register(nameof(Atuacoes), typeof(int), typeof(BicoLequeIndicator), new PropertyMetadata(0, OnGeometryPropertyChanged)); #endregion private static void OnGeometryPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var ctrl = (BicoLequeIndicator)d; ctrl.UpdateGeometry(); } private static void OnVisualPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var ctrl = (BicoLequeIndicator)d; ctrl.UpdateVisualState(); } /// /// Atualiza cor da solenoide e visibilidade do leque. /// private void UpdateVisualState() { if (SolenoidRect == null || SprayPolygon == null) return; var onBrush = (Brush)FindResource("CommandOnBrush"); var offBrush = (Brush)FindResource("CommandOffBrush"); SolenoidRect.Stroke = IsCommandOn ? onBrush : offBrush; SprayPolygon.Visibility = IsSpraying && OpeningAngle > 0 ? Visibility.Visible : Visibility.Collapsed; CenterLine.Visibility = IsSpraying ? Visibility.Visible : Visibility.Collapsed; } /// /// Recalcula geometria do leque e da linha central a partir do ângulo central e abertura. /// private void UpdateGeometry() { if (DrawCanvas == null || SprayPolygon == null || CenterLine == null) return; double width = DrawCanvas.ActualWidth; double height = DrawCanvas.ActualHeight; if (width <= 0) width = 120; if (height <= 0) height = 80; // Origem do bico (ponto da solenoide) double originX = width / 2.0; double originY = 10.0; // um pouco abaixo do topo double length = height - 10.0; // comprimento do jato até próximo do solo // Metade da abertura em radianos double halfAngleRad = OpeningAngle * 0.5 * Math.PI / 180.0; // Deslocamento horizontal na base do leque double dx = Math.Tan(halfAngleRad) * length; // Pontos em sistema local (0,0 = bico; y para baixo) Point apex = new Point(0, 0); Point leftLocal = new Point(-dx, length); Point rightLocal = new Point(dx, length); Point centerLocal = new Point(0, length); // Rotação pelo ângulo central (em radianos). double theta = AnguloControle * Math.PI / 180.0; double cos = Math.Cos(theta); double sin = Math.Sin(theta); Point Rot(Point p) => new Point( p.X * cos - p.Y * sin, p.X * sin + p.Y * cos); Point apexR = Rot(apex); Point leftR = Rot(leftLocal); Point rightR = Rot(rightLocal); Point centerR = Rot(centerLocal); // Translada para as coordenadas do Canvas apexR.Offset(originX, originY); leftR.Offset(originX, originY); rightR.Offset(originX, originY); centerR.Offset(originX, originY); // Atualiza Polygon do leque var pts = new PointCollection { leftR, apexR, rightR }; SprayPolygon.Points = pts; // Atualiza linha central do jato CenterLine.X1 = apexR.X; CenterLine.Y1 = apexR.Y; CenterLine.X2 = centerR.X; CenterLine.Y2 = centerR.Y; // Reposiciona a solenoide exatamente na origem Canvas.SetLeft(SolenoidRect, originX - SolenoidRect.Width / 2.0); Canvas.SetTop(SolenoidRect, originY - SolenoidRect.Height / 2.0); } } public class BicoModel : INotifyPropertyChanged { private int _index; public int Index { get => _index; set { _index = value; OnPropertyChanged(); } } private double _anguloControle; public double AnguloControle { get => _anguloControle; set { _anguloControle = value; OnPropertyChanged(); } } private double _openingAngle = 110; public double OpeningAngle { get => _openingAngle; set { _openingAngle = value; OnPropertyChanged(); } } private bool _isCommandOn; public bool IsCommandOn { get => _isCommandOn; set { _isCommandOn = value; OnPropertyChanged(); } } private bool _isSpraying; public bool IsSpraying { get => _isSpraying; set { _isSpraying = value; OnPropertyChanged(); } } private double _vazao; public double Vazao { get => _vazao; set { _vazao = value; OnPropertyChanged(); } } private double _tempoAtuado; public double TempoAtuado { get => _tempoAtuado; set { _tempoAtuado = value; OnPropertyChanged(); } } private int _atuacoes; public int Atuacoes { get => _atuacoes; set { _atuacoes = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); } }