Exemple #1
0
        /// <summary>
        /// Converts given IEEE C37.118 type 2 <paramref name="sourceFrame"/> into a type 3 configuration frame.
        /// </summary>
        /// <param name="sourceFrame">Source configuration frame.</param>
        /// <returns>New <see cref="ConfigurationFrame3"/> frame based on source configuration.</returns>
        /// <remarks>
        /// This function allow an explicit downcast of a typical IEEE C37.118 configuration type 2 frame to a type 3 frame.
        /// </remarks>
        public static ConfigurationFrame3 CastToConfigurationFrame3(ConfigurationFrame2 sourceFrame)
        {
            ConfigurationFrame3 derivedFrame;

            // Create a new IEEE C37.118 configuration frame converted from equivalent configuration information
            derivedFrame = new ConfigurationFrame3(sourceFrame.Timebase, sourceFrame.IDCode, sourceFrame.Timestamp, sourceFrame.FrameRate);

            foreach (ConfigurationCell sourceCell in sourceFrame.Cells)
            {
                // Create new derived configuration cell
                ConfigurationCell derivedCell = new ConfigurationCell(derivedFrame, sourceCell.IDCode, sourceCell.NominalFrequency);

                string stationName = sourceCell.StationName;
                string idLabel     = sourceCell.IDLabel;

                if (!string.IsNullOrWhiteSpace(stationName))
                {
                    derivedCell.StationName = stationName.TruncateLeft(derivedCell.MaximumStationNameLength);
                }

                if (!string.IsNullOrWhiteSpace(idLabel))
                {
                    derivedCell.IDLabel = idLabel.TruncateLeft(derivedCell.IDLabelLength);
                }

                derivedCell.PhasorCoordinateFormat = sourceCell.PhasorCoordinateFormat;
                derivedCell.PhasorAngleFormat      = sourceCell.PhasorAngleFormat;
                derivedCell.PhasorDataFormat       = sourceCell.PhasorDataFormat;
                derivedCell.FrequencyDataFormat    = sourceCell.FrequencyDataFormat;
                derivedCell.AnalogDataFormat       = sourceCell.AnalogDataFormat;

                // Create equivalent derived phasor definitions
                foreach (PhasorDefinition sourcePhasor in sourceCell.PhasorDefinitions)
                {
                    derivedCell.PhasorDefinitions.Add(new PhasorDefinition(derivedCell, sourcePhasor.Label, sourcePhasor.ScalingValue, sourcePhasor.Offset, sourcePhasor.PhasorType, null));
                }

                // Create equivalent derived frequency definition
                FrequencyDefinition sourceFrequency = sourceCell.FrequencyDefinition as FrequencyDefinition;
                derivedCell.FrequencyDefinition = new FrequencyDefinition(derivedCell, sourceFrequency?.Label);

                // Create equivalent derived analog definitions (assuming analog type = SinglePointOnWave)
                foreach (IAnalogDefinition sourceAnalog in sourceCell.AnalogDefinitions)
                {
                    derivedCell.AnalogDefinitions.Add(new AnalogDefinition(derivedCell, sourceAnalog.Label, sourceAnalog.ScalingValue, sourceAnalog.Offset, sourceAnalog.AnalogType));
                }

                // Create equivalent derived digital definitions
                foreach (IDigitalDefinition sourceDigital in sourceCell.DigitalDefinitions)
                {
                    derivedCell.DigitalDefinitions.Add(new GSF.PhasorProtocols.IEEEC37_118.DigitalDefinition(derivedCell, sourceDigital.Label, 0, 0));
                }

                // Add cell to frame
                derivedFrame.Cells.Add(derivedCell);
            }

            return(derivedFrame);
        }
Exemple #2
0
        /// <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 (!ValidateIDCode || commandFrame.IDCode == IDCode)
                {
                    switch (commandFrame.Command)
                    {
                    case DeviceCommand.SendConfigurationFrame1:
                        if (commandChannel != null)
                        {
                            ConfigurationFrame1 configFrame1 = CastToConfigurationFrame1(m_configurationFrame);
                            commandChannel.SendToAsync(clientID, configFrame1.BinaryImage, 0, configFrame1.BinaryLength);
                            OnStatusMessage(MessageLevel.Info, $"Received request for \"{commandFrame.Command}\" from \"{connectionID}\" - type 1 config frame was returned.");
                        }
                        break;

                    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}\" - type 2 config frame was returned.");
                        }
                        break;

                    case DeviceCommand.SendConfigurationFrame3:
                        if (commandChannel != null)
                        {
                            ConfigurationFrame3 configFrame3 = CastToConfigurationFrame3(m_configurationFrame);
                            commandChannel.SendToAsync(clientID, configFrame3.BinaryImage, 0, configFrame3.BinaryLength);
                            OnStatusMessage(MessageLevel.Info, $"Received request for \"{commandFrame.Command}\" from \"{connectionID}\" - type 3 config frame was returned.");
                        }
                        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:yyyy-MM-dd HH:mm:ss.fff} UTC\r\n", RealTime);

                            HeaderFrame headerFrame = new HeaderFrame(status.ToString());
                            commandChannel.SendToAsync(clientID, headerFrame.BinaryImage, 0, headerFrame.BinaryLength);
                            OnStatusMessage(MessageLevel.Info, $"Received request for \"SendHeaderFrame\" 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));
            }
        }