Files
Tempering-Machine-Control-S…/DaireApplication/Views/MainWindow.axaml.cs

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");
}
}