/// <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); }
/// <summary> /// Parses the <see cref="ControlFile"/>. /// </summary> /// <exception cref="InvalidOperationException">"No EMAX control file name was specified.</exception> /// <exception cref="FileNotFoundException">EMAX control file was not found.</exception> public void Parse() { if (string.IsNullOrEmpty(FileName)) { throw new InvalidOperationException("No EMAX control file name was specified."); } if (!File.Exists(FileName)) { throw new FileNotFoundException(string.Format("EMAX control file {0} not found.", FileName)); } m_parsedSuccesses.Clear(); m_parsedFailures.Clear(); byte byteValue; using (FileStream stream = File.OpenRead(FileName)) { // Read in header and file structure definitions using (BinaryReader reader = new BinaryReader(stream, Encoding.ASCII, true)) { // Read control header Header = reader.ReadStructure <CTL_HEADER>(); // Read byte that defines number of analog channels m_configuredAnalogChannels = BinaryCodedDecimal.Decode(Header.id.LowByte()); // Read byte that defines data size (i.e., 12 or 16 bits) byteValue = Header.id.HighByte(); if (!Enum.IsDefined(typeof(DataSize), byteValue)) { throw new InvalidOperationException("Invalid EMAX data size code encountered: 0x" + byteValue.ToString("X").PadLeft(2, '0')); } DataSize = (DataSize)byteValue; // Create array of file structures List <CTL_FILE_STRUCT> fileStructures = new List <CTL_FILE_STRUCT>(); CTL_FILE_STRUCT fileStructure = new CTL_FILE_STRUCT(reader); // ReSharper disable once LoopVariableIsNeverChangedInsideLoop while (fileStructure.type != StructureType.EndOfStructures) { if (fileStructure.type != StructureType.Unknown) { fileStructures.Add(fileStructure); } fileStructure = new CTL_FILE_STRUCT(reader); } FileStructures = fileStructures.ToArray(); } // Read in actual file structures for (int index = 0; index < FileStructures.Length; index++) { CTL_FILE_STRUCT fileStructure = FileStructures[index]; if (fileStructure.type == StructureType.Unknown) { continue; } // Set current type m_currentType = fileStructure.type; // Locate structure in the file stream.Position = fileStructure.offset; // Parse the structure type using (BinaryReader reader = new BinaryReader(stream, Encoding.ASCII, true)) { switch (m_currentType) { case StructureType.SYSTEM_PARAMETERS: AttemptParse(() => SystemParameters = reader.ReadStructure <SYSTEM_PARAMETERS>()); break; case StructureType.SYS_SETTINGS: AttemptParse(() => SystemSettings = reader.ReadStructure <SYS_SETTINGS>()); break; case StructureType.A_E_RSLTS: AttemptParse(() => AnalogEventResults = new A_E_RSLTS(reader, m_configuredAnalogChannels, SystemParameters.analog_groups)); break; case StructureType.ANALOG_GROUP: AttemptParse(() => AnalogGroup = new ANALOG_GROUP(reader, m_configuredAnalogChannels)); break; case StructureType.EVENT_GROUP: AttemptParse(() => EventGroup = reader.ReadStructure <EVENT_GROUP>()); break; case StructureType.ANLG_CHNL_NEW: AttemptParse(() => { AnalogChannelSettings = new Dictionary <int, ANLG_CHNL_NEW>(); ScalingFactors = new Dictionary <int, double>(); ANLG_CHNL_NEW settings; uint nextOffset = (index + 1 < FileStructures.Length) ? FileStructures[index + 1].offset : (uint)stream.Length; uint length = nextOffset - fileStructure.offset; Func <ANLG_CHNL_NEW> channelFactory; if (Marshal.SizeOf <ANLG_CHNL_NEW2>() * ConfiguredAnalogChannels <= length) { channelFactory = () => reader.ReadStructure <ANLG_CHNL_NEW2>().ToAnlgChnlNew(); } else { channelFactory = () => reader.ReadStructure <ANLG_CHNL_NEW1>().ToAnlgChnlNew(); } for (int i = 0; i < ConfiguredAnalogChannels; i++) { settings = channelFactory(); AnalogChannelSettings.Add(settings.ChannelNumber, settings); ScalingFactors.Add(settings.ChannelNumber, settings.ScalingFactor); } }); break; case StructureType.EVNT_CHNL_NEW: AttemptParse(() => { EventChannelSettings = new Dictionary <int, EVNT_CHNL_NEW>(); EVNT_CHNL_NEW settings; uint nextOffset = (index + 1 < FileStructures.Length) ? FileStructures[index + 1].offset : (uint)stream.Length; uint length = nextOffset - fileStructure.offset; Func <EVNT_CHNL_NEW> channelFactory; if (Marshal.SizeOf <EVNT_CHNL_NEW2>() * ConfiguredDigitalChannels <= length) { channelFactory = () => reader.ReadStructure <EVNT_CHNL_NEW2>().ToEvntChnlNew(); } else { channelFactory = () => reader.ReadStructure <EVNT_CHNL_NEW1>().ToEvntChnlNew(); } for (int i = 0; i < ConfiguredDigitalChannels; i++) { settings = channelFactory(); EventChannelSettings.Add(settings.EventNumber, settings); } }); break; case StructureType.ANLG_CHNLS: // TODO: Add decoder once structure definition is known... m_parsedFailures.Add(new Tuple <StructureType, Exception>(m_currentType, new NotImplementedException())); break; case StructureType.SHORT_HEADER: // TODO: Add decoder once structure definition is known... m_parsedFailures.Add(new Tuple <StructureType, Exception>(m_currentType, new NotImplementedException())); break; case StructureType.FAULT_HEADER: // TODO: Add decoder once structure definition is known... m_parsedFailures.Add(new Tuple <StructureType, Exception>(m_currentType, new NotImplementedException())); break; case StructureType.EVENT_DISPLAY: AttemptParse(() => EventDisplay = new EVENT_DISPLAY(reader, SystemParameters.event_groups)); break; case StructureType.IDENTSTRING: AttemptParse(() => { IdentityString = reader.ReadStructure <IDENTSTRING>(); IdentityString.value.TruncateRight(IdentityString.length); }); break; case StructureType.A_SELECTION: AttemptParse(() => AnalogSelection = new A_SELECTION(reader)); break; case StructureType.E_GROUP_SELECT: AttemptParse(() => EventGroupSelection = new E_GRP_SELECT(reader)); break; case StructureType.PHASOR_GROUP: AttemptParse(() => PhasorGroups = new PHASOR_GROUPS(reader)); break; case StructureType.LINE_CONSTANTS: AttemptParse(() => LineConstants = new LINE_CONSTANTS(reader)); break; case StructureType.LINE_NAMES: AttemptParse(() => LineNames = new LINE_NAMES(reader)); break; case StructureType.FAULT_LOCATION: AttemptParse(() => FaultLocations = new FAULT_LOCATIONS(reader)); break; case StructureType.SENS_RSLTS: AttemptParse(() => SensorResults = reader.ReadStructure <SENS_RSLTS>()); break; case StructureType.SEQUENCE_CHANNELS: AttemptParse(() => SequenceChannels = new SEQUENCE_CHANNELS(reader)); break; case StructureType.TPwrRcd: AttemptParse(() => PowerRecord = reader.ReadStructure <TPwrRcd>()); break; case StructureType.BoardAnalogEventChannels: AttemptParse(() => BoardAnalogEventChannels = reader.ReadStructure <BoardAnalogEventChannels>()); break; case StructureType.BREAKER_TRIP_TIMES: AttemptParse(() => BreakerTripTimes = new BREAKER_TRIP_TIMES(reader, m_configuredAnalogChannels, SystemParameters.analog_groups)); break; default: throw new ArgumentOutOfRangeException(); } } } } }