2025-10-31 15:18:28 +08:00
|
|
|
|
//#define _TEST_BASIC_VAR
|
|
|
|
|
|
#define _TEST_PLCTAG
|
|
|
|
|
|
|
2025-08-05 15:29:54 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Security.Cryptography;
|
|
|
|
|
|
using System.Text;
|
2025-11-24 16:49:05 +08:00
|
|
|
|
using System.Threading.Tasks;
|
2025-08-05 15:29:54 +08:00
|
|
|
|
using S7CommPlusDriver;
|
|
|
|
|
|
|
|
|
|
|
|
using S7CommPlusDriver.ClientApi;
|
|
|
|
|
|
|
|
|
|
|
|
namespace DriverTest
|
|
|
|
|
|
{
|
|
|
|
|
|
class Program
|
|
|
|
|
|
{
|
|
|
|
|
|
static void Main(string[] args)
|
|
|
|
|
|
{
|
2025-11-08 12:20:08 +08:00
|
|
|
|
string HostIp = "192.168.0.250";
|
2025-08-05 15:29:54 +08:00
|
|
|
|
string Password = "";
|
|
|
|
|
|
int res;
|
|
|
|
|
|
List<ItemAddress> readlist = new List<ItemAddress>();
|
|
|
|
|
|
Console.WriteLine("Main - START");
|
|
|
|
|
|
// Als Parameter lässt sich die IP-Adresse übergeben, sonst Default-Wert von oben
|
|
|
|
|
|
if (args.Length >= 1) {
|
|
|
|
|
|
HostIp = args[0];
|
|
|
|
|
|
}
|
|
|
|
|
|
// Als Parameter lässt sich das Passwort übergeben, sonst Default-Wert von oben (kein Passwort)
|
|
|
|
|
|
if (args.Length >= 2) {
|
|
|
|
|
|
Password = args[1];
|
|
|
|
|
|
}
|
|
|
|
|
|
Console.WriteLine("Main - Versuche Verbindungsaufbau zu: " + HostIp);
|
|
|
|
|
|
|
|
|
|
|
|
S7CommPlusConnection conn = new S7CommPlusConnection();
|
2025-11-08 12:20:08 +08:00
|
|
|
|
conn.OnlySecurePGOrPCAndHMI = false;
|
|
|
|
|
|
|
2025-10-31 15:18:28 +08:00
|
|
|
|
System.Diagnostics.Stopwatch stopwatch1 = new System.Diagnostics.Stopwatch();
|
|
|
|
|
|
stopwatch1.Start();
|
2025-08-05 15:29:54 +08:00
|
|
|
|
res = conn.Connect(HostIp, Password);
|
2025-10-31 15:18:28 +08:00
|
|
|
|
stopwatch1.Stop();
|
2025-11-24 16:49:05 +08:00
|
|
|
|
byte[] var1_crc_bytes = { 0x88, 0xdd, 0xa4, 0x83, 0x34 };
|
2025-10-31 15:18:28 +08:00
|
|
|
|
Console.WriteLine($"PLCType: {conn.PLCInformation.PLCType} | MLFB: {conn.PLCInformation.MLFB} | Firmware: {conn.PLCInformation.Firmware}");
|
|
|
|
|
|
Console.WriteLine($"连接耗时{stopwatch1.ElapsedMilliseconds}ms.");
|
2025-08-05 15:29:54 +08:00
|
|
|
|
if (res == 0)
|
|
|
|
|
|
{
|
2025-10-31 15:18:28 +08:00
|
|
|
|
Console.WriteLine("按任意键浏览变量");
|
|
|
|
|
|
Console.ReadKey();
|
2025-08-05 15:29:54 +08:00
|
|
|
|
Console.WriteLine("Main - Connect fertig");
|
|
|
|
|
|
|
|
|
|
|
|
#region Variablenhaushalt browsen
|
|
|
|
|
|
Console.WriteLine("Main - Starte Browse...");
|
|
|
|
|
|
// Variablenhaushalt auslesen
|
|
|
|
|
|
List<VarInfo> vars = new List<VarInfo>();
|
|
|
|
|
|
res = conn.Browse(out vars);
|
|
|
|
|
|
Console.WriteLine("Main - Browse res=" + res);
|
|
|
|
|
|
#endregion
|
2025-11-08 12:20:08 +08:00
|
|
|
|
List<VarInfo> vars_ = vars.GetRange(0, 1000);
|
2025-10-31 15:18:28 +08:00
|
|
|
|
#if _TEST_PLCTAG
|
2025-08-05 15:29:54 +08:00
|
|
|
|
#region Werte aller Variablen einlesen
|
|
|
|
|
|
Console.WriteLine("Main - Lese Werte aller Variablen aus");
|
|
|
|
|
|
|
|
|
|
|
|
List<PlcTag> taglist = new List<PlcTag>();
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var v in vars_)
|
|
|
|
|
|
{
|
2025-11-08 12:20:08 +08:00
|
|
|
|
ItemAddress itemAddress = new ItemAddress(v.AccessSequence);
|
2025-11-24 16:49:05 +08:00
|
|
|
|
//S7p.DecodeUInt32Vlq(new MemoryStream(var1_crc_bytes), out itemAddress.SymbolCrc);
|
2025-11-08 12:20:08 +08:00
|
|
|
|
taglist.Add(PlcTags.TagFactory(v.Name, itemAddress, v.Softdatatype));
|
2025-08-05 15:29:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (res == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine("====================== VARIABLENHAUSHALT ======================");
|
|
|
|
|
|
|
|
|
|
|
|
string formatstring = "{0,-80}{1,-30}{2,-20}{3,-20}";
|
|
|
|
|
|
Console.WriteLine(String.Format(formatstring, "SYMBOLIC-NAME", "ACCESS-SEQUENCE", "TYP", "QC: VALUE"));
|
|
|
|
|
|
for (int i = 0; i < vars_.Count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
string s;
|
|
|
|
|
|
|
|
|
|
|
|
s = String.Format(formatstring, taglist[i].Name, taglist[i].Address.GetAccessString(), Softdatatype.Types[taglist[i].Datatype], taglist[i].ToString());
|
|
|
|
|
|
Console.WriteLine(s);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
2025-11-24 16:49:05 +08:00
|
|
|
|
Console.WriteLine("按任意键开始读或订阅");
|
2025-08-05 15:29:54 +08:00
|
|
|
|
Console.ReadKey();
|
2025-11-24 16:49:05 +08:00
|
|
|
|
res = conn.SubscriptionCreate(taglist, 100);
|
|
|
|
|
|
if (res == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Task.Run(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
conn.TestWaitForVariableChangeNotifications(50000000000);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2025-08-05 15:29:54 +08:00
|
|
|
|
while (res == 0)
|
|
|
|
|
|
{
|
2025-10-31 15:18:28 +08:00
|
|
|
|
System.Diagnostics.Stopwatch stopwatch2 = new System.Diagnostics.Stopwatch();
|
|
|
|
|
|
stopwatch2.Start();
|
2025-11-24 16:49:05 +08:00
|
|
|
|
res = PlcTags.ReadTags(conn, taglist);
|
2025-10-31 15:18:28 +08:00
|
|
|
|
stopwatch2.Stop();
|
|
|
|
|
|
long ms = stopwatch2.ElapsedMilliseconds;
|
2025-08-05 15:29:54 +08:00
|
|
|
|
if (res == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
string header = $"读取{vars_.Count}个变量耗时{ms}毫秒";
|
|
|
|
|
|
Console.WriteLine(header);
|
|
|
|
|
|
}
|
2025-11-24 16:49:05 +08:00
|
|
|
|
//System.Threading.Thread.Sleep(50);
|
2025-08-05 15:29:54 +08:00
|
|
|
|
}
|
2025-10-31 15:18:28 +08:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#if _TEST_BASIC_VAR
|
2025-08-05 15:29:54 +08:00
|
|
|
|
#region Werte aller Variablen einlesen
|
|
|
|
|
|
Console.WriteLine("Main - Lese Werte aller Variablen aus");
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var v in vars)
|
|
|
|
|
|
{
|
|
|
|
|
|
readlist.Add(new ItemAddress(v.AccessSequence));
|
|
|
|
|
|
}
|
|
|
|
|
|
List<object> values = new List<object>();
|
|
|
|
|
|
List<UInt64> errors = new List<UInt64>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Fehlerhafte Variable setzen
|
|
|
|
|
|
//readlist[2].LID[0] = 123;
|
|
|
|
|
|
res = conn.ReadValues(readlist, out values, out errors);
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#region Variablenhaushalt mit Werten ausgeben
|
|
|
|
|
|
|
|
|
|
|
|
if (res == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine("====================== VARIABLENHAUSHALT ======================");
|
|
|
|
|
|
|
|
|
|
|
|
// Liste ausgeben
|
|
|
|
|
|
string formatstring = "{0,-80}{1,-30}{2,-20}{3,-20}";
|
|
|
|
|
|
Console.WriteLine(String.Format(formatstring, "SYMBOLIC-NAME", "ACCESS-SEQUENCE", "TYP", "VALUE"));
|
|
|
|
|
|
for (int i = 0; i < vars.Count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
string s = String.Format(formatstring, vars[i].Name, vars[i].AccessSequence, Softdatatype.Types[vars[i].Softdatatype], values[i]);
|
|
|
|
|
|
Console.WriteLine(s);
|
|
|
|
|
|
}
|
|
|
|
|
|
Console.WriteLine("===============================================================");
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
2025-10-31 15:18:28 +08:00
|
|
|
|
#endif
|
2025-08-05 15:29:54 +08:00
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
#region Test: Wert schreiben
|
|
|
|
|
|
List<PValue> writevalues = new List<PValue>();
|
|
|
|
|
|
PValue writeValue = new ValueInt(8888);
|
|
|
|
|
|
writevalues.Add(writeValue);
|
|
|
|
|
|
List<ItemAddress> writelist = new List<ItemAddress>();
|
|
|
|
|
|
writelist.Add(new ItemAddress("8A0E0001.F"));
|
|
|
|
|
|
errors.Clear();
|
|
|
|
|
|
res = conn.WriteValues(writelist, writevalues, out errors);
|
|
|
|
|
|
Console.WriteLine("res=" + res);
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
#region Test: Absolutadressen lesen
|
|
|
|
|
|
// Daten aus nicht "optimierten" Datenbausteinen lesen
|
|
|
|
|
|
readlist.Clear();
|
|
|
|
|
|
ItemAddress absAdr = new ItemAddress();
|
|
|
|
|
|
absAdr.SetAccessAreaToDatablock(100); // DB 100
|
|
|
|
|
|
absAdr.SymbolCrc = 0;
|
|
|
|
|
|
|
|
|
|
|
|
absAdr.AccessSubArea = Ids.DB_ValueActual;
|
|
|
|
|
|
absAdr.LID.Add(3); // LID_OMS_STB_ClassicBlob
|
|
|
|
|
|
absAdr.LID.Add(0); // Blob Start Offset, Anfangsadresse
|
|
|
|
|
|
absAdr.LID.Add(20); // 20 Bytes
|
|
|
|
|
|
|
|
|
|
|
|
readlist.Add(absAdr);
|
|
|
|
|
|
|
|
|
|
|
|
values.Clear();
|
|
|
|
|
|
errors.Clear();
|
|
|
|
|
|
|
|
|
|
|
|
res = conn.ReadValues(readlist, out values, out errors);
|
|
|
|
|
|
Console.WriteLine(values.ToString());
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#region Test: SPS in Stopp setzen
|
|
|
|
|
|
Console.WriteLine("Setze SPS in STOP...");
|
|
|
|
|
|
conn.SetPlcOperatingState(1);
|
|
|
|
|
|
Console.WriteLine("Taste drücken um wieder in RUN zu setzen...");
|
|
|
|
|
|
Console.ReadKey();
|
|
|
|
|
|
Console.WriteLine("Setze SPS in RUN...");
|
|
|
|
|
|
conn.SetPlcOperatingState(3);
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
conn.Disconnect();
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Console.WriteLine("Main - Connect fehlgeschlagen!");
|
|
|
|
|
|
}
|
|
|
|
|
|
Console.WriteLine("Main - ENDE. Bitte Taste drücken.");
|
|
|
|
|
|
Console.ReadKey();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-24 16:49:05 +08:00
|
|
|
|
public static class S7SymbolCrc32
|
|
|
|
|
|
{
|
|
|
|
|
|
private const uint Polynomial = 0xFA567993; // 多项式(x³² + x³¹ + x³⁰ + x²⁹ + x²⁸ + x²⁶ + x²³ + x²¹ + x¹⁹ + x¹⁸ + x¹⁵ + x¹⁴ + x¹³ + x¹² + x⁹ + x⁸ + x⁴ + x + 1)
|
|
|
|
|
|
private const uint InitialValue = 0xFFFFFFFF;
|
|
|
|
|
|
private static readonly uint[] Table;
|
|
|
|
|
|
|
|
|
|
|
|
static S7SymbolCrc32()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 预计算CRC表
|
|
|
|
|
|
Table = new uint[256];
|
|
|
|
|
|
for (uint i = 0; i < 256; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint crc = i << 24;
|
|
|
|
|
|
for (int j = 0; j < 8; j++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ((crc & 0x80000000) != 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
crc = (crc << 1) ^ Polynomial;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
crc <<= 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
Table[i] = crc;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 计算S7CommPlus符号名的CRC32校验和
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="symbolName">符号名(如"DB1.TempBottom")</param>
|
|
|
|
|
|
/// <param name="dataType">数据类型字节</param>
|
|
|
|
|
|
/// <returns>CRC32校验和</returns>
|
|
|
|
|
|
public static uint CalculateSymbolCrc(string symbolName)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 1. 替换分隔符 '.' → 0x09
|
|
|
|
|
|
string processedName = symbolName.Replace('.', '\t');
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 转换为字节数组
|
|
|
|
|
|
byte[] nameBytes = Encoding.ASCII.GetBytes(processedName);
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 添加数据类型字节
|
|
|
|
|
|
byte[] data = new byte[nameBytes.Length];
|
|
|
|
|
|
Array.Copy(nameBytes, 0, data, 0, nameBytes.Length);
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 计算CRC32
|
|
|
|
|
|
uint crc = InitialValue;
|
|
|
|
|
|
foreach (byte b in data)
|
|
|
|
|
|
{
|
|
|
|
|
|
crc = (crc << 8) ^ Table[((crc >> 24) & 0xFF) ^ b];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 根据作者提示,可能需要再次计算(需要验证)
|
|
|
|
|
|
//crc = CalculateSecondPass(crc);
|
|
|
|
|
|
|
|
|
|
|
|
return crc;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果需要二次计算的方法
|
|
|
|
|
|
public static uint CalculateSecondPass(uint firstResult)
|
|
|
|
|
|
{
|
|
|
|
|
|
byte[] crcBytes = BitConverter.GetBytes(firstResult);
|
|
|
|
|
|
if (BitConverter.IsLittleEndian)
|
|
|
|
|
|
{
|
|
|
|
|
|
Array.Reverse(crcBytes); // 确保大端序
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uint crc = InitialValue;
|
|
|
|
|
|
foreach (byte b in crcBytes)
|
|
|
|
|
|
{
|
|
|
|
|
|
crc = (crc << 8) ^ Table[((crc >> 24) & 0xFF) ^ b];
|
|
|
|
|
|
}
|
|
|
|
|
|
return crc;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-05 15:29:54 +08:00
|
|
|
|
}
|