S7netplus 是一个开源的库,主要用于.NET环境中与西门子S7系列PLC进行通信。这个库允许开发者使用C#或其他.NET支持的编程语言来读写PLC的数据块、输入输出、标记等,非常适合于工业自动化领域的应用开发。
使用S7-PLCSIM Advanced 3.0/4.0/5.0 仿真
如果启动许可证找不到,在服务中启动
S7netplus 是一个开源的 .NET 库,用于与西门子 S7 PLC(可编程逻辑控制器)进行通信。这个库主要用于读取和写入西门子 S7 系列 PLC 的数据块,支持多种数据类型的读写,如布尔型、整型、实数型等。S7netplus 通过以太网连接实现与 PLC 的通信,使用了西门子的 S7 通信协议。
S7-1500 数据类型 | S7netplus 数据类型 | C# 数据类型 | 描述 |
---|---|---|---|
BOOL | Bit | bool | 布尔值,占用 1 位 |
BYTE | Byte | byte | 8位无符号整数 |
WORD | Word | ushort | 16位无符号整数 |
DWORD | DWord | uint | 32位无符号整数 |
INT | Int | short | 16位有符号整数 |
DINT | DInt | int | 32位有符号整数 |
REAL | Real | float | 32位浮点数 |
SINT | SInt | sbyte | 8位有符号整数 |
USINT | USInt | byte | 8位无符号整数 |
UINT | UInt | ushort | 16位无符号整数 |
UDINT | UDInt | uint | 32位无符号整数 |
LINT | LInt | long | 64位有符号整数 |
ULINT | ULInt | ulong | 64位无符号整数 |
LREAL | LReal | double | 64位浮点数 |
STRING | String | string | 字符串类型 |
TIME | Time | TimeSpan | 时间,通常以毫秒为单位 |
S5TIME | S5Time | TimeSpan | S5 时间格式 |
DATE | Date | DateTime | 日期 |
TIME_OF_DAY | TimeOfDay | DateTime | 一天中的时间 |
DATE_AND_TIME | DateTime | DateTime | 日期和时间 |
C#Plc plc;
private async void btnConnect_Click(object sender, EventArgs e)
{
plc = new Plc(CpuType.S71500, "192.168.0.10", 0, 1);
try
{
await plc.OpenAsync();
stsMain_lblStatus.Text = "连接成功";
}
catch
{
stsMain_lblStatus.Text = "连接出错";
}
}
C#using S7.Net.Types;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace s7modbus01
{
//
// Summary:
// Conversion methods to convert from Siemens numeric format to C# and back
public static class Conversion
{
//
// Summary:
// Converts a binary string to Int32 value
//
// Parameters:
// txt:
public static int BinStringToInt32(this string txt)
{
int num = 0;
for (int i = 0; i < txt.Length; i++)
{
num = (num << 1) | ((txt[i] == '1') ? 1 : 0);
}
return num;
}
//
// Summary:
// Converts a binary string to a byte. Can return null.
//
// Parameters:
// txt:
public static byte? BinStringToByte(this string txt)
{
if (txt.Length == 8)
{
return (byte)txt.BinStringToInt32();
}
return null;
}
//
// Summary:
// Converts the value to a binary string
//
// Parameters:
// value:
public static string ValToBinString(this object value)
{
int num = 0;
int num2 = 0;
int num3 = 0;
string text = "";
long num4 = 0L;
try
{
if (value.GetType().Name.IndexOf("[]") < 0)
{
switch (value.GetType().Name)
{
case "Byte":
num3 = 7;
num4 = (byte)value;
break;
case "Int16":
num3 = 15;
num4 = (short)value;
break;
case "Int32":
num3 = 31;
num4 = (int)value;
break;
case "Int64":
num3 = 63;
num4 = (long)value;
break;
default:
throw new Exception();
}
for (num = num3; num >= 0; num += -1)
{
text = (((num4 & (long)Math.Pow(2.0, num)) <= 0) ? (text + "0") : (text + "1"));
}
}
else
{
switch (value.GetType().Name)
{
case "Byte[]":
{
num3 = 7;
byte[] array4 = (byte[])value;
for (num2 = 0; num2 <= array4.Length - 1; num2++)
{
for (num = num3; num >= 0; num += -1)
{
text = (((array4[num2] & (byte)Math.Pow(2.0, num)) <= 0) ? (text + "0") : (text + "1"));
}
}
break;
}
case "Int16[]":
{
num3 = 15;
short[] array2 = (short[])value;
for (num2 = 0; num2 <= array2.Length - 1; num2++)
{
for (num = num3; num >= 0; num += -1)
{
text = (((array2[num2] & (byte)Math.Pow(2.0, num)) <= 0) ? (text + "0") : (text + "1"));
}
}
break;
}
case "Int32[]":
{
num3 = 31;
int[] array3 = (int[])value;
for (num2 = 0; num2 <= array3.Length - 1; num2++)
{
for (num = num3; num >= 0; num += -1)
{
text = (((array3[num2] & (byte)Math.Pow(2.0, num)) <= 0) ? (text + "0") : (text + "1"));
}
}
break;
}
case "Int64[]":
{
num3 = 63;
byte[] array = (byte[])value;
for (num2 = 0; num2 <= array.Length - 1; num2++)
{
for (num = num3; num >= 0; num += -1)
{
text = (((array[num2] & (byte)Math.Pow(2.0, num)) <= 0) ? (text + "0") : (text + "1"));
}
}
break;
}
default:
throw new Exception();
}
}
return text;
}
catch
{
return "";
}
}
//
// Summary:
// Helper to get a bit value given a byte and the bit index. Example: DB1.DBX0.5
// -> var bytes = ReadBytes(DB1.DBW0); bool bit = bytes[0].SelectBit(5);
//
// Parameters:
// data:
//
// bitPosition:
public static bool SelectBit(this byte data, int bitPosition)
{
int num = 1 << bitPosition;
return (data & num) != 0;
}
//
// Summary:
// Converts from ushort value to short value; it's used to retrieve negative values
// from words
//
// Parameters:
// input:
public static short ConvertToShort(this ushort input)
{
return short.Parse(input.ToString("X"), NumberStyles.HexNumber);
}
//
// Summary:
// Converts from short value to ushort value; it's used to pass negative values
// to DWs
//
// Parameters:
// input:
public static ushort ConvertToUshort(this short input)
{
return ushort.Parse(input.ToString("X"), NumberStyles.HexNumber);
}
//
// Summary:
// Converts from UInt32 value to Int32 value; it's used to retrieve negative values
// from DBDs
//
// Parameters:
// input:
public static int ConvertToInt(this uint input)
{
return int.Parse(input.ToString("X"), NumberStyles.HexNumber);
}
//
// Summary:
// Converts from Int32 value to UInt32 value; it's used to pass negative values
// to DBDs
//
// Parameters:
// input:
public static uint ConvertToUInt(this int input)
{
return uint.Parse(input.ToString("X"), NumberStyles.HexNumber);
}
//
// Summary:
// Converts from float to DWord (DBD)
//
// Parameters:
// input:
public static uint ConvertToUInt(this float input)
{
return DWord.FromByteArray(Real.ToByteArray(input));
}
//
// Summary:
// Converts from DWord (DBD) to float
//
// Parameters:
// input:
public static float ConvertToFloat(this uint input)
{
return Real.FromByteArray(DWord.ToByteArray(input));
}
/// <summary>
/// 转换日期
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
public static System.DateTime ConvertToDateTime(this byte[] bytes)
{
if (bytes.Length != 8)
throw new ArgumentException("Invalid length of bytes for DATE_AND_TIME.");
// 第0字节表示从1990年开始的年份偏移
int year =2000+ BcdToDecimal(bytes[0]);
int month = BcdToDecimal(bytes[1]);
int day = BcdToDecimal(bytes[2]);
int hour = BcdToDecimal(bytes[3]);
int minute = BcdToDecimal(bytes[4]);
int second = BcdToDecimal(bytes[5]);
// 字节6和7合并表示毫秒
int millisecond = (BcdToDecimal(bytes[6])*10 + BcdToDecimal(bytes[7])/10);
return new System.DateTime(year, month, day, hour, minute, second, millisecond);
}
private static int BcdToDecimal( byte bcd)
{
return ((bcd >> 4) * 10) + bcd % 16;
}
//将前两位计算出来拼接
public static byte[] SToByteArray(this string value)
{
byte[] data = System.Text.Encoding.Default.GetBytes(value);
byte[] head = new byte[2];
head[0] = Convert.ToByte(254);
head[1] = Convert.ToByte(value.Length);
data = head.Concat(data).ToArray();
return data;
}
//将前两位计算出来拼接
public static byte[] WSToByteArray(this string value)
{
byte[] data = System.Text.Encoding.BigEndianUnicode.GetBytes(value);
byte[] head = BitConverter.GetBytes((short)508);
byte[] length = BitConverter.GetBytes((short)value.Length);
Array.Reverse(head);
Array.Reverse(length);
head = head.Concat(length).ToArray();
data = head.Concat(data).ToArray();
byte[] d = new byte[512];
Array.Copy(data, d, data.Length);
return d;
}
}
}
C#private async void btnRead_Click(object sender, EventArgs e)
{
// 从数据块1的位0读取布尔值
var b1 = await plc.ReadAsync("DB1.DBX0.0");
// 从数据块1的字2读取无符号短整型(16位)
var us1 = await plc.ReadAsync("DB1.DBW2.0");
// 从数据块1的双字4读取无符号整型(32位)
var u1 = await plc.ReadAsync("DB1.DBD4.0");
// 从数据块1的双字8读取浮点数,并转换为float类型
var r1 = ((uint)plc.Read("DB1.DBD8.0")).ConvertToFloat();
// 从数据块1的双字12读取无符号整型(32位)
var t1 = await plc.ReadAsync("DB1.DBD12.0");
// 异步从数据块1的字节16开始读取254个字节
var s1 = await plc.ReadBytesAsync(DataType.DataBlock, 1, 16, 254);
// 异步从数据块1的字节272开始读取508个字节
var s2 = await plc.ReadBytesAsync(DataType.DataBlock, 1, 272, 508);
// 从数据块1的字节784开始同步读取8个字节(用于日期和时间)
var d1 = plc.ReadBytes(DataType.DataBlock, 1, 784, 8);
// 将读取的字节转换为DateTime对象
var dateTime = d1.ConvertToDateTime();
// 将读取的值显示在界面上
txt1.Text = b1.ToString(); // 显示布尔值
txt2.Text = us1.ToString(); // 显示无符号短整型
txt3.Text = u1.ToString(); // 显示无符号整型
txt4.Text = r1.ToString(); // 显示浮点数
txt5.Text = t1.ToString(); // 显示无符号整型
txt6.Text = System.Text.Encoding.Default.GetString(s1.Skip(2).ToArray()); // 显示字符串(跳过前两个字节)
txt7.Text = System.Text.Encoding.BigEndianUnicode.GetString(s2.Skip(4).ToArray()); // 显示字符串(跳过前四个字节并使用大端编码)
txt8.Text = dateTime.ToString(); // 显示日期时间
}
在S7-1500中,一个String类型的变量占用256个字节,但是第一个字节是总字符数,第二个字节是当前字符数,所以真正的字符数据是从第三个字节开始的,共254个字节。
同理,WString类型其实就是双字节的Sring,也就是说一个字符占用两个字节,所以一个WString类型的变量占用512个字节,第一、二个字节是总字符数,第三、四个字节是当前字符数,真正的字符数据是从第五个字节开始的,共508个字节。
C#private async void btnWrite_Click(object sender, EventArgs e)
{
await plc.WriteAsync(DataType.DataBlock, 1, 0, txt1.Text == "true" ? true : false);
await plc.WriteAsync(DataType.DataBlock, 1, 2, ushort.Parse(txt2.Text));
await plc.WriteAsync(DataType.DataBlock, 1, 4, uint.Parse(txt3.Text));
await plc.WriteAsync(DataType.DataBlock, 1, 8, float.Parse(txt4.Text));
await plc.WriteAsync(DataType.DataBlock, 1, 12, uint.Parse(txt5.Text));
await plc.WriteAsync(DataType.DataBlock, 1, 16, txt6.Text.SToByteArray());
await plc.WriteAsync(DataType.DataBlock, 1, 272, txt7.Text.WSToByteArray());
await plc.WriteAsync(DataType.DataBlock, 1, 784, System.DateTime.Parse(txt8.Text));
}
C#private async void btnBatchRead_Click(object sender, EventArgs e)
{
List<DataItem> datas = new List<DataItem>();
datas.Add(new DataItem
{
DataType = DataType.DataBlock,
VarType = VarType.Bit,
DB = 1,
BitAdr = 0,
Count = 1,
StartByteAdr = 0,
Value = new object()
});
datas.Add(new DataItem
{
DataType = DataType.DataBlock,
VarType = VarType.Int,
DB = 1,
BitAdr = 0,
Count = 1,
StartByteAdr = 2,
Value = new object()
});
datas.Add(new DataItem
{
DataType = DataType.DataBlock,
VarType = VarType.DInt,
DB = 1,
BitAdr = 0,
Count = 1,
StartByteAdr = 4,
Value = new object()
});
datas.Add(new DataItem
{
DataType = DataType.DataBlock,
VarType = VarType.Real,
DB = 1,
BitAdr = 0,
Count = 1,
StartByteAdr = 8,
Value = new object()
});
//time对应双字
datas.Add(new DataItem
{
DataType = DataType.DataBlock,
VarType = VarType.DWord,
DB = 1,
BitAdr = 0,
Count = 1,
StartByteAdr = 12,
Value = new object()
});
datas.Add(new DataItem
{
DataType = DataType.DataBlock,
VarType = VarType.S7String,
DB = 1,
BitAdr = 0,
Count = 254,
StartByteAdr = 16,
Value = new object()
});
datas.Add(new DataItem
{
DataType = DataType.DataBlock,
VarType = VarType.S7WString,
DB = 1,
BitAdr = 0,
Count = 254,
StartByteAdr = 272,
Value = new object()
});
datas.Add(new DataItem
{
DataType = DataType.DataBlock,
VarType = VarType.DateTime,
DB = 1,
BitAdr = 0,
Count = 1,
StartByteAdr = 784,
Value = new object()
});
await plc.ReadMultipleVarsAsync(datas);
txt1.Text = datas[0].Value.ToString();
txt2.Text = datas[1].Value.ToString();
txt3.Text = datas[2].Value.ToString();
txt4.Text = datas[3].Value.ToString();
txt5.Text = datas[4].Value.ToString();
txt6.Text = datas[5].Value.ToString();
txt7.Text = datas[6].Value.ToString();
txt8.Text = ((System.DateTime)datas[7].Value).ToString("yyyy-MM-dd HH:mm:ss");
}
C#private async void btnBatchWrite_Click(object sender, EventArgs e)
{
List<DataItem> datas = new List<DataItem>();
datas.Add(new DataItem
{
DataType = DataType.DataBlock,
VarType = VarType.Bit,
DB = 1,
BitAdr = 0,
Count = 1,
StartByteAdr = 0,
Value = txt1.Text.ToLower() == "true" ? true : false
});
datas.Add(new DataItem
{
DataType = DataType.DataBlock,
VarType = VarType.Int,
DB = 1,
BitAdr = 0,
Count = 1,
StartByteAdr = 2,
Value = ushort.Parse(txt2.Text)
});
datas.Add(new DataItem
{
DataType = DataType.DataBlock,
VarType = VarType.DInt,
DB = 1,
BitAdr = 0,
Count = 1,
StartByteAdr = 4,
Value = int.Parse(txt3.Text)
});
datas.Add(new DataItem
{
DataType = DataType.DataBlock,
VarType = VarType.Real,
DB = 1,
BitAdr = 0,
Count = 1,
StartByteAdr = 8,
Value = float.Parse(txt4.Text)
});
//time对应双字
datas.Add(new DataItem
{
DataType = DataType.DataBlock,
VarType = VarType.DWord,
DB = 1,
BitAdr = 0,
Count = 1,
StartByteAdr = 12,
Value = int.Parse(txt5.Text)
});
datas.Add(new DataItem
{
DataType = DataType.DataBlock,
VarType = VarType.S7String,
DB = 1,
BitAdr = 0,
Count = 254,
StartByteAdr = 16,
Value = txt6.Text
});
datas.Add(new DataItem
{
DataType = DataType.DataBlock,
VarType = VarType.S7WString,
DB = 1,
BitAdr = 0,
Count = 254,
StartByteAdr = 272,
Value = txt7.Text
});
datas.Add(new DataItem
{
DataType = DataType.DataBlock,
VarType = VarType.DateTime,
DB = 1,
BitAdr = 0,
Count = 1,
StartByteAdr = 784,
Value = System.DateTime.Parse(txt8.Text)
});
await plc.WriteAsync(datas.ToArray());
}
C#public class CData
{
public bool b1 { get; set; }
public ushort us1 { get; set; }
public uint u1 { get; set; }
public float r1 { get; set; }
public int t1 { get; set; }
[S7StringAttribute(S7StringType.S7String, 254)]
public string s1 { get; set; }
[S7StringAttribute(S7StringType.S7WString, 254)]
public string s2 { get; set; }
public byte[] d1 { get; set; } = new byte[8]; // 存储原始字节数据
}
C#private void btnReadClass_Click(object sender, EventArgs e)
{
CData cData= new CData();
plc.ReadClass(cData, 1, 0);
txt1.Text = cData.b1.ToString();
txt2.Text = cData.us1.ToString();
txt3.Text = cData.u1.ToString();
txt4.Text = cData.r1.ToString();
txt5.Text = cData.t1.ToString();
txt6.Text = cData.s1.ToString();
txt7.Text = cData.s2.ToString();
txt8.Text = cData.d1.ConvertToDateTime().ToString("yyyy-MM-dd HH:mm:ss");
}
C#private void btnReadIMQ_Click(object sender, EventArgs e)
{
var db1 = (bool)plc.Read("I0.0");
var db2 = (bool)plc.Read("Q0.1");
var db3 = (ushort)plc.Read("MW1");
var db4 = plc.Read("M0.2");
}
private void btnWriteIMQ_Click(object sender, EventArgs e)
{
plc.Write("I0.0", true);
plc.Write("Q0.0", true);
plc.Write("MW1",(ushort)2);
plc.Write("M0.2", true);
}
C#private void btnConRead_Click(object sender, EventArgs e)
{
// 从数据块1的偏移量0开始读取792字节的数据(784字节数据+8字节日期时间)
byte[] bs = plc.ReadBytes(DataType.DataBlock, 1, 0, 784+8);
// 读取并转换布尔值,位于第一个字节的第0位
var b1 = S7.Net.Types.Boolean.GetValue(bs.Take(1).ToArray()[0], 0);
// 跳过1个字节(通常是填充或未使用的字节),然后读取2字节并转换为无符号短整型
var us1 = S7.Net.Types.Word.FromByteArray(bs.Skip(2).Take(2).ToArray());
// 跳过4字节,读取4字节并转换为无符号整型
var u1 = S7.Net.Types.DWord.FromByteArray(bs.Skip(4).Take(4).ToArray());
// 跳过8字节,读取4字节并转换为浮点数
var r1 = S7.Net.Types.Real.FromByteArray(bs.Skip(8).Take(4).ToArray());
// 跳过12字节,读取4字节并转换为整型
var t1 = S7.Net.Types.DInt.FromByteArray(bs.Skip(12).Take(4).ToArray());
// 跳过16字节,读取256字节并转换为标准字符串
var s1 = S7.Net.Types.S7String.FromByteArray(bs.Skip(16).Take(256).ToArray());
// 跳过272字节,读取512字节并转换为宽字符字符串
var s2 = S7.Net.Types.S7WString.FromByteArray(bs.Skip(272).Take(512).ToArray());
// 跳过784字节,读取接下来的8字节数据作为日期时间
var d = bs.Skip(784).Take(8).ToArray();
var d1 = S7.Net.Types.DateTime.FromByteArray(d);
// 将读取的数据显示在界面上
txt1.Text = b1.ToString();
txt2.Text = us1.ToString();
txt3.Text = u1.ToString();
txt4.Text = r1.ToString();
txt5.Text = t1.ToString();
txt6.Text = s1.ToString();
txt7.Text = s2.ToString();
txt8.Text = d1.ToString("yyyy-MM-dd HH:mm:ss");
}
S7.Net
类型方法(如 Word
, DWord
, Real
等)确保数据按照正确的格式转换。本文作者:rick
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!