wcs_server_kate_suzhou/Tools/PlcTool/Siemens/SiemensS7.cs

827 lines
27 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Reflection;
using System.Text;
using HslCommunication;
using HslCommunication.Profinet.Siemens;
using PlcTool.Siemens.Entity;
using PlcTool.Siemens.PLCAttribute;
namespace PlcTool.Siemens;
public class SiemensS7
{
/*
* 作者:菻蔃
*
* 版本时间2023年4月15日
*
*/
private const string tip = "致其他开发者,此组件为收费组件,若您有意使用,请自行联系作者购买授权,擅自使用本公司激活码将承担责任";
/// <summary>
/// 存储PLC连接
/// </summary>
readonly List<SiemensS7Connection> siemensS7Connections = [];
/// <summary>
/// 存储DB块具体在哪个PLC中
/// </summary>
readonly List<PlcDBName> plcDBNames = [];
/// <summary>
/// 操作PLC的类传入PLC类型地址和组件激活码。
/// </summary>
/// <param name="authorizationCode"></param>
/// <exception cref="Exception"></exception>
public SiemensS7(string? authorizationCode = null)
{
if (string.IsNullOrEmpty(authorizationCode))
{
/*致本公司开发者,此为 10.6.1 版本激活码,若激活失败,尝试使用此版本*/
/*致其他开发者,此组件为收费组件,若您有意使用,请自行联系作者购买授权,擅自使用本公司激活码将承担责任*/
authorizationCode = "f562cc4c-4772-4b32-bdcd-f3e122c534e3";
}
bool isAuthorization = Authorization.SetAuthorizationCode(authorizationCode);
if (!isAuthorization)
{
/*这里抛出异常,一定记得提醒开发者注册*/
throw new Exception("组件未激活上述激活码可能会因为版本更新而失效请联系公司获取最新激活码测试时可注释此行未激活将只能使用24小时切记上线前一定记得激活");
}
}
/// <summary>
/// 添加PLC信息
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="plcs"></param>
/// <returns></returns>
public SiemensS7 SetPlcs<T>(List<T> plcs) where T : class
{
if (plcs == null || plcs.Count == 0)
{
throw new Exception("传入数据为空。");
}
Type type = typeof(T);
var properties = type.GetProperties();
/*定义两个存储属性的变量*/
PropertyInfo? plcIdProperty = null;
PropertyInfo? plcIPProperty = null;
PropertyInfo? plcKindProperty = null;
PropertyInfo? rackProperty = null;
PropertyInfo? soltindProperty = null;
/*轮询查找到的属性给变量赋值*/
foreach (var property in properties)
{
var plcIdattributes = property.GetCustomAttributes(typeof(PlcIdAttribute), false);
if (plcIdattributes.Length != 0)
{
plcIdProperty = property;
}
var plcIPattributes = property.GetCustomAttributes(typeof(PlcIPAttribute), false);
if (plcIPattributes.Length != 0)
{
plcIPProperty = property;
}
var plcKindattributes = property.GetCustomAttributes(typeof(PlcKindAttribute), false);
if (plcKindattributes.Length != 0)
{
plcKindProperty = property;
}
var rackattributes = property.GetCustomAttributes(typeof(RackAttribute), false);
if (rackattributes.Length != 0)
{
rackProperty = property;
}
var soltattributes = property.GetCustomAttributes(typeof(SlotAttribute), false);
if (soltattributes.Length != 0)
{
soltindProperty = property;
}
}
/*判断是否有对应的特性*/
if (plcIdProperty == null || plcIPProperty == null || plcKindProperty == null)
{
throw new Exception("未正确识别类中的属性特性,需要同时添加 PlcIdAttribution、PlcIPAttribute 和 PlcKindAttribute 三个特性。");
}
/*添加对应的值*/
foreach (T plc in plcs)
{
var plckind = plcKindProperty.GetValue(plc)!.ToString().ToPlcKind();
var plcip = plcIPProperty.GetValue(plc)!.ToString();
SiemensS7Connection siemensS7Connection = new()
{
PlcId = Convert.ToInt32(plcIdProperty.GetValue(plc)),
SiemensS7 = new SiemensS7Net(plckind, plcip)
};
siemensS7Connection.SiemensS7.Rack = Convert.ToByte(rackProperty!.GetValue(plc));
siemensS7Connection.SiemensS7.Slot = Convert.ToByte(soltindProperty!.GetValue(plc));
siemensS7Connections.Add(siemensS7Connection);
}
return this;
}
/// <summary>
/// 添加PLC的Db地址信息
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dbNames"></param>
/// <returns></returns>
public SiemensS7 SetPlcDB<T>(List<T>? dbNames) where T : class
{
if (dbNames == default || dbNames.Count == 0)
{
throw new Exception("传入的数据为空");
}
Type type = typeof(T);
var properties = type.GetProperties();
/*定义两个存储属性的变量*/
PropertyInfo? plcIdProperty = null;
PropertyInfo? dbNameProperty = null;
PropertyInfo? dbAddressProperty = null;
/*轮询查找到的属性给变量赋值*/
foreach (var property in properties)
{
var plcIdattributes = property.GetCustomAttributes(typeof(PlcIdAttribute), false);
if (plcIdattributes.Length != 0)
{
plcIdProperty = property;
}
var dbNameattributes = property.GetCustomAttributes(typeof(PlcDBNameAttribute), false);
if (dbNameattributes.Length != 0)
{
dbNameProperty = property;
}
var dbAddressattributes = property.GetCustomAttributes(typeof(PlcDBAddressAttribute), false);
if (dbAddressattributes.Length != 0)
{
dbAddressProperty = property;
}
}
/*判断是否有对应的特性*/
if (plcIdProperty == null || dbNameProperty == null || dbAddressProperty == null)
{
throw new Exception("未正确识别类中的属性特性,需要同时添加 PlcIdAttribute、PlcDBNameAttribute、PlcDBAddressAttribute 三个特性。");
}
/*添加对应的值*/
foreach (T dbName in dbNames)
{
PlcDBName plcDBName = new()
{
PlcId = Convert.ToUInt32(plcIdProperty.GetValue(dbName)),
DBName = dbNameProperty.GetValue(dbName)!.ToString(),
};
var dbaddress = dbAddressProperty.GetValue(dbName);
plcDBName.DBAddress = dbaddress == null ? "" : dbaddress.ToString();
plcDBNames.Add(plcDBName);
}
return this;
}
/// <summary>
/// 连接PLC
/// </summary>
/// <remarks>
/// 请使用异步重载方法
/// </remarks>
public SemS7Result ConnectPlcs()
{
if (siemensS7Connections.Count == 0)
{
return new SemS7Result()
{
Success = false,
ErrCode = 999,
Message = "没有设置PLC连接请先调用 SetPlcs 方法。"
};
}
/*连接PLC*/
foreach (SiemensS7Connection siemensS7Connection in siemensS7Connections)
{
if (siemensS7Connection.SiemensS7 == default)
{
continue;
}
siemensS7Connection.SiemensS7.ConnectTimeOut = 2000;
var connectResult = siemensS7Connection.SiemensS7.ConnectServer();
if (!connectResult.IsSuccess)
{
// 只要有一个连接失败,直接返回错误信息
connectResult.Message =
$"PLC{siemensS7Connection.SiemensS7.IpAddress} 连接失败,异常信息:{connectResult.Message}";
return connectResult.ToSemS7Result();
}
}
return new SemS7Result()
{
Success = true,
ErrCode = 0,
Message = "所有PLC连接成功"
};
}
#region PLC数据
/// <summary>
/// 根据名称写入PLC值目前不支持 string
/// 写入前请提前转换好对应的数据格式
/// </summary>
/// <param name="plcDBName"></param>
/// <param name="values"></param>
/// <returns></returns>
public (SemS7Result result, byte[]? bytes) WritePlcWhithName(string plcDBName, params object[] values)
{
if (values.Length == 0)
{
return (new SemS7Result()
{
Success = false,
ErrCode = 997,
Message = $"写入失败,传入的值为空。"
}, default);
}
/*根据名称获取地址*/
(SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName);
if (siemensS7 == null)
{
return (new SemS7Result()
{
Success = false,
ErrCode = 999,
Message = errText
}, default);
}
(Exception? Ex, byte[]? bytes) getByte = GetPlcBytes(values);
if (getByte.Ex != null)
{
return (new SemS7Result()
{
Success = false,
ErrCode = 996,
Message = $"{getByte.Ex.Message}"
}, default);
}
byte[]? bytes = getByte.bytes;//获取转化成功的数组
OperateResult operateResult = siemensS7.Write(dbAddress, bytes);
return (operateResult.ToSemS7Result(), bytes);
}
/// <summary>
/// 写入bool值
/// </summary>
/// <param name="plcDBName"></param>
/// <param name="values"></param>
/// <returns></returns>
public SemS7Result WriteBoolWhithName(string plcDBName, params bool[] values)
{
if (values.Length == 0)
{
return new SemS7Result()
{
Success = false,
ErrCode = 997,
Message = $"写入失败,传入的值为空。"
};
}
/*根据名称获取地址*/
(SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName);
if (siemensS7 == null)
{
return new SemS7Result()
{
Success = false,
ErrCode = 999,
Message = errText
};
}
OperateResult operateResult = siemensS7.Write(dbAddress, values);
return operateResult.ToSemS7Result();
}
/// <summary>
/// 根据地址写入值
/// </summary>
/// /// <param name="plcNo"></param>
/// <param name="plcDBAddress"></param>
/// <param name="values"></param>
/// <returns></returns>
public SemS7Result WritePlcWhithAddress(int plcNo, string plcDBAddress, params object[] values)
{
if (values.Length == 0)
{
return new SemS7Result()
{
Success = false,
ErrCode = 997,
Message = $"写入失败,传入的值为空。"
};
}
/*根据名称获取地址*/
SiemensS7Net? siemensS7 = GetSiemensS7(plcNo);
if (siemensS7 == default)
{
return new SemS7Result()
{
Success = false,
ErrCode = 999,
Message = "找不到该PLC连接"
};
}
(Exception? Ex, byte[]? bytes) getByte = GetPlcBytes(values);
if (getByte.Ex != null)
{
return new SemS7Result()
{
Success = false,
ErrCode = 996,
Message = $"{getByte.Ex.Message}"
};
}
byte[]? bytes = getByte.bytes;//获取转化成功的数组
OperateResult operateResult = siemensS7.Write(plcDBAddress, bytes);
return operateResult.ToSemS7Result();
}
/// <summary>
/// 根据名称写入PLC值写入string
/// 写入前请提前转换好对应的数据格式
/// </summary>
/// <param name="plcDBName"></param>
/// <param name="value"></param>
/// <param name="encoding">自定的编码信息一般System.Text.Encoding.ASCII即可中文需要 Encoding.GetEncoding("gb2312")</param>
/// <returns></returns>
public SemS7Result WriteStringWhithName(string plcDBName, string value, Encoding encoding)
{
(SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName);
if (siemensS7 == null)
{
return new SemS7Result()
{
Success = false,
ErrCode = 999,
Message = errText
};
}
return siemensS7.Write(dbAddress, value, encoding).ToSemS7Result();
}
/// <summary>
/// 读取一个Byte数组
/// </summary>
/// <param name="plcDBName"></param>
/// <param name="length"></param>
/// <returns></returns>
public SemS7Result<byte[]> ReadByteWithName(string plcDBName, ushort length)
{
(SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName);
if (siemensS7 == null)
{
return new SemS7Result<byte[]>()
{
Success = false,
ErrCode = 999,
Message = errText
};
}
return siemensS7.Read(dbAddress, length).ToSemS7Result();
}
/// <summary>
/// 读取一个Byte
/// </summary>
/// <param name="plcDBName"></param>
/// <returns></returns>
public SemS7Result<byte> ReadByteWithName(string plcDBName)
{
(SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName);
if (siemensS7 == null)
{
return new SemS7Result<byte>()
{
Success = false,
ErrCode = 999,
Message = errText
};
}
SemS7Result<byte[]> actionResult = ReadByteWithName(dbAddress!, 1);
if (!actionResult.Success)
{
return new SemS7Result<byte>()
{
Success = false,
ErrCode = actionResult.ErrCode,
Message = actionResult.Message
};
}
var values = actionResult.Value;
if (values == default || values.Length == 0)
{
return new SemS7Result<byte>()
{
Success = false,
ErrCode = actionResult.ErrCode,
Message = actionResult.Message
};
}
return new SemS7Result<byte>()
{
Success = true,
ErrCode = actionResult.ErrCode,
Message = actionResult.Message,
Value = values[0]
};
}
/// <summary>
/// 根据名称读取一定数量的Int16值
/// 对应PLC数据类型为 W,int,
/// </summary>
/// <param name="plcDBName"></param>
/// <param name="length"></param>
/// <returns></returns>
public SemS7Result<short[]> ReadInt16WithName(string plcDBName, ushort length)
{
(SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName);
if (siemensS7 == null)
{
return new SemS7Result<short[]>()
{
Success = false,
ErrCode = 999,
Message = errText
};
}
return siemensS7.ReadInt16(dbAddress, length).ToSemS7Result();
}
/// <summary>
/// 根据名称读取一个Int16值
/// 对应PLC数据类型为 W,int,
/// </summary>
/// <param name="plcDBName"></param>
/// <returns></returns>
public SemS7Result<short> ReadInt16WithName(string plcDBName)
{
(SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName);
if (siemensS7 == null)
{
return new SemS7Result<short>()
{
Success = false,
ErrCode = 999,
Message = errText
};
}
return siemensS7.ReadInt16(dbAddress).ToSemS7Result();
}
/// <summary>
/// 根据名称读取一定数量的Int32值
/// 对应PLC数据类型为 DW,Dint,
/// </summary>
/// <param name="plcDBName"></param>
/// <param name="length"></param>
/// <returns></returns>
public SemS7Result<int[]> ReadInt32WithName(string plcDBName, ushort length)
{
(SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName);
if (siemensS7 == null)
{
return new SemS7Result<int[]>()
{
Success = false,
ErrCode = 999,
Message = errText
};
}
return siemensS7.ReadInt32(dbAddress, length).ToSemS7Result();
}
/// <summary>
/// 根据名称读取一个Int32值
/// 对应PLC数据类型为 DW,Dint,
/// </summary>
/// <param name="plcDBName"></param>
/// <returns></returns>
public SemS7Result<int> ReadInt32WithName(string plcDBName)
{
(SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName);
if (siemensS7 == null)
{
return new SemS7Result<int>()
{
Success = false,
ErrCode = 999,
Message = errText
};
}
return siemensS7.ReadInt32(dbAddress).ToSemS7Result();
}
/// <summary>
/// 根据名称读取一定数量的bool值
/// 对应PLC数据类型为 X
/// </summary>
/// <param name="plcDBName"></param>
/// /// <param name="length"></param>
/// <returns></returns>
public SemS7Result<bool[]> ReadBoolWithName(string plcDBName, ushort length)
{
(SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName);
if (siemensS7 == null)
{
return new SemS7Result<bool[]>()
{
Success = false,
ErrCode = 999,
Message = errText
};
}
return siemensS7.ReadBool(dbAddress, length).ToSemS7Result();
}
/// <summary>
/// 根据名称读取一个Int32值
/// 对应PLC数据类型为 X
/// </summary>
/// <param name="plcDBName"></param>
/// <returns></returns>
public SemS7Result<bool> ReadBoolWithName(string plcDBName)
{
(SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName);
if (siemensS7 == null)
{
return new SemS7Result<bool>()
{
Success = false,
ErrCode = 999,
Message = errText
};
}
return siemensS7.ReadBool(dbAddress).ToSemS7Result();
}
/// <summary>
/// 根据名称读取string值
/// 对应PLC数据类型为 string
/// </summary>
/// <param name="plcDBName"></param>
/// <param name="length"></param>
/// <param name="encoding">自定的编码信息一般System.Text.Encoding.ASCII即可中文需要 Encoding.GetEncoding("gb2312")</param>
/// <returns></returns>
public SemS7Result<string> ReadStringWithName(string plcDBName, ushort length, Encoding encoding)
{
(SiemensS7Net? siemensS7, string? dbAddress, string? errText) = GetSiemensS7(plcDBName);
if (siemensS7 == null)
{
return new SemS7Result<string>()
{
Success = false,
ErrCode = 999,
Message = errText
};
}
return siemensS7.ReadString(dbAddress, length, encoding).ToSemS7Result();
}
/// <summary>
/// 根据DB名称查找其对应的连接
/// </summary>
/// <param name="dbName"></param>
/// <returns></returns>
private (SiemensS7Net? siemensS7, string? dbAddress, string? errText) GetSiemensS7(string dbName)
{
if (plcDBNames.Count == 0 || siemensS7Connections.Count == 0)
{
return (null, null, "未设置 plc 或者 未设置 DB地址请调用 SetPlcs 方法设置 plc 或者 调用 SetPlcDB 方法设置 DB地址");
}
/*找出该地址对应的ID*/
PlcDBName? plcDBName = plcDBNames.Find(f => f.DBName == dbName);
if (plcDBName == default)
{
return (null, null, "该DB地址不存在请核实wcs数据录入是否正确");
}
/*找出该ID对应的连接*/
SiemensS7Connection? siemensS7Connection = siemensS7Connections.Find(f => f.PlcId == plcDBName.PlcId);
if (siemensS7Connection == default)
{
return (null, null, "该PLC连接不存在请核实wcs数据录入是否正确");
}
return (siemensS7Connection.SiemensS7, plcDBName.DBAddress, null);
}
/// <summary>
/// 根据plcNo 返回连接信息
/// </summary>
/// <param name="plcNo"></param>
/// <returns></returns>
private SiemensS7Net? GetSiemensS7(int plcNo)
{
/*找出该ID对应的连接*/
SiemensS7Connection? siemensS7Connection = siemensS7Connections.Find(f => f.PlcId == plcNo);
if (siemensS7Connection == default)
{
return default;
}
return siemensS7Connection.SiemensS7;
}
#endregion
//NetworkDoubleBase dataTransfrom = new NetworkDoubleBase();
private readonly SiemensS7Net dataTransfrom = new(SiemensPLCS.S1500);
#region byte
/// <summary>
/// 将数组转化为值
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="bytes"></param>
/// <param name="index"></param>
/// <returns></returns>
public object? Trans<T>(byte[] bytes, int index) where T : struct
{
Type type = typeof(T);
if (type == typeof(short))
{
return dataTransfrom.ByteTransform.TransInt16(bytes, index);
}
else if (type == typeof(int))
{
return dataTransfrom.ByteTransform.TransInt32(bytes, index);
}
else if (type == typeof(bool))
{
return dataTransfrom.ByteTransform.TransBool(bytes, index);
}
else if (type == typeof(double))
{
return dataTransfrom.ByteTransform.TransDouble(bytes, index);
}
else if (type == typeof(float))
{
return dataTransfrom.ByteTransform.TransSingle(bytes, index);
}
else if (type == typeof(uint))
{
return dataTransfrom.ByteTransform.TransUInt32(bytes, index);
}
else if (type == typeof(ushort))
{
return dataTransfrom.ByteTransform.TransUInt16(bytes, index);
}
else
{
return null; // 不支持的数据类型
}
}
#endregion
#region byte
/// <summary>
/// 将传入的值转化成byte数组
/// </summary>
/// <param name="values"></param>
/// <returns></returns>
public (Exception? Ex, byte[]? bytes) GetPlcBytes(object[] values)
{
List<byte> bytes = [];
foreach (object value in values)
{
Type type = value.GetType();
if (type == typeof(short))
{
short va = Convert.ToInt16(value);
byte[] data = dataTransfrom.ByteTransform.TransByte(va);
bytes.AddRange(data);
}
else if (type == typeof(int))
{
int va = Convert.ToInt32(value);
byte[] data = dataTransfrom.ByteTransform.TransByte(va);
bytes.AddRange(data);
}
else if (type == typeof(bool))
{
bool va = Convert.ToBoolean(value);
byte[] data = dataTransfrom.ByteTransform.TransByte(va);
bytes.AddRange(data);
}
else if (type == typeof(double))
{
double va = Convert.ToDouble(value);
byte[] data = dataTransfrom.ByteTransform.TransByte(va);
bytes.AddRange(data);
}
else if (type == typeof(float))
{
float va = Convert.ToSingle(value);
byte[] data = dataTransfrom.ByteTransform.TransByte(va);
bytes.AddRange(data);
}
else if (type == typeof(uint))
{
uint va = Convert.ToUInt32(value);
byte[] data = dataTransfrom.ByteTransform.TransByte(va);
bytes.AddRange(data);
}
else if (type == typeof(ushort))
{
ushort va = Convert.ToUInt16(value);
byte[] data = dataTransfrom.ByteTransform.TransByte(va);
bytes.AddRange(data);
}
else
{
return (new Exception($"传入的数据中有不支持的数据类型 {type.Name} "), null);
}
}
return (null, [.. bytes]);
}
#endregion
#region
/// <summary>
/// 根据名称和偏移量返回地址
/// </summary>
/// <param name="dbName"></param>
/// <param name="moveBit"></param>
/// <returns></returns>
public string GetAddressWithNameAndBit(string dbName, int moveBit)
{
/*
* 1、判断有几个点
* 有一个点的不是bool值
* 有两个点的是bool值
* 必须写全如DB100等价于DB100.0但不允许写DB100因为容易识别错误
*/
if (plcDBNames.Count == 0)
{
return string.Empty; // 没有注册DB地址
}
/*找出该地址对应的ID*/
PlcDBName? plcDBName = plcDBNames.Find(f => f.DBName == dbName);
if (plcDBName == default)
{
return string.Empty; // 找不到DB地址
}
string startDBAddress = plcDBName.DBAddress ?? string.Empty;
string[] dbInfos = startDBAddress.Split('.');
if (dbInfos.Length == 2)
{
// 只有一个点判断为非Bool值
int startPoint = Convert.ToInt32(dbInfos[1]);
string newDBAddress = $"{dbInfos[0]}.{startPoint + moveBit}";
return newDBAddress;
}
else if (dbInfos.Length == 3)
{
// 有两个点判断为bool值
int bigAddress = Convert.ToInt32(dbInfos[1]);
int smallAddress = Convert.ToInt32(dbInfos[2]);
while (moveBit > 0)
{
smallAddress++;
if (smallAddress >= 8)
{
bigAddress++;
smallAddress = 0;
}
moveBit--;
}
return $"{dbInfos[0]}.{bigAddress}.{smallAddress}";
}
return string.Empty;
}
#endregion
}