#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 } }