SiemensPLCService.cs 6.37 KB
using AutomaticGrooveCalculationTool.DataAccess;
using AutomaticGrooveCalculationTool.Model;
using Microsoft.EntityFrameworkCore;
using S7.Net;
using S7.Net.Types;
using System.Text;
using System.Text.RegularExpressions;

namespace AutomaticGrooveCalculationTool.Services
{
    public class SiemensPLCService
    {
        public readonly List<Plc> Plcs;

        public readonly List<Equipment> Equipments;
        private readonly ILogger<SiemensPLCService> _logger;
        private readonly IDbContextFactory<DataContext> _dbContextFactory;

        public SiemensPLCService(ILogger<SiemensPLCService> logger, IDbContextFactory<DataContext> dbContextFactory)
        {
            using var context = dbContextFactory.CreateDbContext();
            var result = context.Database.EnsureCreated();
            Equipments = context.Equipment.ToList();
            Plcs = Equipments.DistinctBy(x => x.IP).Select(x => new Plc(x.CpuType, x.IP, 0, x.CpuType switch
            {
                CpuType.S7300 or CpuType.S7400 => 2,
                _ => 0
            })).ToList();
            _logger = logger;
            _dbContextFactory = dbContextFactory;
        }

        /// <summary>
        /// 读取PLC数据
        /// </summary>
        /// <param name="equipmentProps"></param>
        /// <remarks><see cref="EquipmentProp"/>需要关联设备</remarks>
        public void Read(IEnumerable<EquipmentProp> equipmentProps)
        {
            const int limit = 10;
            foreach (var item in equipmentProps.GroupBy(x => x.Equipment.IP))
            {
                var plc = Plcs.Find(x => x.IP == item.Key) ?? throw new Exception($"未找到IP为{item.Key}的Plc对象");
                for (int i = 0; i < item.Count(); i += limit)
                {
                    var readItems = item.Skip(i).Take(limit).Select(ConvertToDataItem).ToList();
                    plc.ReadMultipleVars(readItems);
                    foreach (var dataItem in readItems)
                    {
                        var index = readItems.IndexOf(dataItem);
                        readItems[index].Value = dataItem.Value?.ToString() ?? string.Empty;
                    }
                }
            }
        }

        /// <summary>
        /// 将数据写入PLC
        /// </summary>
        /// <param name="equipmentProps"></param>
        /// <remarks><see cref="EquipmentProp"/>需要关联设备</remarks>
        public void Write(IEnumerable<EquipmentProp> equipmentProps)
        {
            const int limit = 10;
            foreach (var item in equipmentProps.GroupBy(x => x.Equipment.IP))
            {
                var plc = Plcs.Find(x => x.IP == item.Key) ?? throw new Exception($"未找到IP为{item.Key}的Plc对象");
                for (int i = 0; i < item.Count(); i += limit)
                {
                    var writeItems = item.Skip(i).Take(limit).ToList();
                    plc.Write(writeItems.Select(ConvertToDataItem).ToArray());
                }
            }
        }

        private DataItem ConvertToDataItem(EquipmentProp equipmentProp)
        {
            DataType dataType;
            if (equipmentProp.Address.StartsWith("I", StringComparison.OrdinalIgnoreCase))
            {
                dataType = DataType.Input;
            }
            else if (equipmentProp.Address.StartsWith("O", StringComparison.OrdinalIgnoreCase))
            {
                dataType = DataType.Output;
            }
            else if (equipmentProp.Address.StartsWith("M", StringComparison.OrdinalIgnoreCase))
            {
                dataType = DataType.Memory;
            }
            else if (equipmentProp.Address.StartsWith("DB", StringComparison.OrdinalIgnoreCase))
            {
                dataType = DataType.DataBlock;
            }
            else if (equipmentProp.Address.StartsWith("T", StringComparison.OrdinalIgnoreCase))
            {
                dataType = DataType.Timer;
            }
            else
            {
                throw new Exception($"Id:{equipmentProp.Id}地址{equipmentProp.Address}地址格式不正确!");
            }

            var regex = new Regex("[0-9]+");
            var stringArray = equipmentProp.Address.Split('.');
            _ = int.TryParse(regex.Match(stringArray[0]).Value, out int db);
            int startByteAdr = 0;
            byte bitAdr = 0;
            if (stringArray.Length == 2)
            {
                _ = int.TryParse(regex.Match(stringArray[1]).Value, out startByteAdr);
            }
            else if (stringArray.Length == 3)
            {
                _ = int.TryParse(regex.Match(stringArray[1]).Value, out startByteAdr);
                _ = byte.TryParse(regex.Match(stringArray[2]).Value, out bitAdr);
            }
            else if (stringArray.Length > 3)
            {
                throw new Exception($"属性Id为“{equipmentProp.Id}”的{nameof(equipmentProp.Address)}格式不正确!");
            }

            return new DataItem
            {
                DataType = dataType,
                VarType = equipmentProp.VarType,
                DB = db,
                BitAdr = bitAdr,
                Count = 1,
                StartByteAdr = startByteAdr,
                Value = equipmentProp.VarType switch
                {
                    VarType.Bit => Convert.ToBoolean(equipmentProp.Value),
                    VarType.Byte => Encoding.Default.GetBytes(equipmentProp.Value),
                    VarType.Word => Convert.ToUInt16(equipmentProp.Value),
                    VarType.DWord => Convert.ToUInt32(equipmentProp.Value),
                    VarType.Int => Convert.ToInt16(equipmentProp.Value),
                    VarType.DInt => Convert.ToInt32(equipmentProp.Value),
                    VarType.Real => Convert.ToSingle(equipmentProp.Value),
                    VarType.LReal => Convert.ToDouble(equipmentProp.Value),
                    VarType.String => equipmentProp.Value,
                    VarType.S7String => throw new NotImplementedException(),
                    VarType.S7WString => throw new NotImplementedException(),
                    VarType.Timer => throw new NotImplementedException(),
                    VarType.Counter => throw new NotImplementedException(),
                    VarType.DateTime or VarType.DateTimeLong => Convert.ToDateTime(equipmentProp.Value),
                    _ => throw new NotImplementedException(),
                },
            };
        }
    }
}