#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.Common;
using HeuristicLab.Core;
using HeuristicLab.Hive;
namespace HeuristicLab.Clients.Hive {
public class RefreshableHiveExperiment : IHiveItem, IDeepCloneable, IContent, IProgressReporter {
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();
OnHiveExperimentChanged();
}
}
}
/** include jobs when refreshing **/
private bool includeJobs;
public bool IncludeJobs {
get { return includeJobs; }
set { includeJobs = value; }
}
private bool refreshAutomatically;
public bool RefreshAutomatically {
get { return refreshAutomatically; }
set {
lock (locker) {
if (refreshAutomatically != value) {
refreshAutomatically = value;
OnRefreshAutomaticallyChanged();
}
if (RefreshAutomatically) {
if (hiveExperiment.HiveJobs != null && hiveExperiment.HiveJobs.Count > 0 && (jobResultPoller == null || !jobResultPoller.IsPolling)) {
StartResultPolling();
}
} else {
StopResultPolling();
}
}
}
}
// if true, all control buttons should be enabled. otherwise disabled (used for HiveEngine)
private bool isControllable = true;
public bool IsControllable {
get { return isControllable; }
set {
if (value != isControllable) {
isControllable = value;
OnIsControllableChanged();
}
}
}
private ILog log;
public ILog Log {
get { return log; }
set { log = value; }
}
private static object logLocker = new object();
#region Constructors and Cloning
public RefreshableHiveExperiment() {
this.includeJobs = true;
this.refreshAutomatically = true;
this.HiveExperiment = new HiveExperiment();
this.log = new Log();
this.jobDownloader = new ConcurrentJobDownloader(2, 2);
this.jobDownloader.ExceptionOccured += new EventHandler>(jobDownloader_ExceptionOccured);
}
public RefreshableHiveExperiment(HiveExperiment hiveExperiment) {
this.includeJobs = true;
this.refreshAutomatically = true;
this.HiveExperiment = hiveExperiment;
this.log = new Log();
this.jobDownloader = new ConcurrentJobDownloader(2, 2);
this.jobDownloader.ExceptionOccured += new EventHandler>(jobDownloader_ExceptionOccured);
}
protected RefreshableHiveExperiment(RefreshableHiveExperiment original, Cloner cloner) {
cloner.RegisterClonedObject(original, this);
this.HiveExperiment = original.HiveExperiment;
this.IncludeJobs = original.IncludeJobs;
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);
}
public IDeepCloneable Clone(Cloner cloner) {
return new RefreshableHiveExperiment(this, cloner);
}
public object Clone() {
return this.Clone(new Cloner());
}
#endregion
private void hiveExperiment_HiveJobsChanged(object sender, EventArgs e) {
if (jobResultPoller != null && jobResultPoller.IsPolling) {
jobResultPoller.Stop();
DeregisterResultPollingEvents();
}
if (hiveExperiment.HiveJobs != null && hiveExperiment.HiveJobs.Count > 0 && hiveExperiment.GetAllHiveJobs().All(x => x.Job.Id != Guid.Empty)) {
if (this.RefreshAutomatically)
StartResultPolling();
}
}
#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 = !IsControllable;
}
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) {
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) {
LogMessage(string.Format("Downloading job {0}", lightweightJob.Id));
hiveJob.IsDownloading = true;
jobDownloader.DownloadJob(hiveJob.Job, (localJob, itemJob) => {
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()) {
hiveExperiment.ExecutionState = Core.ExecutionState.Stopped;
StopResultPolling();
}
UpdateTotalExecutionTime();
UpdateStatistics();
}
// synchronized logging
private void LogException(Exception exception) {
lock (logLocker) {
this.log.LogException(exception);
}
}
// synchronized logging
private void LogMessage(string message) {
lock (logLocker) {
this.log.LogMessage(message);
}
}
public HiveJob GetHiveJobById(Guid jobId) {
foreach (HiveJob job in hiveExperiment.HiveJobs) {
var hj = job.GetHiveJobByJobId(jobId);
if (hj != null)
return hj;
}
return null;
}
private void UpdateStatistics() {
var jobs = hiveExperiment.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 hiveExperiment.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() {
hiveExperiment.ExecutionTime = TimeSpan.FromMilliseconds(hiveExperiment.GetAllHiveJobs().Sum(x => x.Job.ExecutionTime.HasValue ? x.Job.ExecutionTime.Value.TotalMilliseconds : 0));
}
#endregion
private void RegisterHiveExperimentEvents() {
hiveExperiment.HiveJobsChanged += new EventHandler(hiveExperiment_HiveJobsChanged);
hiveExperiment.ToStringChanged += new EventHandler(hiveExperiment_ToStringChanged);
hiveExperiment.PropertyChanged += new PropertyChangedEventHandler(hiveExperiment_PropertyChanged);
hiveExperiment.ItemImageChanged += new EventHandler(hiveExperiment_ItemImageChanged);
hiveExperiment.ModifiedChanged += new EventHandler(hiveExperiment_ModifiedChanged);
hiveExperiment.IsProgressingChanged += new EventHandler(hiveExperiment_IsProgressingChanged);
hiveExperiment.Loaded += new EventHandler(hiveExperiment_Loaded);
}
private void DergisterHiveExperimentEvents() {
hiveExperiment.HiveJobsChanged -= new EventHandler(hiveExperiment_HiveJobsChanged);
hiveExperiment.ToStringChanged -= new EventHandler(hiveExperiment_ToStringChanged);
hiveExperiment.PropertyChanged -= new PropertyChangedEventHandler(hiveExperiment_PropertyChanged);
hiveExperiment.ItemImageChanged -= new EventHandler(hiveExperiment_ItemImageChanged);
hiveExperiment.ModifiedChanged -= new EventHandler(hiveExperiment_ModifiedChanged);
hiveExperiment.IsProgressingChanged -= new EventHandler(hiveExperiment_IsProgressingChanged);
hiveExperiment.Loaded -= new EventHandler(hiveExperiment_Loaded);
}
private void hiveExperiment_Loaded(object sender, EventArgs e) {
this.UpdateTotalExecutionTime();
if (hiveExperiment.ExecutionState != ExecutionState.Stopped) {
this.RefreshAutomatically = true;
}
}
#region Events
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) {
var handler = PropertyChanged;
if (handler != null) handler(sender, e);
}
public event EventHandler ToStringChanged;
private void hiveExperiment_ToStringChanged(object sender, EventArgs e) {
var handler = ToStringChanged;
if (handler != null) handler(this, e);
}
public event EventHandler IsProgressingChanged;
private void hiveExperiment_IsProgressingChanged(object sender, EventArgs e) {
var handler = IsProgressingChanged;
if (handler != null) handler(sender, e);
}
public event EventHandler IsControllableChanged;
private void OnIsControllableChanged() {
var handler = IsControllableChanged;
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) {
LogException(exception);
var handler = ExceptionOccured;
if (handler != null) handler(this, new EventArgs(exception));
}
#endregion
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; }
}
#region IProgressReporter Members
public IProgress Progress {
get { return HiveExperiment.Progress; }
}
public bool IsProgressing {
get { return HiveExperiment.IsProgressing; }
}
#endregion
public override string ToString() {
return HiveExperiment.ToString();
}
}
}