using System; using System.Collections.Generic; using System.Linq; using System.Text; using ChamChat.Models; namespace ChamChat { public partial class TSx_Client : Client { private const Int32 _OptionRange = 1000; //private const string _RAM = "RAM:20"; private const int _RAM = 20; private const Int32 _ReturnDataWaitms = 300; // Time out for client to prepare and return data private DateTime _ReadyToSend = DateTime.Now; // Timestamp when client is ready to communicate public TSx_Client(Int32 MIDS, ClientType Type) : base(MIDS, Type) { // Set options available in client _Parameters = new List(); _Parameters.Add(Parameter.T); _Parameters.Add(Parameter.PreTemp); _Parameters.Add(Parameter.Tlimit); _Parameters.Add(Parameter.ProceedWithNextStep); // Add option range _ClientOptions = new List(); foreach (ProfileOption o in Enum.GetValues(typeof(ProfileOption))) if ((int)o >= _OptionRange && (int)o < _OptionRange + 999) _ClientOptions.Add(o); // Set availabel addresses for programming _AvailableRAMs = new List(); for (int i = 1; i < 31; i++) _AvailableRAMs.Add(i); _InterfaceType = InterfaceType.FDTI_USB_COM422; } public override Profile ReadProfile(int RAM, ref System.ComponentModel.BackgroundWorker bgw) { string sAddr = _Address.ToString(); string cmd; string read; string ram = string.Format("RAM:{0:00}", RAM); // Communicate with client cmd = sAddr + ",PRGMREAD," + ram; DataTransfer(cmd, out read, _ReturnDataWaitms, 1); bgw.ReportProgress(8); cmd = sAddr + ",LISTTEMP?"; if (!bgw.CancellationPending) DataTransfer(cmd, out read, _ReturnDataWaitms, 1); string LISTTEMP = read; bgw.ReportProgress(16); cmd = sAddr + ",LISTPREAI?"; if (!bgw.CancellationPending) DataTransfer(cmd, out read, _ReturnDataWaitms, 1); string LISTPREAI = read; bgw.ReportProgress(24); cmd = sAddr + ",LISTPRE?"; if (!bgw.CancellationPending) DataTransfer(cmd, out read, _ReturnDataWaitms, 1); string LISTPRE = read; bgw.ReportProgress(32); cmd = sAddr + ",LISTTIME?"; if (!bgw.CancellationPending) DataTransfer(cmd, out read, _ReturnDataWaitms, 1); string LISTTIME = read; bgw.ReportProgress(40); cmd = sAddr + ",LISTCYCLE?"; if (!bgw.CancellationPending) DataTransfer(cmd, out read, _ReturnDataWaitms, 1); string LISTCYCLE = read; bgw.ReportProgress(48); cmd = sAddr + ",LISTSTARTPOSITION?"; if (!bgw.CancellationPending) DataTransfer(cmd, out read, _ReturnDataWaitms, 1); string LISTSTARTPOSITION = read; bgw.ReportProgress(56); cmd = sAddr + ",LISTEND?"; if (!bgw.CancellationPending) DataTransfer(cmd, out read, _ReturnDataWaitms, 1); string LISTEND = read; bgw.ReportProgress(64); cmd = sAddr + ",LISTNAME?"; if (!bgw.CancellationPending) DataTransfer(cmd, out read, _ReturnDataWaitms, 1); string LISTNAME = read; bgw.ReportProgress(72); cmd = sAddr + ",LISTSENSOR?"; if (!bgw.CancellationPending) DataTransfer(cmd, out read, _ReturnDataWaitms, 1); // Not used in software. string LISTSENSOR = read; bgw.ReportProgress(80); cmd = sAddr + ",LISTTEMPLIMIT?"; if (!bgw.CancellationPending) DataTransfer(cmd, out read, _ReturnDataWaitms, 1); string LISTTEMPLIMIT = read; bgw.ReportProgress(88); // 01728 test // 4,PRGMREAD,RAM:20 // 4,LISTTEMPLIMIT? // 4,PRGMREADEND cmd = sAddr + ",PRGMREADEND"; DataTransfer(cmd, out read, _ReturnDataWaitms, 1); bgw.ReportProgress(96); Profile profile = new Profile(); // Cancel read out if (bgw.CancellationPending) { profile.Title = "Cancelled"; return profile; } try { // High temperature double TB = Double.Parse(LISTTEMP.Split(',')[0]); string tB = LISTTIME.Split(',')[1].Trim(); double hrsB = Double.Parse(tB.Split(':')[0]); double minB = Double.Parse(tB.Split(':')[1]); double preTB = Double.Parse(LISTPRE.Split(',')[0]); double HL = Double.Parse(LISTTEMPLIMIT.Split(',')[0]); // Low temperature double TA = Double.Parse(LISTTEMP.Split(',')[1]); string tA = LISTTIME.Split(',')[2].Trim(); double hrsA = Double.Parse(tA.Split(':')[0]); double minA = Double.Parse(tA.Split(':')[1]); double preTA = Double.Parse(LISTPRE.Split(',')[1]); double LL = Double.Parse(LISTTEMPLIMIT.Split(',')[1]); // Number of cycles Int32 numOfCycles = Int32.Parse(LISTCYCLE); // Name String name = LISTNAME; // Options: page 44 & 72 List options = new List(); if (LISTPREAI == "ON") options.Add(ProfileOption.TSx_PreTempAuto); else options.Add(ProfileOption.TSx_PreTempManual); switch (LISTEND) { case "HEATRETURN": options.Add(ProfileOption.TSx_EndModeHeatReturn); break; case "SETUP": options.Add(ProfileOption.TSx_EndModeSetup); break; case "DEFROST": options.Add(ProfileOption.TSx_EndModeDefrost); break; case "DRY": options.Add(ProfileOption.TSx_EndModeDry); break; default: options.Add(ProfileOption.TSx_EndModeOff); break; } Step B = new Step(hrsB * 60 + minB); B.T = TB; B.PreTemp = preTB; B.LimitT = HL; Step A = new Step(hrsA * 60 + minA); A.T = TA; A.PreTemp = preTA; A.LimitT = LL; List steps = new List(); // If test should start in high, then start with B if (LISTSTARTPOSITION == "H") { steps.Add(B); steps.Add(A); } else { steps.Add(A); steps.Add(B); } Loop loop = new Loop(numOfCycles, 0, 1); List loops = new List(); loops.Add(loop); // Create profile profile = new Profile(name, steps, loops, options); } catch { } return profile; } public override Boolean WriteProfile(Profile profile) { // Pass a dummy bacckgroundworker and the default RAM return WriteProfile(profile, _RAM); } public override Boolean WriteProfile(Profile profile, int RAM) { // Pass a dummy bacckgroundworker with no event subscriptions System.ComponentModel.BackgroundWorker bgw = new System.ComponentModel.BackgroundWorker(); return WriteProfile(profile, _RAM, ref bgw); } public override Boolean WriteProfile(Profile profile, int RAM, ref System.ComponentModel.BackgroundWorker bgw) { // Only stand alone profiles allowed! if (!IsProgrammable(profile)) return false; string title = profile.Title.Substring(0, (int)Math.Min(14, profile.Title.Length)).Trim(); string sAddr = _Address.ToString(); string cmd; string read; string ram = string.Format("RAM:{0:00}", RAM); Step A, B; // A is low temperature step, B is high temperature step string startPosition; if (profile.Steps[0].T < profile.Steps[1].T) { A = profile.Steps[0]; B = profile.Steps[1]; startPosition = "L"; } else { A = profile.Steps[1]; B = profile.Steps[0]; startPosition = "H"; } #warning report Progress to bgw bgw.ReportProgress(8); // Communicate with client cmd = sAddr + ", PRGMWRITE," + ram; if (!bgw.CancellationPending) DataTransfer(cmd, out read, _ReturnDataWaitms); // Name of program cmd = sAddr + ", NAME, " + title; if (!bgw.CancellationPending) DataTransfer(cmd, out read, _ReturnDataWaitms); // Temperatures cmd = sAddr + ", TEMP, " + string.Format("S{0:000},{1:+#;-#00}", B.T, A.T); if (A.T == 0) cmd = sAddr + ", TEMP, " + string.Format("S{0:000}, 000", B.T); if (!bgw.CancellationPending) DataTransfer(cmd, out read, _ReturnDataWaitms); // Preheat and precool cmd = sAddr + ", PRE, " + string.Format("{0:000},{1:+#;-#00}", B.PreTemp, A.PreTemp); if (A.PreTemp == 0) cmd = sAddr + ", PRE, " + string.Format("{0:000}, 000", B.PreTemp); if (!bgw.CancellationPending) DataTransfer(cmd, out read, _ReturnDataWaitms); // Dwell times int hrsA = (int)Math.Floor(A.DurationMin / 60); int minA = (int)Math.Floor(A.DurationMin - 60 * hrsA); int hrsB = (int)Math.Floor(B.DurationMin / 60); int minB = (int)Math.Floor(B.DurationMin - 60 * hrsB); cmd = sAddr + ", TIME, " + string.Format("00:00, {0:00}:{1:00}, {2:00}:{3:00}", hrsB, minB, hrsA, minA); if (!bgw.CancellationPending) DataTransfer(cmd, out read, _ReturnDataWaitms); // Start position cmd = sAddr + ", STARTPOSITION, " + startPosition; if (!bgw.CancellationPending) DataTransfer(cmd, out read, _ReturnDataWaitms); // Auto preheat/precool (default is manual): page 44 & 72 string preai = "OFF"; if (profile.Options.Contains(ProfileOption.TSx_PreTempAuto)) preai = "ON"; cmd = sAddr + ", PREAI, " + preai; if (!bgw.CancellationPending) DataTransfer(cmd, out read, _ReturnDataWaitms); // Number of cycles int numOfCycles = 1; if (profile.Loops.Count > 0) numOfCycles = profile.Loops[0].N; cmd = sAddr + ", CYCLE, " + string.Format("{0:0000}", numOfCycles); if (!bgw.CancellationPending) DataTransfer(cmd, out read, _ReturnDataWaitms); // End mode (default is off) string end = "OFF"; if (profile.Options.Contains(ProfileOption.TSx_EndModeHeatReturn)) end = "HEATRETURN"; if (profile.Options.Contains(ProfileOption.TSx_EndModeSetup)) end = "SETUP"; if (profile.Options.Contains(ProfileOption.TSx_EndModeDefrost)) end = "DEFROST"; if (profile.Options.Contains(ProfileOption.TSx_EndModeDry)) end = "DRY"; cmd = sAddr + ", END, " + end; if (!bgw.CancellationPending) DataTransfer(cmd, out read, _ReturnDataWaitms); // Overheat/Overcool protection cmd = sAddr + ", TEMPLIMIT, " + string.Format("{0:000},{1:+#;-#00}", B.LimitT, A.LimitT); if (A.LimitT == 0) cmd = sAddr + ", TEMPLIMIT, " + string.Format("{0:000}, 000", B.LimitT); if (!bgw.CancellationPending) DataTransfer(cmd, out read, _ReturnDataWaitms); // Always perform PRGMWRITEEND, also if bgw.CancellationPending cmd = sAddr + ", PRGMWRITEEND," + ram; DataTransfer(cmd, out read, _ReturnDataWaitms); Boolean result = false; if (_Interface.InterfaceStatus == null) result = true; _Profile = profile; return result; } public override Boolean IsProgrammable(Profile profile) { // Profile is programmable if all below applies // number of steps 2 // number of loops < 2 // number of cycles < 9999 // -77 < tA < 0 // 60 < tB < 205 // -77 < Pre-tA < 0 // 60 1) return false; if(profile.Loops.Count == 1) if(profile.Loops[0].N > 9999) { _ClientMessages.Add(new ClientMessage("Program cannot be written to client: number of repeats must ben smaller than 9999")); return false; } Step A, B; // A is low temperature step, B is high temperature step if (profile.Steps[0].T < profile.Steps[1].T) { A = profile.Steps[0]; B = profile.Steps[1]; } else { A = profile.Steps[1]; B = profile.Steps[0]; } // Step A if (A.PreTemp < preC[0] || A.PreTemp > preC[1]) { string msg = string.Format("pre-cool must be in range {0:0} to {1:0} °C", preC[0] , preC[1]); _ClientMessages.Add(new ClientMessage("Program cannot be written to client: "+msg)); return false; } if (A.T < tA[0] || A.T > tA[1]) { string msg = string.Format("cold temperature must be in range {0:0} to {1:0} °C", tA[0], tA[1]); _ClientMessages.Add(new ClientMessage("Program cannot be written to client: " + msg)); return false; } if (A.LimitT > A.T || A.LimitT < A.T-50) { string msg = string.Format("cold limit must be in range {0:0} to {1:0} °C", A.T - 50, A.T); _ClientMessages.Add(new ClientMessage("Program cannot be written to client: " + msg)); return false; } // Step B if (B.PreTemp < preH[0] || B.PreTemp > preH[1]) { string msg = string.Format("pre-heat must be in range {0:0} to {1:0} °C", preH[0], preH[1]); _ClientMessages.Add(new ClientMessage("Program cannot be written to client: " + msg)); return false; } if (B.T < tB[0] || B.T > tB[1]) { string msg = string.Format("hot temperature must be in range {0:0} to {1:0} °C", tB[0], tB[1]); _ClientMessages.Add(new ClientMessage("Program cannot be written to client: " + msg)); return false; } if (B.LimitT < B.T || B.LimitT > B.T +50) { string msg = string.Format("hot limit must be in range {0:0} to {1:0} °C", B.T, B.T + 50); _ClientMessages.Add(new ClientMessage("Program cannot be written to client: " + msg)); return false; } return true; } public override Boolean HasAlarm(out List alarms) { alarms = new List(); return true; } public override Boolean IsIdle() { // IsIdle() returns true if status of chamber is 'Standby'. See page 34 MODE?. string sAddr = _Address.ToString(); string cmd; string read; cmd = sAddr + ", MODE?"; DataTransfer(cmd,out read, _ReturnDataWaitms); if (read.Contains("STANDBY")) return true; return false; } public override Boolean Start() { if (_Profile == null) return false; string sAddr = _Address.ToString(); string cmd; string read; // Clear number of remaining cycles cmd = sAddr + ", OPECYCLERESET"; // See ASSIGN page 63 DataTransfer(cmd, out read, _ReturnDataWaitms,1); string ram = string.Format("RAM:{0:00}", _RAM); // Assign the profile cmd = sAddr + ", ASSIGN, " + ram; // See ASSIGN page 66 if(!DataTransfer(cmd,out read, _ReturnDataWaitms)) return false; if (_Profile.Options.Contains(ProfileOption.TSx_StartNoSetup)) cmd = sAddr + ", OPETEST"; // See page 61 else cmd = sAddr + ", OPESETUPEND"; // See page 61 return DataTransfer(cmd,out read, _ReturnDataWaitms); } public override Boolean IsFinished() { // Finished() returns true if chamber is not running. See page 34 MODE?. string sAddr = _Address.ToString(); string cmd; string read; cmd = sAddr + ", MODE?"; DataTransfer(cmd, out read, _ReturnDataWaitms); if (read.Contains("POWER-OFF")) return true; if (read.Contains("STANDBY")) return true; if (read.Contains("END-SETUP")) return true; if (read.Contains("END-READY")) return true; if (read.Contains("END-OFF")) return true; return false; } public override Boolean Stop() { string sAddr = _Address.ToString(); string cmd; string read; cmd = sAddr + ", OPESTANDBY"; return DataTransfer(cmd, out read, _ReturnDataWaitms); } public override Boolean IsOnline() { try { string sAddr = _Address.ToString(); string cmd; string read; cmd = sAddr + ", MODEL?"; DataTransfer(cmd, out read, _ReturnDataWaitms); return (read.ToLower().Contains("tsd") || read.ToLower().Contains("tse")); } catch { return false; } } public override String Progress() { string sAddr = _Address.ToString(); string cmd; string read; cmd = sAddr + ", CYCLE?"; // See page 38 DataTransfer(cmd,out read, _ReturnDataWaitms,1); Int32 n = Int32.Parse(read.Split(',')[0]); Int32 N = Int32.Parse(read.Split(',')[1]); double t = CurrentTemperature(); return String.Format("{0:0}/{1:0} cycles executed. Temperature is {2:0} °C", n, N, t); } private bool TxRxSucces(string cmd, string read) { // Command not correctly processed if (read.StartsWith("NA:")) return false; // No response received if (read.Trim().Length < 1) return false; // Handle monitoring commands if (cmd.Trim().Contains("?")) { return true; } else { if (read.Trim().StartsWith("OK:")) return true; } return false; } private Boolean DataTransfer(string cmd, out string read, int ReturnDataWaitms) { // If no maximum number of attempts is given, it defaults to 3. return DataTransfer(cmd, out read, ReturnDataWaitms,3); } // Performs transfer and adds interface logs to list in Client parent class private Boolean DataTransfer(string cmd, out string read, int ReturnDataWaitms, int MaxNumOfAttempts) { // Construct command to send cmd += (char)13; // CR cmd += (char)10; // LF cmd = cmd.Replace(" ", ""); // Wait for _ReadyToSend or max 1 seconds DateTime go = DateTime.Now.AddSeconds(1); while (DateTime.Now < _ReadyToSend && DateTime.Now < go) System.Threading.Thread.Sleep(10); read = ""; bool success = false; int attempts = 0; while (!success && attempts <= MaxNumOfAttempts) { DataTransferLog log = _Interface.DataTransfer(cmd, out read, ReturnDataWaitms); success = TxRxSucces(cmd, read); attempts++; // Mask for showing in GUI if (cmd.Contains("MODE?") || cmd.Contains("CYCLE?")) log.ShowInGUI = false; // Notify user via GUI of failed attempts if (attempts > 1) log.Command += string.Format(" [Attempt {0:0}]", attempts); // Add to logs AppendDataTransferLog(log); // Set _ReadyToSend acc. manual specs if (cmd.Contains("?")) _ReadyToSend = DateTime.Now.AddMilliseconds(300); else _ReadyToSend = DateTime.Now.AddMilliseconds(1000); // Sleeps if (!success) System.Threading.Thread.Sleep(3000); // Sleep 3 seconds before resending if failed } return success; } } }