using CaeGlobals; using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Management; using System.Runtime.Serialization; using System.Text; using System.Threading; using System.Windows.Forms; namespace CaeJob { // [Serializable] public class AnalysisJob : NamedClass, ISerializable { // Variables protected string _workDirectory; // ISerializable protected string _executable; // ISerializable protected string _argument; // ISerializable protected bool _compatibilityMode; // ISerializable protected JobStatus _jobStatus; // ISerializable protected int _numCPUs; // ISerializable protected List _environmentVariables; // ISerializable protected long _datFileLength; // ISerializable protected string _datFileContents; // ISerializable protected long _statusFileLength; // ISerializable protected string _statusFileContents; // ISerializable protected long _convergenceFileLength; // ISerializable protected string _convergenceFileContents; // ISerializable protected DateTime _endTime; // ISerializable protected FEMSolverEnum _femSolver; // ISerializable protected bool _onlyCheckModel; // ISerializable // [NonSerialized] protected Stopwatch _watch; [NonSerialized] private Process _exe; [NonSerialized] private StringBuilder _sbOutput; [NonSerialized] private StringBuilder _sbAllOutput; [NonSerialized] private bool _datFileToLong; [NonSerialized] private string _outputFileName; [NonSerialized] private string _errorFileName; [NonSerialized] private object _myLock; [NonSerialized] private string _inputFileName; [NonSerialized] private int _numOfRunSteps; [NonSerialized] private int _currentRunStep; [NonSerialized] private int _numOfRunIncrements; [NonSerialized] private int _currentRunIncrement; [NonSerialized] private object _tag; [NonSerialized] private bool _useBackgroundWorker; [NonSerialized] private double _prevWatchMilliseconds; // Properties public override string Name { get { return base.Name; } set { if (base.Name != value) { base.Name = value; // if (_femSolver == FEMSolverEnum.Calculix) _argument = Name; else if (_femSolver == FEMSolverEnum.Abaqus) _argument = "job=" + Name; else throw new NotSupportedException(); } } } public string WorkDirectory { get { return _workDirectory; } set { _workDirectory = value; } } public string Executable { get { return _executable; } set { _executable = value; } } public string Argument { get { return _argument; } set { _argument = value; } } public string ResultsFileName { get { if (_femSolver == FEMSolverEnum.Calculix) return Path.Combine(WorkDirectory, _name + ".frd"); else if (_femSolver == FEMSolverEnum.Abaqus) return Path.Combine(WorkDirectory, _name + ".odb"); else throw new NotSupportedException(); } } public bool IsUpToDate { get { DateTime lastWriteTime = File.GetLastWriteTime(ResultsFileName); return _endTime > lastWriteTime; } } public bool CompatibilityMode { get { return _compatibilityMode; } set { _compatibilityMode = value; } } public JobStatus JobStatus { get { return _jobStatus; } } public int NumCPUs { get { return _numCPUs; } set { _numCPUs = value; ProcessStartInfo psi = new ProcessStartInfo(); SetNumberOfProcessors(psi); } } public List EnvironmentVariables { get { return _environmentVariables; } set { _environmentVariables = value; } } public string DatFileData { get { return _datFileContents; } } public string StatusFileData { get { return _statusFileContents; } } public string ConvergenceFileData { get { return _convergenceFileContents; } } public int CurrentRunStep { get { return _currentRunStep; } } public int CurrentRunIncrement { get { return _currentRunIncrement; } } public string InputFileName { get { return _inputFileName; } } public FEMSolverEnum FEMSolver { get { return _femSolver; } set { if (_femSolver != value) { _femSolver = value; // if (_femSolver == FEMSolverEnum.Calculix) _argument = Name; else if (_femSolver == FEMSolverEnum.Abaqus) _argument = "job=" + Name; else throw new NotSupportedException(); } } } public bool OnlyCheckModel { get { return _onlyCheckModel; } set { _onlyCheckModel = value; } } public object Tag { get { return _tag; } set { _tag = value; } } // Events public event Action DataOutputEvent; // Callback public Action JobStatusChanged; public Action PreRun; public Action PostRun; public Action LastRunCompleted; // Constructor public AnalysisJob(string name, string executable, string argument, string workDirectory) : base(name) { _executable = executable; _argument = argument; _workDirectory = workDirectory; _compatibilityMode = false; _numCPUs = 1; _environmentVariables = new List(); _endTime = DateTime.MinValue; // _exe = null; _jobStatus = JobStatus.None; _watch = null; _sbOutput = null; _sbAllOutput = null; _onlyCheckModel = false; } public AnalysisJob(SerializationInfo info, StreamingContext context) : base(info, context) { // Compatibility v 2.3.5 _femSolver = FEMSolverEnum.Calculix; // foreach (SerializationEntry entry in info) { switch (entry.Name) { case "_workDirectory": _workDirectory = (string)entry.Value; break; case "_executable": _executable = (string)entry.Value; break; case "_argument": _argument = (string)entry.Value; break; case "_compatibilityMode": _compatibilityMode = (bool)entry.Value; break; case "_jobStatus": _jobStatus = (JobStatus)entry.Value; break; case "_numCPUs": _numCPUs = (int)entry.Value; break; case "_environmentVariables": _environmentVariables = (List)entry.Value; break; case "_datFileLength": _datFileLength = (long)entry.Value; break; case "_datFileContents": _datFileContents = (string)entry.Value; break; case "_statusFileLength": _statusFileLength = (long)entry.Value; break; case "_statusFileContents": _statusFileContents = (string)entry.Value; break; case "_convergenceFileLength": _convergenceFileLength = (long)entry.Value; break; case "_convergenceFileContents": _convergenceFileContents = (string)entry.Value; break; case "_endTime": _endTime = (DateTime)entry.Value; break; case "_femSolver": _femSolver = (FEMSolverEnum)entry.Value; break; case "_onlyCheckModel": _onlyCheckModel = (bool)entry.Value; break; default: break; } } } // Event handlers void Timer_Tick(object sender, EventArgs e) { //OutputData(); } // Methods public void Submit(int numOfRunSteps, int numOfRunIncrements, bool useBackgroundWorker = true) { if (numOfRunSteps < 1 || numOfRunIncrements < 1) throw new NotSupportedException(); _useBackgroundWorker = useBackgroundWorker; // Reset job _tag = null; _jobStatus = JobStatus.None; // _inputFileName = Path.Combine(_workDirectory, _name + ".inp"); // must be first for the timer to work // _numOfRunSteps = numOfRunSteps; _currentRunStep = -1; // _numOfRunIncrements = numOfRunIncrements; _currentRunIncrement = -1; // _watch = new Stopwatch(); _watch.Start(); _prevWatchMilliseconds = 0; // SubmitNextRun(); } private void SubmitNextRun() { // First run if (_currentRunStep == -1) { _currentRunStep = 1; _currentRunIncrement = 1; } // Other runs else { _currentRunIncrement++; // if (_currentRunIncrement > _numOfRunIncrements) { _currentRunIncrement = 1; _currentRunStep++; } } // if (_currentRunStep <= _numOfRunSteps && (_jobStatus == JobStatus.None || _jobStatus == JobStatus.OK)) { PreRun?.Invoke(this); SubmitOneRun(); } else AllRunsCompleted(); } private void SubmitOneRun() { if (_myLock == null) _myLock = new object(); lock (_myLock) { if (_sbOutput == null) _sbOutput = new StringBuilder(); _sbOutput.Clear(); // if (_sbAllOutput == null) _sbAllOutput = new StringBuilder(); _sbAllOutput.Clear(); } // AppendDataToOutput(DateTime.Now + Environment.NewLine); AppendDataToOutput("######## Starting run step number: " + _currentRunStep + " Increment number: " + _currentRunIncrement + " ########" + Environment.NewLine); AppendDataToOutput("Running command: " + _executable + " " + GetArgument()); // _datFileLength = -1; _datFileToLong = false; _datFileContents = ""; // _statusFileLength = -1; _statusFileContents = ""; // _convergenceFileLength = -1; _convergenceFileContents = ""; // _jobStatus = JobStatus.Running; // JobStatusChanged?.Invoke(_name, _jobStatus); // if (_useBackgroundWorker) { using (BackgroundWorker bwStart = new BackgroundWorker()) { bwStart.DoWork += bwStart_DoWork; bwStart.RunWorkerCompleted += bwStart_RunWorkerCompleted; if (!bwStart.IsBusy) bwStart.RunWorkerAsync(); } } else { bwStart_DoWork(null, null); bwStart_RunWorkerCompleted(null, null); } } private void bwStart_DoWork(object sender, DoWorkEventArgs e) { string fileName = Path.GetFileName(Name); _outputFileName = Path.Combine(_workDirectory, "_output_" + fileName + ".txt"); _errorFileName = Path.Combine(_workDirectory, "_error_" + fileName + ".txt"); // if (File.Exists(_outputFileName)) File.Delete(_outputFileName); if (File.Exists(_errorFileName)) File.Delete(_errorFileName); // if (Tools.IsWindows10orNewer() && !_compatibilityMode) Run_Win10(); else Run_OldWin(); } private void bwStart_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { bool resultsExist = false; if (_femSolver == FEMSolverEnum.Calculix) { string resultsFileName = ResultsFileName; if (File.Exists(resultsFileName)) { long length = new FileInfo(resultsFileName).Length; if (length > 15 * 20) resultsExist = true; } } else if (_femSolver == FEMSolverEnum.Abaqus) { string odbFailFileName = Path.Combine(WorkDirectory, _name + ".odb_f"); if (!File.Exists(odbFailFileName)) resultsExist = true; } else throw new NotSupportedException(); // bool continueAnalysis = false; AppendDataToOutput(""); if (_jobStatus == JobStatus.OK) { if (!resultsExist && !(ContainsNoAnalysis(_sbOutput.ToString()) || ContainsNoAnalysis(_sbAllOutput.ToString()))) { AppendDataToOutput(" Job failed - no results exist."); _jobStatus = JobStatus.Failed; } else if (ContainsError(_sbOutput.ToString()) || ContainsError(_sbAllOutput.ToString())) { _jobStatus = JobStatus.FailedWithResults; } else { PostRun?.Invoke(this); continueAnalysis = true; } } else if (_jobStatus == JobStatus.Killed) { AppendDataToOutput(" Job killed."); } else if (_jobStatus == JobStatus.Failed) { AppendDataToOutput(" Job failed."); if (resultsExist) _jobStatus = JobStatus.FailedWithResults; } // if (continueAnalysis) SubmitNextRun(); else AllRunsCompleted(); } private bool ContainsError(string text) { if (_femSolver == FEMSolverEnum.Calculix) { //*ERROR reading *DYNAMIC: initial increment size exce eds step size if (text.Contains("*ERROR")) { if (text.Contains("*ERROR reading *DYNAMIC: initial increment size")) return text.AllIndicesOf("*ERROR").Count() > 1; else return true; } } else if (_femSolver == FEMSolverEnum.Abaqus) { if (text.Contains("Abaqus/Analysis exited with errors")) return true; } else throw new NotSupportedException(); // return false; } private bool ContainsNoAnalysis(string text) { //*ERROR reading *DYNAMIC: initial increment size exceeds step size if (text.Contains("*WARNING: no analysis option was chosen")) return true; else return false; } private void AllRunsCompleted() { _watch.Stop(); // AppendDataToOutput(""); AppendDataToOutput("Process elapsed time: " + Math.Round(_watch.Elapsed.TotalSeconds, 3).ToString() + " s"); //AppendDataToOutput(" Peak physical memory usage: " + (Math.Round(_peakWorkingSet / 1048576.0, 3)).ToString() + " MB"); //AppendDataToOutput(" Peak paged memory usage: " + (Math.Round(_peakPagedMem / 1048576.0, 3)).ToString() + " MB"); //Console.WriteLine($" Peak virtual memory usage : {_peakVirtualMem / 1024 / 1024}"); // Timer_Tick(null, null); SendDataToOutput(); // JobStatusChanged?.Invoke(_name, _jobStatus); LastRunCompleted?.Invoke(this); // _endTime = DateTime.Now + TimeSpan.FromSeconds(1); // Dereference the links to other objects DataOutputEvent = null; JobStatusChanged = null; PreRun = null; PostRun = null; LastRunCompleted = null; } private void Run_Test() { ProcessStartInfo psi = new ProcessStartInfo(); psi.FileName = @"C:\Temp\test.bat"; ////psi.Arguments = _argument; //psi.WorkingDirectory = _workDirectory; //psi.UseShellExecute = false; //// ////SetEnvironmentVariables(psi); //// _exe = new Process(); _exe.StartInfo = psi; _exe.Start(); //// int ms = 1000 * 3600 * 24 * 7 * 3; // 3 weeks if (_exe.WaitForInputIdle(ms)) { // Process completed. Check process.ExitCode here. // after Kill() _jobStatus is Killed _jobStatus = JobStatus.OK; } else { // Timed out. Kill("Time out."); //Debug.WriteLine(DateTime.Now + " Timeout proces: " + Name + " in: " + _workDirectory); _jobStatus = JobStatus.TimedOut; } _exe.Close(); } private void Run_OldWin() { ProcessStartInfo psi = new ProcessStartInfo(); psi.FileName = _executable; psi.Arguments = GetArgument(); psi.WorkingDirectory = _workDirectory; psi.UseShellExecute = false; // //SetEnvironmentVariables(psi); // _exe = new Process(); _exe.StartInfo = psi; _exe.Start(); // int ms = 1000 * 3600 * 24 * 7 * 3; // 3 weeks if (_exe.WaitForExit(ms)) { // Process completed. Check process.ExitCode here. // after Kill() _jobStatus is Killed _jobStatus = JobStatus.OK; } else { // Timed out. Kill("Time out."); //Debug.WriteLine(DateTime.Now + " Timeout proces: " + Name + " in: " + _workDirectory); _jobStatus = JobStatus.TimedOut; } _exe.Close(); } private void Run_Win10() { ProcessStartInfo psi = new ProcessStartInfo(); psi.CreateNoWindow = true; psi.FileName = _executable; // psi.Arguments = GetArgument(); psi.WorkingDirectory = _workDirectory; psi.WindowStyle = ProcessWindowStyle.Hidden; psi.UseShellExecute = false; psi.RedirectStandardOutput = true; psi.RedirectStandardError = true; // SetEnvironmentVariables(psi); // _exe = new Process(); _exe.StartInfo = psi; // using (AutoResetEvent outputWaitHandle = new AutoResetEvent(false)) using (AutoResetEvent errorWaitHandle = new AutoResetEvent(false)) { _exe.OutputDataReceived += (sender, e) => { if (e.Data == null) { // the safe wait handle closes on kill if (!outputWaitHandle.SafeWaitHandle.IsClosed) outputWaitHandle.Set(); } else { AppendDataToOutput(e.Data); } }; // _exe.ErrorDataReceived += (sender, e) => { if (e.Data == null) { // the safe wait handle closes on kill if (!errorWaitHandle.SafeWaitHandle.IsClosed) errorWaitHandle.Set(); } else { File.AppendAllText(_errorFileName, e.Data + Environment.NewLine); AppendDataToOutput(e.Data); } }; // _exe.Start(); // _exe.BeginOutputReadLine(); _exe.BeginErrorReadLine(); int ms = 1000 * 3600 * 24 * 7 * 3; // 3 weeks if (_exe.WaitForExit(ms) && outputWaitHandle.WaitOne(ms) && errorWaitHandle.WaitOne(ms)) { // Process completed. Check process.ExitCode here. // after Kill() _jobStatus is Killed if (_jobStatus != JobStatus.Killed) _jobStatus = JobStatus.OK; } else { // Timed out. Kill("Time out."); //Debug.WriteLine(DateTime.Now + " Timeout proces: " + Name + " in: " + _workDirectory); _jobStatus = JobStatus.TimedOut; } _exe.Close(); // Abaqus //if (_femSolver == FEMSolverEnum.Abaqus) //{ // string fileName = Path.Combine(_workDirectory, Name + ".dat"); // if (File.Exists(fileName)) // { // AppendDataToOutput(Environment.NewLine + // "######## ABAQUS .dat FILE CONTENT ########" + // Environment.NewLine); // AppendDataToOutput(File.ReadAllText(fileName)); // } //} } } public void Kill(string message) { try { if (_exe != null) { if (message != "The string builder run out of space.") AppendDataToOutput(message); // _watch.Stop(); // try { UInt32 id = (UInt32)_exe.Id; KillAllProcessesSpawnedBy(id); if (!_exe.HasExited) _exe.Kill(); // must be here } catch { } } } finally { _jobStatus = JobStatus.Killed; } } public static void KillAllProcessesSpawnedBy(UInt32 parentProcessId) { // NOTE: Process Ids are reused! ManagementObjectSearcher searcher = new ManagementObjectSearcher( "SELECT * " + "FROM Win32_Process " + "WHERE ParentProcessId=" + parentProcessId); ManagementObjectCollection collection = searcher.Get(); if (collection.Count > 0) { foreach (var item in collection) { UInt32 childProcessId = (UInt32)item["ProcessId"]; if ((int)childProcessId != Process.GetCurrentProcess().Id) { KillAllProcessesSpawnedBy(childProcessId); // try { Process childProcess = Process.GetProcessById((int)childProcessId); childProcess.Kill(); } catch { } } } } } // public string GetAllOutputData() { try { if (_sbAllOutput != null) return _sbAllOutput.ToString(); else return null; } catch { return null; } } private void SendDataToOutput() { if (_myLock == null) _myLock = new object(); lock (_myLock) { string outputData = _sbOutput.ToString(); // if (_outputFileName != null) File.AppendAllText(_outputFileName, outputData); // GetDatFileContents(); GetStatusFileContents(); GetConvergenceFileContents(); // DataOutputEvent?.Invoke(this, outputData); // if (_sbAllOutput.Length > 2_000_000_000) Kill("The string builder run out of space."); // _sbAllOutput.Append(_sbOutput); _sbOutput.Clear(); } } private void AppendDataToOutput(string data) { if (_myLock == null) _myLock = new object(); lock (_myLock) { Application.DoEvents(); _sbOutput.AppendLine(data); } // if (_watch.ElapsedMilliseconds - _prevWatchMilliseconds > 1000) { SendDataToOutput(); _prevWatchMilliseconds = _watch.ElapsedMilliseconds; } } private void GetDatFileContents() { try { if (!_datFileToLong) { string datFileName = Path.Combine(_workDirectory, Name + ".dat"); if (!File.Exists(datFileName)) return; // long size = new FileInfo(datFileName).Length; // if (size != _datFileLength) { using (FileStream fileStream = new FileStream(datFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { using (StreamReader streamReader = new StreamReader(fileStream)) { _datFileContents = streamReader.ReadToEnd(); } } _datFileLength = size; } // int sizeLimit = 100_000; if (_datFileLength > sizeLimit) { if (!_datFileToLong) // first time { _datFileContents = _datFileContents.Substring(0, sizeLimit); _datFileContents += Environment.NewLine + Environment.NewLine + "Warning: .dat file is to log to be displayed in the Monitor form."; } _datFileToLong = true; } } } catch { } } private void GetStatusFileContents() { try { string statusFileName = Path.Combine(_workDirectory, Name + ".sta"); if (!File.Exists(statusFileName)) return; // long size = new FileInfo(statusFileName).Length; // if (size != _statusFileLength) { using (FileStream fileStream = new FileStream(statusFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { using (StreamReader streamReader = new StreamReader(fileStream)) { _statusFileContents = streamReader.ReadToEnd(); } } _statusFileLength = size; } } catch { } } private void GetConvergenceFileContents() { try { string convergenceFileName = Path.Combine(_workDirectory, Name + ".cvg"); if (!File.Exists(convergenceFileName)) return; // long size = new FileInfo(convergenceFileName).Length; // if (size != _convergenceFileLength) { using (FileStream fileStream = new FileStream(convergenceFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { using (StreamReader streamReader = new StreamReader(fileStream)) { _convergenceFileContents = streamReader.ReadToEnd(); } } _convergenceFileLength = size; } } catch { } } // public void ClearFileContents() { _datFileLength = 0; _datFileContents = ""; _statusFileLength = 0; _statusFileContents = ""; _convergenceFileLength = 0; _convergenceFileContents = ""; } // public static bool IsAdministrator() { System.Security.Principal.WindowsIdentity identity = System.Security.Principal.WindowsIdentity.GetCurrent(); System.Security.Principal.WindowsPrincipal principal = new System.Security.Principal.WindowsPrincipal(identity); // DOMAINNAME\Domain Admins RID: 0x200 return principal.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator) || principal.IsInRole(0x200); } public void ResetJobStatus() { _jobStatus = JobStatus.None; } // private void SetEnvironmentVariables(ProcessStartInfo psi) { SetNumberOfProcessors(psi); if (_environmentVariables != null) { foreach (var environmentVariable in _environmentVariables) { SetEnvironmentVariable(psi, environmentVariable); } } } private void SetNumberOfProcessors(ProcessStartInfo psi) { EnvironmentVariable numberOfProcessors = new EnvironmentVariable("OMP_NUM_THREADS", _numCPUs.ToString()); SetEnvironmentVariable(psi, numberOfProcessors); } private void SetEnvironmentVariable(ProcessStartInfo psi, EnvironmentVariable environmentVariable) { try { if (environmentVariable.Active) { if (psi.EnvironmentVariables.ContainsKey(environmentVariable.Name)) psi.EnvironmentVariables.Remove(environmentVariable.Name); psi.EnvironmentVariables.Add(environmentVariable.Name, environmentVariable.Value); if (!psi.EnvironmentVariables.ContainsKey(environmentVariable.Name)) throw new Exception(); } } catch { AppendDataToOutput("To add environmental variable '" + environmentVariable.Name + "' to the analysis the program must be run with elevated permisions (Run as administrator)."); } } private string GetArgument() { string argument = ""; if (_femSolver == FEMSolverEnum.Calculix) argument = _argument; else if (_femSolver == FEMSolverEnum.Abaqus) { if (_onlyCheckModel) argument = "datacheck "; argument += "interactive ask_delete=OFF "; if (_numCPUs > 1) argument += "cpus=" + _numCPUs + " "; argument += _argument; } else throw new NotSupportedException(); // return argument; } // ISerialization public new void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); // Using typeof() works also for null fields info.AddValue("_workDirectory", _workDirectory, typeof(string)); info.AddValue("_executable", _executable, typeof(string)); info.AddValue("_argument", _argument, typeof(string)); info.AddValue("_compatibilityMode", _compatibilityMode, typeof(bool)); info.AddValue("_jobStatus", _jobStatus, typeof(JobStatus)); info.AddValue("_numCPUs", _numCPUs, typeof(int)); info.AddValue("_environmentVariables", _environmentVariables, typeof(List)); info.AddValue("_datFileLength", _datFileLength, typeof(long)); info.AddValue("_datFileContents", _datFileContents, typeof(string)); info.AddValue("_statusFileLength", _statusFileLength, typeof(long)); info.AddValue("_statusFileContents", _statusFileContents, typeof(string)); info.AddValue("_convergenceFileLength", _convergenceFileLength, typeof(long)); info.AddValue("_convergenceFileContents", _convergenceFileContents, typeof(string)); info.AddValue("_endTime", _endTime, typeof(DateTime)); info.AddValue("_femSolver", _femSolver, typeof(FEMSolverEnum)); info.AddValue("_onlyCheckModel", _onlyCheckModel, typeof(bool)); } } }