杨前锦
2025-06-13 b7308bba3d7ffad271ce7fc7a93c8c45d76be87d
HH.WCS.Mobox3/HH.WCS.Mobox3.FJJT/device/S7Helper.cs
New file
@@ -0,0 +1,776 @@
using HH.WCS.Mobox3.FJJT.api;
using HH.WCS.Mobox3.FJJT.util;
using Newtonsoft.Json.Linq;
using S7.Net;
using S7.Net.Types;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Services.Description;
using static HH.WCS.Mobox3.FJJT.util.Settings;
namespace HH.WCS.Mobox3.FJJT.device
{
    /// <summary>
    /// 西门子plc
    /// </summary>
    public class S7Helper
    {
        private static bool debug = true;
        private static S7.Net.Plc plc = null;
        static S7Helper()
        {
            Init();
        }
        private static Dictionary<string, Plc> plcDic = new Dictionary<string, Plc>();
        private static void Init()
        {
            //配置文件读取所有的plc进行初始化
            try
            {
                var plc1 = new Plc(CpuType.S71500, "", 0, 1);
                plcDic.Add("plc1", plc1);
                Link(plc1);
            }
            catch (Exception ex)
            {
                Console.WriteLine("S7Helper Init err=" + ex.Message);
            }
        }
        private static Plc GetPlc(string plc)
        {
            if (plcDic.ContainsKey(plc))
            {
                return plcDic[plc];
            }
            else
            {
                return null;
            }
        }
        public static Dictionary<string, string> s7TestData = new Dictionary<string, string>();
        private static void Link(Plc plc)
        {
            try
            {
                //if (!plc.IsConnected) {
                plc.Close();
                plc.Open();
                if (plc.IsConnected)
                {
                    Console.WriteLine($"已连接到plc{plc.IP}");
                }
                else
                {
                    Console.WriteLine($"plc{plc.IP}连接失败");
                    LogHelper.Info($"plc{plc.IP}连接失败", "Plc");
                }
                //}
            }
            catch (Exception ex)
            {
                Console.WriteLine($"plc{plc.IP}连接失败,err={ex.Message}");
                LogHelper.Info($"plc{plc.IP}连接失败,err={ex.Message}");
                //Init();
            }
        }
        //https://www.ad.siemens.com.cn/productportal/Prods/S7-1200_PLC_EASY_PLUS/SmartSMS/060.html
        //https://www.ad.siemens.com.cn/productportal/Prods/S7-1200_PLC_EASY_PLUS/07-Program/02-basic/01-Data_Type/09-String.html
        internal static short[] ReadInt(string device, int db, int byteAddr, int count)
        {
            short[] result = null;
            try
            {
                if (debug)
                {
                    var s7Key = $"int_{db}_{byteAddr}_{count}";
                    if (s7TestData.ContainsKey(s7Key))
                    {
                        var data = s7TestData[s7Key].Split(',');
                        if (data.Length == count)
                        {
                            result = Array.ConvertAll(data, s => short.Parse(s));
                        }
                        else
                        {
                            result = new short[count];
                            s7TestData[s7Key] = string.Join(",", result);
                        }
                        Console.WriteLine($"读取plc {device}信息成功,  addr={byteAddr} data={string.Join(",", result)}");
                    }
                }
                else
                {
                    var plc = GetPlc(device);
                    if (plc != null)
                    {
                        if (plc.IsConnected)
                        {
                            result = (short[])plc.Read(DataType.DataBlock, db, byteAddr, VarType.Int, count, 0);
                            Console.WriteLine($"读取plc {device}信息成功,ip={plc.IP}  addr={byteAddr} data={string.Join(",", result)}");
                            if (result.Length == 0)
                            {
                                Console.WriteLine($"plc {device}准备重新连接");
                                Link(plc);
                            }
                        }
                        else
                        {
                            Console.WriteLine($"准备连接plc {device}");
                            Link(plc);
                        }
                    }
                    else
                    {
                        Console.WriteLine($"plc {device}不存在");
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"ReadInt,device={device}  addr={byteAddr} count={count} err={ex.Message}");
                LogHelper.Error($"ReadInt,device={device}  addr={byteAddr}  count={count} err={ex.Message}", ex);
            }
            return result;
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="db"></param>
        /// <param name="byteAddr"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        internal static bool WriteInt(int db, int byteAddr, short data)
        {
            var result = false;
            try
            {
                if (plc.IsConnected)
                {
                    plc.Write(DataType.DataBlock, db, byteAddr, data);
                    Console.WriteLine($"写入plc信息,ip={plc.IP} addr={byteAddr} data={data} ");
                    LogHelper.Info($"写入plc信息,ip={plc.IP} addr={byteAddr} data={data} ");
                    if (result)
                    {
                        //写完再读一次确认
                        var readData = (short)plc.Read(DataType.DataBlock, db, byteAddr, VarType.Int, 1, 0);
                        Console.WriteLine($"读取plc信息,ip={plc.IP} addr={byteAddr} data={data} res={string.Join(", ", readData)}");
                        LogHelper.Info($"读取plc信息,ip={plc.IP} addr={byteAddr} data={data} res={string.Join(", ", readData)}", "PLC");
                        result = readData == data;
                    }
                }
                else
                {
                    Console.WriteLine("准备连接plc1");
                    Link(plc);
                }
            }
            catch (Exception ex)
            {
                LogHelper.Error($"写入plc1信息失败,ip={plc.IP} addr={byteAddr} data={data} err={ex.Message}", ex);
            }
            return result;
        }
        public static object ReadBit(string device, int db, int byteAddr, byte bitAddr)
        {
            object result = null;
            try
            {
                if (debug)
                {
                    var s7Key = $"bit_{db}_{byteAddr}_{bitAddr}";
                    if (s7TestData.ContainsKey(s7Key))
                    {
                        var data = s7TestData[s7Key];
                        if (data == "1")
                        {
                            result = true;
                        }
                        else { result = false; }
                        Console.WriteLine($"读取plc {device}信息成功,  addr={byteAddr} data={result.ToString()}");
                    }
                }
                else
                {
                    var plc = GetPlc(device);
                    if (plc != null)
                    {
                        if (plc.IsConnected)
                        {
                            result = plc.Read(DataType.DataBlock, db, byteAddr, VarType.Int, 1, bitAddr);
                            Console.WriteLine($"读取plc {device}信息成功,ip={plc.IP}  addr={byteAddr} data={result.ToString()}");
                        }
                        else
                        {
                            Console.WriteLine($"准备连接plc {device}");
                            Link(plc);
                        }
                    }
                    else
                    {
                        Console.WriteLine($"plc {device}不存在");
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"ReadBit,device={device}  addr={byteAddr} bit={bitAddr} err={ex.Message}");
                LogHelper.Error($"ReadBit,device={device}  addr={byteAddr} bit={bitAddr} err={ex.Message}", ex);
            }
            return result;
        }
        public static string ReadStr(string device, int db, int byteAddr, int count)
        {
            string result = string.Empty;
            try
            {
                if (debug)
                {
                    var s7Key = $"str_{db}_{byteAddr}_{count}";
                    if (s7TestData.ContainsKey(s7Key))
                    {
                        var data = s7TestData[s7Key];
                        if (data.Length == count)
                        {
                            result = data;
                            Console.WriteLine($"ReadStr 成功, addr={byteAddr}  res={result}");
                        }
                    }
                }
                else
                {
                    if (plc.IsConnected)
                    {
                        result = plc.Read(DataType.DataBlock, 100, byteAddr, VarType.String, count, 0).ToString();
                        Console.WriteLine($"ReadStr 成功,ip={plc.IP} addr={byteAddr}  res={result}");
                        if (result.Length == 0)
                        {
                            Link(plc);
                        }
                    }
                    else
                    {
                        Console.WriteLine("准备连接plc");
                        Link(plc);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"ReadStr,device={device}  addr={byteAddr}  count={count} err={ex.Message}");
                LogHelper.Error($"ReadStr,device={device}  addr={byteAddr}  count={count} err={ex.Message}", ex);
            }
            return result;
        }
        #region 批量写入 读取
        /// <summary>
        /// s7写入取值方法
        /// </summary>
        public static void Write()
        {
            var plcInfo = Settings.linePlcInfo.Where(a => (a.deviceNo == "1") && a.enable == 1).FirstOrDefault();
            //获取配置文件写入的地址
            List<S7Model> writemodels = Settings.plcValue.Find(a => a.address == plcInfo.address).read;
            if (writemodels.Count > 0)
            {
                writemodels.Find(a => a.addr == 0).value = "1";
                writemodels.Find(a => a.addr == 2).value = "2";
                writemodels.Find(a => a.addr == 4).value = "4";
                writemodels.Find(a => a.addr == 18).value = "TP24121108";
                byte[] bytes = GetWriteByte(writemodels);
                if (PLCWrite(plcInfo, 100, bytes))
                {
                    Console.WriteLine("写入成功");
                    //写入成功校验数据
                    Read();
                    if (Dictvalues[plcInfo.address].Find(a => a.addr == 2).value == "2")
                    {
                        Console.WriteLine("数据校验成功,修改任务状态");
                    }
                }
                else
                {
                    Console.WriteLine("写入失败");
                }
            }
        }
        /// <summary>
        /// 将要写的数据转换成byte
        /// </summary>
        /// <param name="writemodels"></param>
        /// <returns></returns>
        public static byte[] GetWriteByte(List<S7Model> writemodels)
        {
            var model = writemodels.OrderByDescending(a => a.addr).First();
            var readlen = model.addr + model.length;
            byte[] bytes = new byte[readlen];
            foreach (var item in writemodels)
            {
                if (!string.IsNullOrEmpty(item.value))
                {
                    //转换byte
                    switch (item.type)
                    {
                        case "Int":
                        case "Bit":
                            bytes[item.addr] = (byte)((uint)(int.Parse(item.value) >> 8) & 0xFFu);
                            bytes[item.addr + 1] = (byte)((uint)(int.Parse(item.value) >> 0) & 0xFFu);
                            break;
                        case "DInt":
                        case "Real":
                            bytes[item.addr] = (byte)((uint)(int.Parse(item.value) >> 24) & 0xFFu);
                            bytes[item.addr + 1] = (byte)((uint)(int.Parse(item.value) >> 16) & 0xFFu);
                            bytes[item.addr + 2] = (byte)((uint)(int.Parse(item.value) >> 8) & 0xFFu);
                            bytes[item.addr + 3] = (byte)((uint)(int.Parse(item.value) >> 0) & 0xFFu);
                            break;
                        case "String":
                        case "Byte":
                            var bytevalue = Encoding.UTF8.GetBytes(item.value);
                            if (bytevalue.Count() <= item.length)
                            {
                                for (int i = 0; i < bytevalue.Count(); i++)
                                {
                                    bytes[item.addr + item.length - bytevalue.Count() + i] = bytevalue[i];
                                }
                            }
                            break;
                    }
                }
            }
            return bytes;
        }
        public static Dictionary<string, List<S7Model>> Dictvalues = new Dictionary<string, List<S7Model>>();
        /// <summary>
        /// s7读取方法
        /// </summary>
        public static void Read()
        {
            var plcInfo = Settings.linePlcInfo.Where(a => (a.deviceNo == "1") && a.enable == 1).FirstOrDefault();
            var bytes = new byte[0];
            //获取配置文件读取的地址
            List<S7Model> readmodels = Settings.plcValue.Find(a => a.address == plcInfo.address).read;
            if (readmodels.Count > 0)
            {
                //获取读取长度
                var model = readmodels.OrderByDescending(a => a.addr).First();
                var readlen = model.addr + model.length;
                PLCRead(plcInfo, 100, 0, readlen, out bytes);
                //将配置文件配置的偏移量转换存放到字典里
                // List<S7Value> s7Values = new List<S7Value>();
                foreach (var item in readmodels)
                {
                    //解析数据
                    string value = "";
                    switch (item.type)
                    {
                        case "Int":
                            value = GetInt(bytes, item.addr, VarType.Int).ToString();
                            break;
                        case "DInt":
                            value = GetInt(bytes, item.addr, VarType.DInt).ToString();
                            break;
                        case "Real":
                            value = GetInt(bytes, item.addr, VarType.Real).ToString();
                            break;
                        case "String":
                            value = GetString(bytes, item.addr, VarType.String, item.length).ToString();
                            break;
                            //case "Byte":
                            //    value = S7Help.GetInt(bytes, item.addr, VarType.Byte,item.length).ToString();
                            //    break;
                    }
                    item.value = value;
                    // s7Values.Add(new S7Value { addr = item.addr, type = item.type, value = value });
                }
                //数据存入字典
                if (Dictvalues.Keys.Contains(plcInfo.address))
                {
                    Dictvalues[plcInfo.address] = readmodels;
                }
                else
                {
                    Dictvalues.Add(plcInfo.address, readmodels);
                }
            }
        }
        /// <summary>
        /// S7写入byte数组
        /// </summary>
        /// <param name="plcInfo">配置文件</param>
        /// <param name="db">DB块</param>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static bool PLCWrite(Settings.LinePlcInfo plcInfo, int db, byte[] bytes)
        {
            bool result = false;
            var plc = new Plc(CpuType.S71500, plcInfo.address, 0, 1);
            Link(plc);
            try
            {
                result = plc.WriteBytes(DataType.DataBlock, Convert.ToInt16(db), Convert.ToInt16(plcInfo.writeAddr), bytes) == ErrorCode.NoError;
                return result;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        /// <summary>
        /// S7读取byte数组
        /// </summary>
        /// <param name="plcInfo">配置文件</param>
        /// <param name="db">DB块</param>
        /// <param name="addr">读取起始偏移量</param>
        /// <param name="bytes">输出byte</param>
        /// <returns></returns>
        public static bool PLCRead(Settings.LinePlcInfo plcInfo, int db, int addr, int len, out byte[] bytes)
        {
            bool result = false;
            var plc = new Plc(CpuType.S71500, plcInfo.address, 0, 1);
            Link(plc);
            string res = string.Empty;
            bytes = new byte[len];
            try
            {
                if (plcInfo != null)
                {
                    bytes = plc.ReadBytes(DataType.DataBlock, Convert.ToInt16(db), Convert.ToInt16(plcInfo.readAddr + addr), len);
                    var r1 = "new byte[] {" + string.Join(",", bytes) + " }";
                    res = $" B{db} A{addr} L{len} A{r1}";
                }
                else
                {
                    res = " 没设备。";
                }
            }
            catch (Exception ex)
            {
                res = "Err" + ex.Message + ex.StackTrace;
            }
            //  result = plc.WriteBytes(DataType.DataBlock, Convert.ToInt16(1000), Convert.ToInt16(linePlcInfo.writeAddr + 6), bytes) == ErrorCode.NoError;
            return result;
        }
        /// <summary>
        /// 解析堆垛机byte数组int类型
        /// </summary>
        /// <param name="adr">偏移量</param>
        /// <param name="varType">类型</param>
        /// <param name="action">解析失败调取方法单个读取</param>
        /// <returns></returns>
        public static int GetInt(byte[] bytes, int adr, VarType varType, Func<int> action = null)
        {
            var b = -1;
            byte[] by = new byte[0];
            by = bytes;
            if (by.Count() > 0)
            {
                b = GetInt(adr, varType, by);
            }
            if (b == -1 && action != null)
            {
                b = action();
            }
            return b;
        }
        public static int GetInt(int adr, VarType varType, byte[] d, int length = 1)
        {
            //计算类型长度,string类型为可变长度用配置文件定义的长度
            var vlen = VarTypeToByteLength(varType);
            if (varType == VarType.String)
            {
                vlen = length;
            }
            if (adr + vlen > d.Length)
            {
                return -1;
            }
            var adrbyte = d.Skip(adr).Take(vlen).ToArray();
            var obj = ParseBytes(varType, adrbyte, 1, 0);
            return Convert.ToInt32(obj);
        }
        /// <summary>
        /// 解析string类型
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="adr"></param>
        /// <param name="varType"></param>
        /// <param name="count"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        public static string GetString(byte[] bytes, int adr, VarType varType, int count, Func<int> action = null)
        {
            string b = null;
            byte[] by = new byte[0];
            by = bytes;
            if (by.Count() > 0)
            {
                b = GetString(adr, varType, by, count);
            }
            if (string.IsNullOrEmpty(b) && action != null)
            {
                b = action().ToString();
            }
            return b;
        }
        public static string GetString(int adr, VarType varType, byte[] d, int length = 1)
        {
            //计算类型长度,string类型为可变长度用配置文件定义的长度
            var vlen = VarTypeToByteLength(varType);
            if (varType == VarType.String)
            {
                vlen = length;
            }
            if (adr + vlen > d.Length)
            {
                return "";
            }
            var adrbyte = d.Skip(adr).Take(vlen).ToArray();
            adrbyte = adrbyte.Skip(adrbyte.ToList().FindIndex(x => x > 0)).ToArray();
            var obj = ParseBytes(varType, adrbyte, 1, 0);
            return obj.ToString();
        }
        private static int VarTypeToByteLength(VarType varType, int varCount = 1)
        {
            switch (varType)
            {
                case VarType.Bit:
                    return varCount;
                case VarType.Byte:
                    if (varCount >= 1)
                    {
                        return varCount;
                    }
                    return 1;
                case VarType.String:
                    return varCount;
                case VarType.StringEx:
                    return varCount + 2;
                case VarType.Word:
                case VarType.Int:
                case VarType.Timer:
                case VarType.Counter:
                    return varCount * 2;
                case VarType.DWord:
                case VarType.DInt:
                case VarType.Real:
                    return varCount * 4;
                default:
                    return 0;
            }
        }
        private static object ParseBytes(VarType varType, byte[] bytes, int varCount, byte bitAdr = 0)
        {
            if (bytes == null)
            {
                return null;
            }
            switch (varType)
            {
                case VarType.Byte:
                    if (varCount == 1)
                    {
                        return bytes[0];
                    }
                    return bytes;
                case VarType.Word:
                    if (varCount == 1)
                    {
                        return Word.FromByteArray(bytes);
                    }
                    return Word.ToArray(bytes);
                case VarType.Int:
                    if (varCount == 1)
                    {
                        return Int.FromByteArray(bytes);
                    }
                    return Int.ToArray(bytes);
                case VarType.DWord:
                    if (varCount == 1)
                    {
                        return DWord.FromByteArray(bytes);
                    }
                    return DWord.ToArray(bytes);
                case VarType.DInt:
                    if (varCount == 1)
                    {
                        return DInt.FromByteArray(bytes);
                    }
                    return DInt.ToArray(bytes);
                case VarType.Real:
                    if (varCount == 1)
                    {
                        return S7.Net.Types.Double.FromByteArray(bytes);
                    }
                    return S7.Net.Types.Double.ToArray(bytes);
                case VarType.String:
                    return S7.Net.Types.String.FromByteArray(bytes);
                case VarType.StringEx:
                    return StringEx.FromByteArray(bytes);
                case VarType.Timer:
                    if (varCount == 1)
                    {
                        return S7.Net.Types.Timer.FromByteArray(bytes);
                    }
                    return S7.Net.Types.Timer.ToArray(bytes);
                case VarType.Counter:
                    if (varCount == 1)
                    {
                        return Counter.FromByteArray(bytes);
                    }
                    return Counter.ToArray(bytes);
                case VarType.Bit:
                    if (varCount == 1)
                    {
                        if (bitAdr > 7)
                        {
                            return null;
                        }
                        return Bit.FromByte(bytes[0], bitAdr);
                    }
                    return Bit.ToBitArray(bytes);
                default:
                    return null;
            }
        }
        #endregion
        #region 用于模拟测试
        /// <summary>
        /// short类型,一个占2个byte
        /// </summary>
        public class DBWModel
        {
            public int db { get; set; }
            public int byteAddr { get; set; }
            /// <summary>
            /// int类型需要用逗号分开,string不需要
            /// </summary>
            public string value { get; set; }
        }
        /// <summary>
        /// 字符串类型,一个占1个byte
        /// </summary>
        public class DBBModel
        {
            public int db { get; set; }
            public int byteAddr { get; set; }
            public string value { get; set; }
        }
        public class DBXModel
        {
            public int db { get; set; }
            public int byteAddr { get; set; }
            public int bitAddr { get; set; }
            /// <summary>
            /// 1:true 0:false
            /// </summary>
            public int value { get; set; }
        }
        public static void s7SetInt(DBWModel model)
        {
            var data = model.value.Replace(",", "");
            var s7Key = $"int_{model.db}_{model.byteAddr}_{data.Length}";
            if (s7TestData.ContainsKey(s7Key))
            {
                s7TestData[s7Key] = model.value;
            }
            else
            {
                s7TestData.Add(s7Key, model.value);
            }
        }
        internal static void s7SetBit(DBXModel model)
        {
            var s7Key = $"bit_{model.db}_{model.byteAddr}_{model.bitAddr}";
            var value = model.value == 1 ? "1" : "0";
            if (s7TestData.ContainsKey(s7Key))
            {
                s7TestData[s7Key] = value;
            }
            else
            {
                s7TestData.Add(s7Key, value);
            }
        }
        internal static void s7SetStr(DBBModel model)
        {
            var s7Key = $"str_{model.db}_{model.byteAddr}_{model.value.Length}";
            if (s7TestData.ContainsKey(s7Key))
            {
                s7TestData[s7Key] = model.value;
            }
            else
            {
                s7TestData.Add(s7Key, model.value);
            }
        }
    }
    #endregion
}