PLCHelper.cs 14.6 KB
using RCS.Model.Comm;
using RCS.Model.Entity;
using HslCommunication.Profinet.Siemens;
using HHECS.Communication;
using System.Collections.Concurrent;

namespace RCS.Communication
{
    /// <summary>
    /// PLC操作类
    /// </summary>
    public class PLCHelper
    {
        /// <summary>
        /// SQL异常日志
        /// </summary>
        public static Action<string, string, bool> PLCHelperLog;

        /// <summary>
        /// 开启并行读取的限制数目,于此相关的均需要实际测试性能情况。
        /// </summary>
        const int ParaInt = 1;

        /// <summary>
        /// 读写锁,因为里面已经是多线程处理了,不许外部再进行多线程调用
        /// </summary>
        public object Lock { get; set; } = new object();

        /// <summary>
        /// 西门子PLC的处理封装
        /// </summary>
        public List<IEquipmentCommunication> PLCList = new();

        /// <summary>
        /// PLC操作类的单例
        /// </summary>
        private static readonly PLCHelper plcHelper = new PLCHelper();

        /// <summary>
        /// 不允许外部直接实例化
        /// </summary>
        private PLCHelper() { }

        /// <summary>
        /// 单例模式来获取实例
        /// </summary>
        public static PLCHelper Instance { get { return plcHelper; } }

        /// <summary>
        /// 设备初始化
        /// </summary>
        /// <param name="equipmentList"></param>
        /// <param name="equipmentPropList"></param>
        /// <returns></returns>
        public BllResult<List<Base_Equipment>> InitEquipment(List<Base_Equipment> equipmentList, List<Base_EquipmentProp> equipmentPropList)
        {
            try
            {
                var equipments = new List<Base_Equipment>();
                //初始化属性,包括偏移量计算,占用字节计算,属性对应的设备等
                foreach (var equipmentProp in equipmentPropList)
                {
                    if (!Enum.TryParse(equipmentProp.DataType, out PLCDataType plcDataType))
                    {
                        return BllResult<List<Base_Equipment>>.Error($"初始化设备信息失败,设备的【{equipmentProp.Equipment.Name}】设备属性【{equipmentProp.Code}】数据类型【{equipmentProp.DataType}】不支持!");
                    }
                    var equipment = equipmentList.FirstOrDefault(t => t.Code == equipmentProp.EquipmentCode);
                    if (equipment == null)
                    {
                        return BllResult<List<Base_Equipment>>.Error($"初始化设备信息失败,找不到属性对应的设备【{equipmentProp.EquipmentCode}】,请检查基础数据");
                    }
                    equipmentProp.Equipment = equipment;
                    equipmentProp.EquipmentName = equipmentProp.Equipment.Name;
                    equipmentProp.BatchAddress = equipmentProp.Address;
                    //计算数据占用的字节数
                    if (!SiemensPLC.CheckByteSize(plcDataType, equipmentProp.ByteSize))
                    {
                        return BllResult<List<Base_Equipment>>.Error($"初始化设备信息失败,设备的【{equipmentProp.Equipment.Name}】的属性【{equipmentProp.Address}】对应的字节长度不对【{equipmentProp.ByteSize}】,请检查基础数据");
                    }
                    //如果是BOOL类型要计算位移量,批量读取后要做位移运算来取值
                    //if (plcDataType == PLCDataType.BOOL)
                    //{
                    //    var AddressArray = equipmentProp.Address.Split('.');
                    //    if (AddressArray.Count() != 3)
                    //    {
                    //        return BllResult<List<Base_Equipment>>.Error($"初始化失败,设备的【{equipmentProp.Equipment.Name}】的属性【{equipmentProp.Code}】的地址【{equipmentProp.Address}】格式不对,Bool类型的地址应该只有2个英文句号!");
                    //    }
                    //    equipmentProp.BitOffset = Convert.ToInt32(AddressArray[2]);
                    //    if (equipmentProp.BitOffset < 0 || equipmentProp.BitOffset > 7)
                    //    {
                    //        return BllResult<List<Base_Equipment>>.Error($"初始化失败,设备的【{equipmentProp.Equipment.Name}】的属性【{equipmentProp.Code}】的地址【{equipmentProp.Address}】格式不对,偏移位应该就是0-7之间!");
                    //    }
                    //    //由于批量读取时候,是读取整个字节,所以地址不包含.后的位
                    //    equipmentProp.BatchAddress = AddressArray[0] + "." + AddressArray[1];
                    //}
                    //else
                    //{
                    //    var AddressArray = equipmentProp.Address.Split('.');
                    //    if (AddressArray.Count() != 2)
                    //    {
                    //        return BllResult<List<Base_Equipment>>.Error($"初始化失败,设备的【{equipmentProp.Equipment.Name}】的属性【{equipmentProp.Code}】的地址【{equipmentProp.Address}】格式不对,非Bool类型的地址应该只有1个英文句号!");
                    //    }
                    //}
                }

                //初始化设备的属性
                foreach (var equipment in equipmentList)
                {
                    equipment.EquipmentProps = new ConcurrentBag<Base_EquipmentProp>(equipmentPropList.Where(t => t.EquipmentCode == equipment.Code).Reverse().ToList());
                    if (equipment.EquipmentProps.Count == 0)
                    {
                        return BllResult<List<Base_Equipment>>.Error($"初始化设备信息失败,找不到设备【{equipment.Code}】【{equipment.Name}】的属性,请检查基础数据");
                    }
                    equipments.Add(equipment);
                }
                return BllResult<List<Base_Equipment>>.Success(equipments);
            }
            catch (Exception ex)
            {
                var msg = $"初始化设备信息发生异常,原因:{ex.Message},堆栈:{ex.StackTrace}";
                PLCHelperLog?.Invoke("Init", msg, false);
                return BllResult<List<Base_Equipment>>.Error(msg);
            }
        }

        /// <summary>
        /// PLC的初始化,现在只支持西门子。因为这里调用使用的接口,后面需要可以扩展其他的PLC
        /// </summary>
        /// <returns></returns>
        public BllResult InitPLC(List<IEquipmentCommunication> equipmentList)
        {
            try
            {
                ////HSL授权
                //HslCommunication.Authorization.SetAuthorizationCode("b03c5b8e-5d38-45e0-8c8d-d42ae005a0be");
                //按照IP来创建PLC连接
                // var EquipmentGroupList = equipmentList.GroupBy(t => t.Ip).Select(t => t.FirstOrDefault()).ToList();
                //西门子的S7通信
                //foreach (var equipmentItem in EquipmentGroupList)
                //{
                //    if (Enum.TryParse(equipmentItem.PlcType, out SiemensPLCS siemensPLCS))
                //    {
                //        SiemensPLC siemensPLC = new SiemensPLC(siemensPLCS, equipmentItem.Ip);
                //        PLCList.Add(siemensPLC);
                //    }
                //    else
                //    {
                //        var msg = $"PLC类型【{equipmentItem.PlcType}】无法支持!";
                //        PLCHelperLog?.Invoke("PLCHelper", msg);
                //        return BllResult.Error(msg);
                //    }
                //}

                PLCList = equipmentList;
                foreach (var plc in PLCList)
                {
                    //开启长连接,失败也不退出
                    plc.Connect();
                }
                return BllResult.Success();
            }
            catch (Exception ex)
            {
                return BllResult.Error($"PLC初始化出现异常,原因:{ex.Message},堆栈:{ex.StackTrace}");
            }
        }

        /// <summary>
        /// 读取一个PLC的地址
        /// </summary>
        /// <param name="equipmentProp"></param>
        /// <returns></returns>
        public BllResult Reads(Base_EquipmentProp equipmentProp)
        {
            lock (Lock)
            {
                return Reads(new List<Base_EquipmentProp> { equipmentProp });
            }
        }

        /// <summary>
        /// 读取多个PLC的地址
        /// </summary>
        /// <returns></returns>
        public BllResult Reads(List<Base_EquipmentProp> equipmentProps)
        {
            lock (Lock)
            {
                try
                {
                    var equipmentPropGroupList = equipmentProps.GroupBy(t => t.Equipment.Ip).ToList();
                    foreach (var equipmentPropGroup in equipmentPropGroupList)
                    {
                        if (!PLCList.Exists(t => t.IP == equipmentPropGroup.Key))
                        {
                            var msg = $"读取PLC列表错误,未找到IP为{equipmentPropGroup.Key}的PLC的配置";
                            PLCHelperLog?.Invoke("PLCHelper", msg, false);
                            return BllResult.Error(msg);
                        }
                    }
                    if (equipmentPropGroupList.Count > ParaInt)
                    {
                        var taskList = new List<Task<BllResult>>();
                        foreach (var equipmentPropGroup in equipmentPropGroupList)
                        {
                            var equipmentPropList = equipmentPropGroup.ToList();
                            var plc = PLCList.FirstOrDefault(t => t.IP == equipmentPropGroup.Key);
                            taskList.Add(Task.Run<BllResult>(() =>
                            {
                                var temp = plc.Reads(equipmentPropList.ToArray());
                                if (temp.Success)
                                {
                                    return BllResult.Success(temp);
                                }
                                return BllResult.Error(temp);
                            }));
                        }
                        Task.WaitAll(taskList.ToArray());
                        foreach (var task in taskList)
                        {
                            var result = task.Result;
                            if (result == null)
                            {
                                var msg = $"读取单个PLC错误,读取结果返回了null值,请检查PLC读取实现类";
                                PLCHelperLog?.Invoke("PLCHelper", msg, false);
                                return BllResult.Error(msg);
                            }
                            if (!result.IsSuccess)
                            {
                                return result;
                            }
                        }
                        return BllResult.Success();
                    }
                    else
                    {
                        foreach (var equipmentPropGroup in equipmentPropGroupList)
                        {
                            var equipmentPropList = equipmentPropGroup.ToList();
                            var plc = PLCList.FirstOrDefault(t => t.IP == equipmentPropGroup.Key);
                            var result = plc.Reads(equipmentPropList.ToArray());
                            if (!result.Success)
                            {
                                if (!string.IsNullOrEmpty(result.Msg))
                                {
                                    PLCHelperLog?.Invoke("PLCHelper", result.Msg, false);
                                }
                                if (result.Success)
                                {
                                    return BllResult.Success();
                                }
                                return BllResult.Error();
                            }
                        }
                    }
                    return BllResult.Success();

                }
                catch (Exception ex)
                {
                    return BllResult.Error($"读取PLC列表错误,出现异常,原因:{ex.Message},堆栈:{ex.StackTrace}");
                }
            }
        }

        /// <summary>
        /// 写入一个PLC的地址
        /// </summary>
        /// <param name="equipmentProp"></param>
        /// <returns></returns>
        public BllResult<List<string>> Writes(Base_EquipmentProp equipmentProp)
        {
            lock (Lock)
            {
                return Writes(new List<Base_EquipmentProp> { equipmentProp });
            }
        }

        /// <summary>
        /// 写入多个PLC的地址
        /// 注意:写入值不做线程优化处理,请误大量频繁写入
        /// </summary>
        /// <returns></returns>
        public BllResult<List<string>> Writes(List<Base_EquipmentProp> equipmentProps)
        {
            lock (Lock)
            {
                try
                {
                    var addressGroup = equipmentProps.GroupBy(t => t.Equipment.Ip);
                    foreach (var item in addressGroup)
                    {
                        var plc = PLCList.FirstOrDefault(t => t.IP == item.Key);
                        if (plc == null)
                        {
                            var msg = $"写入PLC失败,未找到IP为{item.Key}的PLC的配置";
                            PLCHelperLog?.Invoke("PLCHelper", msg, false);
                            return BllResult<List<string>>.Error(msg);
                        }
                        var plcEquipmentProp = item.ToList();
                        var result = plc.Writes(plcEquipmentProp.ToArray());
                        //记录PLC写入记录到日志中
                        //result.Data.ForEach(t => PLCHelperLog?.Invoke("PLCHelper", t));
                        //if (!result.Success)
                        //{ 
                        //    //如果写入失败,就把值清空,避免后面判断为写入成功
                        //    plcEquipmentProp.ForEach(t => t.Value = "");
                        //    return result;
                        //}
                    }
                    return BllResult<List<string>>.Success();
                }
                catch (Exception ex)
                {
                    return BllResult<List<string>>.Error($"写入PLC列表失败,出现异常,原因:{ex.Message},堆栈:{ex.StackTrace}");
                }
            }
        }




    }
}