#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());
}
}
}