244 lines
8.9 KiB
C#
244 lines
8.9 KiB
C#
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
|
||
{
|
||
/// <summary>
|
||
/// Interaction logic for BicoControl.xaml
|
||
/// </summary>
|
||
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();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Atualiza cor da solenoide e visibilidade do leque.
|
||
/// </summary>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Recalcula geometria do leque e da linha central a partir do ângulo central e abertura.
|
||
/// </summary>
|
||
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));
|
||
}
|
||
|
||
}
|