4917 lines
252 KiB
C#
4917 lines
252 KiB
C#
using Avalonia;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Controls.Shapes;
|
|
using Avalonia.Interactivity;
|
|
using Avalonia.Layout;
|
|
using Avalonia.Media;
|
|
using Avalonia.Threading;
|
|
using AvaloniaApplication1.ViewModels;
|
|
using DaireApplication.DataBase;
|
|
using DaireApplication.Loops;
|
|
using DaireApplication.ViewModels;
|
|
using DynamicData;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.IO.Ports;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace DaireApplication.Views;
|
|
|
|
public partial class MainWindow : Window
|
|
{
|
|
#region properties
|
|
List<byte> buffer = new List<byte>();
|
|
public ModBusMaster _modBusMaster = new ModBusMaster();
|
|
public MachineTable _machine = new MachineTable();
|
|
public ConfigrationTable _config = new ConfigrationTable();
|
|
public List<ConfigrationTable> _configrations = new List<ConfigrationTable>();
|
|
public Mapping _map = new Mapping();
|
|
public ScreeenTable _screeen = new();
|
|
public ErrorSettingsTable _error = new();
|
|
public List<Mapping> _mapping = new List<Mapping>();
|
|
public static bool isOff { get; set; }
|
|
const double deadZone = 1.5; // Changeable based on stability needs
|
|
bool shouldRunFountain = false;
|
|
public readonly SemaphoreSlim _keepSendingLock = new(1, 1);
|
|
|
|
public SerialPort _port { get; set; }
|
|
private static Thread monitorThread;
|
|
private static Thread screenThread;
|
|
private static Thread touchThread;
|
|
private static Thread internetThread;
|
|
private static Thread InteractiveUIThread;
|
|
private static Thread serialThread;
|
|
public bool serialThreadRunning = true;
|
|
public bool isRunning = true;
|
|
public bool restBoard { get; set; }
|
|
public bool sendConfig { get; set; } = true;
|
|
public bool reSendHolding { get; set; }
|
|
public bool dontResetOutPuts { get; set; }
|
|
|
|
bool allBitsOn = false;
|
|
bool allPedalBitsOn = false;
|
|
public int pedalState { get; set; } = -1;
|
|
public int pedalStateChanged { get; set; } = -1;
|
|
public float recipeHeatingGoal { get; set; }
|
|
public float recipeCoolingGoal { get; set; }
|
|
public float recipePouringGoal { get; set; }
|
|
|
|
public string ActiveColor { get; set; } = "#A4275D";
|
|
public string PassiveColor { get; set; } = "#666666";
|
|
//Pre Heating
|
|
public bool isFlashPreHeating { get; set; } = false;
|
|
public bool isReadingTemp { get; set; } = true;
|
|
public int startPreHeating { get; set; } = -1;
|
|
public int writingMaxTemp { get; set; } = -1;
|
|
|
|
//Mixer Motor
|
|
private Timer mixerTimer;
|
|
private Timer preMixerTimer;
|
|
private Timer unifiedMotorTimer;
|
|
|
|
private static int mixerSeconds = 1;
|
|
private static int preMixerSeconds = 1;
|
|
public bool setMixerTimerOnce { get; set; } = false;
|
|
public bool checkMixerTWT_HWTH { get; set; } = false;
|
|
public bool isMixerMotorOn { get; set; } = false;
|
|
public int startMixerMotor { get; set; } = -1;
|
|
public int startMixerMotorFlashing { get; set; } = -1;
|
|
public int sendComMixerMotor { get; set; } = -1;
|
|
|
|
//Fountain Motor
|
|
private Timer fountainTimer;
|
|
private Timer fountainPauseTimer;
|
|
private Timer noChoiceChoosenTimer;
|
|
|
|
private static int fountainSeconds = 1;
|
|
private static int fountainPauseSeconds = 1;
|
|
private static int noChoiceChoosenSeconds = 0;
|
|
public bool setFountainTimerOnce { get; set; } = false;
|
|
public bool checkFountainTMT_PMT { get; set; } = false;
|
|
public bool isFountainMotorOn { get; set; } = false;
|
|
public int startFountainMotor { get; set; } = -1;
|
|
public int startFountainMotorFlashing { get; set; } = -1;
|
|
public int sendComFountainMotor { get; set; } = -1;
|
|
|
|
public double comTankTemp { get; set; } = 0;
|
|
public double comFountainTemp { get; set; } = 0;
|
|
public double comPumpTemp { get; set; } = 0;
|
|
|
|
//MOLD HEATER(off:0,on:1) , VIBRATION(off:0,on:1) , VIB. HEATER(off:0,on:1)
|
|
public int moldHeaterMotor { get; set; } = -1;
|
|
public int vibrationMotor { get; set; } = -1;
|
|
public int vibHeaterMotor { get; set; } = -1;
|
|
|
|
//Pedal(manual=0,auto=1)
|
|
public int pedalMotor { get; set; } = -1;
|
|
|
|
//Recipe Start
|
|
|
|
public int startRecipe { get; set; } = 0;
|
|
public int sendComTankTemp { get; set; } = -1;
|
|
//phase 1 heating
|
|
public int Heating { get; set; } = -1;
|
|
public int sendComHeating { get; set; } = -1;
|
|
public int setHeatingTimerOnce { get; set; } = -1;
|
|
public Timer heatingTimer;
|
|
public int heatingSeconds { get; set; } = 0;
|
|
//phase 2 cooling
|
|
public int cooling { get; set; } = -1;
|
|
public int sendComCooling { get; set; } = -1;
|
|
public int setCoolingTimerOnce { get; set; } = -1;
|
|
public Timer coolingTimer;
|
|
public int coolingSeconds { get; set; } = 0;
|
|
|
|
//phase 3 pouring
|
|
public int pouring { get; set; } = -1;
|
|
public int sendComPouring { get; set; } = -1;
|
|
public int setPouringTimerOnce { get; set; } = -1;
|
|
public Timer pouringTimer;
|
|
public int pouringSeconds { get; set; } = 0;
|
|
|
|
//start the pumb
|
|
public int PumbOn { get; set; } = -1;
|
|
public Timer pedalOnTimer;
|
|
public Timer pedalOffTimer;
|
|
public int pedalOnSeconds { get; set; } = 0;
|
|
public int pedalOffSeconds { get; set; } = 0;
|
|
// 1 turn off ,0 turn on
|
|
public int setPedalTimerOnce { get; set; } = -1;
|
|
|
|
//Board
|
|
|
|
public bool resetPort { get; set; } = false;
|
|
public bool keepSendingFlag { get; set; } = false;
|
|
|
|
public DateTime lastActivity = DateTime.Now;
|
|
|
|
public ScreeenTable screenData = new();
|
|
public static List<Error> errors = new();
|
|
public bool pause { get; set; }
|
|
public bool unPause { get; set; }
|
|
public bool isPaused { get; set; }
|
|
public bool pauseTimer { get; set; }
|
|
public bool pauseTempTracking { get; set; }
|
|
public string warningMessage { get; set; }
|
|
|
|
public HoldingRegister holdingRegister = new HoldingRegister();
|
|
|
|
public bool turnOnFountainMotor { get; set; }
|
|
public bool stopRecipeFlag { get; set; }
|
|
public bool isCoolingDelayMode { get; set; } = false;
|
|
public bool isPouringDelayMode { get; set; } = false;
|
|
public bool tempWarningAccepted { get; set; } = false;
|
|
public bool isPedalAutoMode { get; set; } = false;
|
|
public bool isAutomaticFountainControlActive { get; set; } = false;
|
|
|
|
// Recipe phase tracking for footer message management
|
|
private enum RecipePhase
|
|
{
|
|
None,
|
|
PreHeating,
|
|
HeatingDelay,
|
|
CoolingPhase,
|
|
CoolingDelay,
|
|
PouringPhase,
|
|
Completed
|
|
}
|
|
private RecipePhase currentRecipePhase = RecipePhase.None;
|
|
|
|
/// <summary>
|
|
/// Safely update footer message based on current recipe phase
|
|
/// </summary>
|
|
private string lastFooterMessage = "";
|
|
private void UpdateFooterMessage(RecipePhase phase, string message)
|
|
{
|
|
// Don't update if recipe is completed (unless setting to completed)
|
|
if (currentRecipePhase == RecipePhase.Completed && phase != RecipePhase.Completed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Only update if message has actually changed
|
|
if (lastFooterMessage == message)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Only update if we're in the correct phase or transitioning to it
|
|
if (currentRecipePhase == phase || phase != RecipePhase.None)
|
|
{
|
|
currentRecipePhase = phase;
|
|
lastFooterMessage = message;
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
footerMsg.Text = message;
|
|
});
|
|
}
|
|
}
|
|
|
|
public Timer stopMixerTimer;
|
|
public int stopMixerSecondes { get; set; }
|
|
public Timer stopFountainTimer;
|
|
public int stopFountainSecondes { get; set; }
|
|
|
|
public DateTime _lastPacketSendTime = DateTime.MinValue;
|
|
public byte[] inputesResponse = new byte[] { 0xFF };
|
|
|
|
// Replace isWriting flag with async method
|
|
public TaskCompletionSource<bool> _writeCompletionSource = new TaskCompletionSource<bool>();
|
|
#endregion
|
|
public Task<bool> WriteToSerialAsync(string caller)
|
|
{
|
|
_writeCompletionSource = new TaskCompletionSource<bool>();
|
|
// Optionally log or store the caller for debugging
|
|
Debug.WriteLine($"WriteToSerialAsync called by: {caller}");
|
|
return _writeCompletionSource.Task;
|
|
}
|
|
public void SetWriteComplete(bool success = true)
|
|
{
|
|
_writeCompletionSource?.TrySetResult(success);
|
|
}
|
|
|
|
|
|
|
|
#region Construction
|
|
public MainWindow()
|
|
{
|
|
InitializeComponent();
|
|
// Hide cursor using unclutter
|
|
try
|
|
{
|
|
var process = new System.Diagnostics.Process();
|
|
process.StartInfo.FileName = "unclutter";
|
|
process.StartInfo.Arguments = "-idle 0"; // Hide immediately
|
|
process.StartInfo.UseShellExecute = false;
|
|
process.StartInfo.CreateNoWindow = true;
|
|
process.Start();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Failed to start unclutter: {ex.Message}");
|
|
}
|
|
|
|
ContentArea.Content = new Home(this);
|
|
this.Closing += OnClosingWindow;
|
|
|
|
_machine = _machine.ReadMachine();
|
|
|
|
_configrations = _config.ReadConfigrations();
|
|
_mapping = _map.ReadMappings();
|
|
serialThread = new Thread(() => serialThreadLoop.SendViaSerial(this))
|
|
{
|
|
IsBackground = true
|
|
};
|
|
serialThread.Start();
|
|
internetThread = new Thread(() => CheckInterNetLoop.CheckInterNet(this))
|
|
{
|
|
IsBackground = true
|
|
};
|
|
internetThread.Start();
|
|
|
|
screenThread = new Thread(() => ScreenLoop.Screen(this))
|
|
{
|
|
IsBackground = true
|
|
};
|
|
screenThread.Start();
|
|
InteractiveUIThread = new Thread(() => InteractiveUILoop.Flashing(this))
|
|
{
|
|
IsBackground = true
|
|
};
|
|
InteractiveUIThread.Start();
|
|
touchThread = new Thread(() => TouchLoop.Touch(this))
|
|
{
|
|
IsBackground = true
|
|
};
|
|
touchThread.Start();
|
|
monitorThread = new Thread(() => MonitorPortsLoop())
|
|
{
|
|
IsBackground = true,
|
|
Priority = ThreadPriority.Highest
|
|
};
|
|
monitorThread.Start();
|
|
|
|
|
|
}
|
|
#endregion
|
|
|
|
private async void MonitorPortsLoop()
|
|
{
|
|
byte[] tankeResponse = new byte[256];
|
|
var fountainResponse = new byte[256];
|
|
|
|
double tankBottomTempValue = -1;
|
|
double tankWallTempValue = -1;
|
|
double pumpTempValue = -1;
|
|
double fountainTempValue = -1;
|
|
var tankBottom = _mapping.Find(x => x.Name == "Tank Bottom Temp");
|
|
var tankWall = _mapping.Find(x => x.Name == "Tank Wall Temp");
|
|
var pump = _mapping.Find(x => x.Name == "Pump Temp");
|
|
var fountain = _mapping.Find(x => x.Name == "Fountain Temp");
|
|
|
|
|
|
static List<bool> ToBinary(int number)
|
|
{
|
|
return Convert.ToString(number, 2)
|
|
.PadLeft(16, '0')
|
|
.Reverse()
|
|
.Select(c => c == '1')
|
|
.ToList();
|
|
}
|
|
|
|
|
|
while (true)
|
|
{
|
|
if (isRunning)
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
footerDate.Text = DateTime.Now.ToString("dd/MM/yyyy");
|
|
footerTime.Text = DateTime.Now.ToString("hh:mm tt");
|
|
});
|
|
screenData = _screeen.ReadScreens()?[0];
|
|
|
|
try
|
|
{
|
|
if (!SerialPort.GetPortNames().Contains(screenData.port))
|
|
{
|
|
if (_port != null && _port.IsOpen)
|
|
{
|
|
_port.Close();
|
|
}
|
|
_port = null;
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
errors.Clear();
|
|
footerMsg.Text = "Not Connected:Port Name Not Found";
|
|
//Debug.WriteLine("port name not found");
|
|
footerMsg.Foreground = Avalonia.Media.Brushes.DarkRed;
|
|
footerMsg.IsVisible = true;
|
|
});
|
|
}
|
|
else
|
|
{
|
|
if (resetPort)
|
|
{
|
|
Debug.WriteLine("Port reset initiated");
|
|
resetAll();
|
|
if (_port != null && _port.IsOpen)
|
|
{
|
|
Debug.WriteLine("Closing existing port");
|
|
_port.Close();
|
|
}
|
|
_port = null;
|
|
if (ConnectToSerialPort())
|
|
{
|
|
Debug.WriteLine("Port reconnected successfully");
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
//Conntected
|
|
footerMsg.Text = "Connected";
|
|
footerMsg.IsVisible = true;
|
|
sendConfig = true;
|
|
});
|
|
}
|
|
else
|
|
{
|
|
Debug.WriteLine("Failed to reconnect port");
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
errors.Clear();
|
|
footerMsg.Text = "Not Connected";
|
|
footerMsg.IsVisible = true;
|
|
});
|
|
}
|
|
resetPort = false;
|
|
Debug.WriteLine("Port reset completed");
|
|
}
|
|
if (_port == null)
|
|
{
|
|
// Connect to the found device
|
|
if (!ConnectToSerialPort())
|
|
{
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
errors.Clear();
|
|
footerMsg.Text = "Not Connected";
|
|
|
|
});
|
|
}
|
|
else
|
|
{
|
|
serialThreadRunning = true;
|
|
|
|
sendConfig = true;
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
footerMsg.Text = "Connected";
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
if (!_port.IsOpen)
|
|
{
|
|
_port.Open();
|
|
}
|
|
|
|
|
|
List<int> inputValues = new List<int>();
|
|
|
|
if (isReadingTemp)
|
|
{
|
|
if (!_port.IsOpen)
|
|
{
|
|
_port.Open();
|
|
}
|
|
// reading inputes
|
|
|
|
var requstReadingInputs = await _modBusMaster.ReadInputRegisters(0, 18);
|
|
|
|
|
|
|
|
if (inputesResponse.Length != 1 && inputesResponse[0] != 0xFF)
|
|
{
|
|
var result = inputesResponse.Skip(3).Take(inputesResponse.Count() - 5).ToArray();
|
|
for (int i = 0; i < result.Length; i = i + 2)
|
|
{
|
|
inputValues.Add(((result[i] << 8) | result[i + 1]));
|
|
}
|
|
var brdFlags = ToBinary(inputValues[0]);
|
|
var inputes = ToBinary(inputValues[1]);
|
|
|
|
#region Errors
|
|
//Errors
|
|
try
|
|
{
|
|
// Grid Vac
|
|
if (inputValues[2] > 220 * 1.1 || inputValues[3] > 220 * 1.1 || inputValues[4] > 220 * 1.1)
|
|
{
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.GridVACHigh) == null)
|
|
{
|
|
errors.Add(new Error
|
|
{
|
|
errorDate = DateTime.Now,
|
|
Condition = Error.GridCondition.GridVACHigh
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.GridVACHigh) != null)
|
|
{
|
|
errors.First(x => x.Condition == Error.GridCondition.GridVACHigh).isDeleted = true;
|
|
}
|
|
|
|
}
|
|
if (inputValues[2] < 220 * 0.9 || inputValues[3] < 220 * 0.9 || inputValues[4] < 220 * 0.9)
|
|
{
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.GridVACLow) == null)
|
|
{
|
|
errors.Add(new Error
|
|
{
|
|
errorDate = DateTime.Now,
|
|
Condition = Error.GridCondition.GridVACLow
|
|
});
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.GridVACLow) != null)
|
|
{
|
|
errors.First(x => x.Condition == Error.GridCondition.GridVACLow).isDeleted = true;
|
|
}
|
|
}
|
|
//// Grid Freq
|
|
_error = _error.ReadErrorSettings()[0];
|
|
if (inputValues[17] > (_error.gridFreq * 10) * 1.1)
|
|
{
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.GridFrequencyHigh) == null)
|
|
{
|
|
errors.Add(new Error
|
|
{
|
|
errorDate = DateTime.Now,
|
|
Condition = Error.GridCondition.GridFrequencyHigh
|
|
});
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.GridFrequencyHigh) != null)
|
|
{
|
|
errors.First(x => x.Condition == Error.GridCondition.GridFrequencyHigh).isDeleted = true;
|
|
}
|
|
}
|
|
if (inputValues[17] < (_error.gridFreq * 10) * 0.9)
|
|
{
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.GridFrequencyLow) == null)
|
|
{
|
|
errors.Add(new Error
|
|
{
|
|
errorDate = DateTime.Now,
|
|
Condition = Error.GridCondition.GridFrequencyLow
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.GridFrequencyLow) != null)
|
|
{
|
|
errors.First(x => x.Condition == Error.GridCondition.GridFrequencyLow).isDeleted = true;
|
|
}
|
|
}
|
|
//// Ext Power
|
|
if (brdFlags[3])
|
|
{
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.NoExternalPower) == null)
|
|
{
|
|
errors.Add(new Error
|
|
{
|
|
errorDate = DateTime.Now,
|
|
Condition = Error.GridCondition.NoExternalPower
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.NoExternalPower) != null)
|
|
{
|
|
errors.First(x => x.Condition == Error.GridCondition.NoExternalPower).isDeleted = true;
|
|
}
|
|
}
|
|
//// missing Phase
|
|
if (brdFlags[5] && _error.ReadErrorSettings()[0].phaseNumber == 3)
|
|
{
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.MissingPhase) == null)
|
|
{
|
|
errors.Add(new Error
|
|
{
|
|
errorDate = DateTime.Now,
|
|
Condition = Error.GridCondition.MissingPhase
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.MissingPhase) != null)
|
|
{
|
|
errors.First(x => x.Condition == Error.GridCondition.MissingPhase).isDeleted = true;
|
|
}
|
|
}
|
|
//// Phase sequence
|
|
if (brdFlags[4] && _error.ReadErrorSettings()[0].phaseNumber == 3)
|
|
{
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.PhaseSequence) == null)
|
|
{
|
|
errors.Add(new Error
|
|
{
|
|
errorDate = DateTime.Now,
|
|
Condition = Error.GridCondition.PhaseSequence
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.PhaseSequence) != null)
|
|
{
|
|
errors.First(x => x.Condition == Error.GridCondition.PhaseSequence).isDeleted = true;
|
|
}
|
|
}
|
|
//com port1
|
|
if (brdFlags[1])
|
|
{
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.ComPort1) == null)
|
|
{
|
|
errors.Add(new Error
|
|
{
|
|
errorDate = DateTime.Now,
|
|
Condition = Error.GridCondition.ComPort1
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.ComPort1) != null)
|
|
{
|
|
errors.First(x => x.Condition == Error.GridCondition.ComPort1).isDeleted = true;
|
|
}
|
|
}
|
|
//com port2
|
|
if (brdFlags[2])
|
|
{
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.ComPort2) == null)
|
|
{
|
|
errors.Add(new Error
|
|
{
|
|
errorDate = DateTime.Now,
|
|
Condition = Error.GridCondition.ComPort2
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.ComPort2) != null)
|
|
{
|
|
errors.First(x => x.Condition == Error.GridCondition.ComPort2).isDeleted = true;
|
|
}
|
|
}
|
|
//hi curr neut
|
|
if (brdFlags[6])
|
|
{
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.HiCurrNeut) == null)
|
|
{
|
|
errors.Add(new Error
|
|
{
|
|
errorDate = DateTime.Now,
|
|
Condition = Error.GridCondition.HiCurrNeut
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.HiCurrNeut) != null)
|
|
{
|
|
errors.First(x => x.Condition == Error.GridCondition.HiCurrNeut).isDeleted = true;
|
|
}
|
|
}
|
|
//hi curr mot1
|
|
if (brdFlags[7])
|
|
{
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.HiCurrMot1) == null)
|
|
{
|
|
errors.Add(new Error
|
|
{
|
|
errorDate = DateTime.Now,
|
|
Condition = Error.GridCondition.HiCurrMot1
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.HiCurrMot1) != null)
|
|
{
|
|
errors.First(x => x.Condition == Error.GridCondition.HiCurrMot1).isDeleted = true;
|
|
}
|
|
}
|
|
//hi curr mot2
|
|
if (brdFlags[8])
|
|
{
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.HiCurrMot2) == null)
|
|
{
|
|
errors.Add(new Error
|
|
{
|
|
errorDate = DateTime.Now,
|
|
Condition = Error.GridCondition.HiCurrMot2
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (errors.FirstOrDefault(x => x.Condition == Error.GridCondition.HiCurrMot2) != null)
|
|
{
|
|
errors.First(x => x.Condition == Error.GridCondition.HiCurrMot2).isDeleted = true;
|
|
}
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
|
|
}
|
|
#endregion
|
|
|
|
Dispatcher.UIThread.Post(async () =>
|
|
{
|
|
if (ContentArea.Content is Diagnostics diagnostics)
|
|
{
|
|
//Board Falgs
|
|
foreach (var item in diagnostics.flagRectangles)
|
|
{
|
|
if (brdFlags[int.Parse(item.Tag.ToString())])
|
|
{
|
|
item.Fill = Brush.Parse(diagnostics.RedColor);
|
|
}
|
|
else
|
|
{
|
|
item.Fill = Brush.Parse(diagnostics.GrayColor);
|
|
|
|
}
|
|
}
|
|
//power Inputes
|
|
diagnostics.ph1.Text = inputValues[2].ToString();
|
|
diagnostics.ph2.Text = inputValues[3].ToString();
|
|
diagnostics.ph3.Text = inputValues[4].ToString();
|
|
|
|
diagnostics.i_nut.Text = (inputValues[5] / 10.0).ToString("0.0");
|
|
diagnostics.gridFreq.Text = (inputValues[17] / 10.0).ToString("0.0");
|
|
// Inputes
|
|
foreach (var item in diagnostics.InputesElements.OfType<Grid>().ToList())
|
|
{
|
|
var text = item.Children[0] as TextBlock;
|
|
var border = item.Children[1] as Border;
|
|
if (inputes[int.Parse(item.Tag.ToString())])
|
|
{
|
|
|
|
text.Text = "ACTIVE";
|
|
border.Background = Brush.Parse(diagnostics.PinkColor);
|
|
diagnostics.InputesElements.OfType<Ellipse>().ToList().Find(x => x.Tag.ToString() == item.Tag.ToString()).Fill = Brush.Parse(diagnostics.GreenColor);
|
|
|
|
}
|
|
else
|
|
{
|
|
text.Text = "PASSIVE";
|
|
border.Background = Brush.Parse(diagnostics.GrayColor);
|
|
diagnostics.InputesElements.OfType<Ellipse>().ToList().Find(x => x.Tag.ToString() == item.Tag.ToString()).Fill = Brush.Parse(diagnostics.GrayColor);
|
|
|
|
|
|
}
|
|
}
|
|
//Analog
|
|
diagnostics.an1.Text = inputValues[12].ToString();
|
|
diagnostics.an2.Text = inputValues[13].ToString();
|
|
//Temp
|
|
diagnostics.t1.Text = ((short)inputValues[8] / 10f).ToString("0.0");
|
|
diagnostics.t2.Text = ((short)inputValues[9] / 10f).ToString("0.0");
|
|
diagnostics.t3.Text = ((short)inputValues[10] / 10f).ToString("0.0");
|
|
diagnostics.t4.Text = ((short)inputValues[11] / 10f).ToString("0.0");
|
|
//InternalTemp
|
|
diagnostics.internalTemp.Text = (inputValues[15] / 10.0).ToString("0.0");
|
|
diagnostics.hsTemp.Text = (inputValues[14] / 10.0).ToString("0.0");
|
|
if (inputValues[16] > 40)
|
|
{
|
|
diagnostics.extPowerLed.Fill = Brush.Parse(diagnostics.RedColor);
|
|
}
|
|
else
|
|
{
|
|
diagnostics.extPowerLed.Fill = Brush.Parse(diagnostics.GrayColor);
|
|
}
|
|
diagnostics.ExtPwr.Text = (inputValues[16] / 10.0).ToString("0.0");
|
|
|
|
}
|
|
|
|
if (ContentArea.Content is Settings settings)
|
|
{
|
|
if (settings.pedalStateTxt.Text != "AUTO")
|
|
{
|
|
var pedal = _mapping.Find(x => x.Name.ToLower() == "pedal");
|
|
|
|
ushort registerValue = (ushort)inputValues[1];
|
|
if (allBitsOn != pedal.BitNumbers.All(bit => (registerValue & (1 << bit)) != 0))
|
|
{
|
|
allBitsOn = pedal.BitNumbers.All(bit => (registerValue & (1 << bit)) != 0);
|
|
}
|
|
if (!allBitsOn)
|
|
{
|
|
settings.pedalUnderLine.Fill = Brush.Parse(settings.PassiveColor);
|
|
|
|
}
|
|
else
|
|
{
|
|
settings.pedalUnderLine.Fill = Brush.Parse(settings.ActiveColor);
|
|
|
|
}
|
|
|
|
}
|
|
if (startFountainMotorFlashing != 1)
|
|
{
|
|
//fountain
|
|
|
|
var fount = _mapping.Find(x => x.Name.ToLower() == "Helix".ToLower());
|
|
|
|
|
|
if (allBitsOn != fount.BitNumbers.All(bit => (holdingRegister.motor & (1 << bit)) != 0))
|
|
{
|
|
allBitsOn = fount.BitNumbers.All(bit => (holdingRegister.motor & (1 << bit)) != 0);
|
|
}
|
|
var fountainLable = settings.FountainSP.Children[1] as Avalonia.Controls.Label;
|
|
var fontainRectangel = settings.FountainSP.Children[2] as Avalonia.Controls.Shapes.Rectangle;
|
|
if (!allBitsOn)
|
|
{
|
|
fountainLable.Content = "OFF";
|
|
fountainLable.Foreground = Brush.Parse("#ff231f20");
|
|
fontainRectangel.Fill = Brush.Parse(PassiveColor);
|
|
//isFountainMotorOn = false;
|
|
}
|
|
else
|
|
{
|
|
fountainLable.Content = "ON";
|
|
fountainLable.Foreground = Brush.Parse("#ff231f20");
|
|
fontainRectangel.Fill = Brush.Parse(ActiveColor);
|
|
//isFountainMotorOn = true;
|
|
}
|
|
}
|
|
|
|
if (startMixerMotorFlashing != 1)
|
|
{
|
|
//mixer
|
|
var mixer = _mapping.Find(x => x.Name.ToLower() == "Mixer".ToLower());
|
|
|
|
|
|
if (allBitsOn != mixer.BitNumbers.All(bit => (holdingRegister.motor & (1 << bit)) != 0))
|
|
{
|
|
allBitsOn = mixer.BitNumbers.All(bit => (holdingRegister.motor & (1 << bit)) != 0);
|
|
}
|
|
var mixerLable = settings.MixerSP.Children[1] as Avalonia.Controls.Label;
|
|
var mixerRectangel = settings.MixerSP.Children[2] as
|
|
Avalonia.Controls.Shapes.Rectangle;
|
|
if (!allBitsOn)
|
|
{
|
|
mixerLable.Content = "OFF";
|
|
mixerLable.Foreground = Brush.Parse("#ff231f20");
|
|
mixerRectangel.Fill = Brush.Parse(PassiveColor);
|
|
//isMixerMotorOn = false;
|
|
}
|
|
else
|
|
{
|
|
mixerLable.Content = "ON";
|
|
mixerLable.Foreground = Brush.Parse("#ff231f20");
|
|
mixerRectangel.Fill = Brush.Parse(ActiveColor);
|
|
//isMixerMotorOn = true;
|
|
}
|
|
}
|
|
|
|
}
|
|
if (ContentArea.Content is ManualControl manual)
|
|
{
|
|
manual.pumbRealTemp.Text = comPumpTemp.ToString("0.0");
|
|
manual.ChocolateRealTemp.Text = comFountainTemp.ToString("0.0");
|
|
manual.tankWallRealTemp.Text = tankWallTempValue.ToString("0.0");
|
|
manual.pumbRealTemp.Text = comTankTemp.ToString("0.0");
|
|
}
|
|
});
|
|
_mapping = _map.ReadMappings();
|
|
//reading Tank Bottom
|
|
tankBottom = _mapping.Find(x => x.Name == "Tank Bottom Temp");
|
|
|
|
if (tankBottom != null)
|
|
{
|
|
if (tankBottom.BitNumbers.Count > 0)
|
|
{
|
|
tankBottomTempValue = 0;
|
|
foreach (var item in tankBottom.BitNumbers)
|
|
{
|
|
tankBottomTempValue += ((short)inputValues[item] / 10f);
|
|
}
|
|
tankBottomTempValue /= tankBottom.BitNumbers.Count;
|
|
tankBottomTempValue = Math.Round(tankBottomTempValue, 1);
|
|
}
|
|
}
|
|
//reading Tank Wall
|
|
tankWall = _mapping.Find(x => x.Name == "Tank Wall Temp");
|
|
|
|
if (tankWall != null)
|
|
{
|
|
if (tankWall.BitNumbers.Count > 0)
|
|
{
|
|
tankWallTempValue = 0;
|
|
foreach (var item in tankWall.BitNumbers)
|
|
{
|
|
tankWallTempValue += ((short)inputValues[item] / 10f);
|
|
}
|
|
tankWallTempValue /= tankWall.BitNumbers.Count;
|
|
tankWallTempValue = Math.Round(tankWallTempValue, 1);
|
|
}
|
|
}
|
|
//reading Pump
|
|
pump = _mapping.Find(x => x.Name == "Pump Temp");
|
|
|
|
if (pump != null)
|
|
{
|
|
if (pump.BitNumbers.Count > 0)
|
|
{
|
|
pumpTempValue = 0;
|
|
|
|
foreach (var item in pump.BitNumbers)
|
|
{
|
|
pumpTempValue += ((short)inputValues[item] / 10f);
|
|
}
|
|
pumpTempValue /= pump.BitNumbers.Count;
|
|
pumpTempValue = Math.Round(pumpTempValue, 1);
|
|
}
|
|
}
|
|
//reading Fountain
|
|
fountain = _mapping.Find(x => x.Name == "Fountain Temp");
|
|
if (fountain != null)
|
|
{
|
|
if (fountain.BitNumbers.Count > 0)
|
|
{
|
|
fountainTempValue = 0;
|
|
|
|
foreach (var item in fountain.BitNumbers)
|
|
{
|
|
fountainTempValue += ((short)inputValues[item] / 10f);
|
|
}
|
|
fountainTempValue /= fountain.BitNumbers.Count;
|
|
fountainTempValue = Math.Round(fountainTempValue, 1);
|
|
}
|
|
}
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (tankBottomTempValue == -1 && tankWallTempValue == -1)
|
|
{
|
|
if (ContentArea.Content is Settings)
|
|
{
|
|
footerMsg.Text = "No Tank Data To Read";
|
|
tankBottomTempValue = 0;
|
|
tankWallTempValue = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//comTankTemp = tankBottomTempValue < tankWallTempValue ? tankBottomTempValue : tankWallTempValue;
|
|
comTankTemp = tankBottomTempValue;
|
|
comTankTemp = comTankTemp == -1 ? 0 : comTankTemp;
|
|
}
|
|
if (pumpTempValue == -1)
|
|
{
|
|
if (ContentArea.Content is Settings)
|
|
{
|
|
footerMsg.Text = "No Pump Data To Read";
|
|
pumpTempValue = 0;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
comPumpTemp = pumpTempValue;
|
|
}
|
|
if (fountainTempValue == -1)
|
|
{
|
|
if (ContentArea.Content is Settings)
|
|
{
|
|
footerMsg.Text = "No Chocolate Data To Read";
|
|
fountainTempValue = 0;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
comFountainTemp = fountainTempValue;
|
|
}
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
if (result.TankTempValue.Content?.ToString() != comTankTemp.ToString() || result.FountainTempValue.Content?.ToString() != comFountainTemp.ToString())
|
|
{
|
|
result.TankTempValue.Content = (comTankTemp).ToString("0.0");
|
|
result.FountainTempValue.Content = (comFountainTemp).ToString("0.0");
|
|
}
|
|
}
|
|
//if (comTankTemp >= _machine.TankMaxHeat && comFountainTemp >= _machine.PumbMaxHeat)
|
|
//{
|
|
// if (preMixerTimer==null)
|
|
// {
|
|
// preMixerTimer = new Timer(PreMixerTimer, null, 0, 1000);
|
|
// }
|
|
// mixerSeconds++;
|
|
//}
|
|
//else
|
|
//{
|
|
// if (preMixerTimer != null)
|
|
// {
|
|
// preMixerTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
// preMixerTimer = null;
|
|
// }
|
|
|
|
|
|
//}
|
|
}
|
|
);
|
|
}
|
|
|
|
//read mot val
|
|
|
|
if (inputValues.Count != 0)
|
|
{
|
|
|
|
Dispatcher.UIThread.Post(async () =>
|
|
{
|
|
if (ContentArea.Content is Diagnostics diagnostics)
|
|
{
|
|
diagnostics.curr1.Text = (inputValues[6] / 10.0).ToString("0.0");
|
|
diagnostics.curr2.Text = (inputValues[7] / 10.0).ToString("0.0");
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
//Pre Heating
|
|
if (startPreHeating == 1)
|
|
{
|
|
List<int> setTempValues = new List<int>();
|
|
setTempValues.AddRange([holdingRegister.setTemp1, holdingRegister.setTemp2, holdingRegister.setTemp3, holdingRegister.setTemp4]);
|
|
|
|
if (writingMaxTemp == 1)
|
|
{
|
|
tankBottom = _mapping.Find(x => x.Name == "Tank Bottom Temp");
|
|
if (tankBottom != null)
|
|
{
|
|
if (tankBottom.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var item in tankBottom.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = (int)_machine.TankMaxHeat * 10;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
}
|
|
tankWall = _mapping.Find(x => x.Name == "Tank Wall Temp");
|
|
if (tankWall != null)
|
|
{
|
|
if (tankWall.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var item in tankWall.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = (int)_machine.TankMaxHeat * 10;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
pump = _mapping.Find(x => x.Name == "Pump Temp");
|
|
if (pump != null)
|
|
{
|
|
if (pump.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var item in pump.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = (int)_machine.PumbMaxHeat * 10;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
fountain = _mapping.Find(x => x.Name == "Fountain Temp");
|
|
if (fountain != null)
|
|
{
|
|
if (fountain.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var item in fountain.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = -10000;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
holdingRegister.setTemp1 = setTempValues[0];
|
|
holdingRegister.setTemp2 = setTempValues[1];
|
|
holdingRegister.setTemp3 = setTempValues[2];
|
|
holdingRegister.setTemp4 = setTempValues[3];
|
|
await WriteToSerialAsync("PreHeating");
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
|
|
result.recipeSettings.IsEnabled = false;
|
|
isFlashPreHeating = true;
|
|
footerMsg.Text = "Pre-Heating Active";
|
|
}
|
|
});
|
|
writingMaxTemp = -1;
|
|
}
|
|
|
|
}
|
|
if (startPreHeating == 0)
|
|
{
|
|
List<int> setTempValues = new List<int>();
|
|
setTempValues.AddRange([holdingRegister.setTemp1, holdingRegister.setTemp2, holdingRegister.setTemp3, holdingRegister.setTemp4]);
|
|
if (writingMaxTemp == 0)
|
|
{
|
|
tankBottom = _mapping.Find(x => x.Name == "Tank Bottom Temp");
|
|
if (tankBottom != null)
|
|
{
|
|
if (tankBottom.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var item in tankBottom.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = -10000;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tankWall = _mapping.Find(x => x.Name == "Tank Wall Temp");
|
|
if (tankWall != null)
|
|
{
|
|
if (tankWall.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var item in tankWall.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = -10000;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
pump = _mapping.Find(x => x.Name == "Pump Temp");
|
|
if (pump != null)
|
|
{
|
|
if (pump.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var item in pump.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = -10000;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
fountain = _mapping.Find(x => x.Name == "Fountain Temp");
|
|
if (fountain != null)
|
|
{
|
|
if (fountain.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var item in fountain.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = -10000;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
holdingRegister.setTemp1 = setTempValues[0];
|
|
holdingRegister.setTemp2 = setTempValues[1];
|
|
holdingRegister.setTemp3 = setTempValues[2];
|
|
holdingRegister.setTemp4 = setTempValues[3];
|
|
await WriteToSerialAsync("PreHeating");
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
result.recipeSettings.IsEnabled = true;
|
|
if (!stopRecipeFlag)
|
|
{
|
|
footerMsg.Text = "Pre-Heating Stopped";
|
|
|
|
}
|
|
}
|
|
});
|
|
writingMaxTemp = -1;
|
|
}
|
|
|
|
}
|
|
//Mixer Motor
|
|
//Mixer Motorf
|
|
if (checkMixerTWT_HWTH)
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
recipeHeatingGoal = result._recipeTable.HeatingGoal;
|
|
recipeCoolingGoal = result._recipeTable.CoolingGoal;
|
|
recipePouringGoal = result._recipeTable.PouringGoal;
|
|
}
|
|
|
|
if ((comTankTemp >= _machine.TankMaxHeat - screenData.warningLimit) && (comTankTemp <= _machine.TankMaxHeat + screenData.warningLimit))
|
|
{
|
|
if (startMixerMotor != 1)
|
|
{
|
|
if (mixerTimer == null)
|
|
{
|
|
if (mixerSeconds == 1)
|
|
{
|
|
mixerSeconds = _machine.MixerDelay;
|
|
}
|
|
mixerTimer = new Timer(MixerTimer, null, 0, 1000);
|
|
//setMixerTimerOnce = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (comTankTemp <= recipeCoolingGoal - 3 && startMixerMotor == 1)
|
|
{
|
|
if (startMixerMotor != 0)
|
|
{
|
|
if (stopMixerTimer == null)
|
|
{
|
|
stopMixerSecondes = 5;
|
|
stopMixerTimer = new Timer(StopMixerTimer, null, 0, 1000);
|
|
}
|
|
}
|
|
startMixerMotorFlashing = 1;
|
|
}
|
|
else if (startMixerMotor != 1)
|
|
{
|
|
if (startMixerMotor != 0)
|
|
{
|
|
sendComMixerMotor = 0;
|
|
}
|
|
startMixerMotorFlashing = 1;
|
|
}
|
|
});
|
|
}
|
|
if (!checkMixerTWT_HWTH)
|
|
{
|
|
if (sendComMixerMotor == 0 && startMixerMotorFlashing == 0)
|
|
{
|
|
startMixerMotor = -1;
|
|
var mixer = _mapping.Find(x => x.Name.ToLower() == "Mixer".ToLower());
|
|
if (mixer != null)
|
|
{
|
|
if (mixer.BitNumbers.Count > 0)
|
|
{
|
|
//turn the motor off and make the button stable
|
|
|
|
|
|
foreach (var bit in mixer.BitNumbers)
|
|
{
|
|
holdingRegister.motor &= (ushort)~(1 << bit);
|
|
}
|
|
await WriteToSerialAsync("Mixer Off due Clicking");
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
var motorLable = result.MixerSP.Children[1] as Avalonia.Controls.Label;
|
|
var motorRectangel = result.MixerSP.Children[2] as
|
|
Avalonia.Controls.Shapes.Rectangle;
|
|
motorLable.Content = "OFF";
|
|
motorLable.Foreground = Brush.Parse("#ff231f20");
|
|
motorRectangel.Fill = Brush.Parse(PassiveColor);
|
|
if (startRecipe != 1 && !stopRecipeFlag)
|
|
{
|
|
footerMsg.Text = "Mixer is OFF";
|
|
}
|
|
}
|
|
});
|
|
sendComMixerMotor = -1;
|
|
startMixerMotorFlashing = -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (startMixerMotorFlashing == 1)
|
|
{
|
|
if (sendComMixerMotor == 0)
|
|
{
|
|
//turn the motor off
|
|
startMixerMotor = 0;
|
|
|
|
var mixer = _mapping.Find(x => x.Name.ToLower() == "Mixer".ToLower());
|
|
if (mixer != null)
|
|
{
|
|
if (mixer.BitNumbers.Count > 0)
|
|
{
|
|
//turn the motor off and make the button stable
|
|
|
|
foreach (var bit in mixer.BitNumbers)
|
|
{
|
|
|
|
holdingRegister.motor &= (ushort)~(1 << bit);
|
|
}
|
|
await WriteToSerialAsync("Mixer Off due to drop in temp");
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (startRecipe != 1)
|
|
{
|
|
footerMsg.Text = "waiting for tank target temperature";
|
|
}
|
|
});
|
|
sendComMixerMotor = -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (startMixerMotorFlashing == 0 && sendComMixerMotor == 1 && !isPaused)
|
|
{
|
|
startMixerMotor = 1;
|
|
//turn the motor on and make the button stable
|
|
var mixer = _mapping.Find(x => x.Name.ToLower() == "Mixer".ToLower());
|
|
if (mixer != null)
|
|
{
|
|
if (mixer.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var bit in mixer.BitNumbers)
|
|
{
|
|
|
|
holdingRegister.motor |= (ushort)(1 << bit);
|
|
}
|
|
await WriteToSerialAsync("Mixer On");
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
var motorLable = result.MixerSP.Children[1] as Avalonia.Controls.Label;
|
|
var motorRectangel = result.MixerSP.Children[2] as
|
|
Avalonia.Controls.Shapes.Rectangle;
|
|
motorLable.Content = "ON";
|
|
motorLable.Foreground = Brush.Parse("#ff231f20");
|
|
motorRectangel.Fill = Brush.Parse(ActiveColor);
|
|
if (startRecipe != 1)
|
|
{
|
|
footerMsg.Text = "Temperature is OK,Mixer is on";
|
|
}
|
|
//if (comTankTemp < result._recipeTable.PouringGoal ||
|
|
// comFountainTemp < result._recipeTable.PouringGoal)
|
|
//{
|
|
// mixerSeconds = 1;
|
|
// //checkTMT_PMT = true;
|
|
// setMixerTimerOnce = true;
|
|
//}
|
|
//else
|
|
//{
|
|
// //checkTMT_PMT = true;
|
|
// setMixerTimerOnce = true;
|
|
//}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
startMixerMotorFlashing = -1;
|
|
sendComMixerMotor = -1;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
//Fountain Motor - Normal temperature-based control (when not in pedal auto mode)
|
|
if (checkFountainTMT_PMT && !isPedalAutoMode)
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
recipeHeatingGoal = result._recipeTable.HeatingGoal;
|
|
recipeCoolingGoal = result._recipeTable.CoolingGoal;
|
|
recipePouringGoal = result._recipeTable.PouringGoal;
|
|
|
|
|
|
}
|
|
|
|
if ((comPumpTemp >= _machine.PumbMaxHeat - screenData.warningLimit) && (comPumpTemp <= _machine.PumbMaxHeat + screenData.warningLimit)) // check the temp and make it stop only if it below cooling temp - 3 degrees
|
|
{
|
|
if (startFountainMotor != 1)
|
|
{
|
|
if (fountainTimer is null)
|
|
{
|
|
if (fountainSeconds == 1)
|
|
{
|
|
fountainSeconds = _machine.PumbDelay;
|
|
}
|
|
fountainTimer = new Timer(FountainTimer, null, 0, 1000);
|
|
//setFountainTimerOnce = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (comPumpTemp <= recipeCoolingGoal - 3 && startFountainMotor == 1)
|
|
{
|
|
if (startFountainMotor != 0)
|
|
{
|
|
if (stopFountainTimer == null)
|
|
{
|
|
stopFountainSecondes = 5;
|
|
stopFountainTimer = new Timer(StopFountainTimer, null, 0, 1000);
|
|
}
|
|
}
|
|
if (!isPedalAutoMode)
|
|
{
|
|
startFountainMotorFlashing = 1;
|
|
}
|
|
}
|
|
else if (startFountainMotor != 1)
|
|
{
|
|
if (startFountainMotor != 0)
|
|
{
|
|
sendComFountainMotor = 0;
|
|
}
|
|
if (!isPedalAutoMode)
|
|
{
|
|
startFountainMotorFlashing = 1;
|
|
}
|
|
}
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
if (startFountainMotor != 1 && fountainTimer == null)
|
|
{
|
|
if (!result.fountainDelayCounter.Text.Equals(comPumpTemp.ToString("0.0")))
|
|
{
|
|
result.fountainDelayTxt.Text = "Current Temp:";
|
|
result.fountainDelayCounter.Text = comPumpTemp.ToString("0.0");
|
|
result.fountainTargetTxt.Text = "Target Temp:";
|
|
result.fountainTagetTemp.Text = _machine.PumbMaxHeat.ToString("0.0");
|
|
result.fountainDelayTxt.IsVisible = true;
|
|
result.fountainDelayCounter.IsVisible = true;
|
|
result.fountainTargetTxt.IsVisible = true;
|
|
result.fountainTagetTemp.IsVisible = true;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
});
|
|
});
|
|
}
|
|
//Fountain Motor - Manual control (when not in pedal auto mode)
|
|
if (!checkFountainTMT_PMT && !isPedalAutoMode)
|
|
{
|
|
if (sendComFountainMotor == 0 && startFountainMotorFlashing == 0)
|
|
{
|
|
startFountainMotor = -1;
|
|
var fount = _mapping.Find(x => x.Name.ToLower() == "Helix".ToLower());
|
|
if (fount != null)
|
|
{
|
|
if (fount.BitNumbers.Count > 0)
|
|
{
|
|
//turn the motor off and make the button stable
|
|
foreach (var bit in fount.BitNumbers)
|
|
{
|
|
holdingRegister.motor &= (ushort)~(1 << bit);
|
|
}
|
|
await WriteToSerialAsync("Fountain Off due to click");
|
|
//waitting for 10 sec before pause
|
|
if (fountainPauseTimer == null && startRecipe == 1 && (Heating == 1 || heatingTimer != null || cooling == 1 || coolingTimer != null || pouring == 1 || pouringTimer != null
|
|
))
|
|
{
|
|
fountainPauseSeconds = 10;
|
|
fountainPauseTimer = new Timer(FountainPauseTimer, null, 0, 1000);
|
|
}
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
result.fountainDelayCounter.Text = "-1";
|
|
var fountainLable = result.FountainSP.Children[1] as Avalonia.Controls.Label;
|
|
var fontainRectangel = result.FountainSP.Children[2] as
|
|
Avalonia.Controls.Shapes.Rectangle;
|
|
fountainLable.Content = "OFF";
|
|
fountainLable.Foreground = Brush.Parse("#ff231f20");
|
|
fontainRectangel.Fill = Brush.Parse(PassiveColor);
|
|
if (startRecipe != 1 && !stopRecipeFlag)
|
|
{
|
|
footerMsg.Text = "Chocolate is OFF";
|
|
}
|
|
stopRecipeFlag = false;
|
|
}
|
|
});
|
|
|
|
|
|
sendComFountainMotor = -1;
|
|
startFountainMotorFlashing = -1;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
if (startFountainMotorFlashing == 1 && !isPedalAutoMode)
|
|
{
|
|
if (sendComFountainMotor == 0)
|
|
{
|
|
startFountainMotor = 0;
|
|
|
|
//turn the motor off
|
|
var fount = _mapping.Find(x => x.Name.ToLower() == "Helix".ToLower());
|
|
if (fount != null)
|
|
{
|
|
if (fount.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var bit in fount.BitNumbers)
|
|
{
|
|
|
|
holdingRegister.motor &= (ushort)~(1 << bit);
|
|
}
|
|
await WriteToSerialAsync("Fountain Off due to drop in temp");
|
|
//pause the recipe if it started
|
|
//waitting for 10 sec before pause
|
|
if (fountainPauseTimer == null && startRecipe == 1 && (Heating == 1 || heatingTimer != null || cooling == 1 || coolingTimer != null || pouring == 1 || pouringTimer != null
|
|
))
|
|
{
|
|
fountainPauseSeconds = 10;
|
|
fountainPauseTimer = new Timer(FountainPauseTimer, null, 0, 1000);
|
|
}
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (startRecipe == 1 && (Heating == 1 || cooling == 1 || pouring == 1))
|
|
{
|
|
//footerMsg.Text = "Recipe Paused... waiting for target temperature";
|
|
}
|
|
else if (startRecipe != 1)
|
|
{
|
|
footerMsg.Text = "waiting for pump target temperature";
|
|
|
|
}
|
|
});
|
|
//checkTMT_PMT = true;
|
|
sendComFountainMotor = -1;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
if (startFountainMotorFlashing == 0 && sendComFountainMotor == 1 && errors.Count == 0 && !isPaused)
|
|
{
|
|
startFountainMotor = 1;
|
|
//turn the motor on and make the button stable
|
|
var fount = _mapping.Find(x => x.Name.ToLower() == "Helix".ToLower());
|
|
if (fount != null)
|
|
{
|
|
if (fount.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var bit in fount.BitNumbers)
|
|
{
|
|
holdingRegister.motor |= (ushort)(1 << bit);
|
|
}
|
|
await WriteToSerialAsync("Fountain On");
|
|
if (isPaused)
|
|
{
|
|
unPause = true;
|
|
}
|
|
//if (startRecipe == 1 && (Heating == 10 || cooling == 10 || pouring == 10))
|
|
//{
|
|
// if (Heating == 10)
|
|
// {
|
|
// Heating = 1;
|
|
// sendComHeating = 1;
|
|
// }
|
|
// else if (cooling == 10)
|
|
// {
|
|
// cooling = 1;
|
|
// sendComCooling = 1;
|
|
// }
|
|
// else if (pouring == 10)
|
|
// {
|
|
// pouring = 1;
|
|
// sendComPouring = 1;
|
|
// }
|
|
//}
|
|
if (fountainPauseTimer != null)
|
|
{
|
|
fountainPauseTimer = null;
|
|
fountainPauseSeconds = 10;
|
|
}
|
|
Dispatcher.UIThread.Post(async () =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
var fountainLable = result.FountainSP.Children[1] as Avalonia.Controls.Label;
|
|
var fountainRectangel = result.FountainSP.Children[2] as
|
|
Avalonia.Controls.Shapes.Rectangle;
|
|
fountainLable.Content = "ON";
|
|
fountainLable.Foreground = Brush.Parse("#ff231f20");
|
|
fountainRectangel.Fill = Brush.Parse(ActiveColor);
|
|
if (startRecipe == 1 && (Heating == 1 || cooling == 1 || pouring == 1))
|
|
{
|
|
if (Heating == 1)
|
|
{
|
|
footerMsg.Text = "Heating phase";
|
|
|
|
}
|
|
else if (cooling == 1)
|
|
{
|
|
footerMsg.Text = "Cooling phase";
|
|
|
|
}
|
|
else if (pouring == 1)
|
|
{
|
|
footerMsg.Text = "Prepare for pouring";
|
|
}
|
|
}
|
|
else if (startRecipe != 1)
|
|
{
|
|
footerMsg.Text = "Temperature is OK,Chocolate is on";
|
|
}
|
|
//if (comTankTemp < result._recipeTable.PouringGoal ||
|
|
// comFountainTemp < result._recipeTable.PouringGoal)
|
|
//{
|
|
// fountainSeconds = 1;
|
|
// setFountainTimerOnce = true;
|
|
//}
|
|
//else
|
|
//{
|
|
// setFountainTimerOnce = true;
|
|
//}
|
|
}
|
|
});
|
|
|
|
startFountainMotorFlashing = -1;
|
|
sendComFountainMotor = -1;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// Fountain Motor - Direct control is now handled by pedal state in auto mode
|
|
|
|
if (moldHeaterMotor == 0) // off MOLD HEATER
|
|
{
|
|
var moldHeater = _mapping.Find(x => x.Name == "Mold Heater");
|
|
if (moldHeater != null)
|
|
{
|
|
if (moldHeater.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var bit in moldHeater.BitNumbers)
|
|
{
|
|
holdingRegister.lvOut &= (ushort)~(1 << bit);
|
|
}
|
|
await WriteToSerialAsync("MoldHeater");
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
var StackPanel = result.moldHeaterBtn.Content as StackPanel;
|
|
var Label = StackPanel.Children;
|
|
var underLine = Label[2] as Avalonia.Controls.Shapes.Rectangle;
|
|
var targetLable = Label[1] as Label;
|
|
targetLable.Content = "OFF";
|
|
underLine.Fill = Brush.Parse("#666666");
|
|
}
|
|
else if (ContentArea.Content is ManualControl manual)
|
|
{
|
|
manual.MoldHeaterStatus.Text = "OFF";
|
|
manual.MoldHeaterUnderline.Fill = Brush.Parse("#666666");
|
|
}
|
|
});
|
|
|
|
}
|
|
else
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
footerMsg.Text = "Contact Admin";
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
footerMsg.Text = "Contact Programming Team";
|
|
});
|
|
}
|
|
|
|
|
|
moldHeaterMotor = -1;
|
|
}
|
|
else if (moldHeaterMotor == 1) // on MOLD HEATER
|
|
{
|
|
var moldHeater = _mapping.Find(x => x.Name == "Mold Heater");
|
|
if (moldHeater != null)
|
|
{
|
|
if (moldHeater.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var bit in moldHeater.BitNumbers)
|
|
{
|
|
|
|
holdingRegister.lvOut |= (ushort)(1 << bit);
|
|
}
|
|
await WriteToSerialAsync("MoldHeater");
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
var StackPanel = result.moldHeaterBtn.Content as StackPanel;
|
|
var Label = StackPanel.Children;
|
|
var underLine = Label[2] as Avalonia.Controls.Shapes.Rectangle;
|
|
var targetLable = Label[1] as Label;
|
|
targetLable.Content = "ON";
|
|
underLine.Fill = Brush.Parse("#A4275D");
|
|
}
|
|
else if (ContentArea.Content is ManualControl manual)
|
|
{
|
|
manual.MoldHeaterStatus.Text = "ON";
|
|
manual.MoldHeaterUnderline.Fill = Brush.Parse("#A4275D");
|
|
}
|
|
});
|
|
|
|
}
|
|
else
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
footerMsg.Text = "Contact Admin";
|
|
});
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
footerMsg.Text = "Contact Programming Team";
|
|
});
|
|
}
|
|
|
|
moldHeaterMotor = -1;
|
|
}
|
|
if (vibrationMotor == 0) // off VIBRATION
|
|
{
|
|
var vibrator = _mapping.Find(x => x.Name == "Vibrator");
|
|
|
|
if (vibrator != null)
|
|
{
|
|
if (vibrator.BitNumbers.Count > 0)
|
|
{
|
|
|
|
foreach (var bit in vibrator.BitNumbers)
|
|
{
|
|
holdingRegister.hvOut &= (ushort)~(1 << bit);
|
|
}
|
|
await WriteToSerialAsync("Vibrator");
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
var StackPanel = result.vibrationBtn.Content as StackPanel;
|
|
var Label = StackPanel.Children;
|
|
var underLine = Label[2] as Avalonia.Controls.Shapes.Rectangle;
|
|
var targetLable = Label[1] as Label;
|
|
targetLable.Content = "OFF";
|
|
underLine.Fill = Brush.Parse("#666666");
|
|
}
|
|
else if (ContentArea.Content is ManualControl manual)
|
|
{
|
|
manual.VibrationStatus.Text = "OFF";
|
|
manual.VibrationUnderline.Fill = Brush.Parse("#666666");
|
|
}
|
|
});
|
|
|
|
}
|
|
else
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
footerMsg.Text = "Contact Admin";
|
|
});
|
|
}
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
footerMsg.Text = "Contact Programming Team";
|
|
});
|
|
}
|
|
|
|
vibrationMotor = -1;
|
|
}
|
|
else if (vibrationMotor == 1) // on VIBRATION
|
|
{
|
|
var vibrator = _mapping.Find(x => x.Name == "Vibrator");
|
|
|
|
if (vibrator != null)
|
|
{
|
|
if (vibrator.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var bit in vibrator.BitNumbers)
|
|
{
|
|
holdingRegister.hvOut |= (ushort)(1 << bit);
|
|
}
|
|
await WriteToSerialAsync("Vibrator");
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
var StackPanel = result.vibrationBtn.Content as StackPanel;
|
|
var Label = StackPanel.Children;
|
|
var underLine = Label[2] as Avalonia.Controls.Shapes.Rectangle;
|
|
var targetLable = Label[1] as Label;
|
|
targetLable.Content = "ON";
|
|
underLine.Fill = Brush.Parse("#A4275D");
|
|
}
|
|
else if (ContentArea.Content is ManualControl manual)
|
|
{
|
|
manual.VibrationStatus.Text = "ON";
|
|
manual.VibrationUnderline.Fill = Brush.Parse("#A4275D");
|
|
}
|
|
});
|
|
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
footerMsg.Text = "Contact Admin";
|
|
});
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
footerMsg.Text = "Contact Programming Team";
|
|
});
|
|
}
|
|
vibrationMotor = -1;
|
|
}
|
|
if (vibHeaterMotor == 0) // off VIB. HEATER
|
|
{
|
|
var vibHeater = _mapping.Find(x => x.Name == "Vibrator Heater");
|
|
if (vibHeater != null)
|
|
{
|
|
if (vibHeater.BitNumbers.Count > 0)
|
|
{
|
|
|
|
foreach (var bit in vibHeater.BitNumbers)
|
|
{
|
|
|
|
holdingRegister.lvOut &= (ushort)~(1 << bit);
|
|
}
|
|
await WriteToSerialAsync("VibratorHeater");
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
var StackPanel = result.vibHeaterBtn.Content as StackPanel;
|
|
var Label = StackPanel.Children;
|
|
var underLine = Label[2] as Avalonia.Controls.Shapes.Rectangle;
|
|
var targetLable = Label[1] as Label;
|
|
targetLable.Content = "OFF";
|
|
underLine.Fill = Brush.Parse("#666666");
|
|
}
|
|
else if (ContentArea.Content is ManualControl manual)
|
|
{
|
|
manual.VibHeaterStatus.Text = "OFF";
|
|
manual.VibHeaterUnderline.Fill = Brush.Parse("#666666");
|
|
}
|
|
});
|
|
|
|
}
|
|
else
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
footerMsg.Text = "Contact Admin";
|
|
});
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
footerMsg.Text = "Contact Programming Team";
|
|
});
|
|
}
|
|
|
|
vibHeaterMotor = -1;
|
|
}
|
|
else if (vibHeaterMotor == 1) // on VIB. HEATER
|
|
{
|
|
var vibHeater = _mapping.Find(x => x.Name == "Vibrator Heater");
|
|
if (vibHeater != null)
|
|
{
|
|
if (vibHeater.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var bit in vibHeater.BitNumbers)
|
|
{
|
|
holdingRegister.lvOut |= (ushort)(1 << bit);
|
|
}
|
|
await WriteToSerialAsync("VibratorHeater");
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
var StackPanel = result.vibHeaterBtn.Content as StackPanel;
|
|
var Label = StackPanel.Children;
|
|
var underLine = Label[2] as Avalonia.Controls.Shapes.Rectangle;
|
|
var targetLable = Label[1] as Label;
|
|
targetLable.Content = "ON";
|
|
underLine.Fill = Brush.Parse("#A4275D");
|
|
}
|
|
else if (ContentArea.Content is ManualControl manual)
|
|
{
|
|
manual.VibHeaterStatus.Text = "ON";
|
|
manual.VibHeaterUnderline.Fill = Brush.Parse("#A4275D");
|
|
}
|
|
});
|
|
}
|
|
else
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
footerMsg.Text = "Contact Admin";
|
|
});
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
footerMsg.Text = "Contact Programming Team";
|
|
});
|
|
}
|
|
vibHeaterMotor = -1;
|
|
}
|
|
|
|
|
|
//Start Recipe
|
|
if (startRecipe == 1)
|
|
{
|
|
var tankBtm = _mapping.Find(x => x.Name.ToLower() == "Tank Bottom Temp".ToLower());
|
|
tankWall = _mapping.Find(x => x.Name.ToLower() == "Tank Wall Temp".ToLower());
|
|
var pumb = _mapping.Find(x => x.Name.ToLower() == "Pump Temp".ToLower());
|
|
var fount = _mapping.Find(x => x.Name.ToLower() == "Fountain Temp".ToLower());
|
|
var fountainMotor = _mapping.Find(x => x.Name.ToLower() == "Helix".ToLower());
|
|
var mixerMotor = _mapping.Find(x => x.Name.ToLower() == "Mixer".ToLower());
|
|
|
|
if (tankBtm != null && tankWall != null && pumb != null && fount != null)
|
|
{
|
|
if (tankBtm.BitNumbers.Count > 0 && tankWall.BitNumbers.Count > 0 && pumb.BitNumbers.Count > 0 && fount.BitNumbers.Count > 0)
|
|
{
|
|
List<int> setTempValues = new List<int>();
|
|
setTempValues.AddRange([holdingRegister.setTemp1, holdingRegister.setTemp2, holdingRegister.setTemp3, holdingRegister.setTemp4]);
|
|
byte[] response = new byte[] { 0xFF };
|
|
|
|
var isFountOn = false;
|
|
var isMixerOn = false;
|
|
if (isFountOn != fountainMotor.BitNumbers.All(bit => (holdingRegister.motor & (1 << bit)) != 0))
|
|
{
|
|
isFountOn = fountainMotor.BitNumbers.All(bit => (holdingRegister.motor & (1 << bit)) != 0);
|
|
}
|
|
if (isMixerOn != mixerMotor.BitNumbers.All(bit => (holdingRegister.motor & (1 << bit)) != 0))
|
|
{
|
|
isMixerOn = mixerMotor.BitNumbers.All(bit => (holdingRegister.motor & (1 << bit)) != 0);
|
|
}
|
|
if (isFountOn && isMixerOn) // both motors are on
|
|
{
|
|
// Check if both mixer and chocolate have reached heating goal
|
|
bool mixerGoalMet = comTankTemp >= recipeHeatingGoal;
|
|
bool chocolateGoalMet = comFountainTemp >= recipeHeatingGoal;
|
|
bool bothGoalsMet = mixerGoalMet && chocolateGoalMet;
|
|
|
|
if (bothGoalsMet && (cooling != 1 && pouring != 1))
|
|
{
|
|
// Both goals met - check temperature before starting heating timer
|
|
bool tempTooHigh = comFountainTemp > (recipeHeatingGoal + screenData.warningLimit);
|
|
|
|
if (tempTooHigh && !tempWarningAccepted && heatingTimer == null)
|
|
{
|
|
// Show error window when temperature is too high before starting heating delay
|
|
if (noChoiceChoosenTimer == null)
|
|
{
|
|
noChoiceChoosenSeconds = 0;
|
|
noChoiceChoosenTimer = new Timer(NoChoiceChoosenTimer, null, 0, 1000);
|
|
}
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings settings)
|
|
{
|
|
settings.tempErrorPopupOverlay.IsVisible = true;
|
|
}
|
|
// Don't change footer message - keep showing pre-heating status
|
|
});
|
|
}
|
|
else if (heatingTimer == null)
|
|
{
|
|
// Temperature is acceptable OR user accepted warning - start heating timer
|
|
heatingTimer = new Timer(HeatingTimer, null, 1000, 1000);
|
|
heatingSeconds = _machine.HeatingDelay;
|
|
|
|
|
|
}
|
|
}
|
|
else if ((Heating != 1) && (cooling != 1 || cooling != 10) && (pouring != 1 || pouring != 10) && heatingTimer == null && coolingTimer == null && pouringTimer == null && startRecipe == 1)
|
|
{
|
|
// Goals not met yet - continue heating (only if recipe is still running)
|
|
sendComTankTemp = 1;
|
|
Heating = 1;
|
|
sendComHeating = 1;
|
|
setHeatingTimerOnce = 1;
|
|
|
|
// Only update footer if we're in pre-heating phase
|
|
UpdateFooterMessage(RecipePhase.PreHeating,
|
|
$"Pre-heating");
|
|
}
|
|
}
|
|
//if (comPumpTemp>=_machine.PumbMaxHeat)
|
|
//{
|
|
// //start timer
|
|
// if (fountainTimer==null)
|
|
// {
|
|
// if (fountainSeconds == 1)
|
|
// {
|
|
// fountainSeconds = _machine.PumbDelay;
|
|
// }
|
|
// fountainTimer = new Timer(FountainTimer, null, 0, 1000);
|
|
// }
|
|
// if (turnOnFountainMotor)
|
|
// {
|
|
// fount = _mapping.Find(x => x.Name.ToLower() == "Helix".ToLower());
|
|
// if (fount != null)
|
|
// {
|
|
// if (fount.BitNumbers.Count > 0)
|
|
// {
|
|
// foreach (var bit in fount.BitNumbers)
|
|
// {
|
|
// holdingRegister.motor |= (ushort)(1 << bit);
|
|
// }
|
|
// isWriting = true;
|
|
|
|
// }
|
|
// }
|
|
|
|
// turnOnFountainMotor = false;
|
|
// }
|
|
//}
|
|
//else
|
|
//{
|
|
// startFountainMotorFlashing = 1;
|
|
//}
|
|
if (sendComTankTemp == 1)
|
|
{
|
|
sendComTankTemp = -1;
|
|
}
|
|
if (pause)
|
|
{
|
|
isPaused = true;
|
|
if (Heating == 1)
|
|
{
|
|
Heating = 10;
|
|
}
|
|
else if (cooling == 1)
|
|
{
|
|
cooling = 10;
|
|
}
|
|
else if (pouring == 1)
|
|
{
|
|
pouring = 10;
|
|
}
|
|
//PumbOn = -1;
|
|
if (tankBtm != null)
|
|
{
|
|
if (tankBtm.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var item in tankBtm.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = (int)_machine.TankMaxHeat * 10;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
}
|
|
if (tankWall != null)
|
|
{
|
|
if (tankWall.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var item in tankWall.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = (int)_machine.TankMaxHeat * 10;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
if (pumb != null)
|
|
{
|
|
if (pumb.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var item in pumb.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = (int)_machine.PumbMaxHeat * 10;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
if (fount != null)
|
|
{
|
|
if (fount.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var item in fount.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = -10000;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
holdingRegister.setTemp1 = setTempValues[0];
|
|
holdingRegister.setTemp2 = setTempValues[1];
|
|
holdingRegister.setTemp3 = setTempValues[2];
|
|
holdingRegister.setTemp4 = setTempValues[3];
|
|
holdingRegister.motor = 0;
|
|
|
|
await WriteToSerialAsync("RecipePause");
|
|
startMixerMotor = 0;
|
|
startFountainMotor = 0;
|
|
if (heatingTimer != null)
|
|
{
|
|
heatingTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
heatingSeconds = _machine.HeatingDelay;
|
|
heatingTimer = null;
|
|
}
|
|
if (coolingTimer != null)
|
|
{
|
|
coolingTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
coolingSeconds = _machine.CoolingDelay;
|
|
coolingTimer = null;
|
|
|
|
}
|
|
if (pouringTimer != null)
|
|
{
|
|
pouringTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
pouringSeconds = _machine.PouringDelay;
|
|
pouringTimer = null;
|
|
|
|
}
|
|
if (pedalOffTimer != null)
|
|
{
|
|
pedalOffTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
pedalOffSeconds = 0;
|
|
}
|
|
if (pedalOnTimer != null)
|
|
{
|
|
pedalOnTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
pedalOnSeconds = 0;
|
|
}
|
|
if (fountainPauseTimer != null)
|
|
{
|
|
fountainPauseTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
fountainPauseSeconds = 10;
|
|
fountainPauseTimer = null;
|
|
}
|
|
if (fountainTimer != null)
|
|
{
|
|
fountainTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
fountainTimer = null;
|
|
}
|
|
if (mixerTimer != null)
|
|
{
|
|
mixerTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
mixerTimer = null;
|
|
}
|
|
Dispatcher.UIThread.Post(async () =>
|
|
{
|
|
footerMsg.Text = "Recipe Paused";
|
|
|
|
});
|
|
pause = false;
|
|
}
|
|
else if (unPause)
|
|
{
|
|
isPaused = false;
|
|
if (Heating == 10)
|
|
{
|
|
Heating = 1;
|
|
sendComHeating = 1;
|
|
}
|
|
else if (cooling == 10)
|
|
{
|
|
cooling = 1;
|
|
sendComCooling = 1;
|
|
}
|
|
else if (pouring == 10)
|
|
{
|
|
pouring = 1;
|
|
sendComPouring = 1;
|
|
}
|
|
PumbOn = 1;
|
|
|
|
Dispatcher.UIThread.Post(async () =>
|
|
{
|
|
footerMsg.Text = "Recipe Continued";
|
|
});
|
|
unPause = false;
|
|
}
|
|
if (Heating == 1)
|
|
{
|
|
if (errors.Count > 0)
|
|
{
|
|
if ((DateTime.Now - errors.Min(x => x.errorDate)).TotalSeconds >= 3.5)
|
|
{
|
|
if (!isPaused)
|
|
{
|
|
pause = true;
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
Dispatcher.UIThread.Post(async () =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
// Only start heating delay timer when temperature reaches or exceeds the heating goal
|
|
if (comFountainTemp * 10 >= (result._recipeTable?.HeatingGoal * 10) - (screenData.warningLimit * 10))
|
|
{
|
|
if (setHeatingTimerOnce == 1)
|
|
{
|
|
heatingTimer = new Timer(HeatingTimer, null, 1000, 1000);
|
|
heatingSeconds = _machine.HeatingDelay;
|
|
setHeatingTimerOnce = -1;
|
|
|
|
}
|
|
}
|
|
else if (sendComHeating == 1)
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
recipeHeatingGoal = result._recipeTable.HeatingGoal;
|
|
recipeCoolingGoal = result._recipeTable.CoolingGoal;
|
|
recipePouringGoal = result._recipeTable.PouringGoal;
|
|
}
|
|
});
|
|
|
|
foreach (var item in tankBtm.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = (int)_machine.TankMaxHeat;
|
|
}
|
|
foreach (var item in tankWall.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = (int)_machine.TankMaxHeat;
|
|
}
|
|
foreach (var item in pumb.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = (int)_machine.PumbMaxHeat;
|
|
}
|
|
foreach (var item in fount.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = (int)recipeHeatingGoal;
|
|
}
|
|
holdingRegister.setTemp1 = setTempValues[0] * 10;
|
|
holdingRegister.setTemp2 = setTempValues[1] * 10;
|
|
holdingRegister.setTemp3 = setTempValues[2] * 10;
|
|
holdingRegister.setTemp4 = setTempValues[3] * 10;
|
|
if (fountainMotor != null)
|
|
{
|
|
if (fountainMotor.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var bit in fountainMotor.BitNumbers)
|
|
{
|
|
holdingRegister.motor |= (ushort)(1 << bit);
|
|
}
|
|
isFountainMotorOn = true;
|
|
|
|
}
|
|
}
|
|
if (mixerMotor != null)
|
|
{
|
|
if (mixerMotor.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var bit in mixerMotor.BitNumbers)
|
|
{
|
|
holdingRegister.motor |= (ushort)(1 << bit);
|
|
}
|
|
isMixerMotorOn = true;
|
|
|
|
}
|
|
}
|
|
await WriteToSerialAsync("HeatingPhase");
|
|
Dispatcher.UIThread.Post(async () =>
|
|
{
|
|
footerMsg.Text = "Heating phase";
|
|
|
|
});
|
|
|
|
sendComHeating = -1;
|
|
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
}
|
|
else if (cooling == 1)
|
|
{
|
|
if (errors.Count > 0)
|
|
{
|
|
if (!isPaused)
|
|
{
|
|
//pause = true;
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (sendComCooling == 1)
|
|
{
|
|
foreach (var item in tankBtm.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = -1000;
|
|
}
|
|
foreach (var item in tankWall.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = -1000;
|
|
}
|
|
foreach (var item in pumb.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = (int)_machine.PumbMinHeat;
|
|
|
|
}
|
|
foreach (var item in fount.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = (int)recipeCoolingGoal;
|
|
|
|
}
|
|
holdingRegister.setTemp1 = setTempValues[0] * 10;
|
|
holdingRegister.setTemp2 = setTempValues[1] * 10;
|
|
holdingRegister.setTemp3 = setTempValues[2] * 10;
|
|
holdingRegister.setTemp4 = setTempValues[3] * 10;
|
|
if (fountainMotor != null)
|
|
{
|
|
if (fountainMotor.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var bit in fountainMotor.BitNumbers)
|
|
{
|
|
holdingRegister.motor |= (ushort)(1 << bit);
|
|
}
|
|
isFountainMotorOn = true;
|
|
|
|
}
|
|
}
|
|
if (mixerMotor != null)
|
|
{
|
|
if (mixerMotor.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var bit in mixerMotor.BitNumbers)
|
|
{
|
|
holdingRegister.motor |= (ushort)(1 << bit);
|
|
}
|
|
isMixerMotorOn = true;
|
|
|
|
}
|
|
}
|
|
await WriteToSerialAsync("CoolingPhase");
|
|
Dispatcher.UIThread.Post(async () =>
|
|
{
|
|
footerMsg.Text = "Cooling phase";
|
|
});
|
|
sendComCooling = -1;
|
|
|
|
|
|
}
|
|
|
|
Dispatcher.UIThread.Post(async () =>
|
|
{
|
|
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
// Check if current temperature is already at pouring goal - skip cooling phase
|
|
if (Math.Abs((double)(comFountainTemp * 10) - (double)(result._recipeTable?.PouringGoal * 10 ?? 0)) <= (screenData.warningLimit * 10))
|
|
{
|
|
if (setCoolingTimerOnce == 1)
|
|
{
|
|
// Skip cooling phase, go directly to pouring
|
|
cooling = -1;
|
|
pouring = 1;
|
|
sendComPouring = 1;
|
|
setPouringTimerOnce = 1;
|
|
setCoolingTimerOnce = -1;
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
footerMsg.Text = "Already at pouring temperature - starting pouring phase";
|
|
});
|
|
}
|
|
}
|
|
// Only start cooling delay timer when temperature reaches or goes below the cooling goal
|
|
else if (comFountainTemp * 10 <= (result._recipeTable?.CoolingGoal * 10) + (screenData.warningLimit * 10))
|
|
{
|
|
if (setCoolingTimerOnce == 1)
|
|
{
|
|
if (coolingTimer == null)
|
|
{
|
|
coolingTimer = new Timer(CoolingTimer, null, 1000, 1000);
|
|
}
|
|
else
|
|
{
|
|
// Ensure the timer is running at the correct interval
|
|
coolingTimer.Change(1000, 1000);
|
|
}
|
|
coolingSeconds = _machine.CoolingDelay;
|
|
setCoolingTimerOnce = -1;
|
|
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
}
|
|
else if (pouring == 1)
|
|
{
|
|
if (errors.Count > 0)
|
|
{
|
|
if (!isPaused)
|
|
{
|
|
//pause = true;
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (sendComPouring == 1)
|
|
{
|
|
|
|
foreach (var item in tankBtm.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = (int)_machine.TankMaxHeat;
|
|
|
|
}
|
|
foreach (var item in tankWall.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = (int)_machine.TankMaxHeat;
|
|
|
|
}
|
|
foreach (var item in pumb.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = (int)_machine.PumbMaxHeat;
|
|
|
|
}
|
|
foreach (var item in fount.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = (int)recipePouringGoal;
|
|
|
|
}
|
|
holdingRegister.setTemp1 = setTempValues[0] * 10;
|
|
holdingRegister.setTemp2 = setTempValues[1] * 10;
|
|
holdingRegister.setTemp3 = setTempValues[2] * 10;
|
|
holdingRegister.setTemp4 = setTempValues[3] * 10;
|
|
if (fountainMotor != null)
|
|
{
|
|
if (fountainMotor.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var bit in fountainMotor.BitNumbers)
|
|
{
|
|
holdingRegister.motor |= (ushort)(1 << bit);
|
|
}
|
|
isFountainMotorOn = true;
|
|
|
|
}
|
|
}
|
|
if (mixerMotor != null)
|
|
{
|
|
if (mixerMotor.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var bit in mixerMotor.BitNumbers)
|
|
{
|
|
holdingRegister.motor |= (ushort)(1 << bit);
|
|
}
|
|
isMixerMotorOn = true;
|
|
|
|
}
|
|
}
|
|
await WriteToSerialAsync("PouringPhase");
|
|
Dispatcher.UIThread.Post(async () =>
|
|
{
|
|
footerMsg.Text = "Prepare for pouring";
|
|
});
|
|
sendComPouring = -1;
|
|
|
|
}
|
|
Dispatcher.UIThread.Post(async () =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
// Only start pouring delay timer when temperature reaches the pouring goal (within tolerance)
|
|
if (Math.Abs((double)(comFountainTemp * 10) - (double)(result._recipeTable?.PouringGoal * 10 ?? 0)) <= (screenData.warningLimit * 10))
|
|
{
|
|
if (setPouringTimerOnce == 1)
|
|
{
|
|
foreach (var item in tankBtm.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = (int)(recipePouringGoal + _machine.PreHeatingTemp);
|
|
|
|
}
|
|
foreach (var item in tankWall.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = (int)(recipePouringGoal + _machine.PreHeatingTemp);
|
|
|
|
}
|
|
foreach (var item in pumb.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = -1000;
|
|
|
|
}
|
|
foreach (var item in fount.BitNumbers)
|
|
{
|
|
setTempValues[item - 8] = (int)recipePouringGoal;
|
|
}
|
|
holdingRegister.setTemp1 = setTempValues[0] * 10;
|
|
holdingRegister.setTemp2 = setTempValues[1] * 10;
|
|
holdingRegister.setTemp3 = setTempValues[2] * 10;
|
|
holdingRegister.setTemp4 = setTempValues[3] * 10;
|
|
if (fountainMotor != null)
|
|
{
|
|
if (fountainMotor.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var bit in fountainMotor.BitNumbers)
|
|
{
|
|
holdingRegister.motor |= (ushort)(1 << bit);
|
|
}
|
|
isFountainMotorOn = true;
|
|
}
|
|
}
|
|
if (mixerMotor != null)
|
|
{
|
|
if (mixerMotor.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var bit in mixerMotor.BitNumbers)
|
|
{
|
|
holdingRegister.motor |= (ushort)(1 << bit);
|
|
}
|
|
isMixerMotorOn = true;
|
|
}
|
|
}
|
|
await WriteToSerialAsync("PouringPhase");
|
|
if (pouringTimer == null)
|
|
{
|
|
pouringTimer = new Timer(PouringTimer, null, 1000, 1000);
|
|
}
|
|
else
|
|
{
|
|
// Ensure the timer is running at the correct interval
|
|
pouringTimer.Change(1000, 1000);
|
|
}
|
|
pouringSeconds = _machine.PouringDelay;
|
|
setPouringTimerOnce = -1;
|
|
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
footerMsg.Text = "Contact Admin";
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (PumbOn == 1)
|
|
{
|
|
//Dispatcher.UIThread.Post(() =>
|
|
//{
|
|
// recipeStartBtn.IsEnabled = true;
|
|
|
|
//});
|
|
if (pedalMotor == 0) // Manual Pedal
|
|
{
|
|
// Reset auto mode flag and stop timers
|
|
isPedalAutoMode = false;
|
|
|
|
// Stop pedal timers
|
|
if (pedalOnTimer != null)
|
|
{
|
|
pedalOnTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
pedalOnTimer.Dispose();
|
|
pedalOnTimer = null;
|
|
}
|
|
if (pedalOffTimer != null)
|
|
{
|
|
pedalOffTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
pedalOffTimer.Dispose();
|
|
pedalOffTimer = null;
|
|
}
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings settings)
|
|
{
|
|
settings.pedalDelayTxt.IsVisible = false;
|
|
settings.pedalDelayCounter.IsVisible = false;
|
|
}
|
|
});
|
|
|
|
var pedal = _mapping.Find(x => x.Name.ToLower() == "pedal");
|
|
if (pedal != null)
|
|
{
|
|
// READING THE INPUT REGISTER
|
|
if (errors.Count == 0)
|
|
{
|
|
ushort registerValue = (ushort)inputValues[1];
|
|
if (allPedalBitsOn != pedal.BitNumbers.All(bit => (registerValue & (1 << bit)) != 0))
|
|
{
|
|
allPedalBitsOn = pedal.BitNumbers.All(bit => (registerValue & (1 << bit)) != 0);
|
|
pedalStateChanged = 1;
|
|
}
|
|
else
|
|
{
|
|
pedalStateChanged = 0;
|
|
}
|
|
|
|
if (!allPedalBitsOn)
|
|
{
|
|
pedalState = 1;// All bits ON
|
|
}
|
|
else
|
|
{
|
|
pedalState = 0; // At least one bit is OFF
|
|
}
|
|
|
|
// READING THE motor REGISTER
|
|
var fount = _mapping.Find(x => x.Name.ToLower() == "Helix".ToLower());
|
|
if (fount != null)
|
|
{
|
|
if (fount.BitNumbers.Count > 0)
|
|
{
|
|
bool valueChanged = false;
|
|
if (pedalState == 1) // If all monitored bits are ON
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
result.pedalUnderLine.Fill = Brush.Parse(result.PassiveColor);
|
|
}
|
|
});
|
|
if (!(comPumpTemp <= recipeCoolingGoal - 3))
|
|
{
|
|
foreach (var bit in fount.BitNumbers)
|
|
{
|
|
if (!ToBinary(holdingRegister.motor)[bit])
|
|
{
|
|
//Debug.WriteLine("input value:" + registerValue);
|
|
//Debug.WriteLine("pedal on:" + allBitsOn);
|
|
holdingRegister.motor |= (ushort)(1 << bit);
|
|
valueChanged = true;
|
|
|
|
}
|
|
}
|
|
if (valueChanged)
|
|
{
|
|
await WriteToSerialAsync("PedalManual");
|
|
valueChanged = false;
|
|
if (isPaused)
|
|
{
|
|
unPause = true;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
else // If at least one monitored bit is OFF
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
result.pedalUnderLine.Fill = Brush.Parse(result.ActiveColor);
|
|
}
|
|
});
|
|
if (pedalStateChanged == 1)
|
|
{
|
|
foreach (var bit in fount.BitNumbers)
|
|
{
|
|
if (ToBinary(holdingRegister.motor)[bit])
|
|
{
|
|
holdingRegister.motor &= (ushort)~(1 << bit);
|
|
valueChanged = true;
|
|
}
|
|
|
|
}
|
|
if (valueChanged)
|
|
{
|
|
await WriteToSerialAsync("PedalManual");
|
|
valueChanged = false;
|
|
}
|
|
|
|
}
|
|
|
|
if (fountainPauseTimer == null && startRecipe == 1 && (Heating == 1 || heatingTimer != null || cooling == 1 || coolingTimer != null || pouring == 1 || pouringTimer != null
|
|
))
|
|
{
|
|
fountainPauseSeconds = 10;
|
|
fountainPauseTimer = new Timer(FountainPauseTimer, null, 0, 1000);
|
|
}
|
|
|
|
}
|
|
|
|
if (pedalStateChanged == 1)
|
|
{
|
|
// WRITE UPDATED VALUE TO HVO REGISTER
|
|
//isWriting = true;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
else if (pedalMotor == 1) // auto Pedal
|
|
{
|
|
// Set auto mode flag
|
|
isPedalAutoMode = true;
|
|
|
|
// Direct fountain control based on pedal state
|
|
var fount = _mapping.Find(x => x.Name.ToLower() == "Helix".ToLower());
|
|
if (fount != null && fount.BitNumbers.Count > 0)
|
|
{
|
|
if (pedalState == 0) // Pedal ON - Turn fountain ON
|
|
{
|
|
foreach (var bit in fount.BitNumbers)
|
|
{
|
|
holdingRegister.motor |= (ushort)(1 << bit); // Set the motor bit ON
|
|
}
|
|
isFountainMotorOn = true;
|
|
startFountainMotor = 1;
|
|
sendComFountainMotor = 1;
|
|
startFountainMotorFlashing = -1; // No flashing
|
|
await WriteToSerialAsync("Pedal Auto - Fountain ON");
|
|
|
|
// Update UI to show fountain is ON
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
var fountainLable = result.FountainSP.Children[1] as Avalonia.Controls.Label;
|
|
var fountainRectangel = result.FountainSP.Children[2] as Avalonia.Controls.Shapes.Rectangle;
|
|
fountainLable.Content = "ON";
|
|
fountainLable.Foreground = Brush.Parse("#ff231f20");
|
|
fountainRectangel.Fill = Brush.Parse(ActiveColor);
|
|
}
|
|
});
|
|
}
|
|
else if (pedalState == 1) // Pedal OFF - Turn fountain OFF
|
|
{
|
|
foreach (var bit in fount.BitNumbers)
|
|
{
|
|
holdingRegister.motor &= (ushort)~(1 << bit); // Clear the motor bit OFF
|
|
}
|
|
isFountainMotorOn = false;
|
|
startFountainMotor = 0;
|
|
sendComFountainMotor = 0;
|
|
startFountainMotorFlashing = -1; // No flashing
|
|
await WriteToSerialAsync("Pedal Auto - Fountain OFF");
|
|
|
|
// Update UI to show fountain is OFF
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
var fountainLable = result.FountainSP.Children[1] as Avalonia.Controls.Label;
|
|
var fountainRectangel = result.FountainSP.Children[2] as Avalonia.Controls.Shapes.Rectangle;
|
|
fountainLable.Content = "OFF";
|
|
fountainLable.Foreground = Brush.Parse("#ff231f20");
|
|
fountainRectangel.Fill = Brush.Parse(PassiveColor);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// Reset pedal state and handle timing
|
|
pedalState = -1;
|
|
|
|
// Handle pedal timing (alternating ON/OFF based on recipe settings)
|
|
if (setPedalTimerOnce == 1) // Start OFF timer
|
|
{
|
|
if (pedalOffTimer == null)
|
|
{
|
|
pedalOffTimer = new Timer(PedalOffTimer, null, 1000, 1000);
|
|
}
|
|
else
|
|
{
|
|
pedalOffTimer.Change(1000, 1000);
|
|
}
|
|
setPedalTimerOnce = -1;
|
|
}
|
|
else if (setPedalTimerOnce == 0) // Start ON timer
|
|
{
|
|
if (pedalOnTimer == null)
|
|
{
|
|
pedalOnTimer = new Timer(PedalOnTimer, null, 1000, 1000);
|
|
}
|
|
else
|
|
{
|
|
pedalOnTimer.Change(1000, 1000);
|
|
}
|
|
setPedalTimerOnce = -1;
|
|
}
|
|
}
|
|
|
|
//change diag UI
|
|
var hvo = ToBinary(holdingRegister.hvOut);
|
|
var lvo = ToBinary(holdingRegister.lvOut);
|
|
var motor = ToBinary(holdingRegister.motor);
|
|
|
|
Dispatcher.UIThread.Post(async () =>
|
|
{
|
|
if (ContentArea.Content is Diagnostics diagnostics)
|
|
{
|
|
foreach (var item in diagnostics.MotoreState.OfType<Grid>().ToList())
|
|
{
|
|
var stackPanel = item.Children[0] as StackPanel;
|
|
var text = stackPanel.Children[1] as TextBlock;
|
|
var border = item.Children[1] as Border;
|
|
if (motor[int.Parse(item.Tag.ToString())])
|
|
{
|
|
|
|
text.Text = "ON";
|
|
border.Background = Brush.Parse(diagnostics.PinkColor);
|
|
diagnostics.MotoreState.OfType<Ellipse>().ToList().Find(x => x.Tag.ToString() == item.Tag.ToString()).Fill = Brush.Parse(diagnostics.OrangeColor);
|
|
|
|
}
|
|
else
|
|
{
|
|
text.Text = "OFF";
|
|
border.Background = Brush.Parse(diagnostics.GrayColor);
|
|
diagnostics.MotoreState.OfType<Ellipse>().ToList().Find(x => x.Tag.ToString() == item.Tag.ToString()).Fill = Brush.Parse(diagnostics.GrayColor);
|
|
|
|
|
|
}
|
|
}
|
|
|
|
// HVO
|
|
foreach (var item in diagnostics.hvoOutPuts.OfType<Grid>().ToList())
|
|
{
|
|
var text = item.Children[1] as TextBlock;
|
|
var border = item.Children[2] as Border;
|
|
if (hvo[int.Parse(item.Tag.ToString())])
|
|
{
|
|
|
|
text.Text = "ON";
|
|
border.Background = Brush.Parse(diagnostics.PinkColor);
|
|
diagnostics.hvoOutPuts.OfType<Ellipse>().ToList().Find(x => x.Tag.ToString() == item.Tag.ToString()).Fill = Brush.Parse(diagnostics.OrangeColor);
|
|
|
|
}
|
|
else
|
|
{
|
|
text.Text = "OFF";
|
|
border.Background = Brush.Parse(diagnostics.GrayColor);
|
|
diagnostics.hvoOutPuts.OfType<Ellipse>().ToList().Find(x => x.Tag.ToString() == item.Tag.ToString()).Fill = Brush.Parse(diagnostics.GrayColor);
|
|
|
|
|
|
}
|
|
}
|
|
|
|
// LVO
|
|
foreach (var item in diagnostics.lvoOutPuts.OfType<Grid>().ToList())
|
|
{
|
|
var text = item.Children[1] as TextBlock;
|
|
var border = item.Children[2] as Border;
|
|
if (lvo[int.Parse(item.Tag.ToString())])
|
|
{
|
|
|
|
text.Text = "ON";
|
|
border.Background = Brush.Parse(diagnostics.PinkColor);
|
|
diagnostics.lvoOutPuts.OfType<Ellipse>().ToList().Find(x => x.Tag.ToString() == item.Tag.ToString()).Fill = Brush.Parse(diagnostics.GreenColor);
|
|
|
|
}
|
|
else
|
|
{
|
|
text.Text = "OFF";
|
|
border.Background = Brush.Parse(diagnostics.GrayColor);
|
|
diagnostics.lvoOutPuts.OfType<Ellipse>().ToList().Find(x => x.Tag.ToString() == item.Tag.ToString()).Fill = Brush.Parse(diagnostics.GrayColor);
|
|
|
|
|
|
}
|
|
}
|
|
foreach (var item in diagnostics.lvoOutPuts.OfType<StackPanel>().ToList())
|
|
{
|
|
var text = item.Children[1] as TextBlock;
|
|
var border = item.Children[2] as Border;
|
|
if (lvo[int.Parse(item.Tag.ToString())])
|
|
{
|
|
|
|
text.Text = "ON";
|
|
border.Background = Brush.Parse(diagnostics.PinkColor);
|
|
diagnostics.lvoOutPuts.OfType<Ellipse>().ToList().Find(x => x.Tag.ToString() == item.Tag.ToString()).Fill = Brush.Parse(diagnostics.GreenColor);
|
|
|
|
}
|
|
else
|
|
{
|
|
text.Text = "OFF";
|
|
border.Background = Brush.Parse(diagnostics.GrayColor);
|
|
diagnostics.lvoOutPuts.OfType<Ellipse>().ToList().Find(x => x.Tag.ToString() == item.Tag.ToString()).Fill = Brush.Parse(diagnostics.GrayColor);
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
await Task.Delay(100);
|
|
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
finally
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool ConnectToSerialPort()
|
|
{
|
|
screenData = _screeen.ReadScreens()?[0];
|
|
try
|
|
{
|
|
if (_port != null && _port.IsOpen)
|
|
{
|
|
_port.Close();
|
|
}
|
|
_port = null;
|
|
_port = new SerialPort(screenData.port, screenData.boundRate);
|
|
switch (screenData.parity)
|
|
{
|
|
case 0:
|
|
_port.Parity = Parity.None;
|
|
break;
|
|
case 1:
|
|
_port.Parity = Parity.Odd;
|
|
break;
|
|
case 2:
|
|
_port.Parity = Parity.Even;
|
|
break;
|
|
case 3:
|
|
_port.Parity = Parity.Mark;
|
|
break;
|
|
case 4:
|
|
_port.Parity = Parity.Space;
|
|
break;
|
|
default:
|
|
_port.Parity = Parity.None;
|
|
break;
|
|
}
|
|
switch (screenData.stopBits)
|
|
{
|
|
case 2:
|
|
_port.StopBits = StopBits.Two;
|
|
break;
|
|
default:
|
|
_port.StopBits = StopBits.One;
|
|
break;
|
|
}
|
|
_port.DataBits = 8;
|
|
_port.Handshake = Handshake.None;
|
|
_port.DtrEnable = true;
|
|
|
|
// Open the serial port
|
|
|
|
_port.Open();
|
|
return true;
|
|
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
|
|
Console.WriteLine($"Error connecting to port {screenData.port}: {ex.Message}");
|
|
_port = null;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
public void closeConnection()
|
|
{
|
|
isRunning = false;
|
|
monitorThread.Abort();
|
|
}
|
|
private async void OnClosing1(object? sender, CancelEventArgs e)
|
|
{
|
|
closeConnection();
|
|
|
|
}
|
|
private void OnClosingWindow(object? sender, WindowClosingEventArgs e)
|
|
{
|
|
if (_port != null && _port.IsOpen)
|
|
{
|
|
_port.Close();
|
|
//_port = null;
|
|
}
|
|
// Example: Cancel the close if needed
|
|
// e.Cancel = true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//Main Window Functions
|
|
private void errorLogoClick(object? sender, RoutedEventArgs e)
|
|
{
|
|
errorPopupOverlay.IsVisible = true;
|
|
//errorTitel.Text = errorLogo.Tag.ToString();
|
|
errorMsg.Text = errorMsg.Text.Trim();
|
|
}
|
|
private void warningLogoClick(object? sender, RoutedEventArgs e)
|
|
{
|
|
warningPopupOverlay.IsVisible = true;
|
|
warningTitel.Text = warningLogo.Tag.ToString();
|
|
warningMsg.Text = warningMsg.Text.Trim();
|
|
}
|
|
private void HomeTraclBtn(object? sender, RoutedEventArgs e)
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
result.DeletePopupOverlay.IsVisible = true;
|
|
result.DeletePopupOverlay.Tag = "home";
|
|
|
|
}
|
|
else if (ContentArea.Content is Diagnostics diagnostics)
|
|
{
|
|
restBoard = true;
|
|
|
|
this.UserName.Content = "Select User";
|
|
footerMsg.Text = "";
|
|
ContentArea.Content = new Home(this);
|
|
}
|
|
else
|
|
{
|
|
this.UserName.Content = "Select User";
|
|
footerMsg.Text = "";
|
|
ContentArea.Content = new Home(this);
|
|
}
|
|
}
|
|
private void DiagnosticsBtn(object? sender, RoutedEventArgs e)
|
|
{
|
|
if (ContentArea.Content is AdvanceSettings advanceSettings)
|
|
{
|
|
footerMsg.Text = "";
|
|
ContentArea.Content = new Diagnostics(this, true);
|
|
}
|
|
else if (ContentArea.Content is ManualControl)
|
|
{
|
|
footerMsg.Text = "";
|
|
ContentArea.Content = new Diagnostics(this, false, true);
|
|
}
|
|
else
|
|
{
|
|
footerMsg.Text = "";
|
|
ContentArea.Content = new Diagnostics(this);
|
|
}
|
|
|
|
}
|
|
private void ChefManualBtn(object? sender, RoutedEventArgs e)
|
|
{
|
|
footerMsg.Text = "";
|
|
ContentArea.Content = new ManualControl(this);
|
|
|
|
}
|
|
public void AdvanceSettingsView(object? sender, RoutedEventArgs e)
|
|
{
|
|
if (ContentArea.Content is Diagnostics)
|
|
{
|
|
ContentArea.Content = new AdvanceSettings(this, true, false);
|
|
|
|
}
|
|
else if (ContentArea.Content is Software)
|
|
{
|
|
ContentArea.Content = new AdvanceSettings(this, false, true);
|
|
|
|
}
|
|
else
|
|
{
|
|
ContentArea.Content = new AdvanceSettings(this);
|
|
|
|
}
|
|
|
|
}
|
|
private void RecipeSelTrackBtn(object? sender, RoutedEventArgs e)
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
result.DeletePopupOverlay.IsVisible = true;
|
|
result.DeletePopupOverlay.Tag = "recipeSel";
|
|
}
|
|
else
|
|
{
|
|
footerMsg.Text = "";
|
|
ContentArea.Content = new Recipe(this, Program.currentUser);
|
|
}
|
|
|
|
|
|
}
|
|
private void SettingTrackBtn(object? sender, RoutedEventArgs e)
|
|
{
|
|
if (ContentArea.Content is Diagnostics diagnostics)
|
|
{
|
|
restBoard = true;
|
|
}
|
|
footerMsg.Text = "";
|
|
ContentArea.Content = new Admin(this, Program.currentUser);
|
|
}
|
|
private void SoftwareBtn(object? sender, RoutedEventArgs e)
|
|
{
|
|
if (ContentArea.Content is AdvanceSettings)
|
|
{
|
|
footerMsg.Text = "";
|
|
ContentArea.Content = new Software(this, true);
|
|
}
|
|
else if (ContentArea.Content is ManualControl)
|
|
{
|
|
footerMsg.Text = "";
|
|
ContentArea.Content = new Software(this, false, true);
|
|
}
|
|
else
|
|
{
|
|
footerMsg.Text = "";
|
|
ContentArea.Content = new Software(this);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
private async void PreHeatingClick(object? sender, RoutedEventArgs e)
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
if (result.recipeSettings.IsEnabled)
|
|
{
|
|
startPreHeating = 1;
|
|
writingMaxTemp = 1;
|
|
//if (!isMixerMotorOn)
|
|
//{
|
|
// result.mixerBtn.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
|
|
//}
|
|
//if (!isFountainMotorOn)
|
|
//{
|
|
// result.fountainBtn.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
|
|
//}
|
|
}
|
|
else
|
|
{
|
|
startPreHeating = 0;
|
|
writingMaxTemp = 0;
|
|
isFlashPreHeating = false;
|
|
}
|
|
}
|
|
|
|
}
|
|
//Mixer
|
|
public async void MotorClick(object? sender, RoutedEventArgs e)
|
|
{
|
|
if (!isMixerMotorOn)
|
|
{
|
|
isMixerMotorOn = true;
|
|
checkMixerTWT_HWTH = true;
|
|
setMixerTimerOnce = true;
|
|
}
|
|
else
|
|
{
|
|
isMixerMotorOn = false;
|
|
checkMixerTWT_HWTH = false;
|
|
startMixerMotorFlashing = 0;
|
|
sendComMixerMotor = 0;
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings settings)
|
|
{
|
|
settings.mixerDelayTxt.IsVisible = false;
|
|
settings.mixerDelayCounter.IsVisible = false;
|
|
}
|
|
});
|
|
if (mixerTimer != null)
|
|
{
|
|
mixerTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
mixerTimer = null;
|
|
}
|
|
|
|
}
|
|
}
|
|
private void MixerTimer(object state)
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings settings)
|
|
{
|
|
if (comTankTemp >= recipeCoolingGoal - 3)
|
|
{
|
|
mixerSeconds--;
|
|
}
|
|
else
|
|
{
|
|
//mixerTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
}
|
|
|
|
if (mixerSeconds <= 0)
|
|
{
|
|
// Stop the timer after 15 seconds
|
|
settings.mixerDelayTxt.IsVisible = false;
|
|
settings.mixerDelayCounter.IsVisible = false;
|
|
|
|
if (mixerTimer != null)
|
|
{
|
|
mixerTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
mixerTimer = null;
|
|
}
|
|
startMixerMotorFlashing = 0;
|
|
sendComMixerMotor = 1;
|
|
return;
|
|
}
|
|
if (mixerSeconds <= _machine.MixerDelay)
|
|
{
|
|
settings.mixerDelayTxt.IsVisible = true;
|
|
settings.mixerDelayCounter.Text = mixerSeconds.ToString();
|
|
settings.mixerDelayCounter.IsVisible = true;
|
|
|
|
}
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
private void StopMixerTimer(object state)
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings settings)
|
|
{
|
|
if (comTankTemp <= recipeCoolingGoal - 3)
|
|
{
|
|
stopMixerSecondes--;
|
|
}
|
|
else
|
|
{
|
|
//mixerTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
}
|
|
|
|
if (stopMixerSecondes <= 0)
|
|
{
|
|
sendComMixerMotor = 0;
|
|
|
|
|
|
|
|
stopMixerTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
stopMixerTimer = null;
|
|
return;
|
|
}
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
//Fountain
|
|
public async void FountainClick(object? sender, RoutedEventArgs e)
|
|
{
|
|
// Reset pedal auto mode when user manually controls fountain
|
|
if (isPedalAutoMode)
|
|
{
|
|
isPedalAutoMode = false;
|
|
// Stop pedal timers when user takes manual control
|
|
if (pedalOnTimer != null)
|
|
{
|
|
pedalOnTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
pedalOnTimer.Dispose();
|
|
pedalOnTimer = null;
|
|
}
|
|
if (pedalOffTimer != null)
|
|
{
|
|
pedalOffTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
pedalOffTimer.Dispose();
|
|
pedalOffTimer = null;
|
|
}
|
|
pedalMotor = -1; // Reset to manual mode
|
|
}
|
|
|
|
if (!isFountainMotorOn)
|
|
{
|
|
isFountainMotorOn = true;
|
|
checkFountainTMT_PMT = true;
|
|
setFountainTimerOnce = true;
|
|
}
|
|
else
|
|
{
|
|
isFountainMotorOn = false;
|
|
|
|
checkFountainTMT_PMT = false;
|
|
startFountainMotorFlashing = 0;
|
|
sendComFountainMotor = 0;
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings settings)
|
|
{
|
|
settings.fountainDelayTxt.IsVisible = false;
|
|
settings.fountainDelayCounter.IsVisible = false;
|
|
settings.fountainTargetTxt.IsVisible = false;
|
|
settings.fountainTagetTemp.IsVisible = false;
|
|
}
|
|
});
|
|
if (fountainTimer != null)
|
|
{
|
|
fountainTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
fountainTimer = null;
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
private void FountainTimer(object state)
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings settings)
|
|
{
|
|
if (comPumpTemp >= recipeCoolingGoal - 3)
|
|
{
|
|
fountainSeconds--;
|
|
}
|
|
else
|
|
{
|
|
//fountainTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
}
|
|
if (fountainSeconds <= 0)
|
|
{
|
|
// Stop the timer after 15 seconds
|
|
settings.fountainDelayTxt.IsVisible = false;
|
|
settings.fountainDelayCounter.IsVisible = false;
|
|
settings.fountainTargetTxt.IsVisible = false;
|
|
settings.fountainTagetTemp.IsVisible = false;
|
|
|
|
if (fountainTimer != null)
|
|
{
|
|
fountainTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
fountainTimer = null;
|
|
}
|
|
startFountainMotorFlashing = 0;
|
|
sendComFountainMotor = 1;
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
if (startRecipe == 1 && !isPaused)
|
|
{
|
|
PumbOn = 1;
|
|
|
|
}
|
|
if (result._recipeTable.Pedal.Value)
|
|
{
|
|
pedalMotor = 0;
|
|
}
|
|
else
|
|
{
|
|
pedalMotor = 1;
|
|
pedalState = 0;
|
|
setPedalTimerOnce = 1;
|
|
}
|
|
}
|
|
|
|
});
|
|
|
|
return;
|
|
}
|
|
if (fountainSeconds <= _machine.PumbDelay)
|
|
{
|
|
|
|
|
|
settings.fountainDelayTxt.Text = "Chocolate Delay: ";
|
|
settings.fountainDelayTxt.IsVisible = true;
|
|
settings.fountainDelayCounter.Text = fountainSeconds.ToString();
|
|
settings.fountainDelayCounter.IsVisible = true;
|
|
settings.fountainTagetTemp.IsVisible = false;
|
|
settings.fountainTargetTxt.IsVisible = false;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
private void StopFountainTimer(object state)
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings settings)
|
|
{
|
|
if (comPumpTemp <= recipeCoolingGoal - 3)
|
|
{
|
|
stopFountainSecondes--;
|
|
}
|
|
else
|
|
{
|
|
//mixerTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
}
|
|
|
|
if (stopFountainSecondes <= 0)
|
|
{
|
|
sendComFountainMotor = 0;
|
|
|
|
|
|
stopFountainTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
stopFountainTimer = null;
|
|
return;
|
|
}
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
private void FountainPauseTimer(object state)
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
var fount = _mapping.Find(x => x.Name.ToLower() == "Helix".ToLower());
|
|
|
|
if (allBitsOn != fount.BitNumbers.All(bit => (holdingRegister.motor & (1 << bit)) != 0))
|
|
{
|
|
allBitsOn = fount.BitNumbers.All(bit => (holdingRegister.motor & (1 << bit)) != 0);
|
|
}
|
|
if (fountainPauseSeconds <= 0)
|
|
{
|
|
pause = true;
|
|
if (fountainPauseTimer != null)
|
|
{
|
|
fountainPauseTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
fountainPauseTimer = null;
|
|
}
|
|
|
|
return;
|
|
}
|
|
if (!allBitsOn)
|
|
{
|
|
//motor is off
|
|
//increase the counter
|
|
fountainPauseSeconds--;
|
|
//footerMsg.Text ="Fountain is off recipe will pause after: "+ fountainPauseSeconds.ToString();
|
|
}
|
|
else
|
|
{
|
|
//motor is on
|
|
//cancel the timer
|
|
if (fountainPauseTimer != null)
|
|
{
|
|
|
|
fountainPauseTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
fountainPauseTimer = null;
|
|
}
|
|
}
|
|
|
|
//if the counter greater than 10 then pause the recipe
|
|
});
|
|
|
|
|
|
}
|
|
private void NoChoiceChoosenTimer(object state)
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
//increase the coiunter
|
|
if (ContentArea.Content is Settings settings)
|
|
{
|
|
if (settings.tempErrorPopupOverlay.IsVisible)
|
|
{
|
|
noChoiceChoosenSeconds++;
|
|
//footerMsg.Text = "sec" + noChoiceChoosenSeconds.ToString();
|
|
}
|
|
else
|
|
{
|
|
//stop the timer
|
|
if (noChoiceChoosenTimer != null)
|
|
{
|
|
noChoiceChoosenTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
noChoiceChoosenTimer = null;
|
|
}
|
|
}
|
|
if (noChoiceChoosenSeconds >= 180)//change to 3 min
|
|
{
|
|
//if it reach the 3 min then stop the recipe
|
|
startRecipe = 0;
|
|
Heating = 0;
|
|
cooling = 0;
|
|
pouring = 0;
|
|
PumbOn = -1;
|
|
pedalMotor = -1;
|
|
if (heatingTimer != null)
|
|
{
|
|
heatingTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
heatingSeconds = _machine.HeatingDelay;
|
|
heatingTimer = null;
|
|
}
|
|
if (coolingTimer != null)
|
|
{
|
|
coolingTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
coolingSeconds = _machine.CoolingDelay;
|
|
coolingTimer = null;
|
|
|
|
}
|
|
if (pouringTimer != null)
|
|
{
|
|
pouringTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
pouringSeconds = _machine.PouringDelay;
|
|
pouringTimer = null;
|
|
|
|
}
|
|
if (pedalOffTimer != null)
|
|
{
|
|
pedalOffTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
pedalOffSeconds = 0;
|
|
}
|
|
if (pedalOnTimer != null)
|
|
{
|
|
pedalOnTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
pedalOnSeconds = 0;
|
|
}
|
|
if (fountainPauseTimer != null)
|
|
{
|
|
fountainPauseTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
fountainPauseSeconds = 10;
|
|
fountainPauseTimer = null;
|
|
}
|
|
if (fountainTimer != null)
|
|
{
|
|
fountainTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
fountainTimer = null;
|
|
}
|
|
if (mixerTimer != null)
|
|
{
|
|
mixerTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
mixerTimer = null;
|
|
}
|
|
var fount = _mapping.Find(x => x.Name.ToLower() == "Helix".ToLower());
|
|
if (fount != null)
|
|
{
|
|
if (fount.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var bit in fount.BitNumbers)
|
|
{
|
|
|
|
holdingRegister.motor &= (ushort)~(1 << bit);
|
|
}
|
|
}
|
|
}
|
|
startPreHeating = 0;
|
|
writingMaxTemp = 0;
|
|
isFlashPreHeating = false;
|
|
holdingRegister.setTemp1 = -10000;
|
|
holdingRegister.setTemp2 = -10000;
|
|
holdingRegister.setTemp3 = -10000;
|
|
holdingRegister.setTemp4 = -10000;
|
|
settings.tempErrorPopupOverlay.IsVisible = false;
|
|
Dispatcher.UIThread.Post(async () =>
|
|
{
|
|
await WriteToSerialAsync("NoChoiceChoosenTimer");
|
|
|
|
recipeStartBtn.Foreground = Avalonia.Media.Brushes.White;
|
|
recipeStartBtn.Background = Brush.Parse("#008000");
|
|
footerMsg.Text = "waitting for too long... Recipe Stoped";
|
|
recipeStartBtn.Content = "START RECIPE";
|
|
PreHeatingBtn.IsEnabled = true;
|
|
recipeStartBtn.IsEnabled = true;
|
|
});
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
/// <summary>
|
|
/// Enhanced heating timer with improved goal checking and phase transition logic
|
|
/// </summary>
|
|
private void HeatingTimer(object state)
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings settings)
|
|
{
|
|
if (fountainPauseTimer == null)
|
|
{
|
|
if (!pauseTimer)
|
|
{
|
|
// Continue heating delay countdown
|
|
if (heatingSeconds > 0)
|
|
{
|
|
heatingSeconds--;
|
|
}
|
|
Heating = 1;
|
|
warningMessage = ""; // Clear any previous warnings
|
|
}
|
|
|
|
// Clear warning message during heating phase
|
|
warningMessage = "";
|
|
|
|
// Check if heating phase is complete
|
|
if (heatingSeconds <= 0)
|
|
{
|
|
// Heating phase complete - stop heating timer first
|
|
warningMessage = "";
|
|
heatingTimer?.Change(Timeout.Infinite, Timeout.Infinite);
|
|
heatingTimer?.Dispose();
|
|
heatingTimer = null;
|
|
pauseTimer = false;
|
|
pauseTempTracking = false;
|
|
Heating = -1;
|
|
|
|
// Check if chocolate temperature equals cooling goal (within tolerance)
|
|
// Using a small tolerance (0.5°C) for floating point comparison
|
|
bool chocolateAtCoolingTemp = Math.Abs(comFountainTemp - recipeCoolingGoal) <= 0.5;
|
|
|
|
if (chocolateAtCoolingTemp)
|
|
{
|
|
// Chocolate is already at cooling temperature - show cooling delay
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings settings)
|
|
{
|
|
// Hide cooling delay display
|
|
settings.coolingDelayTxt.IsVisible = false;
|
|
settings.coolingDelayCounter.IsVisible = false;
|
|
settings.coolingDelayCounter.Text = _machine.CoolingDelay.ToString();
|
|
UpdateFooterMessage(RecipePhase.CoolingDelay,
|
|
$"Cooling delay: {_machine.CoolingDelay} seconds");
|
|
}
|
|
});
|
|
|
|
// Start cooling timer for delay countdown
|
|
cooling = 1;
|
|
sendComCooling = 1;
|
|
setCoolingTimerOnce = 1;
|
|
coolingSeconds = _machine.CoolingDelay;
|
|
|
|
// Set flag to indicate we're in cooling delay mode
|
|
isCoolingDelayMode = true;
|
|
|
|
if (coolingTimer == null)
|
|
{
|
|
coolingTimer = new Timer(CoolingTimer, null, 1000, 1000);
|
|
}
|
|
else
|
|
{
|
|
// Ensure the timer is running at the correct interval
|
|
coolingTimer.Change(1000, 1000);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Chocolate needs to cool down - start cooling phase
|
|
UpdateFooterMessage(RecipePhase.CoolingPhase, "Cooling phase");
|
|
|
|
cooling = 1;
|
|
sendComCooling = 1;
|
|
setCoolingTimerOnce = 1;
|
|
coolingSeconds = 0; // No countdown during cooling phase
|
|
|
|
// Set flag to indicate we're in cooling phase mode (not delay mode)
|
|
isCoolingDelayMode = false;
|
|
|
|
if (coolingTimer == null)
|
|
{
|
|
coolingTimer = new Timer(CoolingTimer, null, 1000, 1000);
|
|
}
|
|
else
|
|
{
|
|
// Ensure the timer is running at the correct interval
|
|
coolingTimer.Change(1000, 1000);
|
|
}
|
|
}
|
|
}
|
|
else if (heatingSeconds <= _machine.HeatingDelay && !pauseTimer)
|
|
{
|
|
// Show heating delay countdown
|
|
UpdateFooterMessage(RecipePhase.HeatingDelay, $"Heating delay: {heatingSeconds} seconds");
|
|
}
|
|
else if (heatingSeconds > _machine.HeatingDelay)
|
|
{
|
|
// Show heating phase status with current temperature
|
|
footerMsg.Text = $"Heating phase";
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
/// <summary>
|
|
/// Enhanced cooling timer with improved goal checking and conditional phase transitions
|
|
/// Timer runs every 1000ms (1 second) to count down seconds properly
|
|
/// </summary>
|
|
private void CoolingTimer(object state)
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings settings)
|
|
{
|
|
if (fountainPauseTimer == null)
|
|
{
|
|
// Check if we're in cooling phase and chocolate has reached cooling goal
|
|
// Using same tolerance as heating phase for consistency
|
|
if (!isCoolingDelayMode && Math.Abs(comFountainTemp - recipeCoolingGoal) <= 0.5)
|
|
{
|
|
// Chocolate reached cooling goal during cooling phase - switch to cooling delay
|
|
isCoolingDelayMode = true;
|
|
coolingSeconds = _machine.CoolingDelay; // Reset timer for cooling delay
|
|
|
|
// Hide cooling delay display
|
|
settings.coolingDelayTxt.IsVisible = false;
|
|
settings.coolingDelayCounter.IsVisible = false;
|
|
settings.coolingDelayCounter.Text = coolingSeconds.ToString();
|
|
UpdateFooterMessage(RecipePhase.CoolingDelay,
|
|
$"Cooling delay: {coolingSeconds} seconds");
|
|
}
|
|
|
|
if (!pauseTimer && isCoolingDelayMode && coolingSeconds > 0)
|
|
{
|
|
// Only count down during cooling DELAY, not during cooling PHASE
|
|
coolingSeconds--;
|
|
warningMessage = ""; // Clear any previous warnings
|
|
|
|
// Update the display to show the current countdown
|
|
settings.coolingDelayCounter.Text = coolingSeconds.ToString();
|
|
}
|
|
else if (!pauseTimer && !isCoolingDelayMode)
|
|
{
|
|
// During cooling PHASE, just monitor temperature, don't count down
|
|
warningMessage = ""; // Clear any previous warnings
|
|
}
|
|
|
|
// Check for warning conditions - only warn if temperature is too high
|
|
bool tempTooHighForWarning = comFountainTemp > (recipeCoolingGoal + screenData.warningLimit);
|
|
|
|
if (tempTooHighForWarning)
|
|
{
|
|
// Show warning when temperature is approaching the upper limit
|
|
if (warningMessage != "Cooling temperature approaching maximum")
|
|
{
|
|
warningMessage = "Cooling temperature approaching maximum";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
warningMessage = "";
|
|
}
|
|
|
|
// Check if cooling phase is complete (only when in delay mode)
|
|
if (isCoolingDelayMode && coolingSeconds <= 0)
|
|
{
|
|
// Cooling phase complete - transition to pouring phase
|
|
warningMessage = "";
|
|
|
|
// Safely stop the cooling timer
|
|
if (coolingTimer != null)
|
|
{
|
|
coolingTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
coolingTimer.Dispose();
|
|
coolingTimer = null;
|
|
}
|
|
|
|
pauseTimer = false;
|
|
pauseTempTracking = false;
|
|
cooling = -1;
|
|
|
|
// Check if chocolate temperature equals pouring goal
|
|
bool chocolateAtPouringTemp = Math.Abs(comFountainTemp - recipePouringGoal) <= 0.5;
|
|
|
|
if (chocolateAtPouringTemp)
|
|
{
|
|
// Chocolate is already at pouring temperature - show pouring delay
|
|
pouring = 1;
|
|
sendComPouring = 1;
|
|
setPouringTimerOnce = 1;
|
|
pouringSeconds = _machine.PouringDelay;
|
|
isPouringDelayMode = true;
|
|
|
|
// Start pouring timer
|
|
if (pouringTimer == null)
|
|
{
|
|
pouringTimer = new Timer(PouringTimer, null, 1000, 1000);
|
|
}
|
|
else
|
|
{
|
|
// Ensure the timer is running at the correct interval
|
|
pouringTimer.Change(1000, 1000);
|
|
}
|
|
|
|
UpdateFooterMessage(RecipePhase.PouringPhase,
|
|
$"Pouring delay: {pouringSeconds} seconds");
|
|
}
|
|
else
|
|
{
|
|
// Chocolate needs to reach pouring temperature - start pouring phase
|
|
pouring = 1;
|
|
sendComPouring = 1;
|
|
setPouringTimerOnce = 1;
|
|
pouringSeconds = 0; // No countdown during pouring phase
|
|
isPouringDelayMode = false;
|
|
|
|
// Start pouring timer
|
|
if (pouringTimer == null)
|
|
{
|
|
pouringTimer = new Timer(PouringTimer, null, 1000, 1000);
|
|
}
|
|
else
|
|
{
|
|
// Ensure the timer is running at the correct interval
|
|
pouringTimer.Change(1000, 1000);
|
|
}
|
|
|
|
UpdateFooterMessage(RecipePhase.PouringPhase, "Pouring phase");
|
|
}
|
|
|
|
coolingTimer = null;
|
|
|
|
// Hide cooling delay labels when cooling phase completes
|
|
settings.coolingDelayTxt.IsVisible = false;
|
|
settings.coolingDelayCounter.IsVisible = false;
|
|
isCoolingDelayMode = false; // Reset the flag
|
|
}
|
|
else if (isCoolingDelayMode && !pauseTimer)
|
|
{
|
|
// Show cooling delay countdown when chocolate is already at cooling temperature
|
|
// Hide cooling delay display
|
|
settings.coolingDelayTxt.IsVisible = false;
|
|
settings.coolingDelayCounter.IsVisible = false;
|
|
settings.coolingDelayCounter.Text = coolingSeconds.ToString();
|
|
UpdateFooterMessage(RecipePhase.CoolingDelay,
|
|
$"Cooling delay: {coolingSeconds} seconds");
|
|
}
|
|
else if (!isCoolingDelayMode && !pauseTimer)
|
|
{
|
|
// Show cooling phase status when chocolate needs to cool down
|
|
settings.coolingDelayTxt.IsVisible = false;
|
|
settings.coolingDelayCounter.IsVisible = false;
|
|
UpdateFooterMessage(RecipePhase.CoolingPhase, "Cooling phase");
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
/// <summary>
|
|
/// Enhanced pouring timer with improved goal checking and recipe completion logic
|
|
/// Timer runs every 1000ms (1 second) to count down seconds properly
|
|
/// </summary>
|
|
private void PouringTimer(object state)
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
try
|
|
{
|
|
if (ContentArea.Content is Settings settings)
|
|
{
|
|
if (fountainPauseTimer == null)
|
|
{
|
|
// For pouring phase: Check if temperature is within acceptable range around the goal
|
|
// Both too high and too low are problematic for pouring
|
|
bool tempInRange = (comFountainTemp >= (recipePouringGoal - screenData.errorLimit)) &&
|
|
(comFountainTemp <= (recipePouringGoal + screenData.errorLimit));
|
|
|
|
// Check if we're in pouring phase and chocolate has reached pouring goal
|
|
if (!isPouringDelayMode && Math.Abs(comFountainTemp - recipePouringGoal) <= 0.5)
|
|
{
|
|
// Chocolate reached pouring goal during pouring phase - switch to pouring delay
|
|
isPouringDelayMode = true;
|
|
pouringSeconds = _machine.PouringDelay; // Reset timer for pouring delay
|
|
|
|
UpdateFooterMessage(RecipePhase.PouringPhase,
|
|
$"Pouring delay: {pouringSeconds} seconds");
|
|
}
|
|
|
|
if (!pauseTimer && isPouringDelayMode && pouringSeconds > 0)
|
|
{
|
|
// Only count down during pouring DELAY
|
|
pouringSeconds--;
|
|
warningMessage = ""; // Clear any previous warnings
|
|
|
|
// Update the display to show the current countdown
|
|
UpdateFooterMessage(RecipePhase.PouringPhase,
|
|
$"Pouring delay: {pouringSeconds} seconds");
|
|
}
|
|
else if (!pauseTimer && !isPouringDelayMode)
|
|
{
|
|
// During pouring PHASE, just monitor temperature
|
|
warningMessage = ""; // Clear any previous warnings
|
|
}
|
|
|
|
// Check for warning conditions - warn if approaching limits
|
|
bool tempInWarningRange = (comFountainTemp >= (recipePouringGoal - screenData.warningLimit)) &&
|
|
(comFountainTemp <= (recipePouringGoal + screenData.warningLimit));
|
|
|
|
if (!tempInWarningRange)
|
|
{
|
|
// Show warning when temperature is approaching limits
|
|
if (warningMessage != "Pouring temperature approaching limits")
|
|
{
|
|
warningMessage = "Pouring temperature approaching limits";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
warningMessage = "";
|
|
}
|
|
|
|
// Check if pouring phase is complete (only when in delay mode)
|
|
if (isPouringDelayMode && pouringSeconds <= 0)
|
|
{
|
|
// Pouring phase complete - recipe finished
|
|
warningMessage = "";
|
|
|
|
// Safely stop the pouring timer
|
|
if (pouringTimer != null)
|
|
{
|
|
pouringTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
pouringTimer.Dispose();
|
|
pouringTimer = null;
|
|
}
|
|
|
|
pauseTimer = false;
|
|
pauseTempTracking = false;
|
|
pouring = -1;
|
|
isPouringDelayMode = false; // Reset the flag
|
|
|
|
// Recipe completed successfully
|
|
Dispatcher.UIThread.Post(async () =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
// Handle pedal control based on recipe settings
|
|
if (result._recipeTable.Pedal.Value)
|
|
{
|
|
pedalMotor = 0; // Manual mode
|
|
}
|
|
else
|
|
{
|
|
pedalMotor = 1; // Auto mode
|
|
pedalState = 0;
|
|
setPedalTimerOnce = 1;
|
|
}
|
|
|
|
// Fountain control is now handled directly by pedal state in auto mode
|
|
|
|
UpdateFooterMessage(RecipePhase.Completed, "Ready for pouring");
|
|
startRecipe = 0; // Mark recipe as completed
|
|
}
|
|
});
|
|
}
|
|
else if (isPouringDelayMode && !pauseTimer)
|
|
{
|
|
// Show pouring delay countdown
|
|
UpdateFooterMessage(RecipePhase.PouringPhase, $"Pouring delay: {pouringSeconds} seconds");
|
|
}
|
|
else if (!isPouringDelayMode && !pauseTimer)
|
|
{
|
|
// Show pouring phase status
|
|
UpdateFooterMessage(RecipePhase.PouringPhase, "Pouring phase");
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
footerMsg.Text = $"Error: {ex.Message}";
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
/// <summary>
|
|
/// Pedal OFF timer - counts down the OFF time and then switches to ON mode
|
|
/// Timer runs every 1000ms (1 second) to count down seconds properly
|
|
/// </summary>
|
|
private void PedalOffTimer(object state)
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
pedalOffSeconds++;
|
|
|
|
// Show countdown during OFF time
|
|
if (pedalOffSeconds <= result._recipeTable.PedalOffTime)
|
|
{
|
|
result.pedalDelayTxt.Text = "Pedal OFF: ";
|
|
result.pedalDelayCounter.Text = (result._recipeTable.PedalOffTime - pedalOffSeconds + 1).ToString();
|
|
result.pedalDelayTxt.IsVisible = true;
|
|
result.pedalDelayCounter.IsVisible = true;
|
|
}
|
|
|
|
// When OFF time is complete, switch to ON mode
|
|
if (pedalOffSeconds >= result._recipeTable.PedalOffTime)
|
|
{
|
|
pedalOffSeconds = 0;
|
|
PumbOn = 1;
|
|
pedalMotor = 1;
|
|
pedalState = 1; // Set to ON state
|
|
|
|
// Stop the OFF timer
|
|
if (pedalOffTimer != null)
|
|
{
|
|
pedalOffTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
pedalOffTimer.Dispose();
|
|
pedalOffTimer = null;
|
|
}
|
|
|
|
// If still in auto mode, continue the cycle by starting the ON timer
|
|
if (isPedalAutoMode)
|
|
{
|
|
setPedalTimerOnce = 0;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
/// <summary>
|
|
/// Pedal ON timer - counts down the ON time and then switches to OFF mode
|
|
/// Timer runs every 1000ms (1 second) to count down seconds properly
|
|
/// </summary>
|
|
private void PedalOnTimer(object state)
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
pedalOnSeconds++;
|
|
|
|
// Show countdown during ON time
|
|
if (pedalOnSeconds <= result._recipeTable.PedalOnTime)
|
|
{
|
|
result.pedalDelayTxt.Text = "Pedal ON: ";
|
|
result.pedalDelayCounter.Text = (result._recipeTable.PedalOnTime - pedalOnSeconds + 1).ToString();
|
|
result.pedalDelayTxt.IsVisible = true;
|
|
result.pedalDelayCounter.IsVisible = true;
|
|
}
|
|
|
|
// When ON time is complete, switch to OFF mode
|
|
if (pedalOnSeconds >= result._recipeTable.PedalOnTime)
|
|
{
|
|
pedalOnSeconds = 0;
|
|
PumbOn = 1;
|
|
pedalMotor = 1;
|
|
pedalState = 0; // Set to OFF state
|
|
|
|
// Stop the ON timer
|
|
if (pedalOnTimer != null)
|
|
{
|
|
pedalOnTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
pedalOnTimer.Dispose();
|
|
pedalOnTimer = null;
|
|
}
|
|
|
|
// If still in auto mode, continue the cycle by starting the OFF timer
|
|
if (isPedalAutoMode)
|
|
{
|
|
setPedalTimerOnce = 1;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
//Recipe Start
|
|
public async void RecipeStartBtn(object? sender, RoutedEventArgs e)
|
|
{
|
|
if (sender is Button button)
|
|
{
|
|
if (startRecipe == 0)
|
|
{
|
|
// Initialize and validate recipe parameters
|
|
if (!await InitializeAndValidateRecipe())
|
|
{
|
|
return; // Exit if validation fails
|
|
}
|
|
|
|
startRecipe = 1;
|
|
stopRecipeFlag = false;
|
|
tempWarningAccepted = false; // Reset temperature warning acceptance for new recipe
|
|
currentRecipePhase = RecipePhase.PreHeating; // Start with pre-heating phase
|
|
isPedalAutoMode = false; // Reset pedal auto mode for new recipe
|
|
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
// Retrieve recipe goals from settings
|
|
recipeHeatingGoal = result._recipeTable.HeatingGoal;
|
|
recipeCoolingGoal = result._recipeTable.CoolingGoal;
|
|
recipePouringGoal = result._recipeTable.PouringGoal;
|
|
|
|
// Check if pre-heating is required
|
|
if (comPumpTemp * 10 < _machine.PumbMaxHeat * 10 || comTankTemp * 10 < _machine.TankMaxHeat * 10)
|
|
{
|
|
startPreHeating = 1;
|
|
writingMaxTemp = 1;
|
|
footerMsg.Text = "Pre-heating in progress...";
|
|
}
|
|
|
|
// Ensure motors are running if needed
|
|
if (!isMixerMotorOn)
|
|
{
|
|
result.mixerBtn.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
|
|
}
|
|
if (!isFountainMotorOn)
|
|
{
|
|
result.fountainBtn.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
|
|
}
|
|
|
|
// Start goal monitoring and phase transitions
|
|
await StartRecipePhaseMonitoring();
|
|
|
|
// Update UI to indicate recipe is running
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
result.mixerBtn.IsEnabled = false;
|
|
result.fountainBtn.IsEnabled = false;
|
|
PreHeatingBtn.IsEnabled = false;
|
|
recipeStartBtn.IsEnabled = true;
|
|
button.Background = Avalonia.Media.Brushes.Red;
|
|
button.Content = "STOP RECIPE";
|
|
});
|
|
}
|
|
}
|
|
else if (startRecipe == 1)
|
|
{
|
|
// Stop recipe execution
|
|
await StopRecipeExecution(button);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialize and validate recipe parameters before starting
|
|
/// </summary>
|
|
private async Task<bool> InitializeAndValidateRecipe()
|
|
{
|
|
try
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
// Validate recipe data exists
|
|
if (result._recipeTable == null)
|
|
{
|
|
footerMsg.Text = "Error: No recipe selected";
|
|
return false;
|
|
}
|
|
|
|
// Validate temperature goals are reasonable
|
|
if (result._recipeTable.HeatingGoal <= 0 || result._recipeTable.CoolingGoal <= 0 || result._recipeTable.PouringGoal <= 0)
|
|
{
|
|
footerMsg.Text = "Error: Invalid temperature goals in recipe";
|
|
return false;
|
|
}
|
|
|
|
// Validate cooling goal is less than heating goal
|
|
if (result._recipeTable.CoolingGoal >= result._recipeTable.HeatingGoal)
|
|
{
|
|
footerMsg.Text = "Error: Cooling goal must be less than heating goal";
|
|
return false;
|
|
}
|
|
|
|
footerMsg.Text = "Ready";
|
|
return true;
|
|
}
|
|
|
|
footerMsg.Text = "Error: Settings not available";
|
|
return false;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
footerMsg.Text = $"Error initializing recipe: {ex.Message}";
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start monitoring recipe phases and handle goal checking
|
|
/// </summary>
|
|
private async Task StartRecipePhaseMonitoring()
|
|
{
|
|
try
|
|
{
|
|
// Start continuous monitoring of temperature goals
|
|
await Task.Run(async () =>
|
|
{
|
|
while (startRecipe == 1 && !stopRecipeFlag)
|
|
{
|
|
await CheckAndHandlePhaseTransitions();
|
|
await Task.Delay(1000); // Check every second
|
|
}
|
|
});
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Log error silently - don't show in UI since recipe is working
|
|
// Console.WriteLine($"Phase monitoring error: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check current temperatures and handle phase transitions based on goals
|
|
/// </summary>
|
|
private async Task CheckAndHandlePhaseTransitions()
|
|
{
|
|
try
|
|
{
|
|
if (ContentArea.Content is Settings settings)
|
|
{
|
|
// Check if heating goals are met for both mixer and chocolate
|
|
bool mixerHeatingGoalMet = await CheckMixerHeatingGoal();
|
|
bool chocolateHeatingGoalMet = await CheckChocolateHeatingGoal();
|
|
|
|
// If both heating goals are met, start timers
|
|
if (mixerHeatingGoalMet && chocolateHeatingGoalMet)
|
|
{
|
|
await StartHeatingPhaseTimers();
|
|
}
|
|
|
|
// Check chocolate temperature for cooling phase transition
|
|
await HandleCoolingPhaseTransition(settings);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
footerMsg.Text = $"Error in phase transition: {ex.Message}";
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check if mixer heating goal is reached
|
|
/// </summary>
|
|
private async Task<bool> CheckMixerHeatingGoal()
|
|
{
|
|
// Check if tank temperature (mixer) has reached heating goal
|
|
// For heating: temperature should be at or above the goal
|
|
bool goalMet = comTankTemp >= recipeHeatingGoal;
|
|
|
|
if (goalMet && Heating == -1)
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
footerMsg.Text = $"Mixer heating goal reached: {comTankTemp:F1}°C (Target: {recipeHeatingGoal}°C)";
|
|
});
|
|
}
|
|
|
|
return goalMet;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check if chocolate heating goal is reached
|
|
/// </summary>
|
|
private async Task<bool> CheckChocolateHeatingGoal()
|
|
{
|
|
// Check if fountain temperature (chocolate) has reached heating goal
|
|
// For heating: temperature should be at or above the goal
|
|
bool goalMet = comFountainTemp >= recipeHeatingGoal;
|
|
|
|
if (goalMet && Heating == -1)
|
|
{
|
|
|
|
}
|
|
|
|
return goalMet;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start heating phase timers when goals are met
|
|
/// </summary>
|
|
private async Task StartHeatingPhaseTimers()
|
|
{
|
|
if (Heating == -1 && setHeatingTimerOnce == -1)
|
|
{
|
|
Heating = 1;
|
|
sendComHeating = 1;
|
|
setHeatingTimerOnce = 1;
|
|
heatingSeconds = _machine.HeatingDelay;
|
|
|
|
// Start heating timer
|
|
if (heatingTimer == null)
|
|
{
|
|
heatingTimer = new Timer(HeatingTimer, null, 1000, 1000);
|
|
}
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
footerMsg.Text = "Heating phase started - timers initiated";
|
|
});
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle cooling phase transition based on chocolate temperature
|
|
/// </summary>
|
|
private async Task HandleCoolingPhaseTransition(Settings settings)
|
|
{
|
|
// Check if chocolate temperature has reached cooling threshold
|
|
// For cooling: temperature should be at or below the cooling goal
|
|
bool chocolateAtCoolingTemp = comFountainTemp <= recipeCoolingGoal;
|
|
|
|
if (chocolateAtCoolingTemp && cooling == -1 && Heating == -1)
|
|
{
|
|
// Chocolate temperature equals or is below cooling threshold - show cooling delay
|
|
await ShowCoolingDelay(settings);
|
|
}
|
|
else if (!chocolateAtCoolingTemp && cooling == -1 && Heating == -1)
|
|
{
|
|
// Chocolate temperature not at cooling threshold - initiate cooling phase
|
|
await InitiateCoolingPhase(settings);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Show cooling delay when chocolate temperature equals cooling goal
|
|
/// </summary>
|
|
private async Task ShowCoolingDelay(Settings settings)
|
|
{
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
settings.coolingDelayTxt.IsVisible = true;
|
|
settings.coolingDelayCounter.IsVisible = true;
|
|
settings.coolingDelayCounter.Text = "0";
|
|
footerMsg.Text = $"Cooling delay: Chocolate at target temperature ({comFountainTemp:F1}°C)";
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initiate cooling phase when chocolate temperature is not at cooling goal
|
|
/// </summary>
|
|
private async Task InitiateCoolingPhase(Settings settings)
|
|
{
|
|
if (setCoolingTimerOnce == -1)
|
|
{
|
|
cooling = 1;
|
|
sendComCooling = 1;
|
|
setCoolingTimerOnce = 1;
|
|
coolingSeconds = _machine.CoolingDelay;
|
|
|
|
// Start cooling timer
|
|
if (coolingTimer == null)
|
|
{
|
|
coolingTimer = new Timer(CoolingTimer, null, 1000, 1000);
|
|
}
|
|
else
|
|
{
|
|
// Ensure the timer is running at the correct interval
|
|
coolingTimer.Change(1000, 1000);
|
|
}
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
settings.coolingDelayTxt.IsVisible = false;
|
|
settings.coolingDelayCounter.IsVisible = false;
|
|
footerMsg.Text = $"Cooling phase initiated - Target: {recipeCoolingGoal}°C, Current: {comFountainTemp:F1}°C";
|
|
});
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stop recipe execution and clean up resources
|
|
/// </summary>
|
|
private async Task StopRecipeExecution(Button button)
|
|
{
|
|
startRecipe = 0;
|
|
stopRecipeFlag = true;
|
|
Heating = -1;
|
|
cooling = -1;
|
|
pouring = -1;
|
|
PumbOn = -1;
|
|
pedalMotor = -1;
|
|
isCoolingDelayMode = false; // Reset the cooling delay mode flag
|
|
isPouringDelayMode = false; // Reset the pouring delay mode flag
|
|
tempWarningAccepted = false; // Reset temperature warning acceptance
|
|
currentRecipePhase = RecipePhase.None; // Reset recipe phase
|
|
lastFooterMessage = ""; // Reset last footer message
|
|
isPedalAutoMode = false; // Reset pedal auto mode
|
|
|
|
// Stop all timers
|
|
await StopAllRecipeTimers();
|
|
|
|
// Reset UI elements
|
|
await ResetRecipeUI(button);
|
|
|
|
// Reset temperature settings
|
|
holdingRegister.setTemp1 = -10000;
|
|
holdingRegister.setTemp2 = -10000;
|
|
holdingRegister.setTemp3 = -10000;
|
|
holdingRegister.setTemp4 = -10000;
|
|
holdingRegister.motor = 0;
|
|
|
|
await WriteToSerialAsync("RecipeStop");
|
|
footerMsg.Text = "Recipe Stopped";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stop all recipe-related timers
|
|
/// </summary>
|
|
private async Task StopAllRecipeTimers()
|
|
{
|
|
// Stop heating timer
|
|
if (heatingTimer != null)
|
|
{
|
|
heatingTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
heatingSeconds = _machine.HeatingDelay;
|
|
heatingTimer = null;
|
|
}
|
|
|
|
// Stop cooling timer
|
|
if (coolingTimer != null)
|
|
{
|
|
coolingTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
coolingSeconds = _machine.CoolingDelay;
|
|
coolingTimer = null;
|
|
}
|
|
|
|
// Stop pouring timer
|
|
if (pouringTimer != null)
|
|
{
|
|
pouringTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
pouringSeconds = _machine.PouringDelay;
|
|
pouringTimer = null;
|
|
}
|
|
|
|
// Stop pedal timers
|
|
if (pedalOffTimer != null)
|
|
{
|
|
pedalOffTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
pedalOffSeconds = 0;
|
|
}
|
|
if (pedalOnTimer != null)
|
|
{
|
|
pedalOnTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
pedalOffSeconds = 0;
|
|
}
|
|
|
|
// Stop fountain timers
|
|
if (fountainPauseTimer != null)
|
|
{
|
|
fountainPauseTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
fountainPauseSeconds = 10;
|
|
fountainPauseTimer = null;
|
|
}
|
|
if (fountainTimer != null)
|
|
{
|
|
fountainTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
fountainTimer = null;
|
|
}
|
|
|
|
// Stop mixer timer
|
|
if (mixerTimer != null)
|
|
{
|
|
mixerTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
mixerTimer = null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reset UI elements when recipe is stopped
|
|
/// </summary>
|
|
private async Task ResetRecipeUI(Button button)
|
|
{
|
|
startPreHeating = 0;
|
|
writingMaxTemp = 0;
|
|
isFlashPreHeating = false;
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings settings)
|
|
{
|
|
// Turn off motors if they were on
|
|
if (isMixerMotorOn)
|
|
{
|
|
settings.mixerBtn.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
|
|
}
|
|
if (isFountainMotorOn)
|
|
{
|
|
settings.fountainBtn.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
|
|
}
|
|
|
|
// Hide delay indicators
|
|
settings.mixerDelayTxt.IsVisible = false;
|
|
settings.mixerDelayCounter.IsVisible = false;
|
|
settings.fountainDelayTxt.IsVisible = false;
|
|
settings.fountainDelayCounter.IsVisible = false;
|
|
settings.fountainTargetTxt.IsVisible = false;
|
|
settings.fountainTagetTemp.IsVisible = false;
|
|
settings.coolingDelayTxt.IsVisible = false;
|
|
settings.coolingDelayCounter.IsVisible = false;
|
|
settings.pedalDelayTxt.IsVisible = false;
|
|
settings.pedalDelayCounter.IsVisible = false;
|
|
|
|
// Re-enable buttons
|
|
settings.mixerBtn.IsEnabled = true;
|
|
settings.fountainBtn.IsEnabled = true;
|
|
}
|
|
|
|
// Reset recipe start button
|
|
button.Content = "START RECIPE";
|
|
PreHeatingBtn.IsEnabled = true;
|
|
recipeStartBtn.IsEnabled = true;
|
|
recipeStartBtn.Foreground = Avalonia.Media.Brushes.White;
|
|
recipeStartBtn.Background = Brush.Parse("#008000");
|
|
});
|
|
}
|
|
|
|
public void resetAll()
|
|
{
|
|
if (heatingTimer != null)
|
|
{
|
|
heatingTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
heatingSeconds = _machine.HeatingDelay;
|
|
heatingTimer = null;
|
|
}
|
|
if (coolingTimer != null)
|
|
{
|
|
coolingTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
coolingSeconds = _machine.CoolingDelay;
|
|
coolingTimer = null;
|
|
|
|
}
|
|
if (pouringTimer != null)
|
|
{
|
|
pouringTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
pouringSeconds = _machine.PouringDelay;
|
|
pouringTimer = null;
|
|
|
|
}
|
|
if (pedalOffTimer != null)
|
|
{
|
|
pedalOffTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
pedalOffSeconds = 0;
|
|
}
|
|
if (pedalOnTimer != null)
|
|
{
|
|
pedalOnTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
pedalOnSeconds = 0;
|
|
}
|
|
if (fountainPauseTimer != null)
|
|
{
|
|
fountainPauseTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
fountainPauseSeconds = 10;
|
|
fountainPauseTimer = null;
|
|
}
|
|
if (fountainTimer != null)
|
|
{
|
|
fountainTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
fountainTimer = null;
|
|
}
|
|
if (mixerTimer != null)
|
|
{
|
|
mixerTimer.Change(Timeout.Infinite, Timeout.Infinite);
|
|
mixerTimer = null;
|
|
}
|
|
|
|
|
|
|
|
pedalState = -1;
|
|
pedalStateChanged = -1;
|
|
recipeHeatingGoal = 0;
|
|
recipeCoolingGoal = 0;
|
|
recipePouringGoal = 0;
|
|
//pre Heating
|
|
isFlashPreHeating = false;
|
|
startPreHeating = -1;
|
|
writingMaxTemp = -1;
|
|
// mixer
|
|
mixerSeconds = 1;
|
|
setMixerTimerOnce = false;
|
|
checkMixerTWT_HWTH = false;
|
|
isMixerMotorOn = false;
|
|
startMixerMotor = -1;
|
|
startMixerMotorFlashing = -1;
|
|
sendComMixerMotor = -1;
|
|
//Fountain Motor
|
|
fountainSeconds = 1;
|
|
setFountainTimerOnce = false;
|
|
checkFountainTMT_PMT = false;
|
|
isFountainMotorOn = false;
|
|
startFountainMotor = -1;
|
|
startFountainMotorFlashing = -1;
|
|
sendComFountainMotor = -1;
|
|
//MOLD HEATER(off:0,on:1) , VIBRATION(off:0,on:1) , VIB. HEATER(off:0,on:1)
|
|
moldHeaterMotor = -1;
|
|
vibrationMotor = -1;
|
|
vibHeaterMotor = -1;
|
|
//Pedal(manual=0,auto=1)
|
|
pedalMotor = -1;
|
|
//Recipe Start
|
|
startRecipe = 0;
|
|
sendComTankTemp = -1;
|
|
//phase 1 heating
|
|
Heating = -1;
|
|
sendComHeating = -1;
|
|
setHeatingTimerOnce = -1;
|
|
heatingSeconds = 0;
|
|
//phase 2 cooling
|
|
cooling = -1;
|
|
sendComCooling = -1;
|
|
setCoolingTimerOnce = -1;
|
|
coolingSeconds = 0;
|
|
//phase 3 pouring
|
|
pouring = -1;
|
|
sendComPouring = -1;
|
|
setPouringTimerOnce = -1;
|
|
pouringSeconds = 0;
|
|
//start the pumb
|
|
PumbOn = -1;
|
|
pedalOffSeconds = 0;
|
|
pedalOnSeconds = 0;
|
|
setPedalTimerOnce = -1;
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
recipeStartBtn.Foreground = Avalonia.Media.Brushes.White;
|
|
recipeStartBtn.Background = Brush.Parse("#008000");
|
|
recipeStartBtn.Content = "START RECIPE";
|
|
PreHeatingBtn.IsEnabled = true;
|
|
recipeStartBtn.IsEnabled = true;
|
|
});
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
private async void ResetErrors(object? sender, RoutedEventArgs e)
|
|
{
|
|
holdingRegister.resetError = (ushort)(1 << 0);
|
|
await WriteToSerialAsync("ResetErrors");
|
|
|
|
}
|
|
private async void OnWarningPopupOverlayPointerPressed(object? sender, RoutedEventArgs e)
|
|
{
|
|
warningPopupOverlay.IsVisible = false;
|
|
|
|
}
|
|
private async void OnErrorPopupOverlayPointerPressed(object? sender, RoutedEventArgs e)
|
|
{
|
|
errorPopupOverlay.IsVisible = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static class MessageBox
|
|
{
|
|
public static async Task Show(Window owner, string message, string title)
|
|
{
|
|
var dialog = new Window
|
|
{
|
|
Title = title,
|
|
Width = 300,
|
|
Height = 150,
|
|
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
|
Topmost = false,
|
|
Content = new StackPanel
|
|
{
|
|
Children =
|
|
{
|
|
new TextBlock
|
|
{
|
|
Text = message,
|
|
Margin = new Thickness(10),
|
|
HorizontalAlignment = HorizontalAlignment.Center
|
|
},
|
|
new Button
|
|
{
|
|
Content = "OK",
|
|
Margin = new Thickness(10),
|
|
HorizontalAlignment = HorizontalAlignment.Center
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
var button = (Button)((StackPanel)dialog.Content).Children[1];
|
|
button.Click += (s, e) => dialog.Close();
|
|
|
|
owner.Topmost = false;
|
|
await dialog.ShowDialog(owner);
|
|
owner.Topmost = true;
|
|
owner.Activate();
|
|
|
|
// Restart keyboard to bring it on top
|
|
var (fileName, args) = GetKeyboardCommand();
|
|
if (!string.IsNullOrEmpty(fileName))
|
|
{
|
|
try
|
|
{
|
|
Process.Start(fileName, args);
|
|
}
|
|
catch
|
|
{
|
|
// Handle exceptions if needed
|
|
}
|
|
}
|
|
}
|
|
|
|
private static (string? fileName, string args) GetKeyboardCommand()
|
|
{
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
return ("osk.exe", "");
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
|
return ("onboard", ""); // or "florence", "matchbox-keyboard"
|
|
return (null, "");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handles automatic fountain control after pouring phase completion based on recipe settings
|
|
/// </summary>
|
|
private async Task HandleAutomaticFountainControlAfterPouring(Settings settings)
|
|
{
|
|
try
|
|
{
|
|
// Only proceed if pedal is in Auto mode
|
|
if (!settings._recipeTable.Pedal.Value) // Pedal.Value = false means Auto mode
|
|
{
|
|
// Set flag to indicate automatic fountain control is active
|
|
isAutomaticFountainControlActive = true;
|
|
|
|
// Check the second control box (RecipeTable.Fountain) to determine fountain state
|
|
if (settings._recipeTable.Fountain.Value)
|
|
{
|
|
// Second box is checked/enabled - Turn ON the fountain
|
|
await TurnOnFountainAutomatically();
|
|
}
|
|
else
|
|
{
|
|
// Second box is unchecked/disabled - Keep fountain OFF
|
|
await TurnOffFountainAutomatically();
|
|
}
|
|
|
|
Debug.WriteLine($"Automatic fountain control activated after pouring phase. Fountain state: {(settings._recipeTable.Fountain.Value ? "ON" : "OFF")}");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"Error in HandleAutomaticFountainControlAfterPouring: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Automatically turns on the fountain motor
|
|
/// </summary>
|
|
private async Task TurnOnFountainAutomatically()
|
|
{
|
|
try
|
|
{
|
|
// Set fountain motor state to ON
|
|
isFountainMotorOn = true;
|
|
startFountainMotor = 1;
|
|
startFountainMotorFlashing = 0;
|
|
sendComFountainMotor = 1;
|
|
|
|
// Actually send the command to turn ON the fountain motor
|
|
var fount = _mapping.Find(x => x.Name.ToLower() == "Helix".ToLower());
|
|
if (fount != null && fount.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var bit in fount.BitNumbers)
|
|
{
|
|
holdingRegister.motor |= (ushort)(1 << bit);
|
|
}
|
|
await WriteToSerialAsync("Automatic Fountain On");
|
|
Debug.WriteLine("Fountain motor command sent to hardware - ON");
|
|
}
|
|
|
|
// Update UI to show fountain is ON
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
var fountainLable = result.FountainSP.Children[1] as Avalonia.Controls.Label;
|
|
var fountainRectangel = result.FountainSP.Children[2] as Avalonia.Controls.Shapes.Rectangle;
|
|
fountainLable.Content = "ON";
|
|
fountainLable.Foreground = Brush.Parse("#ff231f20");
|
|
fountainRectangel.Fill = Brush.Parse(ActiveColor);
|
|
}
|
|
});
|
|
|
|
Debug.WriteLine("Fountain automatically turned ON after pouring phase completion");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"Error turning on fountain automatically: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Automatically turns off the fountain motor
|
|
/// </summary>
|
|
private async Task TurnOffFountainAutomatically()
|
|
{
|
|
try
|
|
{
|
|
// Set fountain motor state to OFF
|
|
isFountainMotorOn = false;
|
|
startFountainMotor = 0;
|
|
startFountainMotorFlashing = -1; // Prevent flashing in automatic mode
|
|
sendComFountainMotor = 0;
|
|
|
|
// Actually send the command to turn OFF the fountain motor
|
|
var fount = _mapping.Find(x => x.Name.ToLower() == "Helix".ToLower());
|
|
if (fount != null && fount.BitNumbers.Count > 0)
|
|
{
|
|
foreach (var bit in fount.BitNumbers)
|
|
{
|
|
holdingRegister.motor &= (ushort)~(1 << bit);
|
|
}
|
|
await WriteToSerialAsync("Automatic Fountain Off");
|
|
Debug.WriteLine("Fountain motor command sent to hardware - OFF");
|
|
}
|
|
|
|
// Update UI to show fountain is OFF
|
|
Dispatcher.UIThread.Post(() =>
|
|
{
|
|
if (ContentArea.Content is Settings result)
|
|
{
|
|
var fountainLable = result.FountainSP.Children[1] as Avalonia.Controls.Label;
|
|
var fontainRectangel = result.FountainSP.Children[2] as Avalonia.Controls.Shapes.Rectangle;
|
|
fountainLable.Content = "OFF";
|
|
fountainLable.Foreground = Brush.Parse("#ff231f20");
|
|
fontainRectangel.Fill = Brush.Parse(PassiveColor);
|
|
}
|
|
});
|
|
|
|
Debug.WriteLine("Fountain automatically turned OFF after pouring phase completion");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Debug.WriteLine($"Error turning off fountain automatically: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resets the automatic fountain control flag to allow normal fountain control
|
|
/// </summary>
|
|
public void ResetAutomaticFountainControl()
|
|
{
|
|
isAutomaticFountainControlActive = false;
|
|
Debug.WriteLine("Automatic fountain control flag reset - normal fountain control restored");
|
|
}
|
|
} |