/// <summary> /// Casts the parsed <see cref="IChannelFrame"/> to its specific implementation (i.e., <see cref="DataFrame"/>, <see cref="ConfigurationFrame"/>, <see cref="CommandFrame"/> or <see cref="HeaderFrame"/>). /// </summary> /// <param name="frame"><see cref="IChannelFrame"/> that was parsed by <see cref="FrameImageParserBase{TTypeIdentifier,TOutputType}"/> that implements protocol specific common frame header interface.</param> protected override void OnReceivedChannelFrame(IChannelFrame frame) { // Raise abstract channel frame events as a priority (i.e., IDataFrame, IConfigurationFrame, etc.) base.OnReceivedChannelFrame(frame); // Raise IEEE C37.118 specific channel frame events, if any have been subscribed if (frame != null && (ReceivedDataFrame != null || ReceivedConfigurationFrame2 != null || ReceivedConfigurationFrame1 != null || ReceivedHeaderFrame != null || ReceivedCommandFrame != null)) { DataFrame dataFrame = frame as DataFrame; if (dataFrame != null) { if (ReceivedDataFrame != null) { ReceivedDataFrame(this, new EventArgs <DataFrame>(dataFrame)); } } else { // Configuration frame type 2 is more specific than type 1 (and more common), so we check it first ConfigurationFrame2 configFrame2 = frame as ConfigurationFrame2; if (configFrame2 != null) { if (ReceivedConfigurationFrame2 != null) { ReceivedConfigurationFrame2(this, new EventArgs <ConfigurationFrame2>(configFrame2)); } } else { ConfigurationFrame1 configFrame1 = frame as ConfigurationFrame1; if (configFrame1 != null) { if (ReceivedConfigurationFrame1 != null) { ReceivedConfigurationFrame1(this, new EventArgs <ConfigurationFrame1>(configFrame1)); } } else { HeaderFrame headerFrame = frame as HeaderFrame; if (headerFrame != null) { if (ReceivedHeaderFrame != null) { ReceivedHeaderFrame(this, new EventArgs <HeaderFrame>(headerFrame)); } } else { CommandFrame commandFrame = frame as CommandFrame; if (commandFrame != null) { if (ReceivedCommandFrame != null) { ReceivedCommandFrame(this, new EventArgs <CommandFrame>(commandFrame)); } } } } } } } }
/// <summary> /// Sends the specified <see cref="DeviceCommand"/> to the remote device. /// </summary> /// <param name="command"><see cref="DeviceCommand"/> to send to the remote device.</param> /// <remarks> /// Command will only be sent if <see cref="DeviceSupportsCommands"/> is <c>true</c> and <see cref="MultiProtocolFrameParser"/>. /// </remarks> /// <returns>A <see cref="WaitHandle"/>.</returns> public WaitHandle SendDeviceCommand(DeviceCommand command) { WaitHandle handle = null; try { if (m_deviceSupportsCommands && ((object)m_dataChannel != null || (object)m_serverBasedDataChannel != null || (object)m_commandChannel != null)) { ICommandFrame commandFrame; // Only the IEEE, SEL Fast Message and Macrodyne protocols support commands switch (m_phasorProtocol) { case PhasorProtocol.IEEEC37_118V2: case PhasorProtocol.IEEEC37_118V1: case PhasorProtocol.IEEEC37_118D6: commandFrame = new IEEEC37_118.CommandFrame(m_deviceID, command, 1); break; case PhasorProtocol.IEEE1344: commandFrame = new IEEE1344.CommandFrame(m_deviceID, command); break; case PhasorProtocol.IEC61850_90_5: commandFrame = new IEC61850_90_5.CommandFrame(m_deviceID, command, 1); break; case PhasorProtocol.SelFastMessage: // Get defined message period MessagePeriod messagePeriod = MessagePeriod.DefaultRate; SelFastMessage.ConnectionParameters connectionParameters = m_connectionParameters as SelFastMessage.ConnectionParameters; if ((object)connectionParameters != null) messagePeriod = connectionParameters.MessagePeriod; commandFrame = new SelFastMessage.CommandFrame(command, messagePeriod); break; case PhasorProtocol.Macrodyne: commandFrame = new Macrodyne.CommandFrame(command); break; default: commandFrame = null; break; } if ((object)commandFrame != null) { byte[] buffer = commandFrame.BinaryImage(); // Send command over appropriate communications channel - command channel, if defined, // will take precedence over other communications channels for command traffic... if ((object)m_commandChannel != null && m_commandChannel.CurrentState == ClientState.Connected) { handle = m_commandChannel.SendAsync(buffer, 0, buffer.Length); } else if ((object)m_dataChannel != null && m_dataChannel.CurrentState == ClientState.Connected) { handle = m_dataChannel.SendAsync(buffer, 0, buffer.Length); } else if ((object)m_serverBasedDataChannel != null && m_serverBasedDataChannel.CurrentState == ServerState.Running) { WaitHandle[] handles = m_serverBasedDataChannel.MulticastAsync(buffer, 0, buffer.Length); if ((object)handles != null && handles.Length > 0) handle = handles[0]; } if ((object)SentCommandFrame != null) SentCommandFrame(this, new EventArgs<ICommandFrame>(commandFrame)); } } } catch (Exception ex) { OnParsingException(ex, "Failed to send device command \"{0}\": {1}", command, ex.Message); } return handle; }
/// <summary> /// Handles incoming commands from devices connected over the command channel. /// </summary> /// <param name="clientID">Guid of client that sent the command.</param> /// <param name="connectionID">Remote client connection identification (i.e., IP:Port).</param> /// <param name="commandBuffer">Data buffer received from connected client device.</param> /// <param name="length">Valid length of data within the buffer.</param> protected override void DeviceCommandHandler(Guid clientID, string connectionID, byte[] commandBuffer, int length) { try { // Interpret data received from a client as a command frame CommandFrame commandFrame = new CommandFrame(commandBuffer, 0, length); IServer commandChannel = (IServer)CommandChannel ?? DataChannel; // Validate incoming ID code if requested if (!m_validateIDCode || commandFrame.IDCode == this.IDCode) { switch (commandFrame.Command) { case DeviceCommand.SendConfigurationFrame1: case DeviceCommand.SendConfigurationFrame2: if (commandChannel != null) { commandChannel.SendToAsync(clientID, m_configurationFrame.BinaryImage, 0, m_configurationFrame.BinaryLength); OnStatusMessage("Received request for \"{0}\" from \"{1}\" - frame was returned.", commandFrame.Command, connectionID); } break; case DeviceCommand.SendHeaderFrame: if (commandChannel != null) { StringBuilder status = new StringBuilder(); status.Append("IEEE C37.118 Concentrator:\r\n\r\n"); status.AppendFormat(" Auto-publish config frame: {0}\r\n", AutoPublishConfigurationFrame); status.AppendFormat(" Auto-start data channel: {0}\r\n", AutoStartDataChannel); status.AppendFormat(" Data stream ID code: {0}\r\n", IDCode); status.AppendFormat(" Derived system time: {0} UTC\r\n", ((DateTime)RealTime).ToString("yyyy-MM-dd HH:mm:ss.fff")); HeaderFrame headerFrame = new HeaderFrame(status.ToString()); commandChannel.SendToAsync(clientID, headerFrame.BinaryImage, 0, headerFrame.BinaryLength); OnStatusMessage("Received request for \"SendHeaderFrame\" from \"{0}\" - frame was returned.", connectionID); } break; case DeviceCommand.EnableRealTimeData: // Only responding to stream control command if auto-start data channel is false if (!AutoStartDataChannel) { StartDataChannel(); OnStatusMessage("Received request for \"EnableRealTimeData\" from \"{0}\" - concentrator real-time data stream was started.", connectionID); } else OnStatusMessage("Request for \"EnableRealTimeData\" from \"{0}\" was ignored - concentrator data channel is set for auto-start.", connectionID); break; case DeviceCommand.DisableRealTimeData: // Only responding to stream control command if auto-start data channel is false if (!AutoStartDataChannel) { StopDataChannel(); OnStatusMessage("Received request for \"DisableRealTimeData\" from \"{0}\" - concentrator real-time data stream was stopped.", connectionID); } else OnStatusMessage("Request for \"DisableRealTimeData\" from \"{0}\" was ignored - concentrator data channel is set for auto-start.", connectionID); break; default: OnStatusMessage("Request for \"{0}\" from \"{1}\" was ignored - device command is unsupported.", commandFrame.Command, connectionID); break; } } else OnStatusMessage("WARNING: Concentrator ID code validation failed for device command \"{0}\" from \"{1}\" - no action was taken.", commandFrame.Command, connectionID); } catch (Exception ex) { OnProcessException(new InvalidOperationException(string.Format("Remotely connected device \"{0}\" sent an unrecognized data sequence to the concentrator, no action was taken. Exception details: {1}", connectionID, ex.Message), ex)); } }