using System;
using System.Text;
using S7.Net;
using S7.Net.Types;

namespace MES.Utility.Network.S7netplus
{
    public class S7Helper : IDisposable
    {
        private object _lock = new object();
        private Plc plc;
        public S7Helper(CpuType cpuType, string ipAddress) : this(cpuType, ipAddress, 0, 1)
        {
        }

        public S7Helper(CpuType cpuType, string ipAddress, short rack, short slot)
        {
            plc = new Plc(cpuType, ipAddress, 102, rack, slot);
            plc.Open(1000);
        }

        #region BOOL类型读写 
        /// <summary>
        /// 写入BOOL
        /// </summary>
        /// <param name="address">例如：DB45.DBX52.0</param>
        /// <param name="value">true/false</param>
        /// <returns></returns>
        public bool WriteBool(string address, bool value)
        {
            lock (_lock)
                try
                {

                    plc.Write(address, value);
                    return true;
                }
                catch
                {
                    return false;
                }
        }

        /// <summary>
        /// 读取bool类型
        /// </summary> 
        /// <param name="address">例如：DB45.DBX52.0</param>
        /// <param name="value">输出bool值</param>
        /// <returns>返回false 读取失败，PLC通信异常</returns>
        public bool ReadBool(string address, out bool value)
        {
            lock (_lock)
                try
                {
                    value = (bool)plc.Read(address);
                    return true;
                }
                catch
                {
                    value = false;
                    return false;
                }
        }
        #endregion

        #region INT16读写
        public bool ReadInt16(string address, out ushort value)
        {
            lock (_lock)
                try
                {
                    value = (ushort)plc.Read(address);
                    return true;
                }
                catch
                {
                    value = 0;
                    return false;
                }
        }
        public bool WriteInt16(string address, ushort value)
        {
            lock (_lock)
                try
                {
                    plc.Write(address, value);
                    return true;
                }
                catch
                {
                    return false;
                }
        }
        #endregion

        #region REAL读写
        /// <summary>
        /// 
        /// </summary>
        /// <param name="address">例如 DB1.DBD4.0</param>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool ReadSingle(string address, out float value)
        {
            lock (_lock)
                try
                {
                    value = ((uint)plc.Read(address)).ConvertToFloat();
                    return true;
                }
                catch
                {
                    value = 0;
                    return false;
                }
        }

        public bool ReadBytes(int db, int address, int count, out byte[] value)
        {
            try
            {
                value = plc.ReadBytes(DataType.DataBlock, db, address, count);
                return true;
            }
            catch
            {
                value = null;
                return false;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="address">例如 DB1.DBD4.0</param>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool WriteSingle(string address, float value)
        {
            lock (_lock)
                try
                {
                    plc.Write(address, value);
                    return true;
                }
                catch
                {
                    return false;
                }
        }
        #endregion

        #region String读写
        /// <summary>
        /// 
        /// </summary>
        /// <param name="address">例如DB45.DBX52.0</param>
        /// <param name="strLength">字符串长度</param>
        /// <param name="value">返回字符串数据</param>
        /// <returns></returns>
        public bool ReadString(string address, int strLength, out string value)
        {
            lock (_lock)
                try
                {
                    string[] array = address.Split('.');
                    int db = Convert.ToInt32(array[0].Replace("DB", ""));
                    int startAddress = Convert.ToInt32(array[1].Replace("DBX", ""));
                    value = (string)plc.Read(DataType.DataBlock, db, startAddress, VarType.S7String, strLength);//获取对应长度的字符串
                    return true;
                }
                catch
                {
                    value = "";
                    return false;
                }
        }

        /// <summary>
        /// 写入字符串
        /// </summary> 
        /// <param name="address">例如DB45.DBX52.0</param>
        /// <param name="value">写入数据</param>
        /// <param name="value">写入长度</param>
        /// <returns></returns>
        public bool WriteString(string address, string value, int strLength)
        {
            lock (_lock)
                try
                {
                    //转换address,取到DB  和  开始地址
                    string[] array = address.Split('.');
                    int db = Convert.ToInt32(array[0].Replace("DB", ""));
                    int startAddress = Convert.ToInt32(array[1].Replace("DBX", ""));
                    var bytes = S7.Net.Types.S7String.ToByteArray(value, strLength);
                    plc.WriteBytes(DataType.DataBlock, db, startAddress, bytes);
                    return true;
                }
                catch
                {
                    return false;
                }

        }
        #endregion


        public bool ReadBytesString(string address, int strLength, out string value)
        {
            lock (_lock)
                try
                {
                    string[] array = address.Split('.');
                    int db = Convert.ToInt32(array[0].Replace("DB", ""));
                    int startAddress = Convert.ToInt32(array[1].Replace("DBX", ""));
                    byte[] values = plc.ReadBytes(DataType.DataBlock, db, startAddress, strLength);
                    value = Encoding.ASCII.GetString(values);
                    return true;
                }
                catch
                {
                    value = "";
                    return false;
                }
        }

        /// <summary>
        /// 写入字符串
        /// </summary> 
        /// <param name="address">例如DB45.DBX52.0</param>
        /// <param name="value">写入数据</param>
        /// <param name="value">写入长度</param>
        /// <returns></returns>
        public bool WriteBytesString(string address, string value, int strLength)
        {
            lock (_lock)
                try
                {
                    //转换address,取到DB  和  开始地址
                    string[] array = address.Split('.');
                    int db = Convert.ToInt32(array[0].Replace("DB", ""));
                    int startAddress = Convert.ToInt32(array[1].Replace("DBX", ""));
                    var bytes = Encoding.ASCII.GetBytes(value);
                    if (bytes.Length > strLength) throw new ArgumentException($"The provided string length ({bytes.Length} is larger than the specified reserved length ({strLength}).");
                    var buffer = new byte[strLength];
                    Array.Copy(bytes, 0, buffer, 0, bytes.Length);
                    plc.WriteBytes(DataType.DataBlock, db, startAddress, buffer);
                    return true;
                }
                catch
                {
                    return false;
                }

        }

        #region INT32读写
        /// <summary>
        /// 
        /// </summary>
        /// <param name="address">例如：DB1.DBD264.0</param>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool ReadInt32(string address, out int value)
        {
            lock (_lock)
                try
                {
                    value = (int)plc.Read(address);
                    return true;
                }
                catch
                {
                    value = 0;
                    return false;
                }
        }
        public bool WriteInt32(string address, int value)
        {
            lock (_lock)
                try
                {
                    plc.Write(address, value);
                    return true;
                }
                catch
                {
                    return false;
                }
        }

        #endregion

        public void Dispose()
        {
            plc?.Close();
            plc = null;
        }
    }

}
