source: branches/OptimizationNetworks/HeuristicLab.Optimization.Networks/3.3/Core.Networks/MessagePort.cs @ 11530

Last change on this file since 11530 was 11530, checked in by swagner, 8 years ago

#2205: Implemented review comments

  • renamed GenericPort to MessagePort
  • refactored CanConnectToPort
  • refactored PrepareMessage
  • removed IConnectedPort

Additional changes:

  • added UserDefinedMessagePort
  • refactored CloneConnectedPortParameters to CloneParametersFromPort and moved it to ParameterizedPort
  • added ports to NetworkView
File size: 9.1 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2014 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using HeuristicLab.Common;
23using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
24using System;
25using System.Collections.Generic;
26using System.Drawing;
27using System.Linq;
28using System.Threading;
29using System.Threading.Tasks;
30
31namespace HeuristicLab.Core.Networks {
32  [Item("MessagePort", "A port of a network node for sending and receiving messages.")]
33  [StorableClass]
34  public class MessagePort : ParameterizedPort, IMessagePort {
35    public override Image ItemImage {
36      get {
37        if (PortConnectionValid) return base.ItemImage;
38        else return HeuristicLab.Common.Resources.VSImageLibrary.Error;
39      }
40    }
41
42    new public PortParameterCollection Parameters {
43      get { return base.Parameters; }
44    }
45
46    [Storable]
47    protected IMessagePort connectedPort;
48    public IMessagePort ConnectedPort {
49      get { return connectedPort; }
50      set {
51        if (connectedPort != value) {
52          DeregisterConnectedPortEvents();
53          connectedPort = value;
54          RegisterConnectedPortEvents();
55          OnConnectedPortChanged();
56          OnInterfaceChanged();
57        }
58      }
59    }
60    [Storable]
61    protected bool portConnectionValid;
62    public bool PortConnectionValid {
63      get { return portConnectionValid; }
64      protected set {
65        if (value != portConnectionValid) {
66          portConnectionValid = value;
67          OnPortConnectionValidChanged();
68          OnItemImageChanged();
69        }
70      }
71    }
72    [Storable]
73    protected bool logMessages;
74    public bool LogMessages {
75      get { return logMessages; }
76      set {
77        if (value != logMessages) {
78          logMessages = value;
79          OnLogMessagesChanged();
80        }
81      }
82    }
83    [Storable]
84    protected MessageCollection messages;
85    public MessageCollection Messages {
86      get { return messages; }
87    }
88
89    [StorableConstructor]
90    protected MessagePort(bool deserializing) : base(deserializing) { }
91    protected MessagePort(MessagePort original, Cloner cloner)
92      : base(original, cloner) {
93      connectedPort = cloner.Clone(original.connectedPort);
94      portConnectionValid = original.portConnectionValid;
95      logMessages = original.logMessages;
96      messages = cloner.Clone(original.messages);
97      RegisterConnectedPortEvents();
98    }
99    public MessagePort()
100      : base("MessagePort") {
101      portConnectionValid = true;
102      logMessages = false;
103      messages = new MessageCollection();
104    }
105    public MessagePort(string name)
106      : base(name) {
107      portConnectionValid = true;
108      logMessages = false;
109      messages = new MessageCollection();
110    }
111    public MessagePort(string name, string description)
112      : base(name, description) {
113      portConnectionValid = true;
114      logMessages = false;
115      messages = new MessageCollection();
116    }
117
118    [StorableHook(HookType.AfterDeserialization)]
119    private void AfterDeserialization() {
120      RegisterConnectedPortEvents();
121    }
122
123    public override IDeepCloneable Clone(Cloner cloner) {
124      return new MessagePort(this, cloner);
125    }
126
127    public bool CanConnectToPort(IPort port) {
128      if (port == null) return true;
129
130      var p = port as IMessagePort;
131      if (p == null) return false;
132
133      // check if the local parameters are compatible to the input parameters of the port
134      if (!ParametersCompatibleToTarget(Parameters, p.Parameters)) return false;
135      // check if the parameters of the port are compatible to the local input parameters
136      if (!ParametersCompatibleToTarget(p.Parameters, Parameters)) return false;
137
138      return true;
139    }
140
141    public IMessage PrepareMessage() {
142      if (!PortConnectionValid) throw new InvalidOperationException("Port connection is not valid");
143      var message = new Message();
144
145      // collect parameters from local port
146      message.Values.AddRange(Parameters.Select(p => p.CreateMessageValue()));
147
148      // collect remaining parameters from connected port
149      if (ConnectedPort != null) {
150        message.Values.AddRange(
151          ConnectedPort.Parameters
152            .Where(p => !message.Values.ContainsKey(p.Name))
153            .Select(p => p.CreateMessageValue())
154        );
155      }
156
157      return message;
158    }
159    public void SendMessage(IMessage message) {
160      SendMessage(message, CancellationToken.None);
161    }
162    public void SendMessage(IMessage message, CancellationToken token) {
163      if (!PortConnectionValid) throw new InvalidOperationException("Port connection is not valid");
164      if (LogMessages) Messages.Add(message);
165      if (ConnectedPort != null) ConnectedPort.ReceiveMessage(message, token);
166      OnMessageSent(message, token);
167    }
168    public async Task SendMessageAsync(IMessage message) {
169      await SendMessageAsync(message, CancellationToken.None);
170    }
171    public async Task SendMessageAsync(IMessage message, CancellationToken token) {
172      await Task.Run(() => { SendMessage(message, token); }, token);
173    }
174    public event EventHandler<EventArgs<IMessage, CancellationToken>> MessageSent;
175    protected virtual void OnMessageSent(IMessage message, CancellationToken token) {
176      var handler = MessageSent;
177      if (handler != null) handler(this, new EventArgs<IMessage, CancellationToken>(message, token));
178    }
179
180    public void ReceiveMessage(IMessage message, CancellationToken token) {
181      if (!PortConnectionValid) throw new InvalidOperationException("Port connection is not valid");
182      if (LogMessages) Messages.Add(message);
183      OnMessageReceived(message, token);
184    }
185    public event EventHandler<EventArgs<IMessage, CancellationToken>> MessageReceived;
186    protected virtual void OnMessageReceived(IMessage message, CancellationToken token) {
187      var handler = MessageReceived;
188      if (handler != null) handler(this, new EventArgs<IMessage, CancellationToken>(message, token));
189    }
190
191    public event EventHandler ConnectedPortChanged;
192    protected virtual void OnConnectedPortChanged() {
193      var handler = ConnectedPortChanged;
194      if (handler != null) handler(this, EventArgs.Empty);
195    }
196    public event EventHandler PortConnectionValidChanged;
197    protected virtual void OnPortConnectionValidChanged() {
198      var handler = PortConnectionValidChanged;
199      if (handler != null) handler(this, EventArgs.Empty);
200    }
201    public event EventHandler LogMessagesChanged;
202    protected virtual void OnLogMessagesChanged() {
203      var handler = LogMessagesChanged;
204      if (handler != null) handler(this, EventArgs.Empty);
205    }
206
207    protected override void OnInterfaceChanged() {
208      PortConnectionValid = CanConnectToPort(connectedPort);
209      base.OnInterfaceChanged();
210    }
211
212    #region ConnectedPort Events
213    protected virtual void RegisterConnectedPortEvents() {
214      if (connectedPort != null) {
215        connectedPort.InterfaceChanged += ConnectedPort_InterfaceChanged;
216        connectedPort.MessageSent += ConnectedPort_MessageSent;
217      }
218    }
219    protected virtual void DeregisterConnectedPortEvents() {
220      if (connectedPort != null) {
221        connectedPort.InterfaceChanged -= ConnectedPort_InterfaceChanged;
222        connectedPort.MessageSent -= ConnectedPort_MessageSent;
223      }
224    }
225    protected virtual void ConnectedPort_InterfaceChanged(object sender, EventArgs e) {
226      OnInterfaceChanged();
227    }
228    protected virtual void ConnectedPort_MessageSent(object sender, EventArgs<IMessage, CancellationToken> e) {
229      ReceiveMessage(e.Value, e.Value2);
230    }
231    #endregion
232
233    #region Helpers
234    protected virtual bool ParametersCompatibleToTarget(IEnumerable<IPortParameter> source, IEnumerable<IPortParameter> target) {
235      // checks if the source parameters are compatible to the input parameters of the target
236      foreach (var input in target.Where(p => p.Type.HasFlag(PortParameterType.Input))) {
237        var param = source.Where(p => input.Name == p.Name).FirstOrDefault();
238        if (param == null) {
239          if (input.DefaultValue == null) return false;
240        } else {
241          if (!param.Type.HasFlag(PortParameterType.Output)) return false;
242          if (!input.DataType.IsAssignableFrom(param.DataType)) return false;
243        }
244      }
245      return true;
246    }
247    #endregion
248  }
249}
Note: See TracBrowser for help on using the repository browser.