/// <summary> /// Casts the parsed <see cref="IChannelFrame"/> to its specific implementation (i.e., <see cref="DataFrame"/>, <see cref="ConfigurationFrame"/> or <see cref="CommandFrame"/>). /// </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 IEC 61850-90-5 specific channel frame events, if any have been subscribed if (frame != null && (ReceivedDataFrame != null || ReceivedConfigurationFrame != null || ReceivedCommandFrame != null)) { DataFrame dataFrame = frame as DataFrame; if (dataFrame != null) { if (ReceivedDataFrame != null) { ReceivedDataFrame(this, new EventArgs <DataFrame>(dataFrame)); } } else { ConfigurationFrame configFrame = frame as ConfigurationFrame; if (configFrame != null) { if (ReceivedConfigurationFrame != null) { ReceivedConfigurationFrame(this, new EventArgs <ConfigurationFrame>(configFrame)); } } 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(MessageLevel.Info, $"Received request for \"{commandFrame.Command}\" from \"{connectionID}\" - frame was returned."); } break; case DeviceCommand.EnableRealTimeData: // Only responding to stream control command if auto-start data channel is false if (!AutoStartDataChannel) { StartDataChannel(); OnStatusMessage(MessageLevel.Info, $"Received request for \"EnableRealTimeData\" from \"{connectionID}\" - concentrator real-time data stream was started."); } else { OnStatusMessage(MessageLevel.Info, $"Request for \"EnableRealTimeData\" from \"{connectionID}\" was ignored - concentrator data channel is set for auto-start."); } break; case DeviceCommand.DisableRealTimeData: // Only responding to stream control command if auto-start data channel is false if (!AutoStartDataChannel) { StopDataChannel(); OnStatusMessage(MessageLevel.Info, $"Received request for \"DisableRealTimeData\" from \"{connectionID}\" - concentrator real-time data stream was stopped."); } else { OnStatusMessage(MessageLevel.Info, $"Request for \"DisableRealTimeData\" from \"{connectionID}\" was ignored - concentrator data channel is set for auto-start."); } break; default: OnStatusMessage(MessageLevel.Info, $"Request for \"{commandFrame.Command}\" from \"{connectionID}\" was ignored - device command is unsupported."); break; } } else { OnStatusMessage(MessageLevel.Warning, $"Concentrator ID code validation failed for device command \"{commandFrame.Command}\" from \"{connectionID}\" - no action was taken."); } } catch (Exception ex) { OnProcessException(MessageLevel.Warning, new InvalidOperationException($"Remotely connected device \"{connectionID}\" sent an unrecognized data sequence to the concentrator, no action was taken. Exception details: {ex.Message}", ex)); } }