Пример #1
0
        /// <summary>
        /// Raises the <see cref="FrameParserBase{TypeIndentifier}.ReceivedConfigurationFrame"/> event.
        /// </summary>
        /// <param name="frame"><see cref="IConfigurationFrame"/> to send to <see cref="FrameParserBase{TypeIndentifier}.ReceivedConfigurationFrame"/> event.</param>
        protected override void OnReceivedConfigurationFrame(IConfigurationFrame frame)
        {
            // We override this method so we can cache configuration frame when it's received
            base.OnReceivedConfigurationFrame(frame);

            // Cache new configuration frame for parsing subsequent data frames...
            ConfigurationFrame configurationFrame = frame as ConfigurationFrame;

            if ((object)configurationFrame != null)
            {
                m_configurationFrame = configurationFrame;
            }
        }
Пример #2
0
        /// <summary>
        /// Casts the parsed <see cref="IChannelFrame"/> to its specific implementation (i.e., <see cref="DataFrame"/> or <see cref="ConfigurationFrame"/>).
        /// </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 Macrodyne specific channel frame events, if any have been subscribed
            if (frame != null && (ReceivedDataFrame != null || ReceivedConfigurationFrame != null || ReceivedHeaderFrame != null))
            {
                DataFrame dataFrame = frame as DataFrame;

                if (dataFrame != null)
                {
                    if (ReceivedDataFrame != null)
                    {
                        ReceivedDataFrame(this, new EventArgs <DataFrame>(dataFrame));
                    }
                }
                else
                {
                    HeaderFrame headerFrame = frame as HeaderFrame;

                    if (headerFrame != null)
                    {
                        if (ReceivedHeaderFrame != null)
                        {
                            ReceivedHeaderFrame(this, new EventArgs <HeaderFrame>(headerFrame));
                        }
                    }
                    else
                    {
                        ConfigurationFrame configFrame = frame as ConfigurationFrame;

                        if (configFrame != null)
                        {
                            if (ReceivedConfigurationFrame != null)
                            {
                                ReceivedConfigurationFrame(this, new EventArgs <ConfigurationFrame>(configFrame));
                            }
                        }
                    }
                }
            }
        }
Пример #3
0
        /// <summary>
        /// Creates a new <see cref="CommonFrameHeader"/> from given <paramref name="buffer"/>.
        /// </summary>
        /// <param name="buffer">Buffer that contains data to parse.</param>
        /// <param name="startIndex">Start index into buffer where valid data begins.</param>
        /// <param name="protocolVersion">Macrodyne protocol to use for parsing.</param>
        /// <param name="configurationFrame">Configuration frame, if available.</param>
        public CommonFrameHeader(byte[] buffer, int startIndex, ProtocolVersion protocolVersion, ConfigurationFrame configurationFrame)
        {
            byte firstByte = buffer[startIndex];

            // Cache protocol version
            m_protocolVersion = protocolVersion;

            // Validate Macrodyne data image
            if (firstByte != 0xAA && firstByte != 0xBB)
            {
                throw new InvalidOperationException($"Bad data stream, expected 0xAA or 0xBB as first byte in Macrodyne ON-LINE data frame or configuration frame request response, got 0x{buffer[startIndex].ToString("X").PadLeft(2, '0')}");
            }

            // Determine frame type (it's either the sync byte of a data frame or the response by from a command request)
            if (firstByte == 0xAA)
            {
                TypeID = Macrodyne.FrameType.DataFrame;
            }
            else
            {
                TypeID = BigEndian.ToUInt16(buffer, startIndex) switch
                {
                    (ushort)DeviceCommand.RequestOnlineDataFormat => Macrodyne.FrameType.ConfigurationFrame,
                    (ushort)DeviceCommand.RequestUnitIDBufferValue => Macrodyne.FrameType.HeaderFrame,
                    _ => throw new InvalidOperationException($"Bad data stream, expected 0xBB24 or 0xBB48 in response to Macrodyne device command, got 0xBB{buffer[startIndex + 1].ToString("X").PadLeft(2, '0')}"),
                };
            }
Пример #4
0
        /// <summary>
        /// Parses the binary body image.
        /// </summary>
        /// <param name="buffer">Binary image to parse.</param>
        /// <param name="startIndex">Start index into <paramref name="buffer"/> to begin parsing.</param>
        /// <param name="length">Length of valid data within <paramref name="buffer"/>.</param>
        /// <returns>The length of the data that was parsed.</returns>
        protected override int ParseBodyImage(byte[] buffer, int startIndex, int length)
        {
            ConfigurationCell  configCell      = ConfigurationCell;
            ConfigurationFrame configFrame     = configCell.Parent;
            ProtocolVersion    protocolVersion = configFrame.CommonHeader.ProtocolVersion;
            IPhasorValue       phasorValue;
            IDigitalValue      digitalValue;
            int parsedLength, index = startIndex;

            if (protocolVersion == ProtocolVersion.M)
            {
                // Parse out optional STATUS2 flags
                if (configFrame.Status2Included)
                {
                    m_status2Flags = buffer[index];
                    index++;
                }
                else
                {
                    m_status2Flags = 0;
                }

                // We interpret status bytes together as one word (matches other protocols this way)
                base.StatusFlags = Word.MakeWord((byte)Status1Flags, m_status2Flags);
            }
            else
            {
                // Read sample number for G protocol
                m_sampleNumber = BigEndian.ToUInt16(buffer, index);
                index         += 2;
            }

            // Parse out time tag
            if (configFrame.TimestampIncluded)
            {
                m_clockStatusFlags = (ClockStatusFlags)buffer[index];
                index += 1;

                ushort day      = BinaryCodedDecimal.Decode(BigEndian.ToUInt16(buffer, index));
                byte   hours    = BinaryCodedDecimal.Decode(buffer[index + 2]);
                byte   minutes  = BinaryCodedDecimal.Decode(buffer[index + 3]);
                byte   seconds  = BinaryCodedDecimal.Decode(buffer[index + 4]);
                double timebase = 2880.0D;
                index += 5;

                // Read sample number for M protocol
                if (protocolVersion == ProtocolVersion.M)
                {
                    m_sampleNumber = BigEndian.ToUInt16(buffer, index + 5);
                    timebase       = 719.0D;
                    index         += 2;
                }

                // TODO: Think about how to handle year change with floating clock...
                // Calculate timestamp
                Parent.Timestamp = new DateTime(DateTime.UtcNow.Year, 1, 1).AddDays(day - 1).AddHours(hours).AddMinutes(minutes).AddSeconds(seconds + m_sampleNumber / timebase);
            }
            else
            {
                Parent.Timestamp       = DateTime.UtcNow.Ticks;
                SynchronizationIsValid = false;
                m_sampleNumber         = BigEndian.ToUInt16(buffer, index);
                index += 2;
            }

            // Parse out first five phasor values (1 - 5)
            int phasorIndex = 0;

            // Phasor 1 (always present)
            phasorValue = PhasorValue.CreateNewValue(this, configCell.PhasorDefinitions[phasorIndex++], buffer, index, out parsedLength);
            PhasorValues.Add(phasorValue);
            index += parsedLength;

            if ((configFrame.OnlineDataFormatFlags & OnlineDataFormatFlags.Phasor2Enabled) == OnlineDataFormatFlags.Phasor2Enabled)
            {
                // Phasor 2
                phasorValue = PhasorValue.CreateNewValue(this, configCell.PhasorDefinitions[phasorIndex++], buffer, index, out parsedLength);
                PhasorValues.Add(phasorValue);
                index += parsedLength;
            }

            if ((configFrame.OnlineDataFormatFlags & OnlineDataFormatFlags.Phasor3Enabled) == OnlineDataFormatFlags.Phasor3Enabled)
            {
                // Phasor 3
                phasorValue = PhasorValue.CreateNewValue(this, configCell.PhasorDefinitions[phasorIndex++], buffer, index, out parsedLength);
                PhasorValues.Add(phasorValue);
                index += parsedLength;
            }

            if ((configFrame.OnlineDataFormatFlags & OnlineDataFormatFlags.Phasor4Enabled) == OnlineDataFormatFlags.Phasor4Enabled)
            {
                // Phasor 4
                phasorValue = PhasorValue.CreateNewValue(this, configCell.PhasorDefinitions[phasorIndex++], buffer, index, out parsedLength);
                PhasorValues.Add(phasorValue);
                index += parsedLength;
            }

            if ((configFrame.OnlineDataFormatFlags & OnlineDataFormatFlags.Phasor5Enabled) == OnlineDataFormatFlags.Phasor5Enabled)
            {
                // Phasor 5
                phasorValue = PhasorValue.CreateNewValue(this, configCell.PhasorDefinitions[phasorIndex++], buffer, index, out parsedLength);
                PhasorValues.Add(phasorValue);
                index += parsedLength;
            }

            // For 1690M format the frequency, reference phasor, dF/dt and first digital follow phasors 1-5
            if (protocolVersion == ProtocolVersion.M)
            {
                // Parse out frequency value
                FrequencyValue = Macrodyne.FrequencyValue.CreateNewValue(this, configCell.FrequencyDefinition, buffer, index, out parsedLength);
                index         += parsedLength;

                // Parse reference phasor information
                if (configFrame.ReferenceIncluded)
                {
                    m_referenceSampleNumber = BigEndian.ToUInt16(buffer, index);
                    m_referencePhasor       = PhasorValue.CreateNewValue(this, new PhasorDefinition(null, "Reference Phasor", PhasorType.Voltage, null), buffer, index, out parsedLength) as PhasorValue;
                    index += 6;
                }

                // Parse first digital value
                if (configFrame.Digital1Included)
                {
                    digitalValue = DigitalValue.CreateNewValue(this, configCell.DigitalDefinitions[0], buffer, index, out parsedLength);
                    DigitalValues.Add(digitalValue);
                    index += parsedLength;
                }
            }

            // Parse out next five phasor values (6 - 10)
            if ((configFrame.OnlineDataFormatFlags & OnlineDataFormatFlags.Phasor6Enabled) == OnlineDataFormatFlags.Phasor6Enabled)
            {
                // Phasor 6
                phasorValue = PhasorValue.CreateNewValue(this, configCell.PhasorDefinitions[phasorIndex++], buffer, index, out parsedLength);
                PhasorValues.Add(phasorValue);
                index += parsedLength;
            }

            if ((configFrame.OnlineDataFormatFlags & OnlineDataFormatFlags.Phasor7Enabled) == OnlineDataFormatFlags.Phasor7Enabled)
            {
                // Phasor 7
                phasorValue = PhasorValue.CreateNewValue(this, configCell.PhasorDefinitions[phasorIndex++], buffer, index, out parsedLength);
                PhasorValues.Add(phasorValue);
                index += parsedLength;
            }

            if ((configFrame.OnlineDataFormatFlags & OnlineDataFormatFlags.Phasor8Enabled) == OnlineDataFormatFlags.Phasor8Enabled)
            {
                // Phasor 8
                phasorValue = PhasorValue.CreateNewValue(this, configCell.PhasorDefinitions[phasorIndex++], buffer, index, out parsedLength);
                PhasorValues.Add(phasorValue);
                index += parsedLength;
            }

            if ((configFrame.OnlineDataFormatFlags & OnlineDataFormatFlags.Phasor9Enabled) == OnlineDataFormatFlags.Phasor9Enabled)
            {
                // Phasor 9
                phasorValue = PhasorValue.CreateNewValue(this, configCell.PhasorDefinitions[phasorIndex++], buffer, index, out parsedLength);
                PhasorValues.Add(phasorValue);
                index += parsedLength;
            }

            if ((configFrame.OnlineDataFormatFlags & OnlineDataFormatFlags.Phasor10Enabled) == OnlineDataFormatFlags.Phasor10Enabled)
            {
                // Phasor 10
                phasorValue = PhasorValue.CreateNewValue(this, configCell.PhasorDefinitions[phasorIndex++], buffer, index, out parsedLength);
                PhasorValues.Add(phasorValue);
                index += parsedLength;
            }

            // For 1690G format the channel phasors, reference phasor, frequency, dF/dt and digitals follow phasors 1-10
            if (protocolVersion == ProtocolVersion.G)
            {
                // Technically 30 more possible channel phasors can be defined
                for (int i = phasorIndex; i < ConfigurationCell.PhasorDefinitions.Count; i++)
                {
                    phasorValue = PhasorValue.CreateNewValue(this, configCell.PhasorDefinitions[phasorIndex++], buffer, index, out parsedLength);
                    PhasorValues.Add(phasorValue);
                    index += parsedLength;
                }

                // Parse reference phasor information
                if (configFrame.ReferenceIncluded)
                {
                    m_referencePhasor = PhasorValue.CreateNewValue(this, new PhasorDefinition(null, "Reference Phasor", PhasorType.Voltage, null), buffer, index, out parsedLength) as PhasorValue;
                    index            += parsedLength;
                }

                // Parse out frequency value
                FrequencyValue = Macrodyne.FrequencyValue.CreateNewValue(this, configCell.FrequencyDefinition, buffer, index, out parsedLength);
                index         += parsedLength;

                // Parse first digital value
                if (configFrame.Digital1Included)
                {
                    digitalValue = DigitalValue.CreateNewValue(this, configCell.DigitalDefinitions[0], buffer, index, out parsedLength);
                    DigitalValues.Add(digitalValue);
                    index += parsedLength;
                }
            }

            // Parse second digital value
            if (configFrame.Digital2Included)
            {
                digitalValue = DigitalValue.CreateNewValue(this, configCell.DigitalDefinitions[configCell.DigitalDefinitions.Count - 1], buffer, index, out parsedLength);
                DigitalValues.Add(digitalValue);
                index += parsedLength;
            }

            // Return total parsed length
            return(index - startIndex);
        }
Пример #5
0
        /// <summary>
        /// Creates a new <see cref="ConfigurationCell"/> from specified parameters.
        /// </summary>
        /// <param name="parent">The parent <see cref="ConfigurationFrame"/> reference to use.</param>
        /// <param name="deviceLabel">INI section device label to use.</param>
        public ConfigurationCell(ConfigurationFrame parent, string deviceLabel = null)
            : base(parent, 0, Common.MaximumPhasorValues, Common.MaximumAnalogValues, Common.MaximumDigitalValues)
        {
            // Assign station name that came in from header frame
            StationName = parent.StationName;

            if (!string.IsNullOrEmpty(deviceLabel))
            {
                SectionEntry = deviceLabel;
            }

            // Add a single frequency definition
            FrequencyDefinition = new FrequencyDefinition(this)
            {
                Label = "Line frequency"
            };

            OnlineDataFormatFlags flags = parent.OnlineDataFormatFlags;

            PhasorDefinitions.Add(new PhasorDefinition(this, "Phasor 1", PhasorType.Voltage, null));

            if ((flags & OnlineDataFormatFlags.Phasor2Enabled) == OnlineDataFormatFlags.Phasor2Enabled)
            {
                PhasorDefinitions.Add(new PhasorDefinition(this, "Phasor 2", PhasorType.Voltage, null));
            }

            if ((flags & OnlineDataFormatFlags.Phasor3Enabled) == OnlineDataFormatFlags.Phasor3Enabled)
            {
                PhasorDefinitions.Add(new PhasorDefinition(this, "Phasor 3", PhasorType.Voltage, null));
            }

            if ((flags & OnlineDataFormatFlags.Phasor4Enabled) == OnlineDataFormatFlags.Phasor4Enabled)
            {
                PhasorDefinitions.Add(new PhasorDefinition(this, "Phasor 4", PhasorType.Voltage, null));
            }

            if ((flags & OnlineDataFormatFlags.Phasor5Enabled) == OnlineDataFormatFlags.Phasor5Enabled)
            {
                PhasorDefinitions.Add(new PhasorDefinition(this, "Phasor 5", PhasorType.Voltage, null));
            }

            if ((flags & OnlineDataFormatFlags.Phasor6Enabled) == OnlineDataFormatFlags.Phasor6Enabled)
            {
                PhasorDefinitions.Add(new PhasorDefinition(this, "Phasor 6", PhasorType.Voltage, null));
            }

            if ((flags & OnlineDataFormatFlags.Phasor7Enabled) == OnlineDataFormatFlags.Phasor7Enabled)
            {
                PhasorDefinitions.Add(new PhasorDefinition(this, "Phasor 7", PhasorType.Voltage, null));
            }

            if ((flags & OnlineDataFormatFlags.Phasor8Enabled) == OnlineDataFormatFlags.Phasor8Enabled)
            {
                PhasorDefinitions.Add(new PhasorDefinition(this, "Phasor 8", PhasorType.Voltage, null));
            }

            if ((flags & OnlineDataFormatFlags.Phasor9Enabled) == OnlineDataFormatFlags.Phasor9Enabled)
            {
                PhasorDefinitions.Add(new PhasorDefinition(this, "Phasor 9", PhasorType.Voltage, null));
            }

            if ((flags & OnlineDataFormatFlags.Phasor10Enabled) == OnlineDataFormatFlags.Phasor10Enabled)
            {
                PhasorDefinitions.Add(new PhasorDefinition(this, "Phasor 10", PhasorType.Voltage, null));
            }

            if ((flags & OnlineDataFormatFlags.Digital1Enabled) == OnlineDataFormatFlags.Digital1Enabled)
            {
                DigitalDefinitions.Add(new DigitalDefinition(this, "Digital 1"));
            }

            if ((flags & OnlineDataFormatFlags.Digital2Enabled) == OnlineDataFormatFlags.Digital2Enabled)
            {
                DigitalDefinitions.Add(new DigitalDefinition(this, "Digital 2"));
            }
        }
Пример #6
0
        /// <summary>
        /// Creates a new <see cref="CommonFrameHeader"/> from given <paramref name="buffer"/>.
        /// </summary>
        /// <param name="buffer">Buffer that contains data to parse.</param>
        /// <param name="startIndex">Start index into buffer where valid data begins.</param>
        /// <param name="protocolVersion">Macrodyne protocol to use for parsing.</param>
        /// <param name="configurationFrame">Configuration frame, if available.</param>
        public CommonFrameHeader(byte[] buffer, int startIndex, ProtocolVersion protocolVersion, ConfigurationFrame configurationFrame)
        {
            byte firstByte = buffer[startIndex];

            // Cache protocol version
            m_protocolVersion = protocolVersion;

            // Validate Macrodyne data image
            if (firstByte != 0xAA && firstByte != 0xBB)
            {
                throw new InvalidOperationException($"Bad data stream, expected 0xAA or 0xBB as first byte in Macrodyne ON-LINE data frame or configuration frame request response, got 0x{buffer[startIndex].ToString("X").PadLeft(2, '0')}");
            }

            // Determine frame type (it's either the sync byte of a data frame or the response by from a command request)
            if (firstByte == 0xAA)
            {
                TypeID = Macrodyne.FrameType.DataFrame;
            }
            else
            {
                switch (BigEndian.ToUInt16(buffer, startIndex))
                {
                case (ushort)DeviceCommand.RequestOnlineDataFormat:
                    TypeID = Macrodyne.FrameType.ConfigurationFrame;
                    break;

                case (ushort)DeviceCommand.RequestUnitIDBufferValue:
                    TypeID = Macrodyne.FrameType.HeaderFrame;
                    break;

                default:
                    throw new InvalidOperationException($"Bad data stream, expected 0xBB24 or 0xBB48 in response to Macrodyne device command, got 0xBB{buffer[startIndex + 1].ToString("X").PadLeft(2, '0')}");
                }
            }

            // Cache config frame, if defined, for future use
            m_configurationFrame = configurationFrame;

            // Parse relevant common header values
            if (TypeID == Macrodyne.FrameType.DataFrame)
            {
                switch (m_protocolVersion)
                {
                case ProtocolVersion.M:
                    m_statusFlags = (StatusFlags)buffer[startIndex + 1];
                    break;

                case ProtocolVersion.G:
                    if (buffer[startIndex + 1] != 0x02)
                    {
                        throw new InvalidOperationException($"Bad data stream, expected 0xAA02 for Macrodyne 1690G version devices, got 0xAA{buffer[startIndex + 1].ToString("X").PadLeft(2, '0')}");
                    }
                    break;
                }
            }
        }
Пример #7
0
        /// <summary>
        /// Creates a new <see cref="PhasorDefinition"/> from specified parameters.
        /// </summary>
        /// <param name="parent">The <see cref="ConfigurationCell"/> parent of this <see cref="PhasorDefinition"/>.</param>
        /// <param name="index">Index of phasor within INI based configuration file.</param>
        /// <param name="entryValue">The entry value from the INI based configuration file.</param>
        public PhasorDefinition(ConfigurationCell parent, int index, string entryValue)
            : base(parent)
        {
            string[]         entry     = entryValue.Split(',');
            string           entryType = entry[0].Trim().Substring(0, 1).ToUpper();
            PhasorDefinition defaultPhasor;

            if (parent != null)
            {
                ConfigurationFrame configFile = this.Parent.Parent;

                if (entryType == "V")
                {
                    PhasorType    = PhasorType.Voltage;
                    defaultPhasor = configFile.DefaultPhasorV;
                }
                else if (entryType == "I")
                {
                    PhasorType    = PhasorType.Current;
                    defaultPhasor = configFile.DefaultPhasorI;
                }
                else
                {
                    PhasorType    = PhasorType.Voltage;
                    defaultPhasor = configFile.DefaultPhasorV;
                }
            }
            else
            {
                defaultPhasor = new PhasorDefinition(null as ConfigurationCell);
            }

            if (entry.Length > 1)
            {
                Ratio = double.Parse(entry[1].Trim());
            }
            else
            {
                Ratio = defaultPhasor.Ratio;
            }

            if (entry.Length > 2)
            {
                CalFactor = double.Parse(entry[2].Trim());
            }
            else
            {
                ConversionFactor = defaultPhasor.ConversionFactor;
            }

            if (entry.Length > 3)
            {
                Offset = double.Parse(entry[3].Trim());
            }
            else
            {
                Offset = defaultPhasor.Offset;
            }

            if (entry.Length > 4)
            {
                Shunt = double.Parse(entry[4].Trim());
            }
            else
            {
                Shunt = defaultPhasor.Shunt;
            }

            if (entry.Length > 5)
            {
                VoltageReferenceIndex = (int)double.Parse(entry[5].Trim());
            }
            else
            {
                VoltageReferenceIndex = defaultPhasor.VoltageReferenceIndex;
            }

            if (entry.Length > 6)
            {
                Label = entry[6].Trim();
            }
            else
            {
                Label = defaultPhasor.Label;
            }

            this.Index = index;
        }