Free cookie consent management tool by TermsFeed Policy Generator

source: branches/OKB/HeuristicLab.OKB.AlgorithmHost/AlgorithmHost.cs @ 6212

Last change on this file since 6212 was 4312, checked in by swagner, 14 years ago

Worked on OKB user authentication (#1167)

File size: 22.3 KB
RevLine 
[4311]1using System;
2using System.Collections.Generic;
3using System.Diagnostics;
4using System.IO;
5using System.Linq;
6using System.Text;
7using HeuristicLab.Collections;
8using HeuristicLab.Common;
9using HeuristicLab.Common.Resources;
10using HeuristicLab.Core;
11using HeuristicLab.Data;
12using HeuristicLab.OKB.Client;
13using HeuristicLab.Optimization;
14using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
15using HeuristicLab.Persistence.Default.Xml;
16
17namespace HeuristicLab.OKB.AlgorithmHost {
18
19  [Item("OKB Algorithm Host", "Load, run and submit values from and to the OKB")]
20  [Creatable("Optimization Knowledge Base")]
21  [StorableClass]
22  public class AlgorithmHost : NamedItem, IAlgorithm {
23
24    public override System.Drawing.Image ItemImage {
25      get {
26        if (ExecutionState == ExecutionState.Prepared) return VS2008ImageLibrary.ExecutablePrepared;
27        else if (ExecutionState == ExecutionState.Started) return VS2008ImageLibrary.ExecutableStarted;
28        else if (ExecutionState == ExecutionState.Paused) return VS2008ImageLibrary.ExecutablePaused;
29        else if (ExecutionState == ExecutionState.Stopped) return VS2008ImageLibrary.ExecutableStopped;
30        else return VS2008ImageLibrary.Event;
31      }
32    }
33
34    [Storable]
35    private IAlgorithm algorithm;
36    public IAlgorithm Algorithm {
37      get { return algorithm; }
38      protected set {
39        if (algorithm != value) {
40          if (algorithm != null)
41            DeregisterAlgorithmEvents();
42          algorithm = value;
43          if (algorithm != null) {
44            algorithm.Problem = problem;
45            RegisterAlgorithmEvents();
46          }
47          OnAlgorithmChanged();
48          Prepare();
49        }
50      }
51    }
52    public event EventHandler AlgorithmChanged;
53
54    #region constructors
55    public AlgorithmHost() {
56      name = ItemName;
57      description = ItemDescription;
58      executionState = ExecutionState.Stopped;
59      executionTime = TimeSpan.Zero;
60      Runs = new RunCollection();
61    }
62
63    public AlgorithmHost(string name)
64      : base(name) {
65      description = ItemDescription;
66      executionState = ExecutionState.Stopped;
67      executionTime = TimeSpan.Zero;
68      Runs = new RunCollection();
69    }
70
71    public AlgorithmHost(string name, string description)
72      : base(name, description) {
73      executionState = ExecutionState.Stopped;
74      executionTime = TimeSpan.Zero;
75      Runs = new RunCollection();
76    }
77
78    [StorableConstructor]
79    private AlgorithmHost(bool deserializing)
80      : base(deserializing) {
81    }
82    #endregion
83
84    #region Persistence
85    [StorableHook(HookType.AfterDeserialization)]
86    private void Initialize() {
87      if (algorithm != null) RegisterAlgorithmEvents();
88      if (runs != null) RegisterRunsEvents();
89    }
90
91    public override IDeepCloneable Clone(Cloner cloner) {
92      if (ExecutionState == ExecutionState.Started)
93        throw new InvalidOperationException("Clone not allowed while algorithm host is running");
94      AlgorithmHost clone = (AlgorithmHost)base.Clone(cloner);
95      clone.executionState = executionState;
96      clone.executionTime = executionTime;
97      clone.algorithm = (IAlgorithm)cloner.Clone(algorithm);
98      clone.runs = (RunCollection)cloner.Clone(runs);
99      clone.Initialize();
100      return clone;
101    }
102    #endregion
103
104    #region OKB connection
105
106    public OKBRunner.StarterKit StarterKit { get; set; }
107
108    public OKBRunner.StarterKit DownloadStarerKit() {
109      OKBRunner.RunnerServiceClient client =
110        ClientFactory.Create<OKBRunner.RunnerServiceClient, OKBRunner.IRunnerService>();
[4312]111      client.Login(Environment.MachineName);
[4311]112      OKBRunner.StarterKit starterKit = client.GetStarterKit("HL 3.3");
113      client.Logout();
114      client.Close();
115      return starterKit;
116    }
117
118    private ObservableDictionary<string, string> algorithmParameterMapping;
119    private ObservableDictionary<string, string> resultMapping;
120
121    private OKBRunner.Algorithm okbAlgorithm;
122    public OKBRunner.Algorithm OKBAlgorithm {
123      get {
124        return okbAlgorithm;
125      }
126      internal set {
127        if (value == okbAlgorithm) return;
128        LoadAlgorithm(value.Id);
129        okbAlgorithm = value;
130      }
131    }
132
133    private ObservableDictionary<string, string> problemParameterMapping;
134    private OKBRunner.Problem okbProblem;
135    public OKBRunner.Problem OKBProblem {
136      get {
137        return okbProblem;
138      }
139      internal set {
140        if (value == okbProblem) return;
141        LoadProblem(value.Id);
142        okbProblem = value;
143      }
144    }
145
146    private void LoadAlgorithm(int id) {
147      AlgorithmContainer algorithmContainer;
148      using (var stream = new MemoryStream()) {
149        Load(stream, OKBData.EntityType.Algorithm, id);
150        stream.Seek(0, SeekOrigin.Begin);
151        algorithmContainer = XmlParser.Deserialize<AlgorithmContainer>(stream);
152      }
153      Algorithm = algorithmContainer.Algorithm;
154      algorithmParameterMapping = algorithmContainer.ParameterMapping;
155      resultMapping = algorithmContainer.ResultMapping;
156    }
157
158    private void LoadProblem(int id) {
159      ProblemContainer problemContainer;
160      using (var stream = new MemoryStream()) {
161        Load(stream, OKBData.EntityType.Problem, id);
162        stream.Seek(0, SeekOrigin.Begin);
163        problemContainer = XmlParser.Deserialize<ProblemContainer>(stream);
164      }
165      SetProblem(problemContainer.Problem, true);
166      problemParameterMapping = problemContainer.ParameterMapping;
167    }
168
169    private void Load(Stream stream, OKBData.EntityType type, int id) {
170      OKBData.DataServiceClient client = ClientFactory
171       .Create<OKBData.DataServiceClient, OKBData.IDataService>();
172      int total = client.Request(type, id);
173      for (int length = 1; length > 0; ) {
174        byte[] buffer = client.GetNextChunk(100);
175        stream.Write(buffer, 0, buffer.Length);
176        length = buffer.Length;
177      }
178      stream.Flush();
179    }
180
181    public OKBRunner.ExperimentKit PrepareSubmission(IRun run) {
182      OKBRunner.ExperimentKit experimentKit = PrepareExperiment();
183      Debug.Assert(run.Algorithm.Name == Algorithm.Name, "run algorithm must match current algorithm");
184      Debug.Assert(run.Algorithm.Problem.Name == Problem.Name, "run problem must match current problem");
185      SetParameters(run, experimentKit);
186      SetResults(run, experimentKit);
187      return experimentKit;
188    }
189
190    public void SubmitRun(OKBRunner.ExperimentKit experimentKit) {
191      var client = ClientFactory.Create<OKBRunner.RunnerServiceClient, OKBRunner.IRunnerService>();
[4312]192      client.Login(Environment.MachineName);
[4311]193      client.AddRun(experimentKit.Algorithm, experimentKit.Problem, StarterKit.Projects.First());
194      client.Logout();
195      client.Close();
196    }
197
198    public static string FormatSubmission(OKBRunner.ExperimentKit experimentKit) {
199      StringBuilder sb = new StringBuilder();
200      sb.Append(experimentKit.Algorithm.Name).Append("@").AppendLine(experimentKit.Problem.Name);
201      sb.AppendLine("Parameters:");
202      foreach (var p in experimentKit.Algorithm.Algorithm_Parameters.Select(ap => ap.Parameter)) {
203        sb.Append(p.Name).Append(": ");
204        if (p.IntParameterValues != null && p.IntParameterValues.Count > 0)
205          sb.Append(p.IntParameterValues[0].Value).AppendLine();
206        else if (p.FloatParameterValues != null && p.FloatParameterValues.Count > 0)
207          sb.Append(p.FloatParameterValues[0].Value).AppendLine();
208        else if (p.CharParameterValues != null && p.CharParameterValues.Count > 0)
209          sb.AppendLine(p.CharParameterValues[0].Value);
210        else if (p.OperatorParameterValues != null && p.OperatorParameterValues.Count > 0)
211          sb.Append('<').Append(p.OperatorParameterValues[0].DataType.ClrName).AppendLine(">");
212        else
213          sb.AppendLine("???");
214      }
215      sb.AppendLine("Results:");
216      foreach (var r in experimentKit.Algorithm.Algorithm_Results.Select(ar => ar.Result)) {
217        sb.Append(r.Name).Append(": ");
218        if (r.IntResultValues != null && r.IntResultValues.Count > 0) {
219          sb.Append(r.IntResultValues[0].Value).AppendLine();
220        } else if (r.FloatResultValues != null && r.FloatResultValues.Count > 0) {
221          sb.Append(r.FloatResultValues[0].Value).AppendLine();
222        } else if (r.CharResultValues != null && r.CharResultValues.Count > 0) {
223          sb.AppendLine(r.CharResultValues[0].Value);
224        } else if (r.BlobResultValues != null && r.BlobResultValues.Count > 0) {
225          sb.Append("byte[").Append(r.BlobResultValues[0].Value.Bytes.Length).AppendLine("]");
226        } else
227          sb.AppendLine("???");
228      }
229      return sb.ToString();
230    }
231
232    private void SetResults(IRun run, OKBRunner.ExperimentKit experimentKit) {
233      var results = experimentKit.Algorithm.Algorithm_Results.Select(ar => ar.Result).ToList();
234      experimentKit.Algorithm.Algorithm_Results.Clear();
235      foreach (var r in results) {
236        if (resultMapping.ContainsValue(r.Name)) {
237          var rm = resultMapping.Single(kvp => kvp.Value == r.Name);
238          if (run.Results.ContainsKey(rm.Key))
239            experimentKit.Algorithm.AddResultValue(r.Name, run.Results[rm.Key]);
240        }
241      }
242    }
243
244    private void SetParameters(IRun run, OKBRunner.ExperimentKit experimentKit) {
245      var algorithmParameters = experimentKit.Algorithm.Algorithm_Parameters.Select(ap => ap.Parameter).ToList();
246      var problemParameters = experimentKit.Problem.Problem_Parameters.Select(pp => pp.Parameter).ToList();
247      experimentKit.Algorithm.Algorithm_Parameters.Clear();
248      foreach (var p in algorithmParameters) {
249        if (algorithmParameterMapping.ContainsValue(p.Name)) {
250          var pm = algorithmParameterMapping.Single(kvp => kvp.Value == p.Name);
251          if (run.Algorithm.Parameters.ContainsKey(pm.Key)) {
252            experimentKit.Algorithm.AddParameterValue(p.Name, run.Algorithm.Parameters[pm.Key].ActualValue);
253          }
254        }
255      }
256
257      foreach (var p in problemParameters) {
258        if (problemParameterMapping.ContainsValue(p.Name)) {
259          var pm = problemParameterMapping.Single(kvp => kvp.Value == p.Name);
260          if (run.Algorithm.Problem.Parameters.ContainsKey(pm.Key)) {
261            experimentKit.Algorithm.AddParameterValue(p.Name, run.Algorithm.Problem.Parameters[pm.Key].ActualValue);
262          }
263        }
264      }
265    }
266
267    private OKBRunner.ExperimentKit PrepareExperiment() {
268      OKBRunner.RunnerServiceClient client =
269        ClientFactory.Create<OKBRunner.RunnerServiceClient, OKBRunner.IRunnerService>();
[4312]270      client.Login(Environment.MachineName);
[4311]271      OKBRunner.ExperimentKit experimentKit = client.PrepareExperiment(OKBAlgorithm, OKBProblem);
272      client.Logout();
273      client.Close();
274      return experimentKit;
275    }
276    #endregion
277
278    #region IAlgorithm Members
279
280    public void CollectResultValues(IDictionary<string, IItem> values) {
281      if (algorithm != null)
282        algorithm.CollectResultValues(values);
283    }
284
285    public bool HasOKBProblem { get; protected set; }
286    private IProblem problem;
287    public IProblem Problem {
288      get { return problem; }
289      set { SetProblem(value, false); }
290    }
291    protected void SetProblem(IProblem value, bool isOkbProblem) {
292      HasOKBProblem = isOkbProblem;
293      if (value == problem) return;
294      problem = value;
295      if (algorithm != null)
296        algorithm.Problem = value;
297      OnProblemChanged();
298    }
299
300    public event EventHandler ProblemChanged;
301    protected virtual void OnProblemChanged() {
302      EventHandler handler = ProblemChanged;
303      if (handler != null)
304        handler(this, EventArgs.Empty);
305    }
306
307
308    public Type ProblemType {
309      get { return algorithm != null ? algorithm.ProblemType : typeof(IProblem); }
310    }
311
312    public ResultCollection Results {
313      get { return algorithm != null ? algorithm.Results : null; }
314    }
315
316    public bool StoreAlgorithmInEachRun {
317      get { return algorithm.StoreAlgorithmInEachRun; }
318      set {
319        if (value != algorithm.StoreAlgorithmInEachRun) {
320          algorithm.StoreAlgorithmInEachRun = value;
321          OnStoreAlgorithmInEachRunChanged();
322        }
323      }
324    }
325
326    public event EventHandler StoreAlgorithmInEachRunChanged;
327    protected virtual void OnStoreAlgorithmInEachRunChanged() {
328      EventHandler handler = StoreAlgorithmInEachRunChanged;
329      if (handler != null) handler(this, EventArgs.Empty);
330    }
331
332    #endregion
333
334    #region IParameterizedItem Members
335
336    public void CollectParameterValues(IDictionary<string, IItem> values) {
337      if (algorithm != null)
338        algorithm.CollectParameterValues(values);
339    }
340
341    public IKeyedItemCollection<string, IParameter> Parameters {
342      get { return algorithm != null ? algorithm.Parameters : null; }
343    }
344
345    public IKeyedItemCollection<string, IParameter> ProblemParameters {
346      get { return Problem != null ? Problem.Parameters : null; }
347    }
348
349    #endregion
350
351    #region IOptimizer Members
352
353    public void Prepare(bool clearRuns) {
354      if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused) && (ExecutionState != ExecutionState.Stopped))
355        throw new InvalidOperationException(string.Format("Prepare not allowed in execution state \"{0}\".", ExecutionState));
356      if (Algorithm != null) {
357        if (clearRuns) runs.Clear();
358        Algorithm.Prepare(clearRuns);
359      }
360    }
361
362
363    [Storable]
364    private RunCollection runs;
365    public RunCollection Runs {
366      get { return runs; }
367      private set {
368        if (value == null) throw new ArgumentNullException();
369        if (runs != value) {
370          if (runs != null) DeregisterRunsEvents();
371          runs = value;
372          if (runs != null) RegisterRunsEvents();
373        }
374      }
375    }
376
377    #endregion
378
379    #region IExecutable Members
380
381    public event EventHandler<EventArgs<Exception>> ExceptionOccurred;
382
383    [Storable]
384    private ExecutionState executionState;
385    public ExecutionState ExecutionState {
386      get { return executionState; }
387      private set {
388        if (executionState != value) {
389          executionState = value;
390          OnExecutionStateChanged();
391          OnItemImageChanged();
392        }
393      }
394    }
395
396    public event EventHandler ExecutionStateChanged;
397
398    [Storable]
399    private TimeSpan executionTime;
400    public TimeSpan ExecutionTime {
401      get {
402        if ((Algorithm != null) && (Algorithm.ExecutionState != ExecutionState.Stopped))
403          return executionTime + Algorithm.ExecutionTime;
404        else
405          return executionTime;
406      }
407      private set {
408        executionTime = value;
409        OnExecutionTimeChanged();
410      }
411    }
412
413    public event EventHandler ExecutionTimeChanged;
414
415    public void Pause() {
416      if (ExecutionState != ExecutionState.Started)
417        throw new InvalidOperationException(string.Format("Pause not allowed in execution state \"{0}\".", ExecutionState));
418      if ((Algorithm != null) && (Algorithm.ExecutionState == ExecutionState.Started))
419        Algorithm.Pause();
420    }
421
422    public event EventHandler Paused;
423
424    public void Prepare() {
425      Prepare(false);
426    }
427
428    public event EventHandler Prepared;
429
430    public void Start() {
431      if ((ExecutionState != ExecutionState.Prepared) && (ExecutionState != ExecutionState.Paused))
432        throw new InvalidOperationException(string.Format("Start not allowed in execution state \"{0}\".", ExecutionState));
433      if (Algorithm != null) Algorithm.Start();
434    }
435
436    public event EventHandler Started;
437
438    public void Stop() {
439      if ((ExecutionState != ExecutionState.Started) && (ExecutionState != ExecutionState.Paused))
440        throw new InvalidOperationException(string.Format("Stop not allowed in execution state \"{0}\".", ExecutionState));
441      if ((Algorithm != null) &&
442          ((Algorithm.ExecutionState == ExecutionState.Started) || (Algorithm.ExecutionState == ExecutionState.Paused)))
443        Algorithm.Stop();
444    }
445
446    public event EventHandler Stopped;
447
448    #endregion
449
450    #region events
451    private void OnExecutionStateChanged() {
452      EventHandler handler = ExecutionStateChanged;
453      if (handler != null) handler(this, EventArgs.Empty);
454    }
455    private void OnExecutionTimeChanged() {
456      EventHandler handler = ExecutionTimeChanged;
457      if (handler != null) handler(this, EventArgs.Empty);
458    }
459    private void OnAlgorithmChanged() {
460      EventHandler handler = AlgorithmChanged;
461      if (handler != null) handler(this, EventArgs.Empty);
462    }
463    private void OnPrepared() {
464      ExecutionState = ExecutionState.Prepared;
465      EventHandler handler = Prepared;
466      if (handler != null) handler(this, EventArgs.Empty);
467    }
468    private void OnStarted() {
469      ExecutionState = ExecutionState.Started;
470      EventHandler handler = Started;
471      if (handler != null) handler(this, EventArgs.Empty);
472    }
473    private void OnPaused() {
474      ExecutionState = ExecutionState.Paused;
475      EventHandler handler = Paused;
476      if (handler != null) handler(this, EventArgs.Empty);
477    }
478    private void OnStopped() {
479      ExecutionState = ExecutionState.Stopped;
480      EventHandler handler = Stopped;
481      if (handler != null) handler(this, EventArgs.Empty);
482    }
483    private void OnExceptionOccurred(Exception exception) {
484      EventHandler<EventArgs<Exception>> handler = ExceptionOccurred;
485      if (handler != null) handler(this, new EventArgs<Exception>(exception));
486    }
487    private void RegisterAlgorithmEvents() {
488      algorithm.ExceptionOccurred += new EventHandler<EventArgs<Exception>>(Algorithm_ExceptionOccurred);
489      algorithm.ExecutionTimeChanged += new EventHandler(Algorithm_ExecutionTimeChanged);
490      algorithm.Paused += new EventHandler(Algorithm_Paused);
491      algorithm.Prepared += new EventHandler(Algorithm_Prepared);
492      algorithm.Started += new EventHandler(Algorithm_Started);
493      algorithm.Stopped += new EventHandler(Algorithm_Stopped);
494      algorithm.Runs.CollectionReset += new CollectionItemsChangedEventHandler<IRun>(Algorithm_Runs_CollectionReset);
495      algorithm.Runs.ItemsAdded += new CollectionItemsChangedEventHandler<IRun>(Algorithm_Runs_ItemsAdded);
496      algorithm.Runs.ItemsRemoved += new CollectionItemsChangedEventHandler<IRun>(Algorithm_Runs_ItemsRemoved);
497      algorithm.ProblemChanged += new EventHandler(Algorithm_ProblemChanged);
498    }
499
500    private void DeregisterAlgorithmEvents() {
501      algorithm.ExceptionOccurred -= new EventHandler<EventArgs<Exception>>(Algorithm_ExceptionOccurred);
502      algorithm.ExecutionTimeChanged -= new EventHandler(Algorithm_ExecutionTimeChanged);
503      algorithm.Paused -= new EventHandler(Algorithm_Paused);
504      algorithm.Prepared -= new EventHandler(Algorithm_Prepared);
505      algorithm.Started -= new EventHandler(Algorithm_Started);
506      algorithm.Stopped -= new EventHandler(Algorithm_Stopped);
507      algorithm.Runs.CollectionReset -= new CollectionItemsChangedEventHandler<IRun>(Algorithm_Runs_CollectionReset);
508      algorithm.Runs.ItemsAdded -= new CollectionItemsChangedEventHandler<IRun>(Algorithm_Runs_ItemsAdded);
509      algorithm.Runs.ItemsRemoved -= new CollectionItemsChangedEventHandler<IRun>(Algorithm_Runs_ItemsRemoved);
510      algorithm.ProblemChanged -= new EventHandler(Algorithm_ProblemChanged);
511    }
512    private void Algorithm_ExceptionOccurred(object sender, EventArgs<Exception> e) {
513      OnExceptionOccurred(e.Value);
514    }
515    private void Algorithm_ExecutionTimeChanged(object sender, EventArgs e) {
516      OnExecutionTimeChanged();
517    }
518    private void Algorithm_Paused(object sender, EventArgs e) {
519      OnPaused();
520    }
521    private void Algorithm_Prepared(object sender, EventArgs e) {
522      OnPrepared();
523    }
524    private void Algorithm_Started(object sender, EventArgs e) {
525      if (ExecutionState != ExecutionState.Started)
526        OnStarted();
527    }
528    private void Algorithm_Stopped(object sender, EventArgs e) {
529      ExecutionTime = Algorithm.ExecutionTime;
530      OnStopped();
531    }
532    private void Algorithm_Runs_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
533      Runs.RemoveRange(e.OldItems);
534      Runs.AddRange(e.Items);
535    }
536    private void Algorithm_Runs_ItemsAdded(object sender, CollectionItemsChangedEventArgs<IRun> e) {
537      Runs.AddRange(e.Items);
538    }
539    private void Algorithm_Runs_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
540      Runs.RemoveRange(e.Items);
541    }
542    void Algorithm_ProblemChanged(object sender, EventArgs e) {
543      if (Algorithm.Problem == Problem)
544        return;
545      Problem = Algorithm.Problem;
546    }
547
548    private void RegisterRunsEvents() {
549      runs.CollectionReset += new CollectionItemsChangedEventHandler<IRun>(Runs_CollectionReset);
550      runs.ItemsRemoved += new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsRemoved);
551    }
552    private void DeregisterRunsEvents() {
553      runs.CollectionReset -= new CollectionItemsChangedEventHandler<IRun>(Runs_CollectionReset);
554      runs.ItemsRemoved -= new CollectionItemsChangedEventHandler<IRun>(Runs_ItemsRemoved);
555    }
556    private void Runs_CollectionReset(object sender, CollectionItemsChangedEventArgs<IRun> e) {
557      foreach (IRun run in e.OldItems) {
558        IItem item;
559        run.Results.TryGetValue("Execution Time", out item);
560        TimeSpanValue executionTime = item as TimeSpanValue;
561        if (executionTime != null) ExecutionTime -= executionTime.Value;
562      }
563      if (Algorithm != null) Algorithm.Runs.RemoveRange(e.OldItems);
564      foreach (IRun run in e.Items) {
565        IItem item;
566        run.Results.TryGetValue("Execution Time", out item);
567        TimeSpanValue executionTime = item as TimeSpanValue;
568        if (executionTime != null) ExecutionTime += executionTime.Value;
569      }
570    }
571    private void Runs_ItemsRemoved(object sender, CollectionItemsChangedEventArgs<IRun> e) {
572      foreach (IRun run in e.Items) {
573        IItem item;
574        run.Results.TryGetValue("Execution Time", out item);
575        TimeSpanValue executionTime = item as TimeSpanValue;
576        if (executionTime != null) ExecutionTime -= executionTime.Value;
577      }
578      if (Algorithm != null) Algorithm.Runs.RemoveRange(e.Items);
579    }
580    #endregion
581  }
582}
Note: See TracBrowser for help on using the repository browser.