|
|
|
|
using System;
|
|
|
|
|
using Mesnac.Equips;
|
|
|
|
|
using Mesnac.Equips.BaseInfo;
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
|
|
|
|
|
namespace Mesnac.Equip.Siemens.S7.Default
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 西门子S7-300PLC串口通讯设备类
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class Equip : BaseEquip
|
|
|
|
|
{
|
|
|
|
|
private bool _isOpen = false; //是否打开连接
|
|
|
|
|
private IntPtr equipHandle = new IntPtr(Marshal.SizeOf(typeof(int)));
|
|
|
|
|
|
|
|
|
|
private IntPtr structToIntPtr<T>(T s)
|
|
|
|
|
{
|
|
|
|
|
int nSizeOfT = Marshal.SizeOf(typeof(T));
|
|
|
|
|
int nSizeOfIntPtr = Marshal.SizeOf(typeof(IntPtr));
|
|
|
|
|
IntPtr Result = new IntPtr();
|
|
|
|
|
Result = Marshal.AllocHGlobal(nSizeOfT);
|
|
|
|
|
Marshal.StructureToPtr(s, (IntPtr)((UInt32)Result), true);
|
|
|
|
|
return Result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private T IntPtrTostruct<T>(IntPtr p)
|
|
|
|
|
{
|
|
|
|
|
return (T)Marshal.PtrToStructure(p, typeof(T));
|
|
|
|
|
}
|
|
|
|
|
private int Swap(int a)
|
|
|
|
|
{
|
|
|
|
|
return (a >> 8 & 0xFF) + (a << 8 & 0xFF00);
|
|
|
|
|
}
|
|
|
|
|
public override bool Open()
|
|
|
|
|
{
|
|
|
|
|
lock (this)
|
|
|
|
|
{
|
|
|
|
|
if (this._isOpen == true)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
this.State = false;
|
|
|
|
|
//W95_S7.PLCConnParam adressType = new W95_S7.PLCConnParam();
|
|
|
|
|
byte c1 = 1;
|
|
|
|
|
byte[,] p3 = { { 2, 0, 2, 0 }, { 3, 0, 2, 0 }, { 0, 0, 0, 0 } };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int Result = W95_S7.load_tool(c1, "S7ONLINE", p3);
|
|
|
|
|
if ((Result != 0) && (Result != 39)) // 39已经初始化
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("PLC连接失败:" + this.GetErrInfo(Result));
|
|
|
|
|
this.State = false;
|
|
|
|
|
return this.State;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
this.State = true;
|
|
|
|
|
this._isOpen = true;
|
|
|
|
|
}
|
|
|
|
|
return this.State;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// PLC数据读取方法
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="block">要读取的块号</param>
|
|
|
|
|
/// <param name="start">要读取的起始字</param>
|
|
|
|
|
/// <param name="len">要读取的长度,最大255,超过255则不读取</param>
|
|
|
|
|
/// <param name="buff"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public override bool Read(string block, int start, int len, out object[] buff)
|
|
|
|
|
{
|
|
|
|
|
lock (this)
|
|
|
|
|
{
|
|
|
|
|
buff = new object[len];
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (len > 256)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < len; i++)
|
|
|
|
|
{
|
|
|
|
|
buff[i] = 0;
|
|
|
|
|
}
|
|
|
|
|
base.State = false;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
int maxOneLen = 100; //单次允许读取的最大长度,AB500限制为100个字
|
|
|
|
|
int count = len / maxOneLen; //要读取的次数
|
|
|
|
|
int mod = len % maxOneLen; //剩余的长度
|
|
|
|
|
bool flag = true; //保存读取标志
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
object[] _buff = new object[maxOneLen];
|
|
|
|
|
flag = this.ReadByLen(block, start + i * maxOneLen, maxOneLen, out _buff);
|
|
|
|
|
if (flag == false)
|
|
|
|
|
{
|
|
|
|
|
base.State = flag;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
for (int k = i * maxOneLen; k < (i + 1) * maxOneLen; k++)
|
|
|
|
|
{
|
|
|
|
|
buff[k] = _buff[k - i * maxOneLen];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (mod > 0)
|
|
|
|
|
{
|
|
|
|
|
object[] _buff = new object[mod];
|
|
|
|
|
flag = this.ReadByLen(block, start + count * maxOneLen, mod, out _buff);
|
|
|
|
|
if (flag == false)
|
|
|
|
|
{
|
|
|
|
|
base.State = flag;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
for (int k = count * maxOneLen; k < count * maxOneLen + mod; k++)
|
|
|
|
|
{
|
|
|
|
|
buff[k] = _buff[k - count * maxOneLen];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
base.State = flag;
|
|
|
|
|
return flag;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
//ICSharpCode.Core.LoggingService.Error(String.Format("读取PLC(AB500)设备失败-({0})!", ex.Message));
|
|
|
|
|
base.State = false;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 单次读取最长100个字的方法
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="block">块号</param>
|
|
|
|
|
/// <param name="start">起始字</param>
|
|
|
|
|
/// <param name="len">长度,最长不超过100</param>
|
|
|
|
|
/// <param name="buff">数据缓冲区,存放读取的数据</param>
|
|
|
|
|
/// <returns>读取成功返回true,读取失败返回false</returns>
|
|
|
|
|
private bool ReadByLen(string block, int start, int len, out object[] buff)
|
|
|
|
|
{
|
|
|
|
|
lock (this)
|
|
|
|
|
{
|
|
|
|
|
buff = new object[len];
|
|
|
|
|
if (!this.Open())
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
int state = len;
|
|
|
|
|
ushort[] _buff = new ushort[len];
|
|
|
|
|
int iblock = Convert.ToInt32(block);
|
|
|
|
|
int iResult = W95_S7.db_read(iblock, start, ref state, _buff);
|
|
|
|
|
if (iResult != 0)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("PLC读取失败:" + this.GetErrInfo(iResult));
|
|
|
|
|
this.State = false;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
this.State = true;
|
|
|
|
|
}
|
|
|
|
|
int iReadLen = len;
|
|
|
|
|
if (iReadLen > state)
|
|
|
|
|
{
|
|
|
|
|
iReadLen = state;
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; i < iReadLen; i++)
|
|
|
|
|
{
|
|
|
|
|
int value = _buff[i];
|
|
|
|
|
if (value > ushort.MaxValue)
|
|
|
|
|
{
|
|
|
|
|
value = ushort.MaxValue - value;
|
|
|
|
|
}
|
|
|
|
|
buff[i] = Swap(value);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ushort ToValue(object obj, ushort defaultValue)
|
|
|
|
|
{
|
|
|
|
|
ushort result = 0;
|
|
|
|
|
if (obj != null
|
|
|
|
|
&& obj != DBNull.Value
|
|
|
|
|
&& ushort.TryParse(obj.ToString(), out result))
|
|
|
|
|
{
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
return defaultValue;
|
|
|
|
|
}
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// PLC数据写入方法
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="block">要写入的块号</param>
|
|
|
|
|
/// <param name="start">起始字</param>
|
|
|
|
|
/// <param name="buff">要写入PLC的数据</param>
|
|
|
|
|
/// <returns>写入成功返回true,失败返回false</returns>
|
|
|
|
|
public override bool Write(int block, int start, object[] buff)
|
|
|
|
|
{
|
|
|
|
|
lock (this)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (buff.Length > 256)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
int len = buff.Length;
|
|
|
|
|
int maxOneLen = 100; //单次允许读取的最大长度,AB500限制为100个字
|
|
|
|
|
int count = len / maxOneLen; //要读取的次数
|
|
|
|
|
int mod = len % maxOneLen; //剩余的长度
|
|
|
|
|
bool flag = true; //保存写入标志
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
object[] _buff = new object[maxOneLen];
|
|
|
|
|
for (int k = i * maxOneLen; k < (i + 1) * maxOneLen; k++)
|
|
|
|
|
{
|
|
|
|
|
_buff[k - i * maxOneLen] = buff[k];
|
|
|
|
|
}
|
|
|
|
|
flag = this.WriteMax100(block, start + i * maxOneLen, _buff);
|
|
|
|
|
if (flag == false)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (mod > 0)
|
|
|
|
|
{
|
|
|
|
|
object[] _buff = new object[mod];
|
|
|
|
|
for (int k = count * maxOneLen; k < count * maxOneLen + mod; k++)
|
|
|
|
|
{
|
|
|
|
|
_buff[k - count * maxOneLen] = buff[k];
|
|
|
|
|
}
|
|
|
|
|
flag = this.WriteMax100(block, start + count * maxOneLen, _buff);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
return flag;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
//ICSharpCode.Core.LoggingService.Error(String.Format("写入PLC(AB500)设备失败-({0})!", ex.Message));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 单次写入最多100个字至PLC
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="block">要写入的块号</param>
|
|
|
|
|
/// <param name="start">要写入的起始字</param>
|
|
|
|
|
/// <param name="buff">要写入的数据</param>
|
|
|
|
|
/// <returns>写入成功返回true,失败返回false</returns>
|
|
|
|
|
private bool WriteMax100(int block, int start, object[] buff)
|
|
|
|
|
{
|
|
|
|
|
lock (this)
|
|
|
|
|
{
|
|
|
|
|
if (!this.Open())
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
int state = buff.Length;
|
|
|
|
|
ushort[] _buff = new ushort[buff.Length];
|
|
|
|
|
for (int i = 0; i < buff.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
int value = 0;
|
|
|
|
|
int.TryParse(buff[i].ToString(), out value);
|
|
|
|
|
value = Swap(value);
|
|
|
|
|
_buff[i] = (ushort)value;
|
|
|
|
|
}
|
|
|
|
|
int iblock = Convert.ToInt32(block);
|
|
|
|
|
int iResult = W95_S7.db_write(iblock, start, ref state, _buff);
|
|
|
|
|
if (iResult != 0)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("PLC【西门子S7-300】写入失败:" + this.GetErrInfo(iResult));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public override void Close()
|
|
|
|
|
{
|
|
|
|
|
lock (this)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
int result = W95_S7.unload_tool();
|
|
|
|
|
if (result != 0)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("PLC【西门子S7-300】关闭失败:" + this.GetErrInfo(result));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
this.State = false;
|
|
|
|
|
this._isOpen = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("PLC【西门子S7-300】关闭失败:" + ex.Message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>根据错误代码返回错误信息
|
|
|
|
|
/// 例如int errCode=ActiveConn(1); sring errInfo = GetErrInfo(err);
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="errCode">错误码</param>
|
|
|
|
|
/// <returns>错误信息</returns>
|
|
|
|
|
public string GetErrInfo(int errCode)
|
|
|
|
|
{
|
|
|
|
|
switch (errCode)
|
|
|
|
|
{
|
|
|
|
|
case -1: return "User-Defined Error!"; //自定义错误,主要是参数传递错误!
|
|
|
|
|
case 0x0000: return "Success";
|
|
|
|
|
case 0x0001: return "Load dll failed";
|
|
|
|
|
case 0x00E1: return "User max";
|
|
|
|
|
case 0x00E2: return "SCP entry";
|
|
|
|
|
case 0x00E7: return "SCP board open";
|
|
|
|
|
case 0x00E9: return "No Windows server";
|
|
|
|
|
case 0x00EA: return "Protect";
|
|
|
|
|
case 0x00CA: return "SCP no resources";
|
|
|
|
|
case 0x00CB: return "SCP configuration";
|
|
|
|
|
case 0x00CD: return "SCP illegal";
|
|
|
|
|
case 0x00CE: return "SCP incorrect parameter";
|
|
|
|
|
case 0x00CF: return "SCP open device";
|
|
|
|
|
case 0x00D0: return "SCP board";
|
|
|
|
|
case 0x00D1: return "SCP software";
|
|
|
|
|
case 0x00D2: return "SCP memory";
|
|
|
|
|
case 0x00D7: return "SCP no meas";
|
|
|
|
|
case 0x00D8: return "SCP user mem";
|
|
|
|
|
case 0x00DB: return "SCP timeout";
|
|
|
|
|
case 0x00F0: return "SCP db file does not exist";
|
|
|
|
|
case 0x00F1: return "SCP no global dos memory";
|
|
|
|
|
case 0x00F2: return "SCP send not successful";
|
|
|
|
|
case 0x00F3: return "SCP receive not successful";
|
|
|
|
|
case 0x00F4: return "SCP no device available";
|
|
|
|
|
case 0x00F5: return "SCP illegal subsystem";
|
|
|
|
|
case 0x00F6: return "SCP illegal opcode";
|
|
|
|
|
case 0x00F7: return "SCP buffer too short";
|
|
|
|
|
case 0x00F8: return "SCP buffer1 too short";
|
|
|
|
|
case 0x00F9: return "SCP illegal protocol sequence";
|
|
|
|
|
case 0x00FA: return "SCP illegal PDU arrived";
|
|
|
|
|
case 0x00FB: return "SCP request error";
|
|
|
|
|
case 0x00FC: return "SCP no license";
|
|
|
|
|
case 0x0101: return "Connection is not established / parameterized";
|
|
|
|
|
case 0x010a: return "Negative Acknowledgment received / timeout errors";
|
|
|
|
|
case 0x010c: return "Data not available or locked";
|
|
|
|
|
case 0x012A: return "No system memory left";
|
|
|
|
|
case 0x012E: return "Incorrect parameter";
|
|
|
|
|
case 0x0132: return "No storage space in the DPRAM";
|
|
|
|
|
case 0x0200: return "xx";
|
|
|
|
|
case 0x0201: return "Falsche Schnittstelle angegeben";
|
|
|
|
|
case 0x0202: return "Incorrect interface indicated";
|
|
|
|
|
case 0x0203: return "Toolbox already installed";
|
|
|
|
|
case 0x0204: return "Toolbox with other compounds already installed";
|
|
|
|
|
case 0x0205: return "Toolbox is not installed";
|
|
|
|
|
case 0x0206: return "Handle can not be set";
|
|
|
|
|
case 0x0207: return "Data segment can not be blocked";
|
|
|
|
|
case 0x0209: return "Erroneous data field";
|
|
|
|
|
case 0x0300: return "Timer init error";
|
|
|
|
|
case 0x0301: return "Com init error";
|
|
|
|
|
case 0x0302: return "Module is too small, DW does not exist";
|
|
|
|
|
case 0x0303: return "Block boundary erschritten, number correct";
|
|
|
|
|
case 0x0310: return "Could not find any hardware";
|
|
|
|
|
case 0x0311: return "Hardware defective";
|
|
|
|
|
case 0x0312: return "Incorrect configuration parameters";
|
|
|
|
|
case 0x0313: return "Incorrect baud rate/interrupt vector";
|
|
|
|
|
case 0x0314: return "HSA incorrectly parameterized";
|
|
|
|
|
case 0x0315: return "Address already assigned";
|
|
|
|
|
case 0x0316: return "Device already assigned";
|
|
|
|
|
case 0x0317: return "Interrupt not available";
|
|
|
|
|
case 0x0318: return "Interrupt occupied";
|
|
|
|
|
case 0x0319: return "SAP not occupied";
|
|
|
|
|
case 0x031A: return "Could not find any remote station";
|
|
|
|
|
case 0x031B: return "syni error";
|
|
|
|
|
case 0x031C: return "System error";
|
|
|
|
|
case 0x031D: return "Error in buffer size";
|
|
|
|
|
case 0x0320: return "DLL/VxD not found";
|
|
|
|
|
case 0x0321: return "DLL function error";
|
|
|
|
|
case 0x0330: return "Version conflict";
|
|
|
|
|
case 0x0331: return "Com config error";
|
|
|
|
|
case 0x0332: return "smc timeout";
|
|
|
|
|
case 0x0333: return "Com not configured";
|
|
|
|
|
case 0x0334: return "Com not available";
|
|
|
|
|
case 0x0335: return "Serial drive in use";
|
|
|
|
|
case 0x0336: return "No connection";
|
|
|
|
|
case 0x0337: return "Job rejected";
|
|
|
|
|
case 0x0380: return "Internal error";
|
|
|
|
|
case 0x0381: return "Device not in Registry";
|
|
|
|
|
case 0x0382: return "L2 driver not in Registry";
|
|
|
|
|
case 0x0384: return "L4 driver not in Registry";
|
|
|
|
|
case 0x03FF: return "System error";
|
|
|
|
|
case 0x4001: return "Connection is not known";
|
|
|
|
|
case 0x4002: return "Connection is not established";
|
|
|
|
|
case 0x4003: return "Connection is being established";
|
|
|
|
|
case 0x4004: return "Connection is collapsed";
|
|
|
|
|
case 0x0800: return "Toolbox occupied";
|
|
|
|
|
case 0x8001: return "in this mode is not allowed";
|
|
|
|
|
case 0x8101: return "Hardware error";
|
|
|
|
|
case 0x8103: return "Object Access not allowed";
|
|
|
|
|
case 0x8104: return "Context is not supported";
|
|
|
|
|
case 0x8105: return "ungtige Address";
|
|
|
|
|
case 0x8106: return "Type (data) is not supported";
|
|
|
|
|
case 0x8107: return "Type (data) is not consistent";
|
|
|
|
|
case 0x810A: return "Object does not exist";
|
|
|
|
|
case 0x8301: return "Memory on CPU is not sufficient";
|
|
|
|
|
case 0x8404: return "grave error";
|
|
|
|
|
case 0x8500: return "Incorrect PDU Size";
|
|
|
|
|
case 0x8702: return "Invalid address";
|
|
|
|
|
case 0xA0CE: return "User occupied";
|
|
|
|
|
case 0xA0CF: return "User does not pick up";
|
|
|
|
|
case 0xA0D4: return "Connection not available because modem prevents immediate redial (waiting time before repeat dial not kept to) ";
|
|
|
|
|
case 0xA0D5: return "No dial tone";
|
|
|
|
|
case 0xD201: return "Syntax error module name";
|
|
|
|
|
case 0xD202: return "Syntax error function parameter";
|
|
|
|
|
case 0xD203: return "Syntax error Bausteshortyp";
|
|
|
|
|
case 0xD204: return "no memory module in eingeketteter";
|
|
|
|
|
case 0xD205: return "Object already exists";
|
|
|
|
|
case 0xD206: return "Object already exists";
|
|
|
|
|
case 0xD207: return "Module available in the EPROM";
|
|
|
|
|
case 0xD209: return "Module does not exist";
|
|
|
|
|
case 0xD20E: return "no module present";
|
|
|
|
|
case 0xD210: return "Block number is too big";
|
|
|
|
|
case 0xD241: return "Protection level of function is not sufficient";
|
|
|
|
|
case 0xD406: return "Information not available";
|
|
|
|
|
case 0xEF01: return "Wrong ID2";
|
|
|
|
|
case 0xFFFE: return "unknown error FFFE hex";
|
|
|
|
|
case 0xFFFF: return "Timeout error. Interface KVD";
|
|
|
|
|
default: return "Unkonw error";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|