mirror of
https://gitee.com/ThingsGateway/ThingsGateway.git
synced 2025-10-26 21:27:10 +08:00
484 lines
18 KiB
C#
484 lines
18 KiB
C#
#region copyright
|
||
//------------------------------------------------------------------------------
|
||
// 此代码版权声明为全文件覆盖,如有原作者特别声明,会在下方手动补充
|
||
// 此代码版权(除特别声明外的代码)归作者本人Diego所有
|
||
// 源代码使用协议遵循本仓库的开源协议及附加协议
|
||
// Gitee源代码仓库:https://gitee.com/diego2098/ThingsGateway
|
||
// Github源代码仓库:https://github.com/kimdiego2098/ThingsGateway
|
||
// 使用文档:https://diego2098.gitee.io/thingsgateway-docs/
|
||
// QQ群:605534569
|
||
//------------------------------------------------------------------------------
|
||
#endregion
|
||
|
||
using System.Text;
|
||
|
||
using ThingsGateway.Foundation.Extension.Generic;
|
||
using ThingsGateway.Foundation.Extension.String;
|
||
|
||
namespace ThingsGateway.Foundation.Adapter.Siemens
|
||
{
|
||
/// <summary>
|
||
/// 相关命令含义源自网络资料/Shrap7/s7netplus
|
||
/// </summary>
|
||
public partial class SiemensS7PLC : ReadWriteDevicesTcpClientBase
|
||
{
|
||
private readonly SiemensEnum _currentPlc = SiemensEnum.S1200;
|
||
private readonly byte[] ISO_CR;
|
||
private readonly byte[] S7_PN;
|
||
private int pdu_length = 100;
|
||
private byte plc_rack = 0;
|
||
private byte plc_slot = 0;
|
||
/// <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;
|
||
|
||
}
|
||
|
||
}
|
||
/// <summary>
|
||
/// 当前PLC类型
|
||
/// </summary>
|
||
public SiemensEnum CurrentPlc => _currentPlc;
|
||
/// <inheritdoc/>
|
||
public override string GetAddressDescription()
|
||
{
|
||
StringBuilder stringBuilder = new();
|
||
stringBuilder.AppendLine("S7协议寄存器地址格式");
|
||
stringBuilder.AppendLine("Txxxxx Timer寄存器,例如T100/T100.1");
|
||
stringBuilder.AppendLine("Cxxxxx,Counter寄存器,例如C100/C100.1");
|
||
stringBuilder.AppendLine("AIxxxxx,AI寄存器,例如AI100/AI100.1");
|
||
stringBuilder.AppendLine("AQxxxxx,AQ寄存器,例如AQ100/AQ100.1");
|
||
stringBuilder.AppendLine("Ixxxxx,I寄存器,例如I100/I100.1");
|
||
stringBuilder.AppendLine("Qxxxxx,Q寄存器,例如Q100/Q100.1");
|
||
stringBuilder.AppendLine("Mxxxxx,M寄存器,例如M100/M100.1");
|
||
stringBuilder.AppendLine("DBxxxxx,DB寄存器,例如DB100.1/DB100.1.1");
|
||
stringBuilder.AppendLine("");
|
||
|
||
return base.GetAddressDescription() + Environment.NewLine + stringBuilder.ToString();
|
||
}
|
||
/// <summary>
|
||
/// <inheritdoc/>
|
||
/// </summary>
|
||
/// <param name="address"></param>
|
||
/// <returns></returns>
|
||
public override int GetBitOffset(string address)
|
||
{
|
||
if (address.IndexOf('.') > 0)
|
||
{
|
||
string[] addressSplits = address.SplitDot();
|
||
try
|
||
{
|
||
int bitIndex = 0;
|
||
if ((addressSplits.Length == 2 && !address.ToUpper().Contains("DB")) || (addressSplits.Length >= 3 && address.ToUpper().Contains("DB")))
|
||
bitIndex = Convert.ToInt32(addressSplits.Last());
|
||
return bitIndex;
|
||
}
|
||
catch
|
||
{
|
||
return 0;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
/// <inheritdoc/>
|
||
public override bool IsBitReverse(string address)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
#region 设置
|
||
|
||
/// <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
|
||
{
|
||
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];
|
||
}
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// PDULength
|
||
/// </summary>
|
||
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
|
||
|
||
/// <inheritdoc/>
|
||
public override List<T> LoadSourceRead<T, T2>(List<T2> deviceVariables, int maxPack)
|
||
{
|
||
return PackHelper.LoadSourceRead<T, T2>(this, deviceVariables, maxPack);
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public override OperResult<byte[]> Read(string address, int length, CancellationToken cancellationToken = default)
|
||
{
|
||
try
|
||
{
|
||
Connect(cancellationToken);
|
||
var commandResult = GetReadByteCommand(address, length);
|
||
if (commandResult.IsSuccess)
|
||
{
|
||
List<byte> bytes = new();
|
||
foreach (var item in commandResult.Content)
|
||
{
|
||
var result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken);
|
||
if (((MessageBase)result.RequestInfo).IsSuccess)
|
||
bytes.AddRange(((MessageBase)result.RequestInfo).Content);
|
||
else
|
||
return new(((MessageBase)result.RequestInfo));
|
||
}
|
||
return OperResult.CreateSuccessResult(bytes.ToArray());
|
||
}
|
||
|
||
else
|
||
{
|
||
return new(commandResult);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return new OperResult<byte[]>(ex);
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public override async Task<OperResult<byte[]>> ReadAsync(string address, int length, CancellationToken cancellationToken = default)
|
||
{
|
||
try
|
||
{
|
||
await ConnectAsync(cancellationToken);
|
||
var commandResult = GetReadByteCommand(address, length);
|
||
if (commandResult.IsSuccess)
|
||
{
|
||
List<byte> bytes = new();
|
||
foreach (var item in commandResult.Content)
|
||
{
|
||
var result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken);
|
||
if (((MessageBase)result.RequestInfo).IsSuccess)
|
||
bytes.AddRange(((MessageBase)result.RequestInfo).Content);
|
||
else
|
||
return new(((MessageBase)result.RequestInfo));
|
||
}
|
||
return OperResult.CreateSuccessResult(bytes.ToArray());
|
||
}
|
||
|
||
else
|
||
{
|
||
return new(commandResult);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return new OperResult<byte[]>(ex);
|
||
}
|
||
|
||
}
|
||
|
||
|
||
/// <inheritdoc/>
|
||
public override void SetDataAdapter(object socketClient = null)
|
||
{
|
||
SiemensS7PLCDataHandleAdapter dataHandleAdapter = new();
|
||
TcpClient.SetDataHandlingAdapter(dataHandleAdapter);
|
||
}
|
||
|
||
|
||
/// <inheritdoc/>
|
||
public override OperResult Write(string address, byte[] value, CancellationToken cancellationToken = default)
|
||
{
|
||
try
|
||
{
|
||
Connect(cancellationToken);
|
||
var commandResult = GetWriteByteCommand(address, value);
|
||
if (commandResult.IsSuccess)
|
||
{
|
||
List<ResponsedData> bytes = new();
|
||
foreach (var item in commandResult.Content)
|
||
{
|
||
ResponsedData result = WaitingClientEx.SendThenResponse(item, TimeOut, cancellationToken);
|
||
if (!((MessageBase)result.RequestInfo).IsSuccess)
|
||
return new(((MessageBase)result.RequestInfo));
|
||
bytes.Add(result);
|
||
}
|
||
return OperResult.CreateSuccessResult(bytes.ToArray());
|
||
}
|
||
else
|
||
{
|
||
return new(commandResult);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return new OperResult<bool[]>(ex);
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public override OperResult Write(string address, bool[] value, CancellationToken cancellationToken = default)
|
||
{
|
||
if (value.Length > 1)
|
||
{
|
||
return new OperResult("不支持多写");
|
||
}
|
||
try
|
||
{
|
||
Connect(cancellationToken);
|
||
|
||
var commandResult = GetWriteBitCommand(address, value[0]);
|
||
if (commandResult.IsSuccess)
|
||
{
|
||
var result = WaitingClientEx.SendThenResponse(commandResult.Content, TimeOut, cancellationToken);
|
||
return (MessageBase)result.RequestInfo;
|
||
}
|
||
else
|
||
{
|
||
return new(commandResult);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return new OperResult<bool[]>(ex);
|
||
}
|
||
finally
|
||
{
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public override Task<OperResult> WriteAsync(string address, string value, CancellationToken cancellationToken = default)
|
||
{
|
||
IThingsGatewayBitConverter transformParameter = ByteTransformUtil.GetTransByAddress(ref address, ThingsGatewayBitConverter);
|
||
return SiemensHelper.WriteAsync(this, address, value, transformParameter.Encoding);
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public override async Task<OperResult> WriteAsync(string address, byte[] value, CancellationToken cancellationToken = default)
|
||
{
|
||
try
|
||
{
|
||
await ConnectAsync(cancellationToken);
|
||
var commandResult = GetWriteByteCommand(address, value);
|
||
if (commandResult.IsSuccess)
|
||
{
|
||
List<ResponsedData> bytes = new();
|
||
foreach (var item in commandResult.Content)
|
||
{
|
||
ResponsedData result = await WaitingClientEx.SendThenResponseAsync(item, TimeOut, cancellationToken);
|
||
if (!((MessageBase)result.RequestInfo).IsSuccess)
|
||
return new(((MessageBase)result.RequestInfo));
|
||
bytes.Add(result);
|
||
}
|
||
return OperResult.CreateSuccessResult(bytes.ToArray());
|
||
}
|
||
else
|
||
{
|
||
return new(commandResult);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return new OperResult<bool[]>(ex);
|
||
}
|
||
}
|
||
|
||
/// <inheritdoc/>
|
||
public override async Task<OperResult> WriteAsync(string address, bool[] value, CancellationToken cancellationToken = default)
|
||
{
|
||
if (value.Length > 1)
|
||
{
|
||
return new OperResult("不支持多写");
|
||
}
|
||
try
|
||
{
|
||
await ConnectAsync(cancellationToken);
|
||
|
||
var commandResult = GetWriteBitCommand(address, value[0]);
|
||
if (commandResult.IsSuccess)
|
||
{
|
||
var result = await WaitingClientEx.SendThenResponseAsync(commandResult.Content, TimeOut, cancellationToken);
|
||
return (MessageBase)result.RequestInfo;
|
||
}
|
||
else
|
||
{
|
||
return new(commandResult);
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
return new OperResult<bool[]>(ex);
|
||
}
|
||
finally
|
||
{
|
||
}
|
||
}
|
||
|
||
|
||
#region 其他方法
|
||
/// <summary>
|
||
/// 读取日期
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public async Task<OperResult<System.DateTime>> ReadDateAsync(string address, CancellationToken cancellationToken)
|
||
{
|
||
return (await this.ReadAsync(address, 2, cancellationToken)).
|
||
Then(m => OperResult.CreateSuccessResult(ThingsGateway.Foundation.Adapter.Siemens.DateTime.SpecMinimumDateTime.AddDays(
|
||
ThingsGatewayBitConverter.ToUInt16(m, 0)))
|
||
);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取时间
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public async Task<OperResult<System.DateTime>> ReadDateTimeAsync(string address, CancellationToken cancellationToken)
|
||
{
|
||
return ByteTransformUtil.GetResultFromBytes(await ReadAsync(address, 8, cancellationToken), ThingsGateway.Foundation.Adapter.Siemens.DateTime.FromByteArray);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取变长字符串
|
||
/// </summary>
|
||
public async Task<OperResult<string>> ReadStringAsync(string address, Encoding encoding, CancellationToken cancellationToken)
|
||
{
|
||
return await SiemensHelper.ReadStringAsync(this, address, encoding, cancellationToken);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 写入日期
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public async Task<OperResult> WriteDateAsync(string address, System.DateTime dateTime, CancellationToken cancellationToken)
|
||
{
|
||
return await base.WriteAsync(address, Convert.ToUInt16((dateTime - ThingsGateway.Foundation.Adapter.Siemens.DateTime.SpecMinimumDateTime).TotalDays), cancellationToken);
|
||
}
|
||
/// <summary>
|
||
/// 写入时间
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
public async Task<OperResult> WriteDateTimeAsync(string address, System.DateTime dateTime, CancellationToken cancellationToken)
|
||
{
|
||
return await WriteAsync(address, ThingsGateway.Foundation.Adapter.Siemens.DateTime.ToByteArray(dateTime), cancellationToken);
|
||
}
|
||
#endregion
|
||
|
||
/// <inheritdoc/>
|
||
protected override async Task Connected(ITcpClient client, ConnectedEventArgs e)
|
||
{
|
||
try
|
||
{
|
||
NormalDataHandlingAdapter dataHandleAdapter = new();
|
||
TcpClient.SetDataHandlingAdapter(dataHandleAdapter);
|
||
var result1 = await SendThenResponseAsync(ISO_CR);
|
||
if (!result1.IsSuccess)
|
||
{
|
||
Logger?.Warning($"{client.IP} : {client.Port}:ISO_TP握手失败-{result1.Message}");
|
||
return;
|
||
}
|
||
var result2 = await SendThenResponseAsync(S7_PN);
|
||
if (!result2.IsSuccess)
|
||
{
|
||
Logger?.Warning($"{client.IP} : {client.Port}:PDU初始化失败-{result2.Message}");
|
||
return;
|
||
}
|
||
pdu_length = ThingsGatewayBitConverter.ToUInt16(result2.Content.SelectLast(2), 0);
|
||
pdu_length = pdu_length < 200 ? 200 : pdu_length;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Logger.Exception(ex);
|
||
}
|
||
finally
|
||
{
|
||
SetDataAdapter();
|
||
}
|
||
await base.Connected(client, e);
|
||
}
|
||
}
|
||
} |