1114 lines · 44.0 KB · C#
RawDownload
//******************************************************************************************************//  ModbusPoller.cs - Gbtc////  Copyright © 2016, Grid Protection Alliance.  All Rights Reserved.////  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See//  the NOTICE file distributed with this work for additional information regarding copyright ownership.//  The GPA licenses this file to you under the MIT License (MIT), the "License"; you may//  not use this file except in compliance with the License. You may obtain a copy of the License at:////      http://opensource.org/licenses/MIT////  Unless agreed to in writing, the subject software distributed under the License is distributed on an//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the//  License for the specific language governing permissions and limitations.////  Code Modification History://  ----------------------------------------------------------------------------------------------------//  07/26/2016 - J. Ritchie Carroll//       Generated original version of source code.////****************************************************************************************************** using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.IO;using System.IO.Ports;using System.Linq;using System.Net;using System.Net.Sockets;using System.Reflection;using System.Security.Cryptography;using System.Text;using System.Threading;using System.Timers;using GSF;using GSF.Configuration;using GSF.Diagnostics;using GSF.IO;using GSF.Reflection;using GSF.Threading;using GSF.TimeSeries;using GSF.TimeSeries.Adapters;using GSF.TimeSeries.Statistics;using Modbus.Device;using Modbus.Utility;using ModbusAdapters.Model;using Measurement = GSF.TimeSeries.Measurement;using Timer = System.Timers.Timer; #pragma warning disable SCS0006 // Weak hash#pragma warning disable SCS0018 // Path traversal namespace ModbusAdapters{    [Description("Modbus: Implements Modbus polling capabilities")]    [EditorBrowsable(EditorBrowsableState.Advanced)] // Normally defined as an input device protocol    public class ModbusPoller : InputAdapterBase    {        #region [ Members ]         // Nested Types         private enum DerivedType        {            String,            Single,            Double,            UInt16,            Int32,            UInt32,            Int64,            UInt64        }         private enum RecordType        {            DI, // Discrete Input            CO, // Coil            IR, // Input Register            HR  // Holding Register        }         private enum SequenceType        {            Read,            Write        }         private class AddressRecord        {            public readonly RecordType Type;            public readonly ushort Address;             public AddressRecord(string recordType, ushort address)            {                Enum.TryParse(recordType, true, out Type);                Address = address;            }        }         private class DerivedValue        {            public readonly DerivedType Type;            public readonly List<AddressRecord> AddressRecords;             public DerivedValue(DerivedType type)            {                Type = type;                AddressRecords = new List<AddressRecord>();            }             public DerivedValue(string derivedType)            {                Enum.TryParse(derivedType, true, out Type);                AddressRecords = new List<AddressRecord>();            }             public ushort[] GetDataValues(Group[] groups)            {                ushort[] dataValues = new ushort[AddressRecords.Count];                 for (int i = 0; i < dataValues.Length; i++)                {                    ushort address = AddressRecords[i].Address;                     foreach (Group group in groups)                    {                        if (group.HasDataValue(address))                        {                            dataValues[i] = group.DataValues[address - group.StartAddress];                            break;                        }                    }                }                 return dataValues;            }        }         private class Group        {            public RecordType Type;            public ushort StartAddress;            public ushort PointCount;            public ushort[] DataValues;             public bool HasDataValue(ushort address) => address >= StartAddress && address <= StartAddress + PointCount - 1;        }         private class Sequence        {            public readonly SequenceType Type;            public readonly List<Group> Groups = new List<Group>();             public Sequence(SequenceType type)            {                Type = type;            }        }         // Define a IDevice implementation for to provide daily reports        private class DeviceProxy : IDevice        {            private readonly ModbusPoller m_parent;             public DeviceProxy(ModbusPoller parent)            {                m_parent = parent;            }             // Gets or sets total data quality errors of this <see cref="IDevice"/>.            public long DataQualityErrors            {                get;                set;            }             // Gets or sets total time quality errors of this <see cref="IDevice"/>.            public long TimeQualityErrors            {                get;                set;            }             // Gets or sets total device errors of this <see cref="IDevice"/>.            public long DeviceErrors            {                get                {                    return m_parent.m_deviceErrors;                }                set                {                    // Ignoring updates                }            }             // Gets or sets total measurements received for this <see cref="IDevice"/> - in local context "successful connections" per day.            public long MeasurementsReceived            {                get                {                    return m_parent.m_measurementsReceived;                }                set                {                    // Ignoring updates                }            }             // Gets or sets total measurements expected to have been received for this <see cref="IDevice"/> - in local context "attempted connections" per day.            public long MeasurementsExpected            {                get                {                    return m_parent.m_measurementsExpected;                }                set                {                    // Ignoring updates                }            }             // Gets or sets the number of measurements received while this <see cref="IDevice"/> was reporting errors.            public long MeasurementsWithError            {                get                {                    return 0;                }                set                {                    // Ignoring updates                }            }             // Gets or sets the number of measurements (per frame) defined for this <see cref="IDevice"/>.            public long MeasurementsDefined            {                get                {                    return m_parent.OutputMeasurements.Length;                }                set                {                    // Ignoring updates                }            }        }         // Constants        private const byte DefaultUnitID = 255;        private const int DefaultPollingRate = 2000;        private const int DefaultInterSequenceGroupPollDelay = 250;         // Fields        private readonly DeviceProxy m_deviceProxy;        private IModbusMaster m_modbusConnection;        private TcpClient m_tcpClient;        private UdpClient m_udpClient;        private SerialPort m_serialClient;        private byte m_unitID;        private int m_pollingRate;        private int m_interSequenceGroupPollDelay;        private Dictionary<MeasurementKey, DerivedValue> m_derivedValues;        private readonly Dictionary<MeasurementKey, string> m_derivedStrings;        private List<Sequence> m_sequences;        private Timer m_pollingTimer;        private ShortSynchronizedOperation m_pollingOperation;        private long m_pollOperations;        private long m_deviceErrors;        private long m_measurementsReceived;        private long m_measurementsExpected;        private bool m_disposed;         #endregion         #region [ Constructors ]         /// <summary>        /// Creates a new <see cref="ModbusPoller"/>.        /// </summary>        public ModbusPoller()        {            m_deviceProxy = new DeviceProxy(this);            m_derivedStrings = new Dictionary<MeasurementKey, string>();        }         #endregion         #region [ Properties ]         /// <summary>        /// Gets or sets unit ID for Modbus connection.        /// </summary>        [ConnectionStringParameter,        Description("Defines unit ID for Modbus connection."),        DefaultValue(DefaultUnitID)]        public byte UnitID        {            get            {                return m_unitID;            }            set            {                m_unitID = value;            }        }         /// <summary>        /// Gets or sets polling rate, in milliseconds, for Modbus connection.        /// </summary>        [ConnectionStringParameter,        Description("Defines overall polling rate, in milliseconds, for Modbus connection."),        DefaultValue(DefaultPollingRate)]        public int PollingRate        {            get            {                return m_pollingRate;            }            set            {                m_pollingRate = value;            }        }         /// <summary>        /// Gets or sets inter sequence-group poll delay, in milliseconds, for Modbus connection.        /// </summary>        [ConnectionStringParameter,        Description("Defines inter sequence-group poll delay, in milliseconds, for Modbus connection."),        DefaultValue(DefaultInterSequenceGroupPollDelay)]        public int InterSequenceGroupPollDelay        {            get            {                return m_interSequenceGroupPollDelay;            }            set            {                m_interSequenceGroupPollDelay = value;            }        }         /// <summary>        /// Gets total read or derived measurements over the Modbus poller instance lifetime.        /// </summary>        public long MeasurementsReceived => m_measurementsReceived;         /// <summary>        /// Gets flag that determines if the data input connects asynchronously.        /// </summary>        /// <remarks>        /// Derived classes should return true when data input source is connects asynchronously, otherwise return false.        /// </remarks>        protected override bool UseAsyncConnect => false;         /// <summary>        /// Gets the flag indicating if this adapter supports temporal processing.        /// </summary>        public override bool SupportsTemporalProcessing => false;         /// <summary>        /// Returns the detailed status of the data input source.        /// </summary>        public override string Status        {            get            {                StringBuilder status = new StringBuilder();                 status.Append(base.Status);                status.AppendFormat("                   Unit ID: {0}", UnitID);                status.AppendLine();                status.AppendFormat("              Polling Rate: {0:N0}ms", PollingRate);                status.AppendLine();                status.AppendFormat("Inter Seq-Group Poll Delay: {0:N0}ms", InterSequenceGroupPollDelay);                status.AppendLine();                status.AppendFormat("  Executed Poll Operations: {0:N0}", m_pollOperations);                status.AppendLine();                status.AppendFormat("             Device Errors: {0:N0}", m_deviceErrors);                status.AppendLine();                status.AppendFormat("     Measurements Received: {0:N0}", m_measurementsReceived);                status.AppendLine();                status.AppendFormat("     Measurements Expected: {0:N0}", m_measurementsExpected);                status.AppendLine();                status.AppendFormat("      Last Derived Strings: {0:N0} total", m_derivedStrings.Count);                status.AppendLine();                 foreach (KeyValuePair<MeasurementKey, string> item in m_derivedStrings)                {                    status.AppendFormat("{0} = {1}", item.Key.ToString().PadLeft(10), item.Value);                    status.AppendLine();                }                 return status.ToString();            }        }         #endregion         #region [ Methods ]         /// <summary>        /// Releases the unmanaged resources used by the <see cref="ModbusPoller"/> object and optionally releases the managed resources.        /// </summary>        /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>        protected override void Dispose(bool disposing)        {            if (!m_disposed)            {                try                {                    if (disposing)                    {                        StatisticsEngine.Unregister(m_deviceProxy);                         DisposeConnections();                         if ((object)m_pollingTimer != null)                        {                            m_pollingTimer.Enabled = false;                            m_pollingTimer.Elapsed -= m_pollingTimer_Elapsed;                            m_pollingTimer.Dispose();                        }                    }                }                finally                {                    m_disposed = true;          // Prevent duplicate dispose.                    base.Dispose(disposing);    // Call base class Dispose().                }            }        }         private void DisposeConnections()        {            m_tcpClient?.Dispose();            m_udpClient?.Dispose();            m_serialClient?.Dispose();            m_modbusConnection?.Dispose();        }         /// <summary>        /// Initializes <see cref="ModbusPoller" />.        /// </summary>        public override void Initialize()        {            base.Initialize();             ConnectionStringParser<ConnectionStringParameterAttribute> parser = new ConnectionStringParser<ConnectionStringParameterAttribute>();            parser.ParseConnectionString(ConnectionString, this);             // Register downloader with the statistics engine            StatisticsEngine.Register(this, "Modbus", "MOD");            StatisticsEngine.Register(m_deviceProxy, Name, "Device", "PMU");             // Attach to output measurements for Modbus device            OutputMeasurements = ParseOutputMeasurements(DataSource, false, $"FILTER ActiveMeasurements WHERE Device = '{Name}'");             // Parse derived value expressions from defined signal reference fields            m_derivedValues = OutputMeasurements.Select(measurement => measurement.Key).ToDictionary(key => key, key =>            {                DataTable measurements = DataSource.Tables["ActiveMeasurements"];                DerivedValue derivedValue = null;                DataRow[] records = measurements.Select($"ID = '{key}'");                 if (records.Length > 0)                    derivedValue = ParseDerivedValue(records[0]["SignalReference"].ToNonNullString());                 return derivedValue;            });             m_sequences = new List<Sequence>();             Dictionary<string, string> settings = Settings;            string setting;            int sequenceCount = 0;             if (settings.TryGetValue("sequenceCount", out setting))                int.TryParse(setting, out sequenceCount);             for (int i = 0; i < sequenceCount; i++)            {                if (settings.TryGetValue($"sequence{i}", out setting))                {                    Dictionary<string, string> sequenceSettings = setting.ParseKeyValuePairs();                    SequenceType sequenceType = SequenceType.Read;                     if (sequenceSettings.TryGetValue("sequenceType", out setting))                        Enum.TryParse(setting, true, out sequenceType);                     Sequence sequence = new Sequence(sequenceType);                    int groupCount; 

Lines 1–500 of 1114.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t