#region License Information
/* HeuristicLab
* Copyright (C) 2002-2014 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 HeuristicLab.Common;
using HeuristicLab.Core;
using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
using System;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace HeuristicLab.Optimization.Networks {
[Item("GenericPort", "A generic port of an optimization network node.")]
[StorableClass]
public class GenericPort : ParameterizedPort, IGenericPort, IConnectedPort {
public override Image ItemImage {
get {
if (PortConnectionValid) return base.ItemImage;
else return HeuristicLab.Common.Resources.VSImageLibrary.Error;
}
}
[Storable]
protected IConnectedPort connectedPort;
public IConnectedPort ConnectedPort {
get { return connectedPort; }
set {
if (connectedPort != value) {
DeregisterConnectedPortEvents();
connectedPort = value;
RegisterConnectedPortEvents();
OnConnectedPortChanged();
OnInterfaceChanged();
}
}
}
[Storable]
protected bool portConnectionValid;
public bool PortConnectionValid {
get { return portConnectionValid; }
protected set {
if (value != portConnectionValid) {
portConnectionValid = value;
OnItemImageChanged();
}
}
}
[StorableConstructor]
protected GenericPort(bool deserializing) : base(deserializing) { }
protected GenericPort(GenericPort original, Cloner cloner) : base(original, cloner) {
connectedPort = cloner.Clone(original.connectedPort);
portConnectionValid = original.portConnectionValid;
RegisterConnectedPortEvents();
}
public GenericPort() : base("GenericPort") { }
public GenericPort(string name) : base(name) { }
public GenericPort(string name, string description) : base(name, description) { }
[StorableHook(HookType.AfterDeserialization)]
private void AfterDeserialization() {
RegisterConnectedPortEvents();
}
public override IDeepCloneable Clone(Cloner cloner) {
return new GenericPort(this, cloner);
}
public bool CanConnectToPort(IPort port) {
if (port == null) return true;
var cp = port as IConnectedPort;
if (cp == null) return false;
// check connected port input parameters
foreach (var input in cp.Parameters.Where(p => p.Type.HasFlag(PortParameterType.Input))) {
IPortParameter param;
Parameters.TryGetValue(input.Name, out param);
if ((param == null) && (input.DefaultValue == null)) return false;
if (!param.Type.HasFlag(PortParameterType.Output)) return false;
if (!input.DataType.IsAssignableFrom(param.DataType)) return false;
}
// check local port input parameters
foreach (var input in Parameters.Where(p => p.Type.HasFlag(PortParameterType.Input))) {
IPortParameter param;
cp.Parameters.TryGetValue(input.Name, out param);
if ((param == null) && (input.DefaultValue == null)) return false;
if (!param.Type.HasFlag(PortParameterType.Output)) return false;
if (!input.DataType.IsAssignableFrom(param.DataType)) return false;
}
return true;
}
public void CloneConnectedPortParameters() {
Parameters.Clear();
foreach (var p in connectedPort.Parameters) {
var param = (IPortParameter)p.Clone();
if (!(param.Type.HasFlag(PortParameterType.Input) && param.Type.HasFlag(PortParameterType.Output))) {
param.Type = ~param.Type; // bitwise negation: input -> output, output -> input
}
Parameters.Add(param);
}
}
public IMessage PrepareMessage() {
if (!PortConnectionValid) throw new InvalidOperationException("Port connection is not valid");
var message = new Message();
// collect output parameters from local port
message.Values.AddRange(
Parameters.
Where(p => p.Type.HasFlag(PortParameterType.Output)).
Select(p => p.CreateMessageValue())
);
// collect remaining input parameters from connected port
if (ConnectedPort != null) {
message.Values.AddRange(
ConnectedPort.Parameters.
Where(p => p.Type.HasFlag(PortParameterType.Input) && !message.Values.ContainsKey(p.Name)).
Select(p => p.CreateMessageValue())
);
}
// collect output parameters from connected port
if (ConnectedPort != null) {
message.Values.AddRange(
ConnectedPort.Parameters.
Where(p => p.Type.HasFlag(PortParameterType.Output)).
Select(p => p.CreateMessageValue())
);
}
// collect remaining input parameters from local port
message.Values.AddRange(
Parameters.
Where(p => p.Type.HasFlag(PortParameterType.Input) && !message.Values.ContainsKey(p.Name)).
Select(p => p.CreateMessageValue())
);
return message;
}
public void SendMessage(IMessage message) {
SendMessage(message, CancellationToken.None);
}
public void SendMessage(IMessage message, CancellationToken token) {
if (!PortConnectionValid) throw new InvalidOperationException("Port connection is not valid");
if (ConnectedPort != null) ConnectedPort.ReceiveMessage(message, token);
OnMessageSent(message, token);
}
public async Task SendMessageAsync(IMessage message) {
await SendMessageAsync(message, CancellationToken.None);
}
public async Task SendMessageAsync(IMessage message, CancellationToken token) {
await Task.Run(() => { SendMessage(message, token); }, token);
}
public event EventHandler> MessageSent;
protected virtual void OnMessageSent(IMessage message, CancellationToken token) {
var handler = MessageSent;
if (handler != null) handler(this, new EventArgs(message, token));
}
public void ReceiveMessage(IMessage message, CancellationToken token) {
OnMessageReceived(message, token);
}
public event EventHandler> MessageReceived;
protected virtual void OnMessageReceived(IMessage message, CancellationToken token) {
var handler = MessageReceived;
if (handler != null) handler(this, new EventArgs(message, token));
}
public event EventHandler ConnectedPortChanged;
protected virtual void OnConnectedPortChanged() {
var handler = ConnectedPortChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
protected override void OnInterfaceChanged() {
PortConnectionValid = (connectedPort != null) && CanConnectToPort(connectedPort);
base.OnInterfaceChanged();
}
#region ConnectedPort Events
protected virtual void RegisterConnectedPortEvents() {
if (connectedPort != null) {
connectedPort.InterfaceChanged += ConnectedPort_InterfaceChanged;
connectedPort.MessageSent += ConnectedPort_MessageSent;
}
}
protected virtual void DeregisterConnectedPortEvents() {
if (connectedPort != null) {
connectedPort.InterfaceChanged -= ConnectedPort_InterfaceChanged;
connectedPort.MessageSent -= ConnectedPort_MessageSent;
}
}
protected virtual void ConnectedPort_InterfaceChanged(object sender, EventArgs e) {
OnInterfaceChanged();
}
protected virtual void ConnectedPort_MessageSent(object sender, EventArgs e) {
ReceiveMessage(e.Value, e.Value2);
}
#endregion
}
}