using System; using System.Collections.Generic; using System.Linq; using System.Text; using ChamChat.Models; namespace ChamChat { public partial class PLxKPH_Client : Client { private const Int32 _OptionRange = 2000; private const int _RAM = 1; 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 PLxKPH_Client(Int32 MIDS, ClientType Type) : base(MIDS, Type) { // Set options available in client _Parameters = new List(); _Parameters.Add(Parameter.T); _Parameters.Add(Parameter.RH); _Parameters.Add(Parameter.RampT); _Parameters.Add(Parameter.RampRH); // 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 < 21; i++) _AvailableRAMs.Add(i); _InterfaceType = InterfaceType.FDTI_USB_COM422; } // To do public override Profile ReadProfile(int RAM, ref System.ComponentModel.BackgroundWorker bgw) { Profile profile = new Profile(); return profile; } public override Boolean WriteProfile(Profile profile) { return WriteProfile(profile, _RAM); } public override Boolean WriteProfile(Profile profile, int RAM) { // 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 sRAM = string.Format("{0:0}", RAM); string sAddr = _Address.ToString(); string cmd; string read; string cmd_pre = sAddr + ", PRGM DATA WRITE, PGM" + sRAM + ", "; // Cancel RAM program editing cmd = cmd_pre + "EDIT CANCEL"; DataTransfer(cmd, out read, _ReturnDataWaitms,1); if( !read.Contains("ERR-3")) DataTransfer(cmd, out read, _ReturnDataWaitms, 3); // Open RAM program cmd = cmd_pre + "EDIT START"; DataTransfer(cmd,out read, _ReturnDataWaitms); // Steps try { for (int i = 0; i < profile.Steps.Count; i++) { Step step = profile.Steps[i]; cmd = cmd_pre; cmd += String.Format("STEP{0:0}, ", i + 1); cmd += String.Format("TEMP{0:0.0}, ", step.T); cmd += step.RampCtrlT ? "TRAMPON, " : "TRAMPOFF, "; cmd += step.HasRH ? String.Format("HUMI{0:0}, ", step.RH) : "HUMI OFF, "; cmd += step.RampCtrlRH ? "HRAMPON, " : "HRAMPOFF, "; cmd += String.Format("TIME{0:00}:{1:00}, ", step.Hours, step.Minutes); cmd += "GRANTY OFF, "; cmd += "PAUSE OFF"; DataTransfer(cmd, out read, _ReturnDataWaitms); } } catch (Exception ex) { throw new Exception("writing steps - " + ex.Message); } try { // Loops if (profile.Loops.Count > 0) { Loop loop = profile.Loops[0]; cmd = cmd_pre; cmd += String.Format("COUNT, A({0}.{1}.{2})", loop.N - 1, loop.Last + 1, loop.First + 1); if (profile.Loops.Count > 1) { loop = profile.Loops[1]; cmd += String.Format(", B({0}.{1}.{2})", loop.N - 1, loop.Last + 1, loop.First + 1); } DataTransfer(cmd, out read, _ReturnDataWaitms); } } catch (Exception ex) { throw new Exception("writing loops - " + ex.Message); } // End mode try { cmd = profile.Options.Contains(ProfileOption.PLxKPH_EndModeHold) ? cmd_pre + "END, HOLD" : cmd_pre + "END, OFF"; DataTransfer(cmd, out read, _ReturnDataWaitms); } catch (Exception ex) { throw new Exception("writing end mode - " + ex.Message); } // Name of program cmd = cmd_pre + "NAME, " + title; DataTransfer(cmd, out read, _ReturnDataWaitms); // Close and save RAM program cmd = cmd_pre + "EDIT END"; DataTransfer(cmd, out read, _ReturnDataWaitms); // Wait 3 seconds to close program System.Threading.Thread.Sleep(3000); 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 profile steps 99 // number of loops < 3 // -40 < t < 150 if(profile.Steps.Count > 99) return false; if (profile.Loops.Count > 2) return false; foreach (Loop loop in profile.Loops) if (loop.N > 99 || loop.N < 2) return false; foreach (Step step in profile.Steps) { if (step.T < -40 || step.T > 150) 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 36 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; cmd = sAddr + ", PRGM, RUN, RAM:" + _RAM+", STEP1"; // See page 56 return DataTransfer(cmd,out read, _ReturnDataWaitms); } public override Boolean IsFinished() { // Finished() returns true if chamber is not running. See page 36 MODE?. string sAddr = _Address.ToString(); string cmd; string read; cmd = sAddr + ", MON?"; DataTransfer(cmd, out read, _ReturnDataWaitms); try { double t = Double.Parse(read.Split(',')[0]); double RH = Double.Parse(read.Split(',')[1]); _Progress = String.Format("Current conditions are {0:0.0} °C and {1:0} %rh", t, RH); _LastProgressUpdate = DateTime.Now; } catch { _Progress = DateTime.Now.ToString("yyyy-MM-dd @ hh:mm"); } if (read.Contains("STANDBY")) return true; if (read.Contains("OFF")) return true; return false; } public override Boolean Stop() { string sAddr = _Address.ToString(); string cmd; string read; cmd = sAddr + ", MODE, STANDBY"; return DataTransfer(cmd, out read, _ReturnDataWaitms); } public override Boolean IsOnline() { try { string sAddr = _Address.ToString(); string cmd; string read; cmd = sAddr + ", TYPE?"; DataTransfer(cmd, out read, _ReturnDataWaitms); return (read.ToLower().Contains("scp220") ); } catch { return false; } } private string _Progress = ""; private DateTime _LastProgressUpdate = DateTime.Now; public override String Progress() { if(_LastProgressUpdate.AddMinutes(3) < DateTime.Now) IsFinished(); return _Progress; } 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 4. return DataTransfer(cmd, out read, ReturnDataWaitms,4); } // 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 attempt = 0; while (!success && attempt < MaxNumOfAttempts) { DataTransferLog log = _Interface.DataTransfer(cmd, out read, ReturnDataWaitms); success = TxRxSucces(cmd, read); attempt++; // Mask for showing in GUI if (cmd.Contains("MON?") || cmd.Contains("EDIT CANCEL") || cmd.Contains("EDITCANCEL")) log.ShowInGUI = false; // Notify user via GUI of failed attempts if (attempt > 1) log.Command += string.Format(" [Attempt {0:0}]", attempt); // Add to logs AppendDataTransferLog(log); // Set _ReadyToSend acc. manual specs if (cmd.Contains("?")) _ReadyToSend = DateTime.Now.AddMilliseconds(500); else _ReadyToSend = DateTime.Now.AddMilliseconds(1200); // Sleeps if (!success) System.Threading.Thread.Sleep(5000); // Sleep 5 seconds before resending if failed } return success; } } }