添加西门子S7协议插件

This commit is contained in:
2248356998 qq.com
2023-03-21 13:37:17 +08:00
parent 23cfeff685
commit 246aac8ee4
29 changed files with 2196 additions and 62 deletions

View File

@@ -0,0 +1 @@
global using System;

View File

@@ -0,0 +1,226 @@
namespace ThingsGateway.Foundation.Adapter.Siemens
{
public enum S7Area : byte
{
PE = 0x81,
PA = 0x82,
MK = 0x83,
DB = 0x84,
CT = 0x1C,
TM = 0x1D,
AI = 0X06,
AQ = 0x07,
}
/// <summary>
/// 西门子PLC地址数据信息
/// </summary>
public class SiemensAddress : DeviceAddressBase
{
/// <summary>
/// 数据块代码
/// </summary>
public byte DataCode { get; set; }
/// <summary>
/// DB块数据信息
/// </summary>
public ushort DbBlock { get; set; }
public static int CalculateAddressStarted(string address, bool isCounterOrTimer = false)
{
if (address.IndexOf('.') < 0)
{
return isCounterOrTimer ? Convert.ToInt32(address) : Convert.ToInt32(address) * 8;
}
string[] strArray = address.Split('.');
return (Convert.ToInt32(strArray[0]) * 8) + Convert.ToInt32(strArray[1]);
}
public static OperResult<SiemensAddress> ParseFrom(string address)
{
return ParseFrom(address, 0);
}
public static OperResult<SiemensAddress> ParseFrom(string address, int length)
{
SiemensAddress s7AddressData = new SiemensAddress();
try
{
address = address.ToUpper();
s7AddressData.Length = length;
s7AddressData.DbBlock = 0;
if (address.StartsWith("AI"))
{
s7AddressData.DataCode = (byte)S7Area.AI;
if (address.StartsWith("AIX") || address.StartsWith("AIB") || address.StartsWith("AIW") || address.StartsWith("AID"))
{
s7AddressData.AddressStart = CalculateAddressStarted(address.Substring(3));
}
else
{
s7AddressData.AddressStart = CalculateAddressStarted(address.Substring(2));
}
}
else if (address.StartsWith("AQ"))
{
s7AddressData.DataCode = (byte)S7Area.AQ;
if (address.StartsWith("AQX") || address.StartsWith("AQB") || address.StartsWith("AQW") || address.StartsWith("AQD"))
{
s7AddressData.AddressStart = CalculateAddressStarted(address.Substring(3));
}
else
{
s7AddressData.AddressStart = CalculateAddressStarted(address.Substring(2));
}
}
else if (address[0] == 'I')
{
s7AddressData.DataCode = (byte)S7Area.PE;
if (address.StartsWith("IX") || address.StartsWith("IB") || address.StartsWith("IW") || address.StartsWith("ID"))
{
s7AddressData.AddressStart = CalculateAddressStarted(address.Substring(2));
}
else
{
s7AddressData.AddressStart = CalculateAddressStarted(address.Substring(1));
}
}
else if (address[0] == 'Q')
{
s7AddressData.DataCode = (byte)S7Area.PA;
if (address.StartsWith("QX") || address.StartsWith("QB") || address.StartsWith("QW") || address.StartsWith("QD"))
{
s7AddressData.AddressStart = CalculateAddressStarted(address.Substring(2));
}
else
{
s7AddressData.AddressStart = CalculateAddressStarted(address.Substring(1));
}
}
else if (address[0] == 'M')
{
s7AddressData.DataCode = (byte)S7Area.MK;
if (address.StartsWith("MX") || address.StartsWith("MB") || address.StartsWith("MW") || address.StartsWith("MD"))
{
s7AddressData.AddressStart = CalculateAddressStarted(address.Substring(2));
}
else
{
s7AddressData.AddressStart = CalculateAddressStarted(address.Substring(1));
}
}
else if (address[0] == 'D' || address.Substring(0, 2) == "DB")
{
s7AddressData.DataCode = (byte)S7Area.DB;
string[] strArray = address.Split('.');
s7AddressData.DbBlock = address[1] != 'B' ? Convert.ToUInt16(strArray[0].Substring(1)) : Convert.ToUInt16(strArray[0].Substring(2));
string address1 = address.Substring(address.IndexOf('.') + 1);
if (address1.StartsWith("DBX") || address1.StartsWith("DBB") || address1.StartsWith("DBW") || address1.StartsWith("DBD"))
{
address1 = address1.Substring(3);
}
s7AddressData.AddressStart = CalculateAddressStarted(address1);
}
else if (address[0] == 'T')
{
s7AddressData.DataCode = (byte)S7Area.TM;
s7AddressData.AddressStart = CalculateAddressStarted(address.Substring(1), true);
}
else if (address[0] == 'C')
{
s7AddressData.DataCode = (byte)S7Area.CT;
s7AddressData.AddressStart = CalculateAddressStarted(address.Substring(1), true);
}
else
{
if (address[0] != 'V')
{
return new OperResult<SiemensAddress>("不支持的类型");
}
s7AddressData.DataCode = (byte)S7Area.DB;
s7AddressData.DbBlock = 1;
if (address.StartsWith("VB") || address.StartsWith("VW") || address.StartsWith("VD") || address.StartsWith("VX"))
{
s7AddressData.AddressStart = CalculateAddressStarted(address.Substring(2));
}
else
{
s7AddressData.AddressStart = CalculateAddressStarted(address.Substring(1));
}
}
}
catch (Exception ex)
{
return new OperResult<SiemensAddress>(ex.Message);
}
return OperResult.CreateSuccessResult<SiemensAddress>(s7AddressData);
}
public override void Parse(string address, int length)
{
OperResult<SiemensAddress> from = ParseFrom(address, length);
if (!from.IsSuccess)
{
return;
}
AddressStart = from.Content.AddressStart;
Length = from.Content.Length;
DataCode = from.Content.DataCode;
DbBlock = from.Content.DbBlock;
}
/// <inheritdoc />
public override string ToString()
{
if (DataCode == (byte)S7Area.TM)
{
return "T" + AddressStart.ToString();
}
if (DataCode == (byte)S7Area.CT)
{
return "C" + AddressStart.ToString();
}
if (DataCode == (byte)S7Area.AI)
{
return "AI" + GetActualStringAddress(AddressStart);
}
if (DataCode == (byte)S7Area.AQ)
{
return "AQ" + GetActualStringAddress(AddressStart);
}
if (DataCode == (byte)S7Area.PE)
{
return "I" + GetActualStringAddress(AddressStart);
}
if (DataCode == (byte)S7Area.PA)
{
return "Q" + GetActualStringAddress(AddressStart);
}
if (DataCode == (byte)S7Area.MK)
{
return "M" + GetActualStringAddress(AddressStart);
}
return DataCode == (byte)S7Area.DB ? "DB" + DbBlock.ToString() + "." + GetActualStringAddress(AddressStart) : AddressStart.ToString();
}
private static string GetActualStringAddress(int addressStart)
{
return addressStart % 8 == 0 ? (addressStart / 8).ToString() : string.Format("{0}.{1}", addressStart / 8, addressStart % 8);
}
}
}

View File

@@ -0,0 +1,157 @@
using System.Collections.Generic;
namespace ThingsGateway.Foundation.Adapter.Siemens
{
/// <summary>
/// https://github.com/S7NetPlus/s7netplus/blob/develop/S7.Net/Types/DateTime.cs
/// Contains the methods to convert between <see cref="T:System.DateTime"/> and S7 representation of datetime values.
/// </summary>
public static class DateTime
{
/// <summary>
/// The minimum <see cref="T:System.DateTime"/> value supported by the specification.
/// </summary>
public static readonly System.DateTime SpecMinimumDateTime = new System.DateTime(1990, 1, 1);
/// <summary>
/// The maximum <see cref="T:System.DateTime"/> value supported by the specification.
/// </summary>
public static readonly System.DateTime SpecMaximumDateTime = new System.DateTime(2089, 12, 31, 23, 59, 59, 999);
/// <summary>
/// Parses a <see cref="T:System.DateTime"/> value from bytes.
/// </summary>
/// <param name="bytes">Input bytes read from PLC.</param>
/// <returns>A <see cref="T:System.DateTime"/> object representing the value read from PLC.</returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the length of
/// <paramref name="bytes"/> is not 8 or any value in <paramref name="bytes"/>
/// is outside the valid range of values.</exception>
public static System.DateTime FromByteArray(byte[] bytes)
{
return FromByteArrayImpl(bytes);
}
/// <summary>
/// Parses an array of <see cref="T:System.DateTime"/> values from bytes.
/// </summary>
/// <param name="bytes">Input bytes read from PLC.</param>
/// <returns>An array of <see cref="T:System.DateTime"/> objects representing the values read from PLC.</returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the length of
/// <paramref name="bytes"/> is not a multiple of 8 or any value in
/// <paramref name="bytes"/> is outside the valid range of values.</exception>
public static System.DateTime[] ToArray(byte[] bytes)
{
if (bytes.Length % 8 != 0)
throw new ArgumentOutOfRangeException(nameof(bytes), bytes.Length,
$"Parsing an array of DateTime requires a multiple of 8 bytes of input data, input data is '{bytes.Length}' long.");
var cnt = bytes.Length / 8;
var result = new System.DateTime[bytes.Length / 8];
for (var i = 0; i < cnt; i++)
result[i] = FromByteArrayImpl(new ArraySegment<byte>(bytes, i * 8, 8));
return result;
}
private static System.DateTime FromByteArrayImpl(IList<byte> bytes)
{
if (bytes.Count != 8)
throw new ArgumentOutOfRangeException(nameof(bytes), bytes.Count,
$"Parsing a DateTime requires exactly 8 bytes of input data, input data is {bytes.Count} bytes long.");
int DecodeBcd(byte input) => 10 * (input >> 4) + (input & 0b00001111);
int ByteToYear(byte bcdYear)
{
var input = DecodeBcd(bcdYear);
if (input < 90) return input + 2000;
if (input < 100) return input + 1900;
throw new ArgumentOutOfRangeException(nameof(bcdYear), bcdYear,
$"Value '{input}' is higher than the maximum '99' of S7 date and time representation.");
}
int AssertRangeInclusive(int input, byte min, byte max, string field)
{
if (input < min)
throw new ArgumentOutOfRangeException(nameof(input), input,
$"Value '{input}' is lower than the minimum '{min}' allowed for {field}.");
if (input > max)
throw new ArgumentOutOfRangeException(nameof(input), input,
$"Value '{input}' is higher than the maximum '{max}' allowed for {field}.");
return input;
}
var year = ByteToYear(bytes[0]);
var month = AssertRangeInclusive(DecodeBcd(bytes[1]), 1, 12, "month");
var day = AssertRangeInclusive(DecodeBcd(bytes[2]), 1, 31, "day of month");
var hour = AssertRangeInclusive(DecodeBcd(bytes[3]), 0, 23, "hour");
var minute = AssertRangeInclusive(DecodeBcd(bytes[4]), 0, 59, "minute");
var second = AssertRangeInclusive(DecodeBcd(bytes[5]), 0, 59, "second");
var hsec = AssertRangeInclusive(DecodeBcd(bytes[6]), 0, 99, "first two millisecond digits");
var msec = AssertRangeInclusive(bytes[7] >> 4, 0, 9, "third millisecond digit");
var dayOfWeek = AssertRangeInclusive(bytes[7] & 0b00001111, 1, 7, "day of week");
return new System.DateTime(year, month, day, hour, minute, second, hsec * 10 + msec);
}
/// <summary>
/// Converts a <see cref="T:System.DateTime"/> value to a byte array.
/// </summary>
/// <param name="dateTime">The DateTime value to convert.</param>
/// <returns>A byte array containing the S7 date time representation of <paramref name="dateTime"/>.</returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the value of
/// <paramref name="dateTime"/> is before <see cref="P:SpecMinimumDateTime"/>
/// or after <see cref="P:SpecMaximumDateTime"/>.</exception>
public static byte[] ToByteArray(System.DateTime dateTime)
{
byte EncodeBcd(int value)
{
return (byte)((value / 10 << 4) | value % 10);
}
if (dateTime < SpecMinimumDateTime)
throw new ArgumentOutOfRangeException(nameof(dateTime), dateTime,
$"Date time '{dateTime}' is before the minimum '{SpecMinimumDateTime}' supported in S7 date time representation.");
if (dateTime > SpecMaximumDateTime)
throw new ArgumentOutOfRangeException(nameof(dateTime), dateTime,
$"Date time '{dateTime}' is after the maximum '{SpecMaximumDateTime}' supported in S7 date time representation.");
byte MapYear(int year) => (byte)(year < 2000 ? year - 1900 : year - 2000);
int DayOfWeekToInt(DayOfWeek dayOfWeek) => (int)dayOfWeek + 1;
return new[]
{
EncodeBcd(MapYear(dateTime.Year)),
EncodeBcd(dateTime.Month),
EncodeBcd(dateTime.Day),
EncodeBcd(dateTime.Hour),
EncodeBcd(dateTime.Minute),
EncodeBcd(dateTime.Second),
EncodeBcd(dateTime.Millisecond / 10),
(byte) (dateTime.Millisecond % 10 << 4 | DayOfWeekToInt(dateTime.DayOfWeek))
};
}
/// <summary>
/// Converts an array of <see cref="T:System.DateTime"/> values to a byte array.
/// </summary>
/// <param name="dateTimes">The DateTime values to convert.</param>
/// <returns>A byte array containing the S7 date time representations of <paramref name="dateTime"/>.</returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown when any value of
/// <paramref name="dateTimes"/> is before <see cref="P:SpecMinimumDateTime"/>
/// or after <see cref="P:SpecMaximumDateTime"/>.</exception>
public static byte[] ToByteArray(System.DateTime[] dateTimes)
{
var bytes = new List<byte>(dateTimes.Length * 8);
foreach (var dateTime in dateTimes) bytes.AddRange(ToByteArray(dateTime));
return bytes.ToArray();
}
}
}

View File

@@ -0,0 +1,369 @@
using System.Text;
using System.Threading.Tasks;
using ThingsGateway.Foundation.Extension;
namespace ThingsGateway.Foundation.Adapter.Siemens
{
internal partial class SiemensHelper
{
#region
//internal static OperResult<byte[]> AnalysisReadBit(byte[] content)
//{
// int length = 1;
// if (content.Length < 21 || content[20] != 1)
// return new OperResult<byte[]>("数据块长度校验失败");
// byte[] numArray = new byte[length];
// if (content[21] == byte.MaxValue && content[22] == 3)//Bit:3;Byte:4;Counter或者Timer:9
// {
// numArray[0] = content[25];//+4
// }
// else
// {
// return new OperResult<byte[]>((int)content[21] + GetCpuError(content[21]));
// }
// return OperResult.CreateSuccessResult<byte[]>(numArray);
//}
internal static OperResult<byte[]> AnalysisReadByte(byte[] sends, byte[] content)
{
int length = 0;
int itemLen = (sends.Length - 19) / 12;
for (int index = 0; index < itemLen; index++)
{
if (sends[22 + (index * 12)] >= (byte)S7WordLength.Word)
{
length += ((sends[23 + (index * 12)] * 256) + sends[24 + (index * 12)]) * 2;
}
else
{
length += (sends[23 + (index * 12)] * 256) + sends[24 + (index * 12)];
}
}
if (content.Length < 21 || content[20] != itemLen)
{
return new OperResult<byte[]>("数据块长度校验失败");
}
byte[] dataArray = new byte[length];
int index1 = 0;
int dataIndex = 0;
for (int index2 = 21; index2 < content.Length; index2++)
{
if (index2 + 1 < content.Length)
{
int s7len = 0;
if (sends[22 + (index1 * 12)] >= (byte)S7WordLength.Word)
{
s7len = ((sends[23 + (index1 * 12)] * 256) + sends[24 + (index1 * 12)]) * 2;
}
else
{
s7len = (sends[23 + (index1 * 12)] * 256) + sends[24 + (index1 * 12)];
}
if (content[index2] == byte.MaxValue && content[index2 + 1] == 4)//Bit:3;Byte:4;Counter或者Timer:9
{
Array.Copy(content, index2 + 4, dataArray, dataIndex, s7len);
index2 += s7len + 3;
dataIndex += s7len;
index1++;
}
else if (content[index2] == byte.MaxValue && content[index2 + 1] == 9)//Counter或者Timer:9
{
int num = (content[index2 + 2] * 256) + content[index2 + 3];
if (num % 3 == 0)
{
for (int index3 = 0; index3 < num / 3; index3++)
{
Array.Copy(content, index2 + 5 + (3 * index3), dataArray, dataIndex, 2);
dataIndex += 2;
}
}
else
{
for (int index4 = 0; index4 < num / 5; index4++)
{
Array.Copy(content, index2 + 7 + (5 * index4), dataArray, dataIndex, 2);
dataIndex += 2;
}
}
index2 += num + 4;
index1++;
}
else
{
return new OperResult<byte[]>((int)content[index2] + GetCpuError(content[index2]));
}
}
}
return OperResult.CreateSuccessResult(dataArray);
}
internal static OperResult<byte[]> AnalysisWrite(byte[] content)
{
if (content.Length < 22)
{
return new OperResult<byte[]>(content) { Message = "未知错误" };
}
byte err = content[21];
if (err == byte.MaxValue)
{
return new OperResult<byte[]>((int)content[21] + GetCpuError(content[21]));
}
else
{
return OperResult.CreateSuccessResult(content);
}
}
private static string GetCpuError(ushort Error)
{
switch (Error)
{
case 0x05: return "地址超限";
case 0x06: return "返回长度无效";
case 0x07: return "数据大小不匹配";
case 0x0a:
case 0xd209: return "数据块不存在";
case 0x8500: return "超出PDU大小";
case 0xdc01: return "无效的值";
case 0x8104: return "功能不可用";
case 0xd241: return "需要密码";
case 0xd602: return "无效密码";
case 0xd604:
case 0xd605: return "没有设置密码或已清除";
default:
return "未知错误";
};
}
#endregion
#region
internal static OperResult<byte[]> GetReadCommand(SiemensAddress[] siemensAddress)
{
if (siemensAddress == null)
{
return new OperResult<byte[]>("地址为null");
}
int len = siemensAddress.Length <= 19 ? siemensAddress.Length : throw new Exception("读取数量大于19!");
int telegramLen = len * 12 + 19;
int parameterLen = len * 12 + 2;
byte[] numArray = new byte[telegramLen];
Array.Copy(S7_MULRW_HEADER, 0, numArray, 0, S7_MULRW_HEADER.Length);
numArray[2] = (byte)(telegramLen / 256);
numArray[3] = (byte)(telegramLen % 256);
numArray[13] = (byte)(parameterLen / 256);
numArray[14] = (byte)(parameterLen % 256);
numArray[18] = (byte)len;
for (int index = 0; index < len; index++)
{
Array.Copy(S7_MULRD_ITEM, 0, numArray, 19 + (index * 12), S7_MULRD_ITEM.Length);
if (siemensAddress[index].DataCode == (byte)S7WordLength.Counter || siemensAddress[index].DataCode == (byte)S7WordLength.Timer)
{
numArray[22 + (index * 12)] = siemensAddress[index].DataCode;
numArray[23 + (index * 12)] = (byte)(siemensAddress[index].Length / 256);
numArray[24 + (index * 12)] = (byte)(siemensAddress[index].Length % 256);
}
else
{
numArray[22 + (index * 12)] = (byte)S7WordLength.Byte;
numArray[23 + (index * 12)] = (byte)(siemensAddress[index].Length / 256);
numArray[24 + (index * 12)] = (byte)(siemensAddress[index].Length % 256);
}
numArray[25 + (index * 12)] = (byte)(siemensAddress[index].DbBlock / 256U);
numArray[26 + (index * 12)] = (byte)(siemensAddress[index].DbBlock % 256U);
numArray[27 + (index * 12)] = siemensAddress[index].DataCode;
numArray[28 + (index * 12)] = (byte)(siemensAddress[index].AddressStart / 256 / 256 % 256);
numArray[29 + (index * 12)] = (byte)(siemensAddress[index].AddressStart / 256 % 256);
numArray[30 + (index * 12)] = (byte)(siemensAddress[index].AddressStart % 256);
}
return OperResult.CreateSuccessResult(numArray);
}
internal static OperResult<byte[]> GetWriteBitCommand(SiemensAddress address, bool data)
{
int len = 1;
int telegramLen = 16 + 19 + len;//最后的1是写入值的byte数量
int parameterLen = 12 + 2;
byte[] numArray = new byte[telegramLen];
Array.Copy(S7_MULRW_HEADER, 0, numArray, 0, S7_MULRW_HEADER.Length);
numArray[2] = (byte)(telegramLen / 256);
numArray[3] = (byte)(telegramLen % 256);
numArray[13] = (byte)(parameterLen / 256);
numArray[14] = (byte)(parameterLen % 256);
numArray[15] = (byte)((4 + len) / 256);
numArray[16] = (byte)((4 + len) % 256);
numArray[17] = 5;
numArray[18] = (byte)1;
//写入Item与读取大致相同
numArray[19] = (byte)18;
numArray[20] = (byte)10;
numArray[21] = (byte)16;
numArray[22] = (byte)S7WordLength.Bit;
numArray[23] = (byte)(len / 256);
numArray[24] = (byte)(len % 256);
numArray[25] = (byte)(address.DbBlock / 256U);
numArray[26] = (byte)(address.DbBlock % 256U);
numArray[27] = (byte)address.DataCode;
numArray[28] = (byte)(address.AddressStart / 256 / 256);
numArray[29] = (byte)(address.AddressStart / 256);
numArray[30] = (byte)(address.AddressStart % 256);
//后面跟的是写入的数据信息
numArray[31] = 0;
numArray[32] = 3;//Bit:3;Byte:4;Counter或者Timer:9
numArray[33] = (byte)(len / 256);
numArray[34] = (byte)(len % 256);
numArray[35] = (byte)(data ? 1 : 0);
return OperResult.CreateSuccessResult(numArray);
}
internal static OperResult<byte[]> GetWriteByteCommand(SiemensAddress address, byte[] data)
{
int len = data.Length;
int telegramLen = 16 + 19 + len;//最后的1是写入值的byte数量
int parameterLen = 12 + 2;
byte[] numArray = new byte[telegramLen];
Array.Copy(S7_MULRW_HEADER, 0, numArray, 0, S7_MULRW_HEADER.Length);
numArray[2] = (byte)(telegramLen / 256);
numArray[3] = (byte)(telegramLen % 256);
numArray[13] = (byte)(parameterLen / 256);
numArray[14] = (byte)(parameterLen % 256);
numArray[15] = (byte)((4 + len) / 256);
numArray[16] = (byte)((4 + len) % 256);
numArray[17] = 5;
numArray[18] = (byte)1;
//写入Item与读取大致相同
numArray[19] = (byte)18;
numArray[20] = (byte)10;
numArray[21] = (byte)16;
numArray[22] = (byte)S7WordLength.Byte;
numArray[23] = (byte)(len / 256);
numArray[24] = (byte)(len % 256);
numArray[25] = (byte)(address.DbBlock / 256U);
numArray[26] = (byte)(address.DbBlock % 256U);
numArray[27] = (byte)address.DataCode;
numArray[28] = (byte)(address.AddressStart / 256 / 256);
numArray[29] = (byte)(address.AddressStart / 256);
numArray[30] = (byte)(address.AddressStart % 256);
//后面跟的是写入的数据信息
numArray[31] = 0;
numArray[32] = 4;//Bit:3;Byte:4;Counter或者Timer:9
numArray[33] = (byte)(len * 8 / 256);
numArray[34] = (byte)(len * 8 % 256);
data.CopyTo(numArray, 35);
return OperResult.CreateSuccessResult(numArray);
}
#endregion
#region
internal static async Task<OperResult<string>> ReadString(
SiemensS7PLC plc, string address, Encoding encoding)
{
//先读取一次获取长度,再读取实际值
if (plc.CurrentPlc != SiemensEnum.S200Smart)
{
var result1 = await plc.ReadAsync(address, 2);
if (!result1.IsSuccess)
{
return result1.Copy<string>();
}
if (result1.Content[0] == (byte)0 || result1.Content[0] == byte.MaxValue)
{
return new OperResult<string>("在PLC中不是字符串类型");
}
var result2 = await plc.ReadAsync(address, 2 + result1.Content[1]);
if (!result2.IsSuccess)
{
return result2.Copy<string>();
}
else
{
return OperResult.CreateSuccessResult(encoding.GetString(result2.Content, 2, result2.Content.Length - 2));
}
}
else
{
var result1 = await plc.ReadAsync(address, 1);
if (!result1.IsSuccess)
return result1.Copy<string>();
var result2 = await plc.ReadAsync(address, 1 + result1.Content[0]);
if (!result2.IsSuccess)
{
return result2.Copy<string>();
}
else
{
return OperResult.CreateSuccessResult(encoding.GetString(result2.Content, 1, result2.Content.Length - 1));
}
}
}
internal static async Task<OperResult> Write(SiemensS7PLC plc, string address, string value, Encoding encoding)
{
if (value == null)
value = string.Empty;
byte[] inBytes = encoding.GetBytes(value);
//if (encoding == Encoding.Unicode)
// inBytes = inBytes.BytesReverseByWord();
if (plc.CurrentPlc != SiemensEnum.S200Smart)
{
OperResult<byte[]> result = await plc.ReadAsync(address, 2);
if (!result.IsSuccess) return result;
if (result.Content[0] == byte.MaxValue) return new OperResult<string>("在PLC中不是字符串类型");
if (result.Content[0] == 0) result.Content[0] = 254;
if (value.Length > result.Content[0]) return new OperResult<string>("写入值长度超限");
return await plc.WriteAsync(
address,
DataHelper.SpliceArray(new byte[2] { result.Content[0], (byte)value.Length },
inBytes
));
}
return await plc.WriteAsync(address, DataHelper.SpliceArray<byte>(new byte[1]
{
(byte) value.Length
}, inBytes));
}
#endregion
}
public enum S7WordLength : byte
{
Bit = 0x01,
Byte = 0x02,
Char = 0x03,
Word = 0x04,
Int = 0x05,
DWord = 0x06,
DInt = 0x07,
Real = 0x08,
Counter = 0x1C,
Timer = 0x1D,
}
}

View File

@@ -0,0 +1,142 @@
namespace ThingsGateway.Foundation.Adapter.Siemens
{
internal partial class SiemensHelper
{
/// <summary>
/// S7连读写请求头(包含ISO头和COTP头)
/// </summary>
static byte[] S7_MULRW_HEADER = {
0x03,0x00,
0x00,0x1f, // 报文长度(item.len*12+19注意:根据传入读取item数量更改)
0x02,0xf0, 0x80, //COTP信息
0x32, // S7协议ID
0x01, // 类型,请求命令
0x00,0x00, // 冗余识别
0x00,0x01, // 序列号
0x00,0x0e, // parameter长度item.len*12+2注意:根据传入读取item数量更改
0x00,0x00, // Data Length+4 ,写入时填写读取时为0
0x04, // 4 Read Var, 5 Write Var ,注意更改
0x01, // Item数量item.len注意:根据传入读取item数量更改
};
// S7变量多读Item
static byte[] S7_MULRD_ITEM = {
0x12, // Var 规范.
0x0a, // 剩余的字节长度
0x10, // Syntax ID
(byte)S7WordLength.Byte, // 相关的数据长度代码(注意:根据传入的变量更改)
0x00,0x01, // 数据长度 (注意:根据传入的变量更改)
0x00,0x00, // DB编号 (注意:根据传入的变量更改)
0x84, // 数据块类型 (注意:根据传入的变量更改)
0x00,0x00,0x00 // 数据块偏移量 (注意:根据传入的变量更改)
};
// ISO连接请求报文(也包含ISO头和COTP头)
internal static byte[] ISO_CR = {
// TPKT (RFC1006 Header)
0x03, // RFC 1006 ID (3)
0x00, // 保留 0
0x00, // 数据包长度 (整个框架、有效载荷和TPDU包括在内)
0x16, // 数据包长度 (整个框架、有效载荷和TPDU包括在内)
// COTP (ISO 8073 Header)
0x11, // PDU Size Length
0xE0, // CR -连接请求ID
0x00, 0x00, // Dst Reference
0x00, 0x01, // Src Reference
0x00, // Class + Options Flags
//对于S7200/Smart ,下面参数也需要重写
0xC0, // PDU Max Length ID
0x01, 0x0A, // PDU Max Length
0xC1, // Src TSAP Identifier
0x02, // Src TSAP Length (2 bytes)
0x01, 0x00, // Src TSAP (需重写)
0xC2, // Dst TSAP Identifier
0x02, // Dst TSAP Length (2 bytes)
0x01, 0x02 // Dst TSAP (需重写)
};
// ISO连接请求报文(也包含ISO头和COTP头)
internal static byte[] ISO_CR200 = {
// TPKT (RFC1006 Header)
0x03, // RFC 1006 ID (3)
0x00, // 保留 0
0x00, // 数据包长度 (整个框架、有效载荷和TPDU包括在内)
0x16, // 数据包长度 (整个框架、有效载荷和TPDU包括在内)
// COTP (ISO 8073 Header)
0x11, // PDU Size Length
0xE0, // CR -连接请求ID
0x00, 0x00, // Dst Reference
0x00, 0x01, // Src Reference
0x00, // Class + Options Flags
//对于S7200/Smart
0xC1,0x02,
0x4D,0x57, //LOCALTASP
0xC2,
0x02,
0x4D,0x57, //DESTTASP
0xC0,
0x01,0x09
};
// ISO连接请求报文(也包含ISO头和COTP头)
internal static byte[] ISO_CR200SMART = {
// TPKT (RFC1006 Header)
0x03, // RFC 1006 ID (3)
0x00, // 保留 0
0x00, // 数据包长度 (整个框架、有效载荷和TPDU包括在内)
0x16, // 数据包长度 (整个框架、有效载荷和TPDU包括在内)
// COTP (ISO 8073 Header)
0x11, // PDU Size Length
0xE0, // CR -连接请求ID
0x00, 0x00, // Dst Reference
0x00, 0x01, // Src Reference
0x00, // Class + Options Flags
//对于S7200/Smart
0xC1,0x02,
0x10,0x00,//LOCALTASP
0xC2,
0x02,
0x03,0x00,//DESTTASP
0xC0,
0x01,0x0A
};
// PDU获取报文(也包含ISO头和COTP头)
internal static byte[] S7_PN = {
0x03, 0x00, 0x00, 0x19,
0x02, 0xf0, 0x80, // TPKT + COTP
0x32, 0x01, 0x00, 0x00,
//这里对于S7200/Smart需要重写
0x04, 0x00, 0x00, 0x08,
0x00, 0x00, 0xf0, 0x00,
0x00, 0x01, 0x00, 0x01,
0x01,0xE0 // PDU Length Requested 这里默认480字节对于S7200/Smart 960字节
};
// PDU获取报文(也包含ISO头和COTP头)
internal static byte[] S7200_PN = {
0x03, 0x00, 0x00, 0x19,
0x02, 0xf0, 0x80, // TPKT + COTP
0x32, 0x01, 0x00, 0x00,
0x00,0x00,0x00,0x08,
0x00, 0x00, 0xf0, 0x00,
0x00, 0x01, 0x00, 0x01,
0x01,0xE0 // PDU Length Requested 这里默认960字节
};
internal static byte[] S7200SMART_PN = {
0x03, 0x00, 0x00, 0x19,
0x02, 0xf0, 0x80, // TPKT + COTP
0x32, 0x01, 0x00, 0x00,
0xCC,0xC1,0x00,0x08,
0x00, 0x00, 0xf0, 0x00,
0x00, 0x01, 0x00, 0x01,
0x01,0xE0 // PDU Length Requested 这里默认960字节
};
}
}

View File

@@ -0,0 +1,26 @@
namespace ThingsGateway.Foundation.Adapter.Siemens
{
public class SiemensMessage : MessageBase, IMessage
{
public override int HeadBytesLength => 4;
public override bool CheckHeadBytes(byte[] token)
{
HeadBytes = token;
byte[] headBytes = HeadBytes;
if (headBytes == null || headBytes.Length < 4)
BodyLength = 0;
int length = (HeadBytes[2] * 256) + HeadBytes[3] - 4;
if (length < 0)
length = 0;
BodyLength = length;
return HeadBytes != null && HeadBytes[0] == 3 && HeadBytes[1] == 0;
}
protected override void SendBytesThen()
{
}
}
}

View File

@@ -0,0 +1,15 @@
namespace ThingsGateway.Foundation.Adapter.Siemens
{
public enum SiemensEnum
{
S200,
S200Smart,
S300,
S400,
S1200,
S1500,
}
}

View File

@@ -0,0 +1,352 @@
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ThingsGateway.Foundation.Extension;
using TouchSocket.Core;
using TouchSocket.Resources;
using TouchSocket.Sockets;
namespace ThingsGateway.Foundation.Adapter.Siemens
{
/// <summary>
/// 相关命令含义源自网络资料/Shrap7/s7netplus
/// </summary>
public partial class SiemensS7PLC : ReadWriteDevicesTcpClientBase
{
public SiemensS7PLCDataHandleAdapter DataHandleAdapter = new();
private SiemensEnum _currentPlc = SiemensEnum.S1200;
private int pdu_length = 0;
private byte plc_rack = 0;
private byte plc_slot = 0;
private byte[] ISO_CR;
private byte[] S7_PN;
public SiemensEnum CurrentPlc => _currentPlc;
/// <summary>
/// 传入PLC类型程序内会改变相应PLC类型的S7协议LocalTSAP RemoteTSAP等
/// </summary>
/// <param name="tcpClient"></param>
/// <param name="siemensPLCEnum"></param>
public SiemensS7PLC(TcpClient tcpClient, SiemensEnum siemensPLCEnum) : base(tcpClient)
{
_currentPlc = siemensPLCEnum;
RegisterByteLength = 1;
ThingsGatewayBitConverter = new ThingsGatewayBitConverter(EndianType.Big);
ISO_CR = new byte[22];
S7_PN = new byte[25];
Array.Copy(SiemensHelper.ISO_CR, ISO_CR, ISO_CR.Length);
Array.Copy(SiemensHelper.S7_PN, S7_PN, S7_PN.Length);
switch (siemensPLCEnum)
{
case SiemensEnum.S1200:
ISO_CR[21] = 0x00;
break;
case SiemensEnum.S300:
ISO_CR[21] = 0x02;
break;
case SiemensEnum.S400:
ISO_CR[21] = 0x03;
ISO_CR[17] = 0x00;
break;
case SiemensEnum.S1500:
ISO_CR[21] = 0x00;
break;
case SiemensEnum.S200Smart:
ISO_CR = SiemensHelper.ISO_CR200SMART;
S7_PN = SiemensHelper.S7200SMART_PN;
break;
case SiemensEnum.S200:
ISO_CR = SiemensHelper.ISO_CR200;
S7_PN = SiemensHelper.S7200_PN;
break;
}
tcpClient.Connected += Connected;
}
#region
/// <summary>
/// 获取或设置DstTSAP在非200系列时有效
/// </summary>
public byte ConnectionType
{
get => ISO_CR[20];
set
{
if (_currentPlc == SiemensEnum.S200 || _currentPlc == SiemensEnum.S200Smart)
{
return;
}
ISO_CR[20] = value;
}
}
/// <summary>
/// 远程TSAP需重新连接
/// </summary>
public int DestTSAP
{
get
{
return
_currentPlc == SiemensEnum.S200 || _currentPlc == SiemensEnum.S200Smart ?
(ISO_CR[17] * 256) + ISO_CR[18] :
(ISO_CR[20] * 256) + ISO_CR[21];
}
set
{
//TODO:这里优化为ThingsGatewayBitConverter
if (_currentPlc == SiemensEnum.S200 || _currentPlc == SiemensEnum.S200Smart)
{
ISO_CR[17] = BitConverter.GetBytes(value)[1];
ISO_CR[18] = BitConverter.GetBytes(value)[0];
}
else
{
ISO_CR[20] = BitConverter.GetBytes(value)[1];
ISO_CR[21] = BitConverter.GetBytes(value)[0];
}
}
}
/// <summary>
/// 本地TSAP需重新连接
/// </summary>
public int LocalTSAP
{
get
{
return
_currentPlc == SiemensEnum.S200 || _currentPlc == SiemensEnum.S200Smart ?
(ISO_CR[13] * 256) + ISO_CR[14] :
(ISO_CR[16] * 256) + ISO_CR[17];
}
set
{
//TODO:这里优化为ThingsGatewayBitConverter
if (_currentPlc == SiemensEnum.S200 || _currentPlc == SiemensEnum.S200Smart)
{
ISO_CR[13] = BitConverter.GetBytes(value)[1];
ISO_CR[14] = BitConverter.GetBytes(value)[0];
}
else
{
ISO_CR[16] = BitConverter.GetBytes(value)[1];
ISO_CR[17] = BitConverter.GetBytes(value)[0];
}
}
}
public int PDULength => pdu_length;
/// <summary>
/// 机架号,需重新连接
/// </summary>
public byte Rack
{
get => plc_rack;
set
{
plc_rack = value;
if (_currentPlc == SiemensEnum.S200 || _currentPlc == SiemensEnum.S200Smart)
{
return;
}
ISO_CR[21] = (byte)((plc_rack * 0x20) + plc_slot);
}
}
/// <summary>
/// 槽号,需重新连接
/// </summary>
public byte Slot
{
get => plc_slot;
set
{
plc_slot = value;
if (_currentPlc == SiemensEnum.S200 || _currentPlc == SiemensEnum.S200Smart)
{
return;
}
ISO_CR[21] = (byte)((plc_rack * 0x20) + plc_slot);
}
}
#endregion
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken token = default)
{
try
{
await ConnectAsync();
var commandResult = GetReadByteCommand(address, length);
if (commandResult.IsSuccess)
{
List<byte> bytes = new();
foreach (var item in commandResult.Content)
{
var result = TcpClient.GetWaitingClient(new()).SendThenResponse(item, TimeOut, token);
if (result.RequestInfo is MessageBase collectMessage)
{
if (collectMessage.IsSuccess)
bytes.AddRange(collectMessage.Content);
else
return OperResult.CreateFailedResult<byte[]>(collectMessage);
}
}
return bytes.Count > 0 ? OperResult.CreateSuccessResult(bytes.ToArray()) : new OperResult<byte[]>(TouchSocketStatus.UnknownError.GetDescription());
}
else
{
return OperResult.CreateFailedResult<byte[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<byte[]>(ex);
}
finally
{
}
}
/// <summary>
/// 读取变长字符串
/// </summary>
public async Task<OperResult<string>> ReadString(string address, Encoding encoding)
{
return await SiemensHelper.ReadString(this, address, encoding);
}
public async Task<OperResult<System.DateTime>> ReadDateTime(string address)
{
return ByteTransformHelper.GetResultFromBytes(await ReadAsync(address, 8), ThingsGateway.Foundation.Adapter.Siemens.DateTime.FromByteArray);
}
public async Task<OperResult<System.DateTime>> ReadDate(string address)
{
return (await this.ReadAsync(address, 2)).
Then(m => OperResult.CreateSuccessResult(ThingsGateway.Foundation.Adapter.Siemens.DateTime.SpecMinimumDateTime.AddDays(
ThingsGatewayBitConverter.ToUInt16(m, 0)))
);
}
public override void SetDataAdapter()
{
DataHandleAdapter = new();
TcpClient.SetDataHandlingAdapter(DataHandleAdapter);
}
public override Task<OperResult> WriteAsync(string address, string value, Encoding encoding, CancellationToken token = default)
{
return SiemensHelper.Write(this, address, value, encoding);
}
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken token = default)
{
try
{
await ConnectAsync();
var commandResult = GetWriteByteCommand(address, value);
if (commandResult.IsSuccess)
{
List<ResponsedData> bytes = new();
foreach (var item in commandResult.Content)
{
ResponsedData result = TcpClient.GetWaitingClient(new()).SendThenResponse(item, TimeOut, token);
bytes.Add(result);
}
return OperResult.CreateSuccessResult(bytes.ToArray());
}
else
{
return OperResult.CreateFailedResult<bool[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<bool[]>(ex);
}
finally
{
}
}
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken token = default)
{
if (value.Length > 1)
{
return new OperResult("不支持多写");
}
try
{
await ConnectAsync();
var commandResult = GetWriteBitCommand(address, value[0]);
if (commandResult.IsSuccess)
{
var result = TcpClient.GetWaitingClient(new()).SendThenResponse(commandResult.Content, TimeOut, token);
return OperResult.CreateSuccessResult(result);
}
else
{
return OperResult.CreateFailedResult<bool[]>(commandResult);
}
}
catch (Exception ex)
{
return new OperResult<bool[]>(ex);
}
finally
{
}
}
private void Connected(ITcpClient client, MsgEventArgs e)
{
try
{
var result1 = SendThenResponse(ISO_CR);
if (!result1.IsSuccess)
{
Logger?.Error(client.GetIPPort() + "ISO初始化失败");
return;
}
var result2 = SendThenResponse(S7_PN);
if (!result2.IsSuccess)
{
Logger?.Error(client.GetIPPort() + "初始化失败");
return;
}
pdu_length = ThingsGatewayBitConverter.ToUInt16(result2.Content.SelectLast(2), 0);
pdu_length = pdu_length < 200 ? 200 : pdu_length;
}
catch (Exception ex)
{
Logger.Exception(ex);
}
}
public async Task<OperResult> WriteDateTime(string address, System.DateTime dateTime)
{
return await WriteAsync(address, ThingsGateway.Foundation.Adapter.Siemens.DateTime.ToByteArray(dateTime));
}
public async Task<OperResult> WriteDate(string address, System.DateTime dateTime)
{
return await base.WriteAsync(address, Convert.ToUInt16((dateTime - ThingsGateway.Foundation.Adapter.Siemens.DateTime.SpecMinimumDateTime).TotalDays));
}
}
}

View File

@@ -0,0 +1,41 @@
namespace ThingsGateway.Foundation.Adapter.Siemens
{
public class SiemensS7PLCDataHandleAdapter : ReadWriteDevicesTcpDataHandleAdapter<SiemensMessage>
{
public override byte[] PackCommand(byte[] command)
{
return command;
}
protected override void Reset()
{
base.Reset();
}
protected override OperResult<byte[]> UnpackResponse(
byte[] send,
byte[] response)
{
if (response[2] * 256 + response[3] == 7)
{
return new OperResult<byte[]>(response);
}
else
{
//已请求方为准,分开返回类型校验
switch (send[17])
{
case 0x04:
return SiemensHelper.AnalysisReadByte(send, response);
case 0x05:
return SiemensHelper.AnalysisWrite(response);
}
return OperResult.CreateSuccessResult(response);
}
}
protected override SiemensMessage GetInstance()
{
return new SiemensMessage();
}
}
}

View File

@@ -0,0 +1,94 @@
using System.Collections.Generic;
using ThingsGateway.Foundation.Extension;
namespace ThingsGateway.Foundation.Adapter.Siemens
{
public partial class SiemensS7PLC : ReadWriteDevicesTcpClientBase
{
private static OperResult<byte[]> GetWriteBitCommand(string address, bool data)
{
OperResult<SiemensAddress> result = SiemensAddress.ParseFrom(address);
if (!result.IsSuccess)
{
return result.Copy<byte[]>();
}
return SiemensHelper.GetWriteBitCommand(result.Content, data);
}
private OperResult<List<byte[]>> GetReadByteCommand(string address, int length)
{
OperResult<SiemensAddress> from = SiemensAddress.ParseFrom(address, length);
if (!from.IsSuccess) return from.Copy<List<byte[]>>();
ushort num1 = 0;
var listBytes = new List<byte[]>();
while (num1 < length)
{
//pdu长度重复生成报文直至全部生成
ushort num2 = (ushort)Math.Min(length - num1, pdu_length);
from.Content.Length = num2;
var result = GetReadByteCommand(new SiemensAddress[1] { from.Content });
if (!result.IsSuccess) return result.Copy<List<byte[]>>();
listBytes.AddRange(result.Content);
num1 += num2;
if (from.Content.DataCode == (byte)S7WordLength.Timer || from.Content.DataCode == (byte)S7WordLength.Counter)
{
from.Content.AddressStart += num2 / 2;
}
else
{
from.Content.AddressStart += num2 * 8;
}
}
return OperResult.CreateSuccessResult(listBytes);
}
private OperResult<List<byte[]>> GetReadByteCommand(SiemensAddress[] siemensAddress)
{
if (siemensAddress.Length <= 19)
{
return ByteTransformHelper.GetResultFromBytes(SiemensHelper.GetReadCommand(siemensAddress), m => new List<byte[]>() { m });
}
List<byte[]> byteList = new List<byte[]>();
List<SiemensAddress[]> s7AddressDataArrayList = siemensAddress.ArraySplitByLength(19);
for (int index = 0; index < s7AddressDataArrayList.Count; ++index)
{
var result = GetReadByteCommand(s7AddressDataArrayList[index]);
if (!result.IsSuccess)
{
return result;
}
byteList.AddRange(result.Content);
}
return OperResult.CreateSuccessResult(byteList);
}
private OperResult<List<byte[]>> GetWriteByteCommand(string address, byte[] value)
{
return SiemensAddress.ParseFrom(address).Then(m => GetWriteByteCommand(m, value));
}
private OperResult<List<byte[]>> GetWriteByteCommand(SiemensAddress address, byte[] value)
{
int length1 = value.Length;
ushort index = 0;
List<byte[]> bytes = new List<byte[]>();
while (index < length1)
{
//pdu长度重复生成报文直至全部生成
ushort length2 = (ushort)Math.Min(length1 - index, pdu_length);
byte[] data = ThingsGatewayBitConverter.ToByte(value, index, length2);
OperResult<byte[]> result1 = SiemensHelper.GetWriteByteCommand(address, data);
if (!result1.IsSuccess)
{
return result1.Copy<List<byte[]>>();
}
bytes.Add(result1.Content);
index += length2;
address.AddressStart += length2 * 8;
}
return OperResult.CreateSuccessResult(bytes);
}
}
}

View File

@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<LangVersion>latestMajor</LangVersion>
<TargetFrameworks>net462;net6.0;net7.0</TargetFrameworks>
<Version>1.2.0</Version>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\ThingsGateway.Foundation\ThingsGateway.Foundation.csproj" />
</ItemGroup>
</Project>

View File

@@ -10,49 +10,20 @@ namespace ThingsGateway.Foundation.Tests
{
public class Modbus
{
private ITestOutputHelper _output;
TcpClient client;
SerialClient serialClient;
private TouchSocketConfig config;
private ModbusRtuOverTcp RtuTcpClient;
private ModbusRtu RtuClient;
private ModbusRtuOverTcp RtuTcpClient;
private ModbusRtuOverUdp RtuUdpClient;
SerialClient serialClient;
private ModbusTcp TcpClient;
private ModbusUdp UdpClient;
private ITestOutputHelper _output;
public Modbus(ITestOutputHelper output)
{
_output = output;
}
[Fact]
public async Task RtuTcpReadTest()
{
ModbusRtuTcpClient();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 10; i++)
{
string address = $"4{i * 2 + 1};DATA=ABCD;";
var byteConverter = ByteConverterHelper.GetTransByAddress(ref address, RtuTcpClient.ThingsGatewayBitConverter, out int length, out BcdFormat bcdFormat);
var test = await RtuTcpClient.ReadAsync(address, 1);
var data = byteConverter.ToInt16(test.Content, 0);
_output.WriteLine(data.ToJson());
}
for (int i = 100; i < 110; i++)
{
string address = $"4{i * 2 + 1};TEXT=UTF8;LEN=4";
var byteConverter = ByteConverterHelper.GetTransByAddress(ref address, RtuTcpClient.ThingsGatewayBitConverter, out int length, out BcdFormat bcdFormat);
var test = await RtuTcpClient.ReadAsync(address, length / RtuTcpClient.RegisterByteLength);
var data = byteConverter.ToString(test.Content, 0, length);
_output.WriteLine(data.ToJson());
}
stopwatch.Stop();
_output.WriteLine("<22>ܺ<EFBFBD>ʱ<EFBFBD><CAB1>" + stopwatch.Elapsed.TotalSeconds);
}
[Fact]
public async Task RtuReadTest()
{
ModbusRtuClient();
@@ -80,6 +51,36 @@ namespace ThingsGateway.Foundation.Tests
stopwatch.Stop();
_output.WriteLine("<22>ܺ<EFBFBD>ʱ<EFBFBD><CAB1>" + stopwatch.Elapsed.TotalSeconds);
}
[Fact]
public async Task RtuTcpReadTest()
{
ModbusRtuTcpClient();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 10; i++)
{
string address = $"4{i * 2 + 1};DATA=ABCD;";
var byteConverter = ByteConverterHelper.GetTransByAddress(ref address, RtuTcpClient.ThingsGatewayBitConverter, out int length, out BcdFormat bcdFormat);
var test = await RtuTcpClient.ReadAsync(address, 1);
var data = byteConverter.ToInt16(test.Content, 0);
_output.WriteLine(data.ToJson());
}
for (int i = 100; i < 110; i++)
{
string address = $"4{i * 2 + 1};TEXT=UTF8;LEN=4";
var byteConverter = ByteConverterHelper.GetTransByAddress(ref address, RtuTcpClient.ThingsGatewayBitConverter, out int length, out BcdFormat bcdFormat);
var test = await RtuTcpClient.ReadAsync(address, length / RtuTcpClient.RegisterByteLength);
var data = byteConverter.ToString(test.Content, 0, length);
_output.WriteLine(data.ToJson());
}
stopwatch.Stop();
_output.WriteLine("<22>ܺ<EFBFBD>ʱ<EFBFBD><CAB1>" + stopwatch.Elapsed.TotalSeconds);
}
[Fact]
public async Task TcpReadTest()
@@ -102,6 +103,19 @@ namespace ThingsGateway.Foundation.Tests
}
private void ModbusRtuClient()
{
config = new TouchSocketConfig();
config.SetSerialProperty(new() { PortName = "COM6" })
.SetBufferLength(300);
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
serialClient = config.Container.Resolve<SerialClient>();
serialClient.Setup(config);
RtuClient = new(serialClient);
RtuClient.Station = 1;
RtuClient.TimeOut = 5000;
}
private void ModbusRtuTcpClient()
{
config = new TouchSocketConfig();
@@ -154,19 +168,5 @@ namespace ThingsGateway.Foundation.Tests
UdpClient.Station = 1;
UdpClient.TimeOut = 5000;
}
private void ModbusRtuClient()
{
config = new TouchSocketConfig();
config.SetSerialProperty(new() { PortName = "COM6" })
.SetBufferLength(300);
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
serialClient = config.Container.Resolve<SerialClient>();
serialClient.Setup(config);
RtuClient = new(serialClient);
RtuClient.Station = 1;
RtuClient.TimeOut = 5000;
}
}
}

View File

@@ -0,0 +1,67 @@
using System.Diagnostics;
using ThingsGateway.Foundation.Adapter.Siemens;
using Xunit;
using Xunit.Abstractions;
namespace ThingsGateway.Foundation.Tests
{
public class Siemens
{
private ITestOutputHelper _output;
TcpClient client;
private TouchSocketConfig config;
private SiemensS7PLC TcpClient;
public Siemens(ITestOutputHelper output)
{
_output = output;
}
[Fact]
public async Task TcpReadTest()
{
S7TcpClient();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 20; i++)
{
string address = $"M1.{i + 1};";
var byteConverter = ByteConverterHelper.GetTransByAddress(ref address, TcpClient.ThingsGatewayBitConverter, out int length, out BcdFormat bcdFormat);
var test = await TcpClient.ReadAsync(address, 2);
if (test .IsSuccess)
{
var data = byteConverter.ToBoolean(test.Content, 0);
_output.WriteLine(data.ToJson());
}
else
{
_output.WriteLine(test.ToJson());
}
}
stopwatch.Stop();
_output.WriteLine("<22>ܺ<EFBFBD>ʱ<EFBFBD><CAB1>" + stopwatch.Elapsed.TotalSeconds);
}
private void S7TcpClient()
{
config = new TouchSocketConfig();
config.SetRemoteIPHost(new IPHost("127.0.0.1:102"))
.SetBufferLength(300);
//<2F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
client = config.Container.Resolve<TcpClient>();
client.Setup(config);
TcpClient = new(client,SiemensEnum.S1500);
TcpClient.ConnectTimeOut = 5000;
TcpClient.TimeOut = 5000;
}
}
}

View File

@@ -26,6 +26,7 @@
<ProjectReference Include="..\ThingsGateway.Foundation.Adapter.Modbus\ThingsGateway.Foundation.Adapter.Modbus.csproj" />
<ProjectReference Include="..\ThingsGateway.Foundation.Adapter.OPCDA\ThingsGateway.Foundation.Adapter.OPCDA.csproj" />
<ProjectReference Include="..\ThingsGateway.Foundation.Adapter.OPCUA\ThingsGateway.Foundation.Adapter.OPCUA.csproj" />
<ProjectReference Include="..\ThingsGateway.Foundation.Adapter.Siemens\ThingsGateway.Foundation.Adapter.Siemens.csproj" />
</ItemGroup>

View File

@@ -18,8 +18,11 @@
</Target>
<ItemGroup>
<ProjectReference Include="..\..\ThingsGateway.Web.Foundation\ThingsGateway.Web.Foundation.csproj" />
<ProjectReference Include="..\ThingsGateway.Foundation.Adapter.Modbus\ThingsGateway.Foundation.Adapter.Modbus.csproj" />
<ProjectReference Include="..\..\ThingsGateway.Web.Foundation\ThingsGateway.Web.Foundation.csproj" >
<IncludeAssets>Compile</IncludeAssets>
</ProjectReference>
<ProjectReference Include="..\ThingsGateway.Foundation.Adapter.Modbus\ThingsGateway.Foundation.Adapter.Modbus.csproj" >
</ProjectReference>
</ItemGroup>

View File

@@ -0,0 +1,11 @@
global using Microsoft.Extensions.DependencyInjection;
global using System.Net;
global using ThingsGateway.Foundation.Adapter.Siemens;
global using ThingsGateway.Web.Foundation;
global using TouchSocket.Core;
global using TouchSocket.Sockets;

View File

@@ -0,0 +1,87 @@
using System.Text;
using ThingsGateway.Foundation;
namespace ThingsGateway.Siemens
{
public abstract class S7 : DriverBase
{
protected SiemensS7PLC _plc;
protected S7(IServiceScopeFactory scopeFactory) : base(scopeFactory)
{
}
[DeviceProperty("连接超时时间", "")] public ushort ConnectTimeOut { get; set; } = 3000;
[DeviceProperty("默认解析顺序", "")] public DataFormat DataFormat { get; set; }
[DeviceProperty("IP", "")] public string IP { get; set; } = "127.0.0.1";
[DeviceProperty("端口", "")] public int Port { get; set; } = 102;
public override IThingsGatewayBitConverter ThingsGatewayBitConverter { get => _plc?.ThingsGatewayBitConverter; }
[DeviceProperty("读写超时时间", "")] public ushort TimeOut { get; set; } = 3000;
[DeviceMethod("ReadDate", "")]
public Task<OperResult<System.DateTime>> ReadDate(string address)
{
return _plc?.ReadDate(address);
}
[DeviceMethod("ReadString", "")]
public Task<OperResult<string>> ReadString(string address, Encoding encoding)
{
return _plc?.ReadString(address, encoding);
}
[DeviceMethod("WriteDate", "")]
public Task<OperResult> WriteDate(string address, System.DateTime dateTime)
{
return _plc?.WriteDate(address, dateTime);
}
[DeviceMethod("WriteDateTime", "")]
public Task<OperResult> WriteDateTime(string address, System.DateTime dateTime)
{
return _plc?.WriteDateTime(address, dateTime);
}
public override void AfterStop()
{
_plc?.Disconnect();
}
public override async Task BeforStart()
{
await _plc.ConnectAsync();
}
public override void Dispose()
{
_plc.Disconnect();
}
public override bool IsSupportAddressRequest()
{
return true;
}
public override OperResult<List<DeviceVariableSourceRead>> LoadSourceRead(List<CollectVariableRunTime> deviceVariables)
{
Init(null);
_plc.Connect();
var data= deviceVariables.LoadSourceRead(_logger, ThingsGatewayBitConverter, _plc);
_plc?.Disconnect();
return data;
}
public override async Task<OperResult> WriteValueAsync(CollectVariableRunTime deviceVariable, string value)
{
return await _plc.WriteAsync(deviceVariable.DataType, deviceVariable.VariableAddress, value);
}
protected override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken)
{
return await _plc.ReadAsync(address, length);
}
}
}

View File

@@ -0,0 +1,211 @@
using Microsoft.Extensions.Logging;
using ThingsGateway.Foundation;
using ThingsGateway.Foundation.Extension;
namespace ThingsGateway.Siemens
{
internal static class S7Helper
{
internal static OperResult<List<DeviceVariableSourceRead>> LoadSourceRead(this List<CollectVariableRunTime> deviceVariables, ILogger _logger, IThingsGatewayBitConverter byteConverter, SiemensS7PLC siemensS7Net)
{
var result = new List<DeviceVariableSourceRead>();
try
{
//需要先剔除额外信息比如dataformat等
foreach (var item in deviceVariables)
{
var address = item.VariableAddress;
IThingsGatewayBitConverter transformParameter = ByteConverterHelper.GetTransByAddress(
ref address, byteConverter, out int length, out BcdFormat bCDFormat);
item.ThingsGatewayBitConverter = transformParameter;
item.StringLength = length;
item.StringBcdFormat = bCDFormat;
item.VariableAddress = address;
int bitIndex = 0;
string[] addressSplits = new string[] { address };
if (address.IndexOf('.') > 0)
{
addressSplits = address.SplitDot();
try
{
if ((addressSplits.Length == 2 && !address.ToUpper().Contains("DB")) || (addressSplits.Length >= 3 && address.ToUpper().Contains("DB")))
bitIndex = Convert.ToInt32(addressSplits.Last());
}
catch (Exception ex)
{
_logger?.LogError(ex, "自动分包方法获取Bit失败");
}
}
item.Index = bitIndex;
}
//按读取间隔分组
var tags = deviceVariables.GroupBy(it => it.IntervalTime);
foreach (var item in tags)
{
Dictionary<SiemensAddress, CollectVariableRunTime> map = item.ToDictionary(it =>
{
var lastLen = it.DataTypeEnum.GetByteLength(); ;
if (lastLen <= 0)
{
if (it.DataTypeEnum.GetNetType() == typeof(bool))
{
lastLen = 2;
}
else if (it.DataTypeEnum.GetNetType() == typeof(string))
{
lastLen = it.StringLength;
}
else if (it.DataTypeEnum.GetNetType() == typeof(object))
{
lastLen = 1;
}
}
var s7Address = SiemensAddress.ParseFrom(it.VariableAddress);
if (s7Address.IsSuccess)
{
if ((s7Address.Content.DataCode == (byte)S7WordLength.Counter || s7Address.Content.DataCode == (byte)S7WordLength.Timer) && lastLen == 1)
{
lastLen = 2;
}
}
//这里把每个变量的应读取长度都写入变量地址实体中
return SiemensAddress.ParseFrom(it.VariableAddress, (ushort)lastLen).Content;
});
//获取变量的地址
var modbusAddressList = map.Keys.ToList();
//获取S7数据代码
var functionCodes = modbusAddressList.Select(t => t.DataCode).Distinct();
foreach (var functionCode in functionCodes)
{
//相同数据代码的变量集合
var modbusAddressSameFunList = modbusAddressList
.Where(t => t.DataCode == functionCode);
//相同数据代码的变量集合中的不同DB块
var stationNumbers = modbusAddressSameFunList
.Select(t => t.DbBlock).Distinct();
foreach (var stationNumber in stationNumbers)
{
var addressList = modbusAddressSameFunList.Where(t => t.DbBlock == stationNumber)
.ToDictionary(t => t, t => map[t]);
//循环对数据代码,站号都一样的变量进行分配连读包
var tempResult = LoadSourceRead(addressList, functionCode, item.Key, siemensS7Net);
//添加到总连读包
result.AddRange(tempResult.Content);
}
}
}
}
catch (Exception ex)
{
_logger?.LogError(ex, "自动分包失败");
}
return OperResult.CreateSuccessResult(result);
}
private static OperResult<List<DeviceVariableSourceRead>> LoadSourceRead(Dictionary<SiemensAddress, CollectVariableRunTime> addressList, int functionCode, int timeInterval, SiemensS7PLC siemensS7Net)
{
List<DeviceVariableSourceRead> sourceReads = new List<DeviceVariableSourceRead>();
//实际地址与长度排序
var addresss = addressList.Keys.OrderBy(it =>
{
int address = 0;
if (it.DataCode == (byte)S7WordLength.Counter || it.DataCode == (byte)S7WordLength.Timer)
{
address = it.AddressStart * 2;
}
else
{
address = it.AddressStart / 8;
}
return address + it.Length;
}).ToList();
var minAddress = addresss.First().AddressStart;
var maxAddress = addresss.Last().AddressStart;
while (maxAddress >= minAddress)
{
//这里直接避免末位变量长度超限的情况pdu长度-8
int readLength = siemensS7Net.PDULength == 0 ? 200 : siemensS7Net.PDULength - 8;
List<SiemensAddress> tempAddress = new();
if (functionCode == (byte)S7WordLength.Counter || functionCode == (byte)S7WordLength.Timer)
{
tempAddress = addresss.Where(t => t.AddressStart >= minAddress && ((t.AddressStart) + t.Length) <= ((minAddress) + readLength)).ToList();
while ((tempAddress.Last().AddressStart * 2) + tempAddress.Last().Length - (tempAddress.First().AddressStart * 2) > readLength)
{
tempAddress.Remove(tempAddress.Last());
}
}
else
{
tempAddress = addresss.Where(t => t.AddressStart >= minAddress && ((t.AddressStart) + t.Length) <= ((minAddress) + readLength)).ToList();
while ((tempAddress.Last().AddressStart / 8) + tempAddress.Last().Length - (tempAddress.First().AddressStart / 8) > readLength)
{
tempAddress.Remove(tempAddress.Last());
}
}
//读取寄存器长度
int lastAddress = 0;
int firstAddress = 0;
if (functionCode == (byte)S7WordLength.Counter || functionCode == (byte)S7WordLength.Timer)
{
lastAddress = tempAddress.Last().AddressStart * 2;
firstAddress = tempAddress.First().AddressStart * 2;
}
else
{
lastAddress = tempAddress.Last().AddressStart / 8;
firstAddress = tempAddress.First().AddressStart / 8;
}
var sourceLen = lastAddress + tempAddress.Last().Length - firstAddress;
DeviceVariableSourceRead sourceRead = new DeviceVariableSourceRead(timeInterval);
sourceRead.Address = tempAddress.OrderBy(it => it.AddressStart).First().ToString();
sourceRead.Length = sourceLen.ToString();
foreach (var item in tempAddress)
{
var readNode = addressList[item];
if (functionCode == (byte)S7WordLength.Counter || functionCode == (byte)S7WordLength.Timer)
{
if (readNode.DataTypeEnum == DataTypeEnum.Bool)
{
readNode.Index = (((item.AddressStart * 2) - (tempAddress.First().AddressStart * 2)) * 8) + readNode.Index;
}
else
{
readNode.Index = (item.AddressStart * 2) - (tempAddress.First().AddressStart * 2) + readNode.Index;
}
}
else
{
if (readNode.DataTypeEnum == DataTypeEnum.Bool)
{
readNode.Index = (((item.AddressStart / 8) - (tempAddress.First().AddressStart / 8)) * 8) + readNode.Index;
}
else
{
readNode.Index = (item.AddressStart / 8) - (tempAddress.First().AddressStart / 8) + readNode.Index;
}
}
sourceRead.DeviceVariables.Add(readNode);
addresss.Remove(item);
}
sourceReads.Add(sourceRead);
if (addresss.Count > 0)
minAddress = addresss.First().AddressStart;
else
break;
}
return OperResult.CreateSuccessResult(sourceReads);
}
}
}

View File

@@ -0,0 +1,28 @@
namespace ThingsGateway.Siemens
{
public class S7_1200 : S7
{
public S7_1200(IServiceScopeFactory scopeFactory) : base(scopeFactory)
{
}
protected override void Init(CollectDeviceRunTime device, object client = null)
{
if (client == null)
{
TouchSocketConfig.SetRemoteIPHost(new IPHost(IPAddress.Parse(IP), Port))
.SetBufferLength(1024);
client = TouchSocketConfig.Container.Resolve<TcpClient>();
((TcpClient)client).Setup(TouchSocketConfig);
}
//载入配置
_plc = new((TcpClient)client, SiemensEnum.S1200);
_plc.DataFormat = DataFormat;
_plc.ConnectTimeOut = ConnectTimeOut;
_plc.TimeOut = TimeOut;
}
}
}

View File

@@ -0,0 +1,28 @@
namespace ThingsGateway.Siemens
{
public class S7_1500 : S7
{
public S7_1500(IServiceScopeFactory scopeFactory) : base(scopeFactory)
{
}
protected override void Init(CollectDeviceRunTime device, object client = null)
{
if (client == null)
{
TouchSocketConfig.SetRemoteIPHost(new IPHost(IPAddress.Parse(IP), Port))
.SetBufferLength(1024);
client = TouchSocketConfig.Container.Resolve<TcpClient>();
((TcpClient)client).Setup(TouchSocketConfig);
}
//载入配置
_plc = new((TcpClient)client, SiemensEnum.S1500);
_plc.DataFormat = DataFormat;
_plc.ConnectTimeOut = ConnectTimeOut;
_plc.TimeOut = TimeOut;
}
}
}

View File

@@ -0,0 +1,28 @@
namespace ThingsGateway.Siemens
{
public class S7_200 : S7
{
public S7_200(IServiceScopeFactory scopeFactory) : base(scopeFactory)
{
}
protected override void Init(CollectDeviceRunTime device, object client = null)
{
if (client == null)
{
TouchSocketConfig.SetRemoteIPHost(new IPHost(IPAddress.Parse(IP), Port))
.SetBufferLength(1024);
client = TouchSocketConfig.Container.Resolve<TcpClient>();
((TcpClient)client).Setup(TouchSocketConfig);
}
//载入配置
_plc = new((TcpClient)client, SiemensEnum.S200);
_plc.DataFormat = DataFormat;
_plc.ConnectTimeOut = ConnectTimeOut;
_plc.TimeOut = TimeOut;
}
}
}

View File

@@ -0,0 +1,42 @@
namespace ThingsGateway.Siemens
{
public class S7_200SMART : S7
{
public S7_200SMART(IServiceScopeFactory scopeFactory) : base(scopeFactory)
{
}
protected override void Init(CollectDeviceRunTime device, object client = null)
{
if (client == null)
{
TouchSocketConfig.SetRemoteIPHost(new IPHost(IPAddress.Parse(IP), Port))
.SetBufferLength(1024);
client = TouchSocketConfig.Container.Resolve<TcpClient>();
((TcpClient)client).Setup(TouchSocketConfig);
}
//载入配置
_plc = new((TcpClient)client, SiemensEnum.S200Smart);
_plc.DataFormat = DataFormat;
_plc.ConnectTimeOut = ConnectTimeOut;
_plc.TimeOut = TimeOut;
}
//[Method("HotStart", "热启动")]
//public OperResult HotStart()
//{
// return _plc?.HotStart();
//}
//[Method("ColdStart", "冷启动")]
//public OperResult ColdStart()
//{
// return _plc?.ColdStart();
//}
//[Method("Stop", "停止")]
//public OperResult Stop()
//{
// return _plc?.Stop();
//}
}
}

View File

@@ -0,0 +1,28 @@
namespace ThingsGateway.Siemens
{
public class S7_300 : S7
{
public S7_300(IServiceScopeFactory scopeFactory) : base(scopeFactory)
{
}
protected override void Init(CollectDeviceRunTime device, object client = null)
{
if (client == null)
{
TouchSocketConfig.SetRemoteIPHost(new IPHost(IPAddress.Parse(IP), Port))
.SetBufferLength(1024);
client = TouchSocketConfig.Container.Resolve<TcpClient>();
((TcpClient)client).Setup(TouchSocketConfig);
}
//载入配置
_plc = new((TcpClient)client, SiemensEnum.S300);
_plc.DataFormat = DataFormat;
_plc.ConnectTimeOut = ConnectTimeOut;
_plc.TimeOut = TimeOut;
}
}
}

View File

@@ -0,0 +1,28 @@
namespace ThingsGateway.Siemens
{
public class S7_400 : S7
{
public S7_400(IServiceScopeFactory scopeFactory) : base(scopeFactory)
{
}
protected override void Init(CollectDeviceRunTime device, object client = null)
{
if (client == null)
{
TouchSocketConfig.SetRemoteIPHost(new IPHost(IPAddress.Parse(IP), Port))
.SetBufferLength(1024);
client = TouchSocketConfig.Container.Resolve<TcpClient>();
((TcpClient)client).Setup(TouchSocketConfig);
}
//载入配置
_plc = new((TcpClient)client, SiemensEnum.S400);
_plc.DataFormat = DataFormat;
_plc.ConnectTimeOut = ConnectTimeOut;
_plc.TimeOut = TimeOut;
}
}
}

View File

@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<LangVersion>latestMajor</LangVersion>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Version>1.2.0</Version>
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
<EnableDynamicLoading>true</EnableDynamicLoading>
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
</PropertyGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command=" set dir=&quot;$(SolutionDir)ThingsGateway.Web.Server\bin\$(Configuration)\$(TargetFramework)\Plugins\$(AssemblyName)&quot;&#xD;&#xA; if not exist %25dir%25 md %25dir%25 &#xD;&#xA;copy &quot;$(TargetDir)*Siemens*.dll&quot; %25dir%25&#xD;&#xA;&#xD;&#xA;&#xD;&#xA;" />
</Target>
<ItemGroup>
<ProjectReference Include="..\..\ThingsGateway.Web.Foundation\ThingsGateway.Web.Foundation.csproj" >
<IncludeAssets>Compile</IncludeAssets>
</ProjectReference>
<ProjectReference Include="..\ThingsGateway.Foundation.Adapter.Siemens\ThingsGateway.Foundation.Adapter.Siemens.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,5 +1,71 @@
{
"RECORDS": [
{
"Id": "395093826302213",
"AssembleName": "ThingsGateway.Siemens.S7_1200",
"FileName": "ThingsGateway.Siemens",
"DriverTypeEnum": "Collect",
"FilePath": "Plugins\\ThingsGateway.Siemens\\ThingsGateway.Siemens.dll",
"CreateTime": "2023-03-12 20:20:55.726",
"CreateUser": "superAdmin",
"CreateUserId": "212725263002001",
"IsDelete": "0"
},
{
"Id": "395093826302214",
"AssembleName": "ThingsGateway.Siemens.S7_1500",
"FileName": "ThingsGateway.Siemens",
"DriverTypeEnum": "Collect",
"FilePath": "Plugins\\ThingsGateway.Siemens\\ThingsGateway.Siemens.dll",
"CreateTime": "2023-03-12 20:20:55.728",
"CreateUser": "superAdmin",
"CreateUserId": "212725263002001",
"IsDelete": "0"
},
{
"Id": "395093826302215",
"AssembleName": "ThingsGateway.Siemens.S7_200",
"FileName": "ThingsGateway.Siemens",
"DriverTypeEnum": "Collect",
"FilePath": "Plugins\\ThingsGateway.Siemens\\ThingsGateway.Siemens.dll",
"CreateTime": "2023-03-12 20:20:55.728",
"CreateUser": "superAdmin",
"CreateUserId": "212725263002001",
"IsDelete": "0"
},
{
"Id": "395093826302216",
"AssembleName": "ThingsGateway.Siemens.S7_200SMART",
"FileName": "ThingsGateway.Siemens",
"DriverTypeEnum": "Collect",
"FilePath": "Plugins\\ThingsGateway.Siemens\\ThingsGateway.Siemens.dll",
"CreateTime": "2023-03-12 20:20:55.728",
"CreateUser": "superAdmin",
"CreateUserId": "212725263002001",
"IsDelete": "0"
},
{
"Id": "395093826302217",
"AssembleName": "ThingsGateway.Siemens.S7_300",
"FileName": "ThingsGateway.Siemens",
"DriverTypeEnum": "Collect",
"FilePath": "Plugins\\ThingsGateway.Siemens\\ThingsGateway.Siemens.dll",
"CreateTime": "2023-03-12 20:20:55.728",
"CreateUser": "superAdmin",
"CreateUserId": "212725263002001",
"IsDelete": "0"
},
{
"Id": "395093826302218",
"AssembleName": "ThingsGateway.Siemens.S7_400",
"FileName": "ThingsGateway.Siemens",
"DriverTypeEnum": "Collect",
"FilePath": "Plugins\\ThingsGateway.Siemens\\ThingsGateway.Siemens.dll",
"CreateTime": "2023-03-12 20:20:55.728",
"CreateUser": "superAdmin",
"CreateUserId": "212725263002001",
"IsDelete": "0"
},
{
"Id": "389003388313861",
"AssembleName": "ThingsGateway.Modbus.ModbusRtuOverTcp",

View File

@@ -148,17 +148,25 @@
StringNumber tab;
List<CollectDevice> CollectDevices = new();
List<UploadDevice> UploadDevices = new();
List<string> OtherMethods = new();
Dictionary<long, List<string>> OtherMethods = new();
void DeviceChanged(long devId)
{
if (devId > 0)
OtherMethods = this.GetBackgroundService<CollectDeviceHostService>().GetDeviceMethods(devId);
{
var data = this.GetBackgroundService<CollectDeviceHostService>().GetDeviceMethods(devId);
OtherMethods.AddOrUpdate(devId,data);
}
else
OtherMethods = new();
}
RenderFragment GetRenderFragment(VariableEditInput context)
{
if(!OtherMethods.ContainsKey(context.DeviceId))
{
DeviceChanged(context.DeviceId);
}
RenderFragment renderFragment = null;
renderFragment +=
@<div>
@@ -247,17 +255,24 @@
<MSubheader Class="font-weight-black"> @(context.Description(x => x.WriteExpressions)) </MSubheader>
<MTextField Dense Outlined HideDetails="@("auto")" @bind-Value=@context.WriteExpressions />
</MCol>
@if(OtherMethods.ContainsKey(context.DeviceId))
{
<MCol Md=6 Cols=12 class="px-1">
<MSubheader Class="font-weight-black"> @(context.Description(x => x.OtherMethod)) </MSubheader>
<MSelect Class="mr-3" @bind-Value="context.OtherMethod" Outlined
Items=@(OtherMethods) Clearable
<MSubheader Class="font-weight-black"> @(context.Description(x => x.OtherMethod)) </MSubheader>
<MSelect Class="mr-3" @bind-Value="context.OtherMethod" Outlined
Items=@(OtherMethods[context.DeviceId]) Clearable
MenuProps="@(props => { props.Auto = true; props.OffsetY = true; })"
ItemText=@((u) =>u)
ItemValue=@(u =>u)
HideDetails=@("auto") Height="30"
Dense>
</MSelect>
</MCol>
Dense>
</MSelect>
</MCol>
}
</MRow>
</MCard>

View File

@@ -357,7 +357,6 @@
</PModal>
@code {
private string _collectDeviceGroup;
private string _uploadDeviceGroup;
@@ -369,19 +368,21 @@
List<UploadDeviceCore> _uploadDeviceCores = new();
void collectDeviceQuery()
{
_collectDeviceCores = CollectDeviceHostService.CollectDeviceCores.WhereIf(!_collectDeviceGroup.IsNullOrEmpty(), a => a.Device.DeviceGroup == _collectDeviceGroup).ToList();
_collectDeviceCores = CollectDeviceHostService?.CollectDeviceCores?.WhereIf(!_collectDeviceGroup.IsNullOrEmpty(), a => a.Device.DeviceGroup == _collectDeviceGroup).ToList();
}
void uploadDeviceQuery()
{
_uploadDeviceCores = UploadDeviceHostService.UploadDeviceCores.WhereIf(!_uploadDeviceGroup.IsNullOrEmpty(), a => a.Device.DeviceGroup == _uploadDeviceGroup).ToList();
_uploadDeviceCores = UploadDeviceHostService?.UploadDeviceCores?.WhereIf(!_uploadDeviceGroup.IsNullOrEmpty(), a => a.Device.DeviceGroup == _uploadDeviceGroup).ToList();
}
protected override async Task OnParametersSetAsync()
{
_collectDeviceGroups = CollectDeviceService.GetCacheList()?.Select(a => a.DeviceGroup)?.Where(a => a != null).Distinct()?.ToList();
_uploadDeviceGroups = UploadDeviceService.GetCacheList()?.Select(a => a.DeviceGroup)?.Where(a => a != null).Distinct()?.ToList();
collectDeviceQuery();
uploadDeviceQuery();
await base.OnParametersSetAsync();
}
private bool isDownExport;
[Inject]
IJSRuntime JS { get; set; }
@@ -424,8 +425,7 @@
DelayTimer.Elapsed += timer_Elapsed;
DelayTimer.AutoReset = true;
DelayTimer.Start();
collectDeviceQuery();
uploadDeviceQuery();
return base.OnInitializedAsync();
}
private BootstrapDynamicComponent _importComponent;
@@ -459,6 +459,7 @@
isRestart = true;
StateHasChanged();
await Task.Run(async () => await CollectDeviceHostService.UpDeviceThread(devId));
collectDeviceQuery();
}
}
catch (Exception ex)
@@ -480,6 +481,7 @@
isRestart = true;
StateHasChanged();
await Task.Run(() => UploadDeviceHostService.UpDeviceThread(devId));
uploadDeviceQuery();
}
}
catch (Exception ex)
@@ -512,6 +514,8 @@
StateHasChanged();
await Task.Run(async () => await CollectDeviceHostService.RestartDeviceThread());
collectDeviceQuery();
uploadDeviceQuery();
}
}
finally

View File

@@ -66,6 +66,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EventSource", "EventSource"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.RabbitMQ", "Plugins\ThingsGateway.RabbitMQ\ThingsGateway.RabbitMQ.csproj", "{7D38A1C3-56FA-41CE-B541-9FF1B5A632F7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Foundation.Adapter.Siemens", "Plugins\ThingsGateway.Foundation.Adapter.Siemens\ThingsGateway.Foundation.Adapter.Siemens.csproj", "{A0C38DE3-56FD-4ECC-9599-372A4716A4C8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThingsGateway.Siemens", "Plugins\ThingsGateway.Siemens\ThingsGateway.Siemens.csproj", "{12D7E735-8DA0-47FB-849F-84C965E4C746}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Siemens", "Siemens", "{98BB0E39-0629-4619-A01B-4BEB0F8DE879}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -218,6 +224,22 @@ Global
{7D38A1C3-56FA-41CE-B541-9FF1B5A632F7}.Release|Any CPU.Build.0 = Release|Any CPU
{7D38A1C3-56FA-41CE-B541-9FF1B5A632F7}.Release|x86.ActiveCfg = Release|Any CPU
{7D38A1C3-56FA-41CE-B541-9FF1B5A632F7}.Release|x86.Build.0 = Release|Any CPU
{A0C38DE3-56FD-4ECC-9599-372A4716A4C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A0C38DE3-56FD-4ECC-9599-372A4716A4C8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A0C38DE3-56FD-4ECC-9599-372A4716A4C8}.Debug|x86.ActiveCfg = Debug|Any CPU
{A0C38DE3-56FD-4ECC-9599-372A4716A4C8}.Debug|x86.Build.0 = Debug|Any CPU
{A0C38DE3-56FD-4ECC-9599-372A4716A4C8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A0C38DE3-56FD-4ECC-9599-372A4716A4C8}.Release|Any CPU.Build.0 = Release|Any CPU
{A0C38DE3-56FD-4ECC-9599-372A4716A4C8}.Release|x86.ActiveCfg = Release|Any CPU
{A0C38DE3-56FD-4ECC-9599-372A4716A4C8}.Release|x86.Build.0 = Release|Any CPU
{12D7E735-8DA0-47FB-849F-84C965E4C746}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{12D7E735-8DA0-47FB-849F-84C965E4C746}.Debug|Any CPU.Build.0 = Debug|Any CPU
{12D7E735-8DA0-47FB-849F-84C965E4C746}.Debug|x86.ActiveCfg = Debug|Any CPU
{12D7E735-8DA0-47FB-849F-84C965E4C746}.Debug|x86.Build.0 = Debug|Any CPU
{12D7E735-8DA0-47FB-849F-84C965E4C746}.Release|Any CPU.ActiveCfg = Release|Any CPU
{12D7E735-8DA0-47FB-849F-84C965E4C746}.Release|Any CPU.Build.0 = Release|Any CPU
{12D7E735-8DA0-47FB-849F-84C965E4C746}.Release|x86.ActiveCfg = Release|Any CPU
{12D7E735-8DA0-47FB-849F-84C965E4C746}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -246,6 +268,9 @@ Global
{4141C657-56B0-40D9-BA52-9F53A6D9E338} = {D64E48AD-2937-4227-8270-63AEF3008374}
{968A381B-E354-4089-984D-7B32DF2A470D} = {D1DFBF11-65DE-49C8-9AE9-BFDFB66E129A}
{7D38A1C3-56FA-41CE-B541-9FF1B5A632F7} = {968A381B-E354-4089-984D-7B32DF2A470D}
{A0C38DE3-56FD-4ECC-9599-372A4716A4C8} = {98BB0E39-0629-4619-A01B-4BEB0F8DE879}
{12D7E735-8DA0-47FB-849F-84C965E4C746} = {98BB0E39-0629-4619-A01B-4BEB0F8DE879}
{98BB0E39-0629-4619-A01B-4BEB0F8DE879} = {D1DFBF11-65DE-49C8-9AE9-BFDFB66E129A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C44BAB06-0E6E-46A5-84A1-C9C94533DD4D}