#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.Persistence.Default.CompositeSerializers.Storable; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace HeuristicLab.Core.Networks { [Item("MessagePort", "A port of a network node for sending and receiving messages.")] [StorableClass] public class MessagePort : ParameterizedPort, IMessagePort { public override Image ItemImage { get { if (PortConnectionValid) return base.ItemImage; else return HeuristicLab.Common.Resources.VSImageLibrary.Error; } } new public PortParameterCollection Parameters { get { return base.Parameters; } } [Storable] protected IMessagePort connectedPort; public IMessagePort 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; OnPortConnectionValidChanged(); OnItemImageChanged(); } } } [Storable] protected bool logMessages; public bool LogMessages { get { return logMessages; } set { if (value != logMessages) { logMessages = value; OnLogMessagesChanged(); } } } [Storable] protected MessageCollection messages; public MessageCollection Messages { get { return messages; } } [StorableConstructor] protected MessagePort(bool deserializing) : base(deserializing) { } protected MessagePort(MessagePort original, Cloner cloner) : base(original, cloner) { connectedPort = cloner.Clone(original.connectedPort); portConnectionValid = original.portConnectionValid; logMessages = original.logMessages; messages = cloner.Clone(original.messages); RegisterConnectedPortEvents(); } public MessagePort() : base("MessagePort") { portConnectionValid = true; logMessages = false; messages = new MessageCollection(); } public MessagePort(string name) : base(name) { portConnectionValid = true; logMessages = false; messages = new MessageCollection(); } public MessagePort(string name, string description) : base(name, description) { portConnectionValid = true; logMessages = false; messages = new MessageCollection(); } [StorableHook(HookType.AfterDeserialization)] private void AfterDeserialization() { RegisterConnectedPortEvents(); } public override IDeepCloneable Clone(Cloner cloner) { return new MessagePort(this, cloner); } public bool CanConnectToPort(IPort port) { if (port == null) return true; var p = port as IMessagePort; if (p == null) return false; // check if the local parameters are compatible to the input parameters of the port if (!ParametersCompatibleToTarget(Parameters, p.Parameters)) return false; // check if the parameters of the port are compatible to the local input parameters if (!ParametersCompatibleToTarget(p.Parameters, Parameters)) return false; return true; } public IMessage PrepareMessage() { if (!PortConnectionValid) throw new InvalidOperationException("Port connection is not valid"); var message = new Message(); // collect parameters from local port message.Values.AddRange(Parameters.Select(p => p.CreateMessageValue())); // collect remaining parameters from connected port if (ConnectedPort != null) { message.Values.AddRange( ConnectedPort.Parameters .Where(p => !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 (LogMessages) Messages.Add(message); 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) { if (!PortConnectionValid) throw new InvalidOperationException("Port connection is not valid"); if (LogMessages) Messages.Add(message); 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); } public event EventHandler PortConnectionValidChanged; protected virtual void OnPortConnectionValidChanged() { var handler = PortConnectionValidChanged; if (handler != null) handler(this, EventArgs.Empty); } public event EventHandler LogMessagesChanged; protected virtual void OnLogMessagesChanged() { var handler = LogMessagesChanged; if (handler != null) handler(this, EventArgs.Empty); } protected override void OnInterfaceChanged() { PortConnectionValid = 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 #region Helpers protected virtual bool ParametersCompatibleToTarget(IEnumerable source, IEnumerable target) { // checks if the source parameters are compatible to the input parameters of the target foreach (var input in target.Where(p => p.Type.HasFlag(PortParameterType.Input))) { var param = source.Where(p => input.Name == p.Name).FirstOrDefault(); if (param == null) { if (input.DefaultValue == null) return false; } else { if (!param.Type.HasFlag(PortParameterType.Output)) return false; if (!input.DataType.IsAssignableFrom(param.DataType)) return false; } } return true; } #endregion } }