#region License Information /* HeuristicLab * Copyright (C) 2002-2011 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Linq; using HeuristicLab.Collections; using HeuristicLab.Common; using HeuristicLab.Core; namespace HeuristicLab.Clients.Hive { public class RefreshableHiveExperiment : IHiveItem, IDeepCloneable, IContent, IProgressReporter, IComparable { private JobResultPoller jobResultPoller; private ConcurrentJobDownloader jobDownloader; private static object locker = new object(); private HiveExperiment hiveExperiment; public HiveExperiment HiveExperiment { get { return hiveExperiment; } set { if (value != hiveExperiment) { if (value == null) throw new ArgumentNullException(); if (hiveExperiment != null) DergisterHiveExperimentEvents(); hiveExperiment = value; if (hiveExperiment != null) { RegisterHiveExperimentEvents(); hiveExperiment_PropertyChanged(hiveExperiment, new PropertyChangedEventArgs("Id")); } OnHiveExperimentChanged(); OnToStringChanged(this, EventArgs.Empty); } } } private ItemCollection hiveJobs; public ItemCollection HiveJobs { get { return hiveJobs; } set { if (hiveJobs != value) { if (hiveJobs != null) DeregisterHiveJobsEvents(); hiveJobs = value; if (hiveJobs != null) RegisterHiveJobsEvents(); OnHiveJobsChanged(); } } } private ExecutionState executionState; public ExecutionState ExecutionState { get { return executionState; } internal set { if (executionState != value) { executionState = value; OnExecutionStateChanged(); } } } private TimeSpan executionTime; public TimeSpan ExecutionTime { get { return executionTime; } internal set { if (executionTime != value) { executionTime = value; OnExecutionTimeChanged(); } } } private bool refreshAutomatically; public bool RefreshAutomatically { get { return refreshAutomatically; } set { lock (locker) { if (refreshAutomatically != value) { refreshAutomatically = value; OnRefreshAutomaticallyChanged(); } if (RefreshAutomatically) { if (this.HiveJobs != null && this.HiveJobs.Count > 0 && (jobResultPoller == null || !jobResultPoller.IsPolling)) { StartResultPolling(); } } else { StopResultPolling(); } } } } // indicates if download button is enabled private bool isDownloadable = true; public bool IsDownloadable { get { return isDownloadable; } set { if (value != isDownloadable) { isDownloadable = value; OnIsDownloadableChanged(); } } } // if true, all control buttons should be enabled. otherwise disabled private bool isControllable = true; public bool IsControllable { get { return isControllable; } private set { if (value != isControllable) { isControllable = value; OnIsControllableChanged(); if (this.hiveJobs != null) { foreach (var hiveJob in this.hiveJobs) { hiveJob.IsControllable = value; } } } } } // indicates if a user is allowed to share this experiment private bool isSharable = true; public bool IsSharable { get { return isSharable; } private set { if (value != isSharable) { isSharable = value; OnIsSharableChanged(); } } } // may execute jobs with privileged permissions on slaves private bool isAllowedPrivileged = true; public bool IsAllowedPrivileged { get { return isAllowedPrivileged; } set { if (value != isAllowedPrivileged) { isAllowedPrivileged = value; OnIsAllowedPrivilegedChanged(); } } } private bool isProgressing; public bool IsProgressing { get { return isProgressing; } set { if (isProgressing != value) { isProgressing = value; OnIsProgressingChanged(); } } } private IProgress progress; public IProgress Progress { get { return progress; } set { this.progress = value; } } private ThreadSafeLog log; public ILog Log { get { return log; } } public StateLogListList StateLogList { get { return new StateLogListList(this.GetAllHiveJobs().Select(x => x.StateLog)); } } #region Constructors and Cloning public RefreshableHiveExperiment() { this.refreshAutomatically = true; this.HiveExperiment = new HiveExperiment(); this.log = new ThreadSafeLog(new Log()); this.jobDownloader = new ConcurrentJobDownloader(2, 2); this.jobDownloader.ExceptionOccured += new EventHandler>(jobDownloader_ExceptionOccured); this.HiveJobs = new ItemCollection(); } public RefreshableHiveExperiment(HiveExperiment hiveExperiment) { this.refreshAutomatically = true; this.HiveExperiment = hiveExperiment; this.log = new ThreadSafeLog(new Log()); this.jobDownloader = new ConcurrentJobDownloader(2, 2); this.jobDownloader.ExceptionOccured += new EventHandler>(jobDownloader_ExceptionOccured); this.HiveJobs = new ItemCollection(); } protected RefreshableHiveExperiment(RefreshableHiveExperiment original, Cloner cloner) { cloner.RegisterClonedObject(original, this); this.HiveExperiment = original.HiveExperiment; this.IsControllable = original.IsControllable; this.log = cloner.Clone(original.log); this.RefreshAutomatically = false; // do not start results polling automatically this.jobDownloader = new ConcurrentJobDownloader(2, 2); this.jobDownloader.ExceptionOccured += new EventHandler>(jobDownloader_ExceptionOccured); this.HiveJobs = cloner.Clone(original.HiveJobs); this.ExecutionTime = original.ExecutionTime; this.ExecutionState = original.ExecutionState; } public IDeepCloneable Clone(Cloner cloner) { return new RefreshableHiveExperiment(this, cloner); } public object Clone() { return this.Clone(new Cloner()); } #endregion #region JobResultPoller Events public void StartResultPolling() { if (jobResultPoller == null) { jobResultPoller = new JobResultPoller(hiveExperiment.Id, /*ApplicationConstants.ResultPollingInterval*/new TimeSpan(0, 0, 5)); //TODO: find a better place for ApplicationConstants RegisterResultPollingEvents(); jobResultPoller.AutoResumeOnException = true; } if (!jobResultPoller.IsPolling) { jobResultPoller.Start(); } } public void StopResultPolling() { if (jobResultPoller != null && jobResultPoller.IsPolling) { jobResultPoller.Stop(); } } private void RegisterResultPollingEvents() { jobResultPoller.ExceptionOccured += new EventHandler>(jobResultPoller_ExceptionOccured); jobResultPoller.JobResultsReceived += new EventHandler>>(jobResultPoller_JobResultReceived); jobResultPoller.IsPollingChanged += new EventHandler(jobResultPoller_IsPollingChanged); } private void DeregisterResultPollingEvents() { jobResultPoller.ExceptionOccured -= new EventHandler>(jobResultPoller_ExceptionOccured); jobResultPoller.JobResultsReceived -= new EventHandler>>(jobResultPoller_JobResultReceived); jobResultPoller.IsPollingChanged -= new EventHandler(jobResultPoller_IsPollingChanged); } private void jobResultPoller_IsPollingChanged(object sender, EventArgs e) { if (this.refreshAutomatically != jobResultPoller.IsPolling) { this.refreshAutomatically = jobResultPoller.IsPolling; OnRefreshAutomaticallyChanged(); } } private void jobResultPoller_JobResultReceived(object sender, EventArgs> e) { foreach (LightweightJob lightweightJob in e.Value) { HiveJob hiveJob = GetHiveJobById(lightweightJob.Id); if (hiveJob != null) { // lastJobDataUpdate equals DateTime.MinValue right after it was uploaded. When the first results are polled, this value is updated if (hiveJob.Job.State == JobState.Offline && lightweightJob.State != JobState.Finished && lightweightJob.State != JobState.Failed && lightweightJob.State != JobState.Aborted) { hiveJob.Job.LastJobDataUpdate = lightweightJob.LastJobDataUpdate; } hiveJob.UpdateFromLightweightJob(lightweightJob); if (!hiveJob.IsFinishedJobDownloaded && !hiveJob.IsDownloading && hiveJob.Job.LastJobDataUpdate < lightweightJob.LastJobDataUpdate) { log.LogMessage(string.Format("Downloading job {0}", lightweightJob.Id)); hiveJob.IsDownloading = true; jobDownloader.DownloadJob(hiveJob.Job, (localJob, itemJob) => { log.LogMessage(string.Format("Finished downloading job {0}", localJob.Id)); HiveJob localHiveJob = GetHiveJobById(localJob.Id); if (itemJob == null) { localHiveJob.IsDownloading = false; } if (itemJob == null) { // something bad happened to this job. bad job, BAAAD job! } else { // if the job is paused, download but don't integrate into parent optimizer (to avoid Prepare) if (localJob.State == JobState.Paused) { localHiveJob.ItemJob = itemJob; } else { if (localJob.ParentJobId.HasValue) { HiveJob parentHiveJob = GetHiveJobById(localJob.ParentJobId.Value); parentHiveJob.IntegrateChild(itemJob, localJob.Id); } else { localHiveJob.ItemJob = itemJob; } } localHiveJob.IsDownloading = false; localHiveJob.Job.LastJobDataUpdate = localJob.LastJobDataUpdate; } }); } } } GC.Collect(); // force GC, because .NET is too lazy here (deserialization takes a lot of memory) if (AllJobsFinished()) { this.ExecutionState = Core.ExecutionState.Stopped; StopResultPolling(); } UpdateTotalExecutionTime(); UpdateStatistics(); OnStateLogListChanged(); } public HiveJob GetHiveJobById(Guid jobId) { foreach (HiveJob job in this.HiveJobs) { var hj = job.GetHiveJobByJobId(jobId); if (hj != null) return hj; } return null; } private void UpdateStatistics() { var jobs = this.GetAllHiveJobs(); hiveExperiment.JobCount = jobs.Count(); hiveExperiment.CalculatingCount = jobs.Count(j => j.Job.State == JobState.Calculating); hiveExperiment.FinishedCount = jobs.Count(j => j.Job.State == JobState.Finished); OnJobStatisticsChanged(); } public bool AllJobsFinished() { return this.GetAllHiveJobs().All(j => (j.Job.State == JobState.Finished || j.Job.State == JobState.Aborted || j.Job.State == JobState.Failed) && j.IsFinishedJobDownloaded); } private void jobResultPoller_ExceptionOccured(object sender, EventArgs e) { OnExceptionOccured(e.Value); } private void jobDownloader_ExceptionOccured(object sender, EventArgs e) { OnExceptionOccured(e.Value); } public void UpdateTotalExecutionTime() { this.ExecutionTime = TimeSpan.FromMilliseconds(this.GetAllHiveJobs().Sum(x => x.Job.ExecutionTime.TotalMilliseconds)); } #endregion #region HiveExperiment Events private void RegisterHiveExperimentEvents() { hiveExperiment.ToStringChanged += new EventHandler(OnToStringChanged); hiveExperiment.PropertyChanged += new PropertyChangedEventHandler(hiveExperiment_PropertyChanged); hiveExperiment.ItemImageChanged += new EventHandler(hiveExperiment_ItemImageChanged); hiveExperiment.ModifiedChanged += new EventHandler(hiveExperiment_ModifiedChanged); } private void DergisterHiveExperimentEvents() { hiveExperiment.ToStringChanged -= new EventHandler(OnToStringChanged); hiveExperiment.PropertyChanged -= new PropertyChangedEventHandler(hiveExperiment_PropertyChanged); hiveExperiment.ItemImageChanged -= new EventHandler(hiveExperiment_ItemImageChanged); hiveExperiment.ModifiedChanged -= new EventHandler(hiveExperiment_ModifiedChanged); } #endregion #region Event Handler public event EventHandler RefreshAutomaticallyChanged; private void OnRefreshAutomaticallyChanged() { var handler = RefreshAutomaticallyChanged; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler HiveExperimentChanged; private void OnHiveExperimentChanged() { var handler = HiveExperimentChanged; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler ModifiedChanged; private void hiveExperiment_ModifiedChanged(object sender, EventArgs e) { var handler = ModifiedChanged; if (handler != null) handler(sender, e); } public event EventHandler ItemImageChanged; private void hiveExperiment_ItemImageChanged(object sender, EventArgs e) { var handler = ItemImageChanged; if (handler != null) handler(this, e); } public event PropertyChangedEventHandler PropertyChanged; private void hiveExperiment_PropertyChanged(object sender, PropertyChangedEventArgs e) { this.IsSharable = hiveExperiment.Permission == Permission.Full; this.IsControllable = hiveExperiment.Permission == Permission.Full; var handler = PropertyChanged; if (handler != null) handler(sender, e); } public event EventHandler ToStringChanged; private void OnToStringChanged(object sender, EventArgs e) { var handler = ToStringChanged; if (handler != null) handler(this, e); } public event EventHandler IsProgressingChanged; protected virtual void OnIsProgressingChanged() { var handler = IsProgressingChanged; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler IsDownloadableChanged; private void OnIsDownloadableChanged() { var handler = IsDownloadableChanged; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler IsControllableChanged; private void OnIsControllableChanged() { var handler = IsControllableChanged; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler IsSharableChanged; private void OnIsSharableChanged() { var handler = IsSharableChanged; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler IsAllowedPrivilegedChanged; private void OnIsAllowedPrivilegedChanged() { var handler = IsAllowedPrivilegedChanged; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler JobStatisticsChanged; private void OnJobStatisticsChanged() { var handler = JobStatisticsChanged; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler> ExceptionOccured; private void OnExceptionOccured(Exception exception) { log.LogException(exception); var handler = ExceptionOccured; if (handler != null) handler(this, new EventArgs(exception)); } public event EventHandler StateLogListChanged; private void OnStateLogListChanged() { var handler = StateLogListChanged; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler ExecutionTimeChanged; protected virtual void OnExecutionTimeChanged() { var handler = ExecutionTimeChanged; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler ExecutionStateChanged; protected virtual void OnExecutionStateChanged() { var handler = ExecutionStateChanged; if (handler != null) handler(this, EventArgs.Empty); } #endregion #region HiveJobs Events private void RegisterHiveJobsEvents() { this.hiveJobs.ItemsAdded += new CollectionItemsChangedEventHandler(hiveJobs_ItemsAdded); this.hiveJobs.ItemsRemoved += new CollectionItemsChangedEventHandler(hiveJobs_ItemsRemoved); this.hiveJobs.CollectionReset += new CollectionItemsChangedEventHandler(hiveJobs_CollectionReset); } private void DeregisterHiveJobsEvents() { this.hiveJobs.ItemsAdded -= new CollectionItemsChangedEventHandler(hiveJobs_ItemsAdded); this.hiveJobs.ItemsRemoved -= new CollectionItemsChangedEventHandler(hiveJobs_ItemsRemoved); this.hiveJobs.CollectionReset -= new CollectionItemsChangedEventHandler(hiveJobs_CollectionReset); } private void hiveJobs_CollectionReset(object sender, CollectionItemsChangedEventArgs e) { foreach (var item in e.Items) { item.StateLogChanged -= new EventHandler(item_StateLogChanged); } OnHiveJobsReset(e); } private void hiveJobs_ItemsRemoved(object sender, CollectionItemsChangedEventArgs e) { foreach (var item in e.Items) { item.StateLogChanged -= new EventHandler(item_StateLogChanged); } OnHiveJobsRemoved(e); } private void hiveJobs_ItemsAdded(object sender, CollectionItemsChangedEventArgs e) { foreach (var item in e.Items) { item.StateLogChanged += new EventHandler(item_StateLogChanged); item.IsControllable = this.IsControllable; } OnHiveJobsAdded(e); } private void item_StateLogChanged(object sender, EventArgs e) { OnStateLogListChanged(); } #endregion public event EventHandler HiveJobsChanged; protected virtual void OnHiveJobsChanged() { if (jobResultPoller != null && jobResultPoller.IsPolling) { jobResultPoller.Stop(); DeregisterResultPollingEvents(); } if (this.HiveJobs != null && this.HiveJobs.Count > 0 && this.GetAllHiveJobs().All(x => x.Job.Id != Guid.Empty)) { if (this.RefreshAutomatically) StartResultPolling(); } var handler = HiveJobsChanged; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler Loaded; public virtual void OnLoaded() { this.UpdateTotalExecutionTime(); if (this.ExecutionState != ExecutionState.Stopped) { this.RefreshAutomatically = true; } var handler = Loaded; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler> HiveJobsAdded; private void OnHiveJobsAdded(CollectionItemsChangedEventArgs e) { var handler = HiveJobsAdded; if (handler != null) handler(this, e); } public event EventHandler> HiveJobsRemoved; private void OnHiveJobsRemoved(CollectionItemsChangedEventArgs e) { var handler = HiveJobsRemoved; if (handler != null) handler(this, e); } public event EventHandler> HiveJobsReset; private void OnHiveJobsReset(CollectionItemsChangedEventArgs e) { var handler = HiveJobsReset; if (handler != null) handler(this, e); } public Guid Id { get { return hiveExperiment.Id; } set { hiveExperiment.Id = value; } } public bool Modified { get { return hiveExperiment.Modified; } } public void Store() { hiveExperiment.Store(); } public string ItemDescription { get { return hiveExperiment.ItemDescription; } } public Image ItemImage { get { return hiveExperiment.ItemImage; } } public string ItemName { get { return hiveExperiment.ItemName; } } public Version ItemVersion { get { return hiveExperiment.ItemVersion; } } public override string ToString() { return string.Format("{0} {1}", HiveExperiment.DateCreated.ToString("MM.dd.yyyy HH:mm"), HiveExperiment.ToString()); } public bool IsFinished() { return HiveJobs != null && HiveJobs.All(x => x.Job.DateFinished.HasValue && x.Job.DateCreated.HasValue); } public IEnumerable GetAllHiveJobs() { if (hiveJobs == null) return Enumerable.Empty(); var jobs = new List(); foreach (HiveJob job in HiveJobs) { jobs.AddRange(job.GetAllHiveJobs()); } return jobs; } public int CompareTo(RefreshableHiveExperiment other) { return this.ToString().CompareTo(other.ToString()); } } }